diff --git a/libcxx/src/memory.cpp b/libcxx/src/memory.cpp --- a/libcxx/src/memory.cpp +++ b/libcxx/src/memory.cpp @@ -149,23 +149,37 @@ void __sp_mut::lock() noexcept { - auto m = static_cast<__libcpp_mutex_t*>(__lx); - unsigned count = 0; - while (!__libcpp_mutex_trylock(m)) - { - if (++count > 16) - { - __libcpp_mutex_lock(m); - break; - } - this_thread::yield(); + // The __sp_mut class should only be used for the shared_ptr overloads of + // std::atomic_... functions. Ownership of __sp_mut is always released + // before the end of any of these overloads, and none of them spawn new + // threads. Thus, there is no danger of one thread calling __sp_mut::lock + // and skipping acquisition of __lx followed by another thread calling it + // (and acquiring __lx) before the first thread calls __sp_mut::unlock. + // + // We can't acquire the mutex when the threading API is disabled. Since we + // don't need to acquire it when single-threaded anyways, we just don't + // bother aquiring the mutex unless we're multi-threaded (and thus the + // threading API is enabled). + if (!__libcpp_has_spawned_other_threads()) + return; + auto m = static_cast<__libcpp_mutex_t*>(__lx); + unsigned count = 0; + while (!__libcpp_mutex_trylock(m)) { + if (++count > 16) { + __libcpp_mutex_lock(m); + break; } + this_thread::yield(); + } } void __sp_mut::unlock() noexcept { - __libcpp_mutex_unlock(static_cast<__libcpp_mutex_t*>(__lx)); + // See comment in __sp_mut::lock() for discussion of why this is safe. + if (!__libcpp_has_spawned_other_threads()) + return; + __libcpp_mutex_unlock(static_cast<__libcpp_mutex_t*>(__lx)); } __sp_mut&