Std::thread::join is said to \'synchronize-with\' the joined thread, however synchronization doesnt tell anything about visibility of side effects, it merely governs the ord
[thread.thread.member]:
void join();
Effects: Blocks until the thread represented by*this
has completed.
Synchronization: The completion of the thread represented by*this
synchronizes with the corresponding successfuljoin()
return.
Since the completion of the thread execution synchronizes with the return from thread::join
, the completion of the thread inter-thread happens before the return:
An evaluation A inter-thread happens before an evaluation B if
— A synchronizes with B
and thus happens before it:
An evaluation A happens before an evaluation B (or, equivalently, B happens after A) if:
— A inter-thread happens before B
Due to (inter-thread) happens before transitivity (let me skip copypasting the whole definition of inter-thread happens before to show this), everything what happened before the completion of the thread, including the write of the value 1
into g_i
, happens before the return from thread::join
. The return from thread::join
, in turn, happens before the read of value of g_i
in return g_i;
simply because the invocation of thread::join
is sequenced before return g_i;
. Again, using the transitivity, we establish that the write of 1
to g_i
in the non-main thread happens before the read of g_i
in return g_i;
in the main thread.
Write of 1
into g_i
is visible side effect with respect to the read of g_i
in return g_i;
:
A visible side effect A on a scalar object or bit-field M with respect to a value computation B of M satisfies the conditions:
— A happens before B and
— there is no other side effect X to M such that A happens before X and X happens before B.
The value of a non-atomic scalar object or bit-field M, as determined by evaluation B, shall be the value stored by the visible side effect A.
Emphasis of the last sentence is mine and it guarantees that the value read from g_i
in return g_i;
will be 1
.