c++multithreadingmemory-modeldata-race

Is it a data race if I enforce 'happens before' relation between conflicting expressions at runtime?


As per cppreference,

When an evaluation of an expression writes to a memory location and another evaluation reads or modifies the same memory location, the expressions are said to conflict. A program that has two conflicting evaluations has a data race unless:

a) both evaluations execute on the same thread or in the same signal handler, or
b) both conflicting evaluations are atomic operations (see std::atomic), or
c) one of the conflicting evaluations happens-before another (see std::memory_order)

I am confused a little with respect to point c. As per Anthony Williams's book(C++ concurrency in action), section 5.1.2:

If there’s no enforced ordering between two accesses to a single memory location from separate threads, one or both of those accesses is not atomic, and if one or both is a write, then this is a data race and causes undefined behavior. According to the language standard, once an application contains any undefined behavior, all bets are off; the behavior of the complete application is now undefined, and it may do anything at all

I understand that if I enforce the ordering, say by using std::mutex, so that it's not possible for more than one evaluation(of the conflicting expression) to happen at the exact same time as another, then everything is fine and my program will be well defined. I like to think of this as 'compile-time order enforcement'.

I wanted to understand if 'run-time order enforcement' is sufficient to eliminate data-race/undefined behaviour.

Say for example, I design a client-server system like below:

Server Specifications

  • Please note that, there's no inter-thread synchronisation introduced by the networking library/system calls made in the first and last step of the pseudo code.

Client Specifications

In the above example, server's Thread A and Thread B are sharing a global variable. There are 2 conflicting expressions(increment operation of shared variable) and I haven't used any language provided synchronisation mechanism. However, I enforce the ordering at run-time. I understand that the compiler cannot know about the runtime but on run time, because I ensure it, both thread A and thread B can never access the variable at the same time.

So I'm not sure if this falls in the category of data race.

So basically my queries are:

  1. Is it necessary to enforce ordering at compile time rather than run time to avoid data race? Will the above server's code fall in the category of programs having data race?

  2. Is there a way to enforce ordering at compile time in a multi-threaded C++ program when threads are sharing data without using C++ language's synchronisation constructs(mutex/futures/atomic) etc.


Solution

  • Will the above server's code fall in the category of programs having data race?

    Yes.

    Is there a way to enforce ordering at compile time in a multi-threaded C++ program when threads are sharing data without using C++ language's synchronisation constructs(mutex/futures/atomic) etc.

    Implementations can provide additional guarantees beyond what the standard requires.