Index: include/__config =================================================================== --- include/__config +++ include/__config @@ -43,6 +43,7 @@ #define _LIBCPP_ABI_LIST_REMOVE_NODE_POINTER_UB #define _LIBCPP_ABI_FORWARD_LIST_REMOVE_NODE_POINTER_UB #define _LIBCPP_ABI_FIX_UNORDERED_CONTAINER_SIZE_TYPE +#define _LIBCPP_ABI_VARIADIC_LOCK_GUARD #endif #define _LIBCPP_CONCAT1(_LIBCPP_X,_LIBCPP_Y) _LIBCPP_X##_LIBCPP_Y Index: include/__mutex_base =================================================================== --- include/__mutex_base +++ include/__mutex_base @@ -76,8 +76,21 @@ #endif + +// Forward declare lock_guard as a variadic template even in C++03 to keep +// the mangling consistent between dialects. +#if defined(_LIBCPP_ABI_VARIADIC_LOCK_GUARD) +template +class _LIBCPP_TYPE_VIS_ONLY lock_guard; +#endif + template -class _LIBCPP_TYPE_VIS_ONLY _LIBCPP_THREAD_SAFETY_ANNOTATION(scoped_lockable) lock_guard +class _LIBCPP_TYPE_VIS_ONLY _LIBCPP_THREAD_SAFETY_ANNOTATION(scoped_lockable) +#if !defined(_LIBCPP_ABI_VARIADIC_LOCK_GUARD) +lock_guard +#else +lock_guard<_Mutex> +#endif { public: typedef _Mutex mutex_type; @@ -96,8 +109,8 @@ ~lock_guard() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()) {__m_.unlock();} private: - lock_guard(lock_guard const&);// = delete; - lock_guard& operator=(lock_guard const&);// = delete; + lock_guard(lock_guard const&) _LIBCPP_EQUAL_DELETE; + lock_guard& operator=(lock_guard const&) _LIBCPP_EQUAL_DELETE; }; template Index: include/mutex =================================================================== --- include/mutex +++ include/mutex @@ -109,6 +109,19 @@ lock_guard& operator=(lock_guard const&) = delete; }; +template // Variadic lock_guard only provided in ABI V2. +class lock_guard +{ +public: + explicit lock_guard(MutexTypes&... m); + lock_guard(MutexTypes&... m, adopt_lock_t); + ~lock_guard(); + lock_guard(lock_guard const&) = delete; + lock_guard& operator=(lock_guard const&) = delete; +private: + tuple pm; // exposition only +}; + template class unique_lock { @@ -427,6 +440,27 @@ __lock_first(0, __l0, __l1, __l2, __l3...); } +template +inline _LIBCPP_INLINE_VISIBILITY +void __unlock(_L0& __l0) { + __l0.unlock(); +} + +template +inline _LIBCPP_INLINE_VISIBILITY +void __unlock(_L0& __l0, _L1& __l1) { + __l0.unlock(); + __l1.unlock(); +} + +template +inline _LIBCPP_INLINE_VISIBILITY +void __unlock(_L0& __l0, _L1& __l1, _L2& __l2, _L3&... __l3) { + __l0.unlock(); + __l1.unlock(); + _VSTD::__unlock(__l2, __l3...); +} + #endif // _LIBCPP_HAS_NO_VARIADICS #endif // !_LIBCPP_HAS_NO_THREADS @@ -577,6 +611,63 @@ #endif // _LIBCPP_HAS_NO_VARIADICS + +#if defined(_LIBCPP_ABI_VARIADIC_LOCK_GUARD) \ + && !defined(_LIBCPP_CXX03_LANG) +template <> +class _LIBCPP_TYPE_VIS_ONLY lock_guard<> { +public: + explicit lock_guard() = default; + ~lock_guard() = default; + + _LIBCPP_INLINE_VISIBILITY + explicit lock_guard(adopt_lock_t) {} + + lock_guard(lock_guard const&) = delete; + lock_guard& operator=(lock_guard const&) = delete; +}; + +template +class _LIBCPP_TYPE_VIS_ONLY lock_guard +{ + static_assert(sizeof...(_MArgs) >= 2, "At least 2 lock types required"); + typedef tuple<_MArgs&...> _MutexTuple; + +public: + _LIBCPP_INLINE_VISIBILITY + explicit lock_guard(_MArgs&... __margs) + : __t_(__margs...) + { + _VSTD::lock(__margs...); + } + + _LIBCPP_INLINE_VISIBILITY + lock_guard(_MArgs&... __margs, adopt_lock_t) + : __t_(__margs...) + { + } + + _LIBCPP_INLINE_VISIBILITY + ~lock_guard() { + typedef typename __make_tuple_indices::type _Indices; + __unlock_unpack(_Indices{}, __t_); + } + + lock_guard(lock_guard const&) = delete; + lock_guard& operator=(lock_guard const&) = delete; + +private: + template + _LIBCPP_INLINE_VISIBILITY + static void __unlock_unpack(__tuple_indices<_Indx...>, _MutexTuple& __mt) { + _VSTD::__unlock(_VSTD::get<_Indx>(__mt)...); + } + + _MutexTuple __t_; +}; + +#endif // _LIBCPP_ABI_VARIADIC_LOCK_GUARD + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP_MUTEX Index: test/libcxx/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_mutex_mangling.pass.cpp =================================================================== --- /dev/null +++ test/libcxx/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_mutex_mangling.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: libcpp-has-no-threads + +// THIS TESTS C++03 EXTENSIONS. + +// + +// template class lock_guard; + +// Test that the the variadic lock guard implementation mangles the same in +// C++11 and C++03. This is important since the mangling of `lock_guard` depends +// on it being declared as a variadic template, even in C++03. + +#define _LIBCPP_ABI_VARIADIC_LOCK_GUARD +#include +#include +#include +#include + +int main() { + const std::string expect = "NSt3__110lock_guardIJNS_5mutexEEEE"; + assert(typeid(std::lock_guard).name() == expect); +} Index: test/std/thread/thread.mutex/thread.lock/thread.lock.guard/mutex.fail.cpp =================================================================== --- test/std/thread/thread.mutex/thread.lock/thread.lock.guard/mutex.fail.cpp +++ test/std/thread/thread.mutex/thread.lock/thread.lock.guard/mutex.fail.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +// UNSUPPORTED: libcpp-has-no-threads + // // template class lock_guard; @@ -14,35 +16,9 @@ // explicit lock_guard(mutex_type& m); #include -#include -#include -#include - -std::mutex m; - -typedef std::chrono::system_clock Clock; -typedef Clock::time_point time_point; -typedef Clock::duration duration; -typedef std::chrono::milliseconds ms; -typedef std::chrono::nanoseconds ns; - -void f() -{ - time_point t0 = Clock::now(); - time_point t1; - { - std::lock_guard lg = m; - t1 = Clock::now(); - } - ns d = t1 - t0 - ms(250); - assert(d < ns(2500000)); // within 2.5ms -} int main() { - m.lock(); - std::thread t(f); - std::this_thread::sleep_for(ms(250)); - m.unlock(); - t.join(); + std::mutex m; + std::lock_guard lg = m; // expected-error{{no viable conversion}} } Index: test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_adopt_lock.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_adopt_lock.pass.cpp @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: c++98, c++03 + +// + +// template class lock_guard; + +// lock_guard(Mutex&..., adopt_lock_t); + +#define _LIBCPP_ABI_VARIADIC_LOCK_GUARD +#include +#include + +struct TestMutex { + bool locked = false; + TestMutex() = default; + + void lock() { assert(!locked); locked = true; } + bool try_lock() { if (locked) return false; return locked = true; } + void unlock() { assert(locked); locked = false; } + + TestMutex(TestMutex const&) = delete; + TestMutex& operator=(TestMutex const&) = delete; +}; + +int main() +{ + { + using LG = std::lock_guard<>; + LG lg(std::adopt_lock); + } + { + TestMutex m1, m2; + using LG = std::lock_guard; + m1.lock(); m2.lock(); + { + LG lg(m1, m2, std::adopt_lock); + assert(m1.locked && m2.locked); + } + assert(!m1.locked && !m2.locked); + } + { + TestMutex m1, m2, m3; + using LG = std::lock_guard; + m1.lock(); m2.lock(); m3.lock(); + { + LG lg(m1, m2, m3, std::adopt_lock); + assert(m1.locked && m2.locked && m3.locked); + } + assert(!m1.locked && !m2.locked && !m3.locked); + } + +} Index: test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_assign.fail.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_assign.fail.cpp @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: c++98, c++03 + +// + +// template class lock_guard; + +// lock_guard& operator=(lock_guard const&) = delete; + +#define _LIBCPP_ABI_VARIADIC_LOCK_GUARD +#include + +int main() +{ + using M = std::mutex; + M m0, m1, m2; + M om0, om1, om2; + { + using LG = std::lock_guard<>; + LG lg1, lg2; + lg1 = lg2; // expected-error{{overload resolution selected deleted operator '='}} + } + { + using LG = std::lock_guard; + LG lg1(m0, m1); + LG lg2(om0, om1); + lg1 = lg2; // expected-error{{overload resolution selected deleted operator '='}} + } + { + using LG = std::lock_guard; + LG lg1(m0, m1, m2); + LG lg2(om0, om1, om2); + lg1 = lg2; // expected-error{{overload resolution selected deleted operator '='}} + } +} Index: test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_copy.fail.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_copy.fail.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: c++98, c++03 + +// + +// template class lock_guard; + +// lock_guard(lock_guard const&) = delete; + +#define _LIBCPP_ABI_VARIADIC_LOCK_GUARD +#include + +int main() +{ + using M = std::mutex; + M m0, m1, m2; + { + using LG = std::lock_guard<>; + const LG Orig; + LG Copy(Orig); // expected-error{{call to deleted constructor of 'LG'}} + } + { + using LG = std::lock_guard; + const LG Orig(m0, m1); + LG Copy(Orig); // expected-error{{call to deleted constructor of 'LG'}} + } + { + using LG = std::lock_guard; + const LG Orig(m0, m1, m2); + LG Copy(Orig); // expected-error{{call to deleted constructor of 'LG'}} + } +} Index: test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_mutex.fail.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_mutex.fail.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: c++98, c++03 + +// + +// template class lock_guard; + +// explicit lock_guard(Mutex&...); + +#define _LIBCPP_ABI_VARIADIC_LOCK_GUARD +#include + +template +void test_conversion(LG) {} + +int main() +{ + using M = std::mutex; + M m0, m1, m2; + M n0, n1, n2; + { + using LG = std::lock_guard<>; + LG lg = {}; // expected-error{{chosen constructor is explicit in copy-initialization}} + test_conversion({}); // expected-error{{no matching function for call}} + ((void)lg); + } + { + using LG = std::lock_guard; + LG lg = {m0, m1}; // expected-error{{chosen constructor is explicit in copy-initialization}} + test_conversion({n0, n1}); // expected-error{{no matching function for call}} + ((void)lg); + } + { + using LG = std::lock_guard; + LG lg = {m0, m1, m2}; // expected-error{{chosen constructor is explicit in copy-initialization}} + test_conversion({n0, n1, n2}); // expected-error{{no matching function for call}} + } +} Index: test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_mutex.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_mutex.pass.cpp @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: c++98, c++03 + +// + +// template class lock_guard; + +// explicit lock_guard(mutex_type& m); + +#define _LIBCPP_ABI_VARIADIC_LOCK_GUARD +#include +#include + +struct TestMutex { + bool locked = false; + TestMutex() = default; + ~TestMutex() { assert(!locked); } + + void lock() { assert(!locked); locked = true; } + bool try_lock() { if (locked) return false; return locked = true; } + void unlock() { assert(locked); locked = false; } + + TestMutex(TestMutex const&) = delete; + TestMutex& operator=(TestMutex const&) = delete; +}; + +#if !defined(TEST_HAS_NO_EXCEPTIONS) +struct TestMutexThrows { + bool locked = false; + bool throws_on_lock = false; + + TestMutexThrows() = default; + ~TestMutexThrows() { assert(!locked); } + + void lock() { + assert(!locked); + if (throws_on_lock) { + throw 42; + } + locked = true; + } + + bool try_lock() { + if (locked) return false; + lock(); + return true; + } + + void unlock() { assert(locked); locked = false; } + + TestMutexThrows(TestMutexThrows const&) = delete; + TestMutexThrows& operator=(TestMutexThrows const&) = delete; +}; +#endif // !defined(TEST_HAS_NO_EXCEPTIONS) + +int main() +{ + { + using LG = std::lock_guard<>; + LG lg; + } + { + using LG = std::lock_guard; + TestMutex m1, m2; + { + LG lg(m1, m2); + assert(m1.locked && m2.locked); + } + assert(!m1.locked && !m2.locked); + } + { + using LG = std::lock_guard; + TestMutex m1, m2, m3; + { + LG lg(m1, m2, m3); + assert(m1.locked && m2.locked && m3.locked); + } + assert(!m1.locked && !m2.locked && !m3.locked); + } +#if !defined(TEST_HAS_NO_EXCEPTIONS) + { + using MT = TestMutexThrows; + using LG = std::lock_guard; + MT m1, m2; + m1.throws_on_lock = true; + try { + LG lg(m1, m2); + assert(false); + } catch (int) {} + assert(!m1.locked && !m2.locked); + } + { + using MT = TestMutexThrows; + using LG = std::lock_guard; + MT m1, m2, m3; + m2.throws_on_lock = true; + try { + LG lg(m1, m2, m3); + assert(false); + } catch (int) {} + assert(!m1.locked && !m2.locked && !m3.locked); + } +#endif +} Index: test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_mutex_cxx03.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_mutex_cxx03.pass.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: libcpp-has-no-threads + +// + +// template class lock_guard; + +// Test that the variadic lock guard implementation compiles in all standard +// dialects, including C++03, even though it is forward declared using +// variadic templates. + +#define _LIBCPP_ABI_VARIADIC_LOCK_GUARD +#include "mutex.pass.cpp" // Use the existing non-variadic test Index: test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_types.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.mutex/thread.lock/thread.lock.guard/variadic_types.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: c++98, c++03 + +// + +// template +// class lock_guard +// { +// public: +// typedef Mutex mutex_type; +// ... +// }; + +#define _LIBCPP_ABI_VARIADIC_LOCK_GUARD +#include +#include + +struct NAT {}; + +template +auto test_typedef(int) -> typename LG::mutex_type; + +template +auto test_typedef(...) -> NAT; + +template +constexpr bool has_mutex_type() { + return !std::is_same(0)), NAT>::value; +} + +int main() +{ + { + using T = std::lock_guard<>; + static_assert(!has_mutex_type(), ""); + } + { + using M1 = std::mutex; + using T = std::lock_guard; + static_assert(std::is_same::value, ""); + } + { + using M1 = std::recursive_mutex; + using T = std::lock_guard; + static_assert(std::is_same::value, ""); + } + { + using M1 = std::mutex; + using M2 = std::recursive_mutex; + using T = std::lock_guard; + static_assert(!has_mutex_type(), ""); + } + { + using M1 = std::mutex; + using M2 = std::recursive_mutex; + using T = std::lock_guard; + static_assert(!has_mutex_type(), ""); + } + { + using M1 = std::mutex; + using T = std::lock_guard; + static_assert(!has_mutex_type(), ""); + } + { + using M1 = std::recursive_mutex; + using T = std::lock_guard; + static_assert(!has_mutex_type(), ""); + } +} Index: www/cxx1z_status.html =================================================================== --- www/cxx1z_status.html +++ www/cxx1z_status.html @@ -74,7 +74,7 @@ P0006R0LWGAdopt Type Traits Variable Templates for C++17.KonaComplete3.8 P0092R1LWGPolishing <chrono>KonaComplete3.8 P0007R1LWGConstant View: A proposal for a std::as_const helper function template.KonaComplete3.8 - P0156R0LWGVariadic lock_guard(rev 3).Kona + P0156R0LWGVariadic lock_guard(rev 3).KonaComplete (ABI V2 Only)3.9 P0074R0LWGMaking std::owner_less more flexibleKonaComplete3.8 P0013R1LWGLogical type traits rev 2KonaComplete3.8