Search code examples
c++multithreadingc++11atomiclanguage-lawyer

Requirements for std::thread::id. Can it be atomized?


The standard says: "An object of type thread::id provides... a single distinct value for all thread objects that do not represent a thread of execution". Is that a single/distinct value with regard to operator==, or is it the actual bitwise-single/distinct value?

The reason for the question: MSVC2012's std::thread::id::id() leaves garbage in one of its fields, and it breaks code that does compare-exchange on an std::atomic<std::thread::id> (since the latter depends on bitwise comparisons).

Is std::atomic<std::thread::id> a legal construct in the first place?

EDIT: for the reference, the code goes like this:

while( !worker_id.compare_exchange_weak( no_id = thread_id_type(), self_id ) )
    sleep();

Solution

  • Firstly, std::atomic<std::thread::id> is legal: std::thread::id is required to be trivially copyable (30.3.1.1p2), which meets the requirements of std::atomic<> (29.5p1).

    However, it is an opaque class, so there is no requirement that the bit pattern of objects that compare equal be identical.

    Consequently, if you use compare_exchange_weak or compare_exchange_strong then it may fail for values that compare equal.

    Thus, the advice is to use compare_exchange_weak in a loop, leaving the expected value as the result of the previous iteration.

    In your case, the semantics I interpret from your loop are: keep looping whilst worker_id is the ID of another thread, or worker_id was std::thread::id but the exchange failed. You can achieve this with the following:

    no_id=std::thread::id();
    while((no_id!=std::thread::id()) ||
          !worker_id.compare_exchange_weak( no_id, self_id ) ){
        if(no_id!=std::thread::id()) no_id=std::thread::id();
        sleep();
    }
    

    or

    no_id=std::thread::id();
    while(!worker_id.compare_exchange_weak(
              (no_id!=std::thread::id())?(no_id=std::thread::id())?no_id, self_id ) )
        sleep();
    

    i.e. only change the no_id value if it is not std::thread::id().