Reputation: 739
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.
Upvotes: 7
Views: 3564
Reputation: 6901
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:
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
.
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.
Upvotes: 10
Reputation: 51941
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:
s
f1
that is added to strand s
via s.post()
, or s.dispatch()
when s.running_in_this_thread() == false
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
.
Upvotes: 2