What is the difference between post and dispatch in boost::asio? - c++

I am trying to use boost:asio library to create a threadpool. The official documentation says :
dispatch : Request the io_service to invoke the given handler.
post: Request the io_service to invoke the given handler and return immediately.
Could someone explain how these two differ ?

The difference is dispatch may run handler (the CompletionHandler passed to it) inside it which means you will wait for it to finish, if it does, before the function returns. post on the other hand will not run handler itself and returns back to the call site immediately.
So, dispatch is a potentially blocking call while post is a non-blocking call.

Post ensures that the thread that calls post will not immediately attempt to process the task.
https://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/io_service/post.html
but without allowing the io_service to call the handler from inside
this function.
Dispatch makes no such promise; and may be completed by the time the function returns.

Related

boost::asio strand wrapped thread is not working immediately

I have recently used boost::asio::strand. I am inexperienced on this.
What I have faced, if I make a call to function1 with strand::wrap() and call function2 with strand::wrap() in ~function1, function2 is waiting for completion of function1.
ioservice->post(m_strand.wrap(boost::bind( function1 )));
ioservice->post(m_strand.wrap(boost::bind( function2 )));
if I change change the code like this, it is working as my expectation
ioservice->post(m_strand.wrap(boost::bind( function1 )));
ioservice->post((boost::bind( function2 )));
Is this because m_strand is queuing the function calls?
What I have found in my search;
A boost::asio::strand guarantees that, for those handlers that are dispatched through it, an executing handler will be allowed to complete before the next one is started. This is guaranteed irrespective of the number of threads that are calling io_service::run(). Of course, the handlers may still execute concurrently with other handlers that were not dispatched through an boost::asio::strand, or were dispatched through a different boost::asio::strandobject.
When initiating the asynchronous operations, each callback handler is
wrapped using the boost::asio::strand object. The strand::wrap()
function returns a new handler that automatically dispatches its
contained handler through the boost::asio::strand object. By wrapping
the handlers using the same boost::asio::strand, we are ensuring that
they cannot execute concurrently.
What I have to do for execute function2 without waiting for function1 completion? (I know I can execute it without thread or strand, but I want to call it with strand::wrap())
Strands are explicitly designed to only allow one function at a time to execute.
If you are using asynchronous methods then when one function is waiting for an asynchronous method to return other functions will be allowed to execute on the same strand.
If you aren't using asynchronous methods then strands will simply execute the posted functions in serial as you have observed.

Asio async handler is never called, no other handler invoked

I have a asio sync connection. ioService is in one thread (I have only one thread.).
Smaller problem:
boost::asio::async_write(m_socket, boost::asio::buffer(requestStr.data(), requestStr.size()), handler);
The handler is never called, but the server gets it and replies that I get.
The bigger problem:
boost::asio::async_read_until(m_socket, sbuf, '\n', sendingHandler);
It also doesn't call the handler. The sbuf is immediately filled and I can read it there, but I don't know the position of the deliminator. Therefore I need the handler to get the bytes_transferred parameter. (I'm not going to iterate the buffer.)
I tried several things and I could invoke the handler once, but I don't remember what the issue was about after a small refract. Any help? Thanks!
When I used sync messaging, everything was fine, but there is no timeout there.
EDIT:
If you know any nice solution to find the deliminator I don't need the handler.
Because, I would send the msg sync_write and read async.
It wont be called, because it is async. Async methods for writing and reading never call handlers from within the place they're called:
Regardless of whether the asynchronous operation completes immediately
or not, the handler will not be invoked from within this function.
Invocation of the handler will be performed in a manner equivalent to
using boost::asio::io_service::post().
You need to manually call io_service methods like run or run_once to perform operations and that is the moment when your callback will be called.

What does inside a strand mean?

I'm currently trying to get my hands on boost::asio strands. Doing so, I keep reading about "invoking strand post/dispatch inside or outside a strand". Somehow I can't figure out how inside a strand differs from through a strand, and therefore can't grasp the concept of invoking a strand function outside the strand at all.
Probably there is just a small piece missing in my puzzle. Can somebody please give an example how calls to a strand can be inside or outside it?
What I think I've understood so far is that posting something through a strand would be
m_strand.post(myfunctor);
or
m_strand.wrap(myfunctor);
io_svc.post(myfunctor);
Is the latter considered a call to dispatch outside the strand (as opposed to the other being a call to post inside it)? Is there some relation between the strand's "inside realm" and the threads the strand operates on?
If being inside a strand simply meant to invoke a strand's function, then the strand class's documentation would be pointless. It states that strand::post can be invoked outside the strand... That's precisely the part I don't understand.
Even I had some trouble in understanding this concept, but became clear once I started working on libdispatch. It helped me map things with asio better.
Now lets see how to make some sense out of strand. Consider strand as a serial queue of handlers which needs to be executed.
Now, where does these handlers get executed ? Within the worker threads.
Where did these worker threads come from ? From the io_service object you passed while creating the strand.
Something like:
asio::strand s(io_serv_obj);
Now, as you must be knowing, the io_service::run can be called by a single thread or multiple threads. The threads calling the run method of the io_serv_obj are the worker threads for that strand in our case. So, it could be either single threaded or multithreaded.
Coming back to strands, when you post a handler, that handler is always enqueued in the serial queue which we talked about. The worker threads will pick up the handler from the queue one after the other.
Now, when you do a dispatch, asio does some optimization for you:
It checks whether you are calling it from inside one of the worker thread or from some other thread (maybe of some other io_service instance). When it is called outside the current execution context of the strand, thats when it is called outside the strand. So, in the outside case, the dispatch will just enqueue the handler like post when there are other handlers waiting in the queue or will call it directly when it can guarantee that it will not be called concurrently with any other handler from that queue that may be running in one of the worker threads at that moment.
UPDATE:
As noted in the comments section, inside means called within another handler i.e for eg: I posted a handler A and inside that handler, I am doing a dispatch of another handler. Now, as would be explained in #2, if there are no other handlers waiting in the strands serial queue, the dispatch handler will be called synchronously. If this condition is not met, that means, the dispatch is called from outside.
Now, if you call dispatch from outside of the strand i.e not within the current execution context, asio checks its callstack to see if any other handler present in its serial queue is running or not. If not, then it will directly call that handler synchronously. So, there is no cost of enqueueing the handler (I think no extra allocation will be done as well, not sure though).
Lets see the documentation link now:
s.dispatch(a) happens-before s.post(b), where the former is performed
outside the strand
This means that, if dispatch was called from some outside the current run OR there are other handlers already enqueued, then it needs to enqueue the handler, it just cannot call it synchronously. Since its a serial queue, a will get executed before b.
Had there been another call s.dispatch(c) along with a and b but before a and b(in the mentioned order) enqueued, then c will get executed before a and b, but in no way b can get executed before a.
Hope this clears your doubt.
For a given strand object s, running outside s implies that s.running_in_this_thread() returns false. This returns true if the calling thread is executing a handler that was submitted to the strand via post(), dispatch(), or wrap(). Otherwise, it returns false:
io_service.post(handler); // handler will run outside of strand
strand.post(handler); // handler will run inside of strand
strand.dispatch(handler); // handler will run inside of strand
io_service.post(strand.wrap(handler)); // handler will run inside of strand
Given:
a strand object s
a function object f1 that is added to strand s via s.post(), or s.dispatch() when s.running_in_this_thread() == false
a function object f2 that is added to strand s via s.post(), or s.dispatch() when s.running_in_this_thread() == false
then the strand provides a guarantee of ordering and non-concurrency, such that f1 and f2 will not be invoked concurrently. Furthermore, if the addition of f1 happens before the addition of f2, then f1 will be invoked before f2.

Asynchronous Completion Handling

I have this situation:
void foo::bar()
{
RequestsManager->SendRequest(someRequest, this, &foo::someCallback);
}
where RequestsManager works in asynchronous way:
SendRequest puts the request in a queue and returns to the caller
Other thread gets the requests from the queue and process them
When one request is processed the callback is called
Is it possible to have foo::someCallback called in the same thread as SendRequest? If not, how may I avoid following "callback limitation": callbacks should not make time consuming operations to avoid blocking the requests manager.
No - calls/callbacks cannot change thread context - you have to issue some signal to communicate between threads.
Typically, 'someCallback' would either signal an event upon which the thread that originated the 'SendRequest' call is waiting on, (synchronous call), or push the SendRequest, (and so, presumably, results from its processing), onto a queue upon which the thread that originated the 'SendRequest' call will eventually pop , (asynchronous). Just depends on how the originator wshes to be signaled..
Aynch example - the callback might PostMessage/Dispatcher.BeginInvoke the completed SendRequest to a GUI thread for display of the results.
I can see few ways how to achieve it:
A) Implement strategy similar to signal handling
When request processing is over RequestManager puts callback invocation on the waiting list. Next time SendRequest is called, right before returning execution it will check are there any pending callbacks for the thread and execute them. This is relatively simple approach with minimal requirements on the client. Choose it if latency is not of a concern. RequestManager can expose API to forcefully check for pending callbacks
B) Suspend callback-target thread and execute callback in the third thread
This will give you true asynchronous solution with all its caveats. It will look like target-thread execution got interrupted and execution jumped into interrupt handler. Before callback returns target thread needs to be resumed. You wont be able to access thread local storage or original thread's stack from inside the callback.
Depends on "time-consuming operations"'s definition.
The classic way to do this is:
when the request is processed, the RequestManager should execute that &foo::someCallback
to avoid blocking the request manager, you may just rise a flag inside this callback
check that flag periodically inside the thread, which called RequestsManager->SendRequest
This flag will be just a volatile bool inside class foo
If you want to make sure, that the calling thread (foo's) will understand immediately, that the request has been processed, you need additional synchronization.
Implement (or use already implemented) blocking pipe (or use signals/events) between these threads. The idea is:
foo's thread executes SendRequest
foo starts sleeping on some select (for example)
RequestManager executes the request and:
calls &foo::someCallback
"awakes" the foo's thread (by sending something in that file descriptor, which foo sleeps on (using select))
foo is awaken
checks the volatile bool flag for already processed request
does what it needs to do
annuls the flag

message order incorrect (using io_service::wrap)

I've an application to which a GUI connects and receives a lot of messages and the problem is that every once in a while it receives a message out of order.
The connection runs on a separate worker thread (a separate io_service) and exposes the send function(which does a async__write) via io_service::wrap as a callback for others to invoke with a string message as parameter. (i.e. callback = io_service_.wrap(boost::bind(&SomeGUIClass::send,this,_1));),
so to send something to the GUI client, the others call callback(stringMessage) - which should correctly dispatch the send function call on the io_service's queue.
messages are in correct order before invoking the callback, but I can see that sometimes they are out of order within the callback,just before the write call.
my reasoning: wrap translates to a dispatch which will try to call the wrapped fn. within the callback (if it can meet the thread safety guarantees) , and schedule it for later if it cannot.
thus sometimes earlier messages get scheduled for a later write and the latest messages get processed since dispatch was able to process it within the same callback.
please let me know if this reasoning is correct, any ideas appreciated.
thanks!
it was a bug, and thus the attendant confusion. (was making the mistake of calling io_service::run from 2 separate threads wihout realising.) now realized, and problem solved. thanks!