Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/thread/notify all at thread exit"

From cppreference.com
< cpp‎ | thread
(Example: missing includes)
(Added LWG issue #2140 DR.)
 
(19 intermediate revisions by 10 users not shown)
Line 1: Line 1:
 
{{cpp/title|notify_all_at_thread_exit}}
 
{{cpp/title|notify_all_at_thread_exit}}
{{cpp/thread/sidebar}}
+
{{cpp/thread/navbar}}
{{ddcl | header=condition_variable | notes={{mark since c++11}} |
+
{{ddcl|header=condition_variable|since=c++11|
 
void notify_all_at_thread_exit( std::condition_variable& cond,
 
void notify_all_at_thread_exit( std::condition_variable& cond,
 
                                 std::unique_lock<std::mutex> lk );
 
                                 std::unique_lock<std::mutex> lk );
 
}}
 
}}
  
Transfers the ownership of the supplied, previously acquired, lock {{tt|lk}} to internal storage and sets up the execution environment so when the current thread exits, after the destructors for all objects with thread-local storage duration are called, the condition variable is notified as in by
+
{{tt|notify_all_at_thread_exit}} provides a mechanism to notify other threads that a given thread has completely finished, including destroying all {{ltt|cpp/keyword/thread_local}} objects. It operates as follows:
  
{{cpp|
+
* Ownership of the previously acquired lock {{c|lk}} is transferred to internal storage.
lk.unlock();
+
 
cond.notify_all();
+
* The execution environment is modified such that when the current thread exits, the condition variable {{c|cond}} is notified as if by {{c multi|lk.unlock();|cond.notify_all();}}.
}}
+
 
 +
The implied {{c|lk.unlock()}} is [[cpp/atomic/memory_order|sequenced after]] the destruction of all objects with [[cpp/keyword/thread_local|thread local storage duration]] associated with the current thread.
  
The purpose of this function is to provide a mechanism to determine when all thread_local objects in a detached thread have been destructed. Equivalent effect may be achieved with the facilities provided by {{cpp|std::promise}} or {{cpp|std::packaged_task}}.
+
If any of the following conditions is satisfied, the behavior is undefined:
 +
* {{c|lk}} is not locked by the calling thread.
 +
* If some other threads are also waiting on {{c|cond}}, {{c|lk.mutex()}} is different from the mutex unlocked by the waiting functions ({{tt|wait}}, {{lc|wait_for}} and {{lc|wait_until}}) called on {{c|cond}} by those threads.
  
 
===Notes===
 
===Notes===
Calling this function if {{tt|lock.mutex()}} is not locked by the current thread is undefined behavior.
+
An equivalent effect may be achieved with the facilities provided by {{lc|std::promise}} or {{lc|std::packaged_task}}.
  
Calling this function if {{tt|lock.mutex()}} is not the same mutex as the one used by all other threads that are currently waiting on the same condition variable is undefined behavior.
+
The supplied lock {{c|lk}} is held until the thread exits. Once this function has been called, no more threads may acquire the same lock in order to wait on {{c|cond}}. If some threads are waiting on this condition variable, ensure that the condition being waited for is satisfied while holding the lock on {{c|lk}}, and that this lock is not released and reacquired prior to calling {{tt|notify_all_at_thread_exit}} to avoid confusion from spurious wakeups in other threads.
 
+
The supplied lock {{tt|lk}} is held until the thread exits. Once this function has been called, no more threads may acquire the same lock in order to wait on {{tt|cond}}. If some thread is waiting on this condition variable, it should not attempt to release and reacquire the lock when it wakes up spuriously.
+
  
 
In typical use cases, this function is the last thing called by a detached thread.
 
In typical use cases, this function is the last thing called by a detached thread.
  
 
===Parameters===
 
===Parameters===
{{param list begin}}
+
{{par begin}}
{{param list item | cond | the condition variable to notify at thread exit}}
+
{{par|cond|the condition variable to notify at thread exit}}
{{param list item | lk | the lock associated with the condition variable {{tt|cond}} }}
+
{{par|lk|the lock associated with the condition variable {{c|cond}}}}
{{param list end}}
+
{{par end}}
  
 
===Return value===
 
===Return value===
Line 34: Line 35:
  
 
===Example===
 
===Example===
{{example cpp
+
{{example
| Code fragment illustrating the intended use for notify_all_at_thread_exit
+
|This partial code fragment illustrates how {{tt|notify_all_at_thread_exit}} can be used to avoid accessing data that depends on thread locals while those thread locals are in the process of being destructed:
| code=
+
|code=
 +
#include <cassert>
 +
#include <condition_variable>
 
#include <mutex>
 
#include <mutex>
 +
#include <string>
 
#include <thread>
 
#include <thread>
 
+
 
std::mutex m;
 
std::mutex m;
 
std::condition_variable cv;
 
std::condition_variable cv;
 
+
 
bool ready = false;
 
bool ready = false;
ComplexType result;
+
std::string result; // some arbitrary type
 
+
 
void thread_func()
 
void thread_func()
 
{
 
{
 +
    thread_local std::string thread_local_data = "42";
 +
   
 
     std::unique_lock<std::mutex> lk(m);
 
     std::unique_lock<std::mutex> lk(m);
     result = function_that_uses_thread_locals();
+
   
 +
    // assign a value to result using thread_local data
 +
     result = thread_local_data;
 
     ready = true;
 
     ready = true;
 +
   
 
     std::notify_all_at_thread_exit(cv, std::move(lk));
 
     std::notify_all_at_thread_exit(cv, std::move(lk));
} // destroy thread_locals, notify cv, unlock mutex
+
   
 
+
}   // 1. destroy thread_locals;
 +
    // 2. unlock mutex;
 +
    // 3. notify cv.
 +
 
int main()
 
int main()
 
{
 
{
 
     std::thread t(thread_func);
 
     std::thread t(thread_func);
 
     t.detach();
 
     t.detach();
 +
   
 
     // do other work
 
     // do other work
 +
    // ...
 +
   
 +
    // wait for the detached thread
 
     std::unique_lock<std::mutex> lk(m);
 
     std::unique_lock<std::mutex> lk(m);
     while(!ready) {
+
     cv.wait(lk, []{ return ready; });
        cv.wait(lk); // wait for the detached thread
+
      
     }
+
     // result is ready and thread_local destructors have finished, no UB
     process(result); // result is ready and thread_locals are destructed
+
    assert(result == "42");
 
}
 
}
| output=
+
|output=
 
}}
 
}}
 +
 +
===Defect reports===
 +
{{dr list begin}}
 +
{{dr list item|wg=lwg|dr=2140|std=C++11|before=the call to {{tt|notify_all_at_thread_exit}}<br>synchronized with calls to functions waiting on {{c|cond}}|after=updated the synchronization<br>requirement}}
 +
{{dr list end}}
  
 
===See also===
 
===See also===
{{dcl list begin}}
+
{{dsc begin}}
{{dcl list template | cpp/thread/promise/dcl list set_value_at_thread_exit}}
+
{{dsc inc|cpp/thread/promise/dsc set_value_at_thread_exit}}
{{dcl list template | cpp/thread/packaged_task/dcl list make_ready_at_thread_exit}}
+
{{dsc inc|cpp/thread/packaged_task/dsc make_ready_at_thread_exit}}
{{dcl list end}}
+
{{dsc end}}
 +
 
 +
{{langlinks|de|es|fr|it|ja|pt|ru|zh}}

Latest revision as of 23:39, 13 March 2024

 
 
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
(C++11)
Generic lock management
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Condition variables
notify_all_at_thread_exit
(C++11)
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
(C++11)
Safe Reclamation
(C++26)
Hazard Pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11)(deprecated in C++20)
(C++11)(deprecated in C++20)
Memory ordering
Free functions for atomic operations
Free functions for atomic flags
 
Defined in header <condition_variable>
void notify_all_at_thread_exit( std::condition_variable& cond,
                                std::unique_lock<std::mutex> lk );
(since C++11)

notify_all_at_thread_exit provides a mechanism to notify other threads that a given thread has completely finished, including destroying all thread_local objects. It operates as follows:

  • Ownership of the previously acquired lock lk is transferred to internal storage.
  • The execution environment is modified such that when the current thread exits, the condition variable cond is notified as if by lk.unlock();
    cond.notify_all();
    .

The implied lk.unlock() is sequenced after the destruction of all objects with thread local storage duration associated with the current thread.

If any of the following conditions is satisfied, the behavior is undefined:

  • lk is not locked by the calling thread.
  • If some other threads are also waiting on cond, lk.mutex() is different from the mutex unlocked by the waiting functions (wait, wait_for and wait_until) called on cond by those threads.

Contents

[edit] Notes

An equivalent effect may be achieved with the facilities provided by std::promise or std::packaged_task.

The supplied lock lk is held until the thread exits. Once this function has been called, no more threads may acquire the same lock in order to wait on cond. If some threads are waiting on this condition variable, ensure that the condition being waited for is satisfied while holding the lock on lk, and that this lock is not released and reacquired prior to calling notify_all_at_thread_exit to avoid confusion from spurious wakeups in other threads.

In typical use cases, this function is the last thing called by a detached thread.

[edit] Parameters

cond - the condition variable to notify at thread exit
lk - the lock associated with the condition variable cond

[edit] Return value

(none)

[edit] Example

This partial code fragment illustrates how notify_all_at_thread_exit can be used to avoid accessing data that depends on thread locals while those thread locals are in the process of being destructed:

#include <cassert>
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
 
std::mutex m;
std::condition_variable cv;
 
bool ready = false;
std::string result; // some arbitrary type
 
void thread_func()
{
    thread_local std::string thread_local_data = "42";
 
    std::unique_lock<std::mutex> lk(m);
 
    // assign a value to result using thread_local data
    result = thread_local_data;
    ready = true;
 
    std::notify_all_at_thread_exit(cv, std::move(lk));
 
}   // 1. destroy thread_locals;
    // 2. unlock mutex;
    // 3. notify cv.
 
int main()
{
    std::thread t(thread_func);
    t.detach();
 
    // do other work
    // ...
 
    // wait for the detached thread
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{ return ready; });
 
    // result is ready and thread_local destructors have finished, no UB
    assert(result == "42");
}

[edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
LWG 2140 C++11 the call to notify_all_at_thread_exit
synchronized with calls to functions waiting on cond
updated the synchronization
requirement

[edit] See also

sets the result to specific value while delivering the notification only at thread exit
(public member function of std::promise<R>) [edit]
executes the function ensuring that the result is ready only once the current thread exits
(public member function of std::packaged_task<R(Args...)>) [edit]