2

In C++11 there is the std::atomic_flag that is useful for thread loops:

static std::atomic_flag s_done(ATOMIC_FLAG_INIT);

void ThreadMain() {
    while (s_done.test_and_set()) {  // returns current value of s_done and sets to true
        // do some stuff in a thread
    }
}

// Later:
  s_done.clear();  // Sets s_done to false so the thread loop will drop out

The ATOMIC_FLAG_INIT sets the flag to false which means that the thread never gets in the loop. A (bad) solution is possibly to do this:

void ThreadMain() {
    // Sets the flag to true but erases a possible false
    // which is bad as we may get into a deadlock
    s_done.test_and_set();
    while (s_done.test_and_set()) {
        // do some stuff in a thread
    }
}

The default constructor for std::atomic_flag specifies that the flag will be in an unspecified state.

Can I initialize the atomic_flag to true? Is this the correct use of the atomic_flag?

Cassio Neri
  • 19,583
  • 7
  • 46
  • 68
Matt Clarkson
  • 14,106
  • 10
  • 57
  • 85
  • 3
    You probably already know but it is possible to initialize `atomic` to `true` or `false`. – hmjd Jul 26 '13 at 14:24
  • @Matt ATOMIC_FLAG_INIT doesn't set the flag to `false`, but initialises it to zero, which is the default for objects with static or thread local storage duration. – Stefan Jul 26 '13 at 14:38
  • @Stefan "initialises it to zero" is that same as "set the flag to `false`", no? – Matt Clarkson Jul 26 '13 at 15:19
  • @Matt it's the wrong wording, although 0 == false and semantically the same is reached, it is not correct. you want to initialize an object to zero and not to false. – Stefan Jul 26 '13 at 15:29
  • @Stefan so what would you call `static bool s_bool = false;`? Is that "initializing to `false`", "set to `false`" or "initialize to `0`"? The only way I can see "set to `false`" and "initialize to `zero`" meaning different things if you had a system when `false` was an alias for a value where all the bits were not zero? – Matt Clarkson Jul 26 '13 at 15:36
  • @MattClarkson yes you're right and that is exactly the reason why a defect report for the standard on this specific topic exists, concerning atomic_flag. (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1648.htm) – Stefan Jul 26 '13 at 16:04

3 Answers3

5

You could always call test_and_set before starting the thread.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 2
    I have done that in my code but it feels strange and catches you out. I was just wondering why it isn't possible to set the `atomic_flag` to `true` in initialization? – Matt Clarkson Jul 26 '13 at 14:20
  • 6
    Because it's meant as a low-level primitive to build other primitives, and not for general use. `atomic` is where the money is. – R. Martinho Fernandes Jul 26 '13 at 14:26
  • OK, so I now understand that the `atomic_flag` is more used for spinlocks and not for signalling a thread to dump out of it's loop, guess that's what `std::atomic_bool` is for as you said @R.MartinhoFernandes. – Matt Clarkson Jul 26 '13 at 15:20
4

You are incorrectly using atomic_flag to implement a spinlock. The correct form would be:

static std::atomic_flag s_done(ATOMIC_FLAG_INIT);

void ThreadMain() {
    while (s_done.test_and_set()) {  // returns current value of s_done and sets to true
        // spin while s_done is held by another thread
    }

    // do some stuff in a thread

    // Later:
    s_done.clear();  // Sets s_done to false so the thread loop will drop out

Although I recommend using an RAII holder so you can return or throw mid-function and the lock will be automatically released.

ATOMIC_FLAG_INIT is false because the value of an atomic_flag is usually interpreted as indicating whether some resource is being held exclusively by a thread. At program start, there is no such thread, indeed the associated resource may not have even been initialized. So false would be the appropriate initial value.

As for intializing to true, the standard provides no guarantee that it's even possible to assign a boolean value to an atomic_flag.

Casey
  • 41,449
  • 7
  • 95
  • 125
  • 1
    I wasn't trying to implement a spinlock, I wait on a `condition_variable` inside the loop. It's just to signify that the thread should exit. – Matt Clarkson Jul 26 '13 at 15:17
2

An atomic flag which is not initialized with ATOMIC_FLAG_INIT (or { 0 } ) is in an indeterminate state.

What you could do, to solve your problem is this:

std::atomic_flag lock = ATOMIC_FLAG_INIT;

Now acquire the lock:

while (lock.test_and_set(std::memory_order_acquire))
    ; // ...

And release it afterwards:

lock.clear(std::memory_order_release); 

This is what is called a spin-lock.

Stefan
  • 221
  • 2
  • 7