Double-checked locking tries to avoid locking on every access to a lazily initialized resource: it checks the pointer without a lock, then locks and checks again before initializing. The unsynchronized first read can race with initialization, however, causing undefined behavior. section 3.3.1
void undefined_behaviour_with_double_checked_locking()
{
if(!resource_ptr) #1
{
std::lock_guard<std::mutex> lk(resource_mutex);
if(!resource_ptr) #2
{
resource_ptr.reset(new some_resource); #3
}
}
resource_ptr->do_something(); #4
}
!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":2,\"ch\":55},{\"line\":2,\"ch\":57}],[{\"line\":5,\"ch\":55},{\"line\":5,\"ch\":57}],[{\"line\":7,\"ch\":55},{\"line\":7,\"ch\":57}],[{\"line\":10,\"ch\":55},{\"line\":10,\"ch\":57}]]"}
!@%STYLE%@!
The example shows the unlocked pointer check, the locked second check, and the subsequent use of the resource; the first check is the source of the race. section 3.3.1
Use std::once_flag and std::call_once instead to handle one-time initialization safely. section 3.3.1
