Home > Mobile >  How std::atomic wait operation works?
How std::atomic wait operation works?

Time:01-23

Starting C 20, std::atomic has wait() and notify_one()/notify_all() operations. But I didn't get exactly how they are supposed to work. cppreference says:

Performs atomic waiting operations. Behaves as if it repeatedly performs the following steps:

  • Compare the value representation of this->load(order) with that of old.
    • If those are equal, then blocks until *this is notified by notify_one() or notify_all(), or the thread is unblocked spuriously.
    • Otherwise, returns.

These functions are guaranteed to return only if value has changed, even if underlying implementation unblocks spuriously.

I don't exactly get how these 2 parts are related to each other. Does it mean that if the value if not changed, then the function does not return even if I use notify_one()/notify_all() method? meaning that the operation is somehow equal to following pseudocode?

while (*this == val) {
    // block thread
}

CodePudding user response:

Yes, that is exactly it. notify_one/all simply provide the waiting thread a chance to check the value for change. If it remains the same, e.g. because a different thread has set the value back to its original value, the thread will remain blocking.

Note: A valid implementation for this code is to use a global array of mutexes and condition_variables. atomic variables are then mapped to these objects by their pointer via a hash function. That's why you get spurious wakeups. Some atomics share the same condition_variable.

Something like this:


std::mutex atomic_mutexes[64];
std::condition_variable atomic_conds[64];

template<class T>
std::size_t index_for_atomic(std::atomic<T>* ptr) noexcept
{ return reinterpret_cast<std::size_t>(ptr) / sizeof(T) % 64; }

void atomic<T>::wait(T value, std::memory_order order)
{
    if(this->load(order) != value)
        return;
    std::size_t index = index_for_atomic(this);
    std::unique_lock<std::mutex> lock(atomic_mutexes[index]);
    while(this->load(std::memory_order_relaxed) == value)
        atomic_conds[index].wait(lock);
}
template<class T>
void std::atomic_notify_one(std::atomic<T>* ptr)
{
    /* needs t notify_all because we could have multiple waiters
     * in multiple atomics due to aliasing
     */
    atomic_conds[index_for_atomic(ptr)].notify_all();
}
  •  Tags:  
  • Related