starturtle
starturtle

Reputation: 739

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.

Upvotes: 7

Views: 3564

Answers (2)

Arunmu
Arunmu

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:

  1. 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.

  1. 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.

Upvotes: 10

Tanner Sansbury
Tanner Sansbury

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:

  • 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.

Upvotes: 2

Related Questions