4
boost::shared_ptr<A> a = boost::shared_ptr<A>(new A);
a->i = 2;
strand.post([a](){assert(a->i == 2)});

or

io_service.post([a](){assert(a->i == 2)});

When I post a handler to strand or io_service in thread1, does the thread2 which execute handler see data changes before the post?

Java has similar thing Executor which make happen-before relationship:

Actions in a thread prior to the submission of a Runnable to an Executor happen-before its execution begins. Similarly for Callables submitted to an ExecutorService.

What about asio?

Notice that this question does not ask the execute order of handlers which been post to io_service or strand. Asio doc says if using strand, post(a) happen-before post(b). But this question ask: Does actions before(coding order) post(a) happen-before post(a)(handler a's execute)? This is about memory model. Let me clarify:

// in thread1
global variable a = 111;
start a new thread2 which will access global variable a

We know actions in thread1 before start thread2 happen-before thread2 start. That means: 1, final execute order is same as code order(compiler or CPU will not change the order). 2, global variable a value set action will flush to main memory (or sync to thread2's CPU cache), then thread2 will updated to newest value of global variable a (of course, till it start).

asio's post is similar with this example, so I ask this question. I think asio's post should make happen-before relationship, or else it will be useless. But I am not sure, and I want to know how asio makes it.

To make happen-before, there is 3 way: 1, thread start, join. 2, lock and unlock a mutex. 3, memory barrier.

jean
  • 2,825
  • 2
  • 35
  • 72
  • Lets clearify: you think its possible `a->i = 2;` will execute *after* `strand.post` ? – Galimov Albert Aug 18 '15 at 07:15
  • Yes. But there is other situation: the execute order is same as code order, but thread2 can not read the newest value set of a->i. If there is no happen-before relationship, both of them can happen – jean Aug 18 '15 at 07:27
  • 1
    possible duplicate of [Serialize io\_service::post() execution with io\_service::run() called only in a single thread](http://stackoverflow.com/questions/31527192/serialize-io-servicepost-execution-with-io-servicerun-called-only-in-a-s) – sehe Aug 18 '15 at 11:23
  • Note that if you use the strand correctly, the post happens "from the same strand" so the whole flow will be as-if single-threaded sequential – sehe Aug 18 '15 at 11:24
  • @sehe I don't think this is a dup. The question you reference has to do with the ordering of function invocations, but this question has to do with the ordering of memory accesses. The answer will depend on the memory model and whether `post()` has implicit release semantics and whether the functor invocation has implicit acquire semantics. My guess is yes, but I don't know if that is stated anywhere; it may only be a side effect of the internal thread-safe queue `io_service` contains. – rhashimoto Aug 18 '15 at 17:22
  • @rhashimoto I quote from that answer "`then asio_handler_invoke(a1, &a1) happens-before asio_handler_invoke(b1, &b1).`". My guess is that Chris Kohlhoff is using these words specifically to convey this information. Note he's also the one proposing the executor models for c++17, AFAIR. You can assume he knows the implications of a _happens-before relation_ in the context of the c++11 memory model – sehe Aug 19 '15 at 00:16
  • @sehe I think you can argue that your referenced answer also applies to this question (though I'm still a little iffy on that), but this question is not a duplicate of the question you were answering. This question refers to memory access ordering, that one to handler invocation ordering. This question concerns multiple threads, that one explicitly specifies a single thread. This question refers to ordering between a function and a handler it posts, that one to ordering between two handlers. I respectfully think there are interesting distinctions. – rhashimoto Aug 19 '15 at 01:05
  • Well, you can post the same answer here if you prefer. I'm happy just referring to it – sehe Aug 19 '15 at 01:08

1 Answers1

1

All of the following have a happens-before relation on memory ordering: io_service::post(), io_service::dispatch(), strand::post(), and strand::dispatch(). This is primarily the the result of io_service and strand providing a strong guarantee that a single object may be used concurrently. In this case where potential blocking synchronization is not required, such as when a handler posted to dispatch() may be executed within the context of the dispatch() function, then a full memory fence is issued.

The documentation notes the use of memory barriers for handler allocation:

[...] The implementation will insert appropriate memory barriers to ensure correct memory visibility should allocation functions need to be called from different threads.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169