boost.asio. The possibility to expect the completion of any object in queue is necessary - boost

In asio:: io_service I insert objects. asio:: io_service::run() runs in several threads.
The possibility to expect the completion of any object in queue is necessary.
For example:
template <typename T>
struct handler {
void operator()() {
....
}
T get() const {...}
};
asio::io_service ios;
ios.post(handler());
How I can refer to the object in queue?
How can I pause the main program loop, untill handler::operator() is executed?
Thanks.

Here's what I know so far:
1. Several handlers are executing on several threads.
2. Handlers run independently of each other. There is no synchronization needed between threads for data/race conditions.
3. Get can be called on anyone handler. When called the handler should stop calculating and let another thread call handler::get().
This really seems more like a multi-threading / concurrency question then a boost::asio question. At the moment I do not see a need to use an io_service. It seems like several threads could just be started without an io_service and some synchronization could be used between the threads.
The thread calling Handler::get() needs to wait until the io_service thread running operator() completes its calculation before it can return.
Consider using a condition variable. The handler::get() method can wait until the condition is met (i.e. operator() finishes its calculation). The io_service thread that runs operator() would notify the main thread through the condition variable.
An example of one thread notifying another thread via a condition variable is here.

Related

Understand the usage of strand without locking

Reference:
websocket_client_async_ssl.cpp
strands
Question 1> Here is my understanding:
Given a few async operations bound with the same strand, the strand
will guarantee that all associated async operations will be executed
as a strictly sequential invocation.
Does this mean that all above async operations will be executed by a same thread?
Or it just says that at any time, only one asyn operation will be executed by any available thread?
Question 2> The boost::asio::make_strand function creates a strand object for an executor or execution context.
session(net::io_context& ioc, ssl::context& ctx)
: resolver_(net::make_strand(ioc))
, ws_(net::make_strand(ioc), ctx)
Here, resolver_ and ws_ have its own strand, but I have problems to understand how each strand applies to what asyn operations.
For example, in the following aysnc and handler, which functions(i.e aysnc or handler) are bound to the same strand and will not run simultaneously.
run
=>resolver_.async_resolve -->session::on_resolve
=>beast::get_lowest_layer(ws_).async_connect -->session::on_connect
=>ws_.next_layer().async_handshake --> session::on_ssl_handshake
=>ws_.async_handshake --> session::on_handshake
async ================================= handler
Question 3> How can we retrieve the strand from executor?
Is there any difference between these two?
get_associated_executor
get_executor
io_context::get_executor: Obtains the executor associated with the
io_context.
get_associated_executor: Helper function to obtain an object's
associated executor.
Question 4> Is it correct that I use the following method to bind deadline_timer to io_context to prevent race condition?
All other parts of the code is same as the example of websocket_client_async_ssl.cpp.
session(net::io_context& ioc, ssl::context& ctx)
: resolver_(net::make_strand(ioc))
, ws_(net::make_strand(ioc), ctx),
d_timer_(ws_.get_executor())
{ }
void on_heartbeat_write( beast::error_code ec, std::size_t bytes_transferred)
{
d_timer_.expires_from_now(boost::posix_time::seconds(5));
d_timer_.async_wait(beast::bind_front_handler( &session::on_heartbeat, shared_from_this()));
}
void on_heartbeat(const boost::system::error_code& ec)
{
ws_.async_write( net::buffer(text_ping_), beast::bind_front_handler( &session::on_heartbeat_write, shared_from_this()));
}
void on_handshake(beast::error_code ec)
{
d_timer_.expires_from_now(boost::posix_time::seconds(5));
d_timer_.async_wait(beast::bind_front_handler( &session::on_heartbeat, shared_from_this()));
ws_.async_write(net::buffer(text_), beast::bind_front_handler(&session::on_write, shared_from_this()));
}
Note:
I used d_timer_(ws_.get_executor()) to init deadline_timer and hoped that it will make sure they don't write or read the websocket at the same time.
Is this the right way to do it?
Question 1
Does this mean that all above async operations will be executed by a same thread? Or it just says that at any time, only one async operation will be executed by any available thread?
The latter.
Question 2
Here, resolver_ and ws_ have its own strand,
Let me interject that I think that's unnecessarily confusing in the example. They could (should, conceptually) have used the same strand, but I guess they didn't want to go through the trouble of storing a strand. I'd probably have written:
explicit session(net::io_context& ioc, ssl::context& ctx)
: resolver_(net::make_strand(ioc))
, ws_(resolver_.get_executor(), ctx) {}
The initiation functions are called where you decide. The completion handlers are dispatch-ed on the executor that belongs to the IO object that you call the operation on, unless the completion handler is bound to a different executor (e.g. using bind_executor, see get_associated_exectutor). In by far the most circumstances in modern Asio, you will not bind handlers, instead "binding IO objects" to the proper executors. This makes it less typing, and much harder to forget.
So in effect, all the async-initiations in the chain except for the one in run() are all on a strand, because the IO objects are tied to strand executors.
You have to keep in mind to dispatch on a strand when some outside user calls into your classes (e.g. often to stop). It is a good idea therefore to develop a convention. I'd personally make all the "unsafe" methods and members private:, so I will often have pairs like:
public:
void stop() {
dispatch(strand_, [self=shared_from_this()] { self->do_stop(); });
}
private:
void do_stop() {
beast::get_lowest_layer(ws_).cancel();
}
Side Note:
In this particular example, there is only one (main) thread running/polling the io service, so the whole point is moot. But as I explained recently (Does mulithreaded http processing with boost asio require strands?), the examples are here to show some common patterns that allow one to do "real life" work as well
Bonus: Handler Tracking
Let's use BOOST_ASIO_ENABLE_HANDLER_TRACKING to get some insight.¹ Running a sample session shows something like
If you squint a little, you can see that all the strand executors are the same:
0*1|resolver#0x559785a03b68.async_resolve
1*2|strand_executor#0x559785a02c50.execute
2*3|socket#0x559785a05770.async_connect
3*4|strand_executor#0x559785a02c50.execute
4*5|socket#0x559785a05770.async_send
5*6|strand_executor#0x559785a02c50.execute
6*7|socket#0x559785a05770.async_receive
7*8|strand_executor#0x559785a02c50.execute
8*9|socket#0x559785a05770.async_send
9*10|strand_executor#0x559785a02c50.execute
10*11|socket#0x559785a05770.async_receive
11*12|strand_executor#0x559785a02c50.execute
12*13|deadline_timer#0x559785a05958.async_wait
12*14|socket#0x559785a05770.async_send
14*15|strand_executor#0x559785a02c50.execute
15*16|socket#0x559785a05770.async_receive
16*17|strand_executor#0x559785a02c50.execute
17*18|socket#0x559785a05770.async_send
13*19|strand_executor#0x559785a02c50.execute
18*20|strand_executor#0x559785a02c50.execute
20*21|socket#0x559785a05770.async_receive
21*22|strand_executor#0x559785a02c50.execute
22*23|deadline_timer#0x559785a05958.async_wait
22*24|socket#0x559785a05770.async_send
24*25|strand_executor#0x559785a02c50.execute
25*26|socket#0x559785a05770.async_receive
26*27|strand_executor#0x559785a02c50.execute
23*28|strand_executor#0x559785a02c50.execute
Question 3
How can we retrieve the strand from executor?
You don't[*]. However make_strand(s) returns an equivalent strand if s is already a strand.
[*] By default, Asio's IO objects use the type-erased executor (asio::executor or asio::any_io_executor depending on version). So technically you could ask it about its target_type() and, after comparing the type id to some expected types use something like target<net::strand<net::io_context::executor_type>>() to access the original, but there's really no use. You don't want to be inspecting the implementation details. Just honour the handlers (by dispatching them on their associated executors like Asio does).
Is there any difference between these two? get_associated_executor get_executor
get_executor gets an owned executor from an IO object. It is a member function.
asio::get_associated_executor gets associated executors from handler objects. You will observe that get_associated_executor(ws_) doesn't compile (although some IO objects may satisfy the criteria to allow it to work).
Question 4
Is it correct that I use the following method to bind deadline_timer to io_context
You will notice that you did the same as I already mentioned above to tie the timer IO object to the same strand executor. So, kudos.
to prevent race condition?
You don't prevent race conditions here. You prevent data races. That is because in on_heartbeat you access the ws_ object which is an instance of a class that is NOT threadsafe. In effect, you're sharing access to non-threadsafe resources, and you need to serialize access, hence you want to be on the strand that all other accesses are also on.
Note: [...] and hoped that it will make sure they don't write or read the websocket at the same time. Is this the right way to do it?
Yes this is a good start, but it is not enough.
Firstly, you can write or read at the same time, as long as
write operations don't overlap
read operations don't overlap
accesses to the IO object are safely serialized.
In particular, your on_heartbeat might be safely serialized so you don't have a data race on calling the async_write initiation function. However, you need more checks to know whether a write operation is already (still) in progress. One way to achieve that is to have a queue with outgoing messages. If you have strict heartbeat requirements and high load, you might need a priority-queue here.
¹ I simplified the example by replacing the stream type with the Asio native ssl::stream<tcp::socket>. This means we don't get all the internal timers that deal with tcp_stream expirations. See https://pastebin.ubuntu.com/p/sPRYh6Xbwz/

Keep io_service alive

My prefered way to use an io_service is to create a thread on application start that executes the io_service's run method. The problem is, if there is no task for io_service, its run method returns immediately and the thread terminates.
As you can see from the chat_client.cpp
...
boost::asio::io_service io_service;
...
chat_client c(io_service, iterator);
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
the thread is started after some async task is added to io_service. This is done in the chat_clients's constructor.
My question is: Is there a way to create the thread before some task is added to io_service, i. e.
// create io_service and thread on application start
boost::asio::io_service io_service;
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
// add some task to io_service
chat_client c(io_service, iterator);
As you said, its run method returns immediately because it has no work.
You need to use boost::asio::io_service::work . Think of it as a dummy work item, so io_service::run never runs out of work, and thus doesn't return immediately.
Example:
auto work = boost::make_shared<boost::asio::io_service::work>(m_ioservice);
m_ioservice.run();
Some people prefer an io_service::run to always block, waiting for work. Other people prefer the behavior Asio gives you by default.

When do handlers for cancelled boost::asio handlers get to run?

The boost docs say that cancelled async connect, send and receive finish immediately, and the handlers for cancelled operations will be passed the boost::asio::error::operation_aborted error.
I would like to find out if the cancelled handler gets to run (and see the operation_aborted error) before other (non-cancelled, and newly scheduled) completion handlers run.
Here is the timeline that concerns me:
acceptHandler and readHandler are running on the same event loop and the same thread.
time t0 - readHandler is running on oldConnectionSocket
time t1 - acceptHandler runs
time t2 - acceptHandler calls oldConnectionSocket.cancel
time t3 - acceptHandler closes oldConnectionSocket
time t4 - acceptHandler calls newConnectionSocket.async_read(...readHandler...)
time t5 - readHandler is called (from which context?)
Is it possible at t5 for readHandler to be called in the newConnectionSocket context before it is called with the operation_aborted error in the oldConnectionSocket context?
Cancelled operations will immediately post their handlers for deferred invocation. However, the io_service makes no guarantees on the invocation order of handlers. Thus, the io_service could choose to invoke the ReadHandlers in either order. Currently, only a strand specifies guaranteed ordering under certain conditions.
Within a completion handler, if the goal is to know which I/O object was associated with the operation, then consider constructing the completion handler so that it has an explicit handle to the I/O object. This is often accomplished using any of the following:
a custom functor
std::bind() or boost::bind()
a C++11 lambda
One common idiom is to have the I/O object be managed by a single class that inherits from boost::enable_shared_from_this<>. When a class inherits from boost::enable_shared_from_this, it provides a shared_from_this() member function that returns a valid shared_ptr instance to this. A copy of the shared_ptr is passed to completion handlers, such as a capture-list in lambdas or passed as the instance handle to boost::bind(). This allows for the handlers to know the I/O object on which the operation was performed, and causes the lifetime of the I/O object to be extended to at least as long as the handler. See the Boost.Asio asynchronous TCP daytime server tutorial for an example using this approach.
class tcp_connection
: public boost::enable_shared_from_this<tcp_connection>
{
public:
// ...
void start()
{
boost::asio::async_write(socket_, ...,
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_write(
const boost::system::error_code& error,
std::size_t bytes_transferred)
{
// I/O object is this->socket_.
}
tcp::socket socket_;
};
On the other hand, if the goal is to determine if one handler has executed before the other, then:
the application will need to explicitly manage the state
trying to manage multiple dependent call chains may be introducing unnecessary complexity and often indicates a need to reexamine the design
custom handlers could be used to prioritize the order in which handlers are executed. The Boost.Asio Invocation example uses custom handlers that are added to a priority queue, which are then executed at a later point in time.

OSX Objective C BOOL/vars thread safe?

Simple Question... is a global BOOL thread safe for me to use for thread synchronization?
What other data types are actually safe, e.g. long longs..?
Eg:
I have a task that runs - only want it to run once concurrently.
<pre>
BOOL isRunning;
unsigned long long progress;
if(!isRunning){
dispatch_async(secondaryTask,^{
[self doWork];
});
-(void)doWork
{
isRunning=TRUE;
do a long op
isRunning=FALSE;
}
</pre>
For the atomic types, exactly the same rules as ordinary C apply. So there's no guarantee of thread safety on any of them.
Use OSAtomic, NSConditionLock, the NSLocking protocol, serial dispatch queues, individual runloops, memory fences, spin locks, etc, to achieve thread safety.
For the trivial code given, which I accept is probably just for exposition, you'd most likely provide a completion handler block, which the asynchronous block would dispatch upon completion. If it's a serial queue, just push the task to it. Consider a dispatch group if you want synchronisation points within concurrent task groups.

Make parent thread wait till child thread finishes in VC

According to MSDN:
The WaitForSingleObject function can wait for the following objects:
Change notification
Console input
Event
Memory resource notification
Mutex
Process
Semaphore
Thread
Waitable timer
Then we can use WaitForSingleObject to make the parent-thread wait for child ones.
int main()
{
HANDLE h_child_thread = CreateThread(0,0, child, 0,0,0); //create a thread in VC
WaitForSingleObject(h_child_thread, INFINITE); //So, parent-thread will wait
return 0;
}
Question
Is there any other way to make parent-thread wait for child ones in VC or Windows?
I don't quite understand the usage of WaitForSingleObject here, does it mean that the thread's handle will be available when the thread terminates?
You can establish communication between threads in multiple ways and the terminating thread may somehow signal its waiting thread. It could be as simple as writing some special value to a shared memory location that the waiting thread can check. But this won't guarantee that the terminating thread has terminated when the waiting thread sees the special value (ordering/race conditions) or that the terminating thread terminates shortly after that (it can just hang or block on something) and it won't guarantee that the special value gets ever set before the terminating thread actually terminates (the thread can crash). WaitForSingleObject (and its companion WaitForMultipleObjects) is a sure way to know of a thread termination when it occurs. Just use it.
The handle will still be available in the sense that its value won't be gone. But it is practically useless after the thread has terminated, except you need this handle to get the thread exit code. And you still need to close the handle in the end. That is unless you're OK with handle/memory leaks.
for the first queation - yes. The method commonly used here is "Join". the usage is language dependant.
In .NET C++ you can use the Thread's Join method. this is from the msdn:
Thread* newThread = new Thread(new ThreadStart(0, Test::Work));
newThread->Start();
if(newThread->Join(waitTime + waitTime))
{
Console::WriteLine(S"New thread terminated.");
}
else
{
Console::WriteLine(S"Join timed out.");
}
Secondly, the thread is terminated when when you are signaled with "WaitForSingleObject" but the handle is still valid (for a terminated thread). So you still need to explicitly close the handle with CloseHandle.

Resources