Skip to content

[libc++] Make std::unique_lock available with _LIBCPP_HAS_NO_THREADS #99562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

ilovepi
Copy link
Contributor

@ilovepi ilovepi commented Jul 18, 2024

This is a follow up to #98717,
which made lock_guard available under _LIBCPP_HAS_NO_THREADS. We can
make unique_lock available under similar circumstances. This patch
follows the example in #98717, by:

  • Removing the preprocessor guards for _LIBCPP_HAS_NO_THREADS in the
    unique_lock header.
  • providing a set of custom mutex implementations in a local header.
  • using custom locks in tests that can be made to work under
    no-threads.

Created using spr 1.3.4
@ilovepi ilovepi requested a review from a team as a code owner July 18, 2024 20:42
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jul 18, 2024
@llvmbot
Copy link
Member

llvmbot commented Jul 18, 2024

@llvm/pr-subscribers-libcxx

Author: Paul Kirth (ilovepi)

Changes

This is a follow up to #98717,
which made lock_guard available under _LIBCPP_HAS_NO_THREADS. We can
make unique_lock available under similar circumstances. This patch
follows the example in #98717, by:

  • Removing the preprocessor guards for _LIBCPP_HAS_NO_THREADS in the
    unique_lock header.
  • providing a set of custom mutex implementations in a local header.
  • using custom locks in tests that can be made to work under
    no-threads that don't require threading.

Patch is 32.48 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/99562.diff

20 Files Affected:

  • (modified) libcxx/include/__mutex/unique_lock.h (-4)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/implicit_ctad.pass.cpp (+3-3)
  • (renamed) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_assign.compile.pass.cpp (+2-17)
  • (renamed) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_ctor.compile.pass.cpp (+2-15)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/default.pass.cpp (+6-8)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp (+9-11)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_ctor.pass.cpp (+9-11)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_adopt_lock.pass.cpp (+9-10)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_defer_lock.pass.cpp (+9-10)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock.pass.cpp (+24-42)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_for.pass.cpp (+25-46)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_until.pass.cpp (+25-46)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/unlock.pass.cpp (+19-34)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.mod/member_swap.pass.cpp (+13-21)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.mod/nonmember_swap.pass.cpp (+13-21)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.mod/release.pass.cpp (+19-29)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.obs/mutex.pass.cpp (+10-12)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.obs/op_bool.pass.cpp (+13-15)
  • (modified) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.obs/owns_lock.pass.cpp (+10-12)
  • (added) libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/types.h (+88)
diff --git a/libcxx/include/__mutex/unique_lock.h b/libcxx/include/__mutex/unique_lock.h
index 4a616ba51ee1c..5df791de4c742 100644
--- a/libcxx/include/__mutex/unique_lock.h
+++ b/libcxx/include/__mutex/unique_lock.h
@@ -22,8 +22,6 @@
 #  pragma GCC system_header
 #endif
 
-#ifndef _LIBCPP_HAS_NO_THREADS
-
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Mutex>
@@ -172,6 +170,4 @@ inline _LIBCPP_HIDE_FROM_ABI void swap(unique_lock<_Mutex>& __x, unique_lock<_Mu
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_HAS_NO_THREADS
-
 #endif // _LIBCPP___MUTEX_UNIQUE_LOCK_H
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/implicit_ctad.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/implicit_ctad.pass.cpp
index 337ad4c45a94d..8c7ca4279eead 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/implicit_ctad.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/implicit_ctad.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: no-threads
 // UNSUPPORTED: c++03, c++11, c++14
 
 // <mutex>
@@ -18,12 +17,13 @@
 #include <mutex>
 
 #include "test_macros.h"
+#include "types.h"
 
 int main(int, char**) {
-  std::mutex mutex;
+  MyMutex mutex;
   {
     std::unique_lock lock(mutex);
-    ASSERT_SAME_TYPE(decltype(lock), std::unique_lock<std::mutex>);
+    ASSERT_SAME_TYPE(decltype(lock), std::unique_lock<MyMutex>);
   }
 
   return 0;
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_assign.compile.fail.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_assign.compile.pass.cpp
similarity index 59%
rename from libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_assign.compile.fail.cpp
rename to libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_assign.compile.pass.cpp
index 799cb61f9b21e..9ab8369637cdc 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_assign.compile.fail.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_assign.compile.pass.cpp
@@ -13,22 +13,7 @@
 // unique_lock& operator=(unique_lock const&) = delete;
 
 #include <mutex>
-#include <cassert>
 
-int main(int, char**)
-{
-    {
-    typedef std::mutex M;
-    M m0;
-    M m1;
-    std::unique_lock<M> lk0(m0);
-    std::unique_lock<M> lk1(m1);
-    lk1 = lk0;
-    assert(lk1.mutex() == &m0);
-    assert(lk1.owns_lock() == true);
-    assert(lk0.mutex() == nullptr);
-    assert(lk0.owns_lock() == false);
-    }
+#include "../types.h"
 
-  return 0;
-}
+static_assert(!std::is_copy_assignable<std::lock_guard<MyMutex> >::value, "");
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_ctor.compile.fail.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_ctor.compile.pass.cpp
similarity index 61%
rename from libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_ctor.compile.fail.cpp
rename to libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_ctor.compile.pass.cpp
index e258198e60bbf..e846061f5fbd0 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_ctor.compile.fail.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/copy_ctor.compile.pass.cpp
@@ -13,20 +13,7 @@
 // unique_lock(unique_lock const&) = delete;
 
 #include <mutex>
-#include <cassert>
 
-int main(int, char**)
-{
-    {
-    typedef std::mutex M;
-    M m;
-    std::unique_lock<M> lk0(m);
-    std::unique_lock<M> lk = lk0;
-    assert(lk.mutex() == &m);
-    assert(lk.owns_lock() == true);
-    assert(lk0.mutex() == nullptr);
-    assert(lk0.owns_lock() == false);
-    }
+#include "../types.h"
 
-  return 0;
-}
+static_assert(!std::is_copy_constructible<std::lock_guard<MyMutex> >::value, "");
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/default.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/default.pass.cpp
index 2034a26b1d913..6fc4f7f23ced3 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/default.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/default.pass.cpp
@@ -5,8 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
-// UNSUPPORTED: no-threads
 
 // <mutex>
 
@@ -14,16 +12,16 @@
 
 // unique_lock();
 
-#include <mutex>
 #include <cassert>
+#include <mutex>
 
 #include "test_macros.h"
+#include "../types.h"
 
-int main(int, char**)
-{
-    std::unique_lock<std::mutex> ul;
-    assert(!ul.owns_lock());
-    assert(ul.mutex() == nullptr);
+int main(int, char**) {
+  std::unique_lock<MyMutex> ul;
+  assert(!ul.owns_lock());
+  assert(ul.mutex() == nullptr);
 
   return 0;
 }
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp
index 0af918c1e20ee..9563fdebd3e06 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp
@@ -5,8 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
-// UNSUPPORTED: no-threads, c++03
 
 // <mutex>
 
@@ -14,16 +12,16 @@
 
 // unique_lock& operator=(unique_lock&& u);
 
-#include <mutex>
 #include <cassert>
-#include "nasty_containers.h"
+#include <mutex>
 
+#include "nasty_containers.h"
+#include "../types.h"
 #include "test_macros.h"
 
-int main(int, char**)
-{
-    {
-    typedef std::mutex M;
+int main(int, char**) {
+  {
+    typedef MyMutex M;
     M m0;
     M m1;
     std::unique_lock<M> lk0(m0);
@@ -33,8 +31,8 @@ int main(int, char**)
     assert(lk1.owns_lock() == true);
     assert(lk0.mutex() == nullptr);
     assert(lk0.owns_lock() == false);
-    }
-    {
+  }
+  {
     typedef nasty_mutex M;
     M m0;
     M m1;
@@ -45,7 +43,7 @@ int main(int, char**)
     assert(lk1.owns_lock() == true);
     assert(lk0.mutex() == nullptr);
     assert(lk0.owns_lock() == false);
-    }
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_ctor.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_ctor.pass.cpp
index cce0eb5fb9057..ad43b44e6fa96 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_ctor.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_ctor.pass.cpp
@@ -5,8 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
-// UNSUPPORTED: no-threads, c++03
 
 // <mutex>
 
@@ -14,16 +12,16 @@
 
 // unique_lock(unique_lock&& u);
 
-#include <mutex>
 #include <cassert>
-#include "nasty_containers.h"
+#include <mutex>
 
+#include "nasty_containers.h"
+#include "../types.h"
 #include "test_macros.h"
 
-int main(int, char**)
-{
-    {
-    typedef std::mutex M;
+int main(int, char**) {
+  {
+    typedef MyMutex M;
     M m;
     std::unique_lock<M> lk0(m);
     std::unique_lock<M> lk = std::move(lk0);
@@ -31,8 +29,8 @@ int main(int, char**)
     assert(lk.owns_lock() == true);
     assert(lk0.mutex() == nullptr);
     assert(lk0.owns_lock() == false);
-    }
-    {
+  }
+  {
     typedef nasty_mutex M;
     M m;
     std::unique_lock<M> lk0(m);
@@ -41,7 +39,7 @@ int main(int, char**)
     assert(lk.owns_lock() == true);
     assert(lk0.mutex() == nullptr);
     assert(lk0.owns_lock() == false);
-    }
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_adopt_lock.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_adopt_lock.pass.cpp
index 4adbe26777d0b..28cc43853180e 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_adopt_lock.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_adopt_lock.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// UNSUPPORTED: no-threads
 // UNSUPPORTED: c++03
 
 // <mutex>
@@ -15,30 +14,30 @@
 
 // unique_lock(mutex_type& m, adopt_lock_t);
 
-#include <mutex>
 #include <cassert>
-#include "nasty_containers.h"
+#include <mutex>
 
+#include "nasty_containers.h"
+#include "../types.h"
 #include "test_macros.h"
 
-int main(int, char**)
-{
-    {
-    typedef std::mutex M;
+int main(int, char**) {
+  {
+    typedef MyMutex M;
     M m;
     m.lock();
     std::unique_lock<M> lk(m, std::adopt_lock);
     assert(lk.mutex() == std::addressof(m));
     assert(lk.owns_lock() == true);
-    }
-    {
+  }
+  {
     typedef nasty_mutex M;
     M m;
     m.lock();
     std::unique_lock<M> lk(m, std::adopt_lock);
     assert(lk.mutex() == std::addressof(m));
     assert(lk.owns_lock() == true);
-    }
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_defer_lock.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_defer_lock.pass.cpp
index 06ef2043edcef..96a9afbc9438c 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_defer_lock.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/mutex_defer_lock.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// UNSUPPORTED: no-threads
 // UNSUPPORTED: c++03
 
 // <mutex>
@@ -15,28 +14,28 @@
 
 // unique_lock(mutex_type& m, defer_lock_t);
 
-#include <mutex>
 #include <cassert>
-#include "nasty_containers.h"
+#include <mutex>
 
+#include "nasty_containers.h"
+#include "../types.h"
 #include "test_macros.h"
 
-int main(int, char**)
-{
-    {
-    typedef std::mutex M;
+int main(int, char**) {
+  {
+    typedef MyMutex M;
     M m;
     std::unique_lock<M> lk(m, std::defer_lock);
     assert(lk.mutex() == std::addressof(m));
     assert(lk.owns_lock() == false);
-    }
-    {
+  }
+  {
     typedef nasty_mutex M;
     M m;
     std::unique_lock<M> lk(m, std::defer_lock);
     assert(lk.mutex() == std::addressof(m));
     assert(lk.owns_lock() == false);
-    }
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock.pass.cpp
index 4cf5ec2ab5ccf..b49390ae590c8 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock.pass.cpp
@@ -21,53 +21,35 @@
 #include <system_error>
 
 #include "test_macros.h"
+#include "../types.h"
 
-bool try_lock_called = false;
+MyTimedMutex m;
 
-struct mutex
-{
-    bool try_lock()
-    {
-        try_lock_called = !try_lock_called;
-        return try_lock_called;
-    }
-    void unlock() {}
-};
-
-mutex m;
-
-int main(int, char**)
-{
-    std::unique_lock<mutex> lk(m, std::defer_lock);
-    assert(lk.try_lock() == true);
-    assert(try_lock_called == true);
-    assert(lk.owns_lock() == true);
+int main(int, char**) {
+  std::unique_lock<MyTimedMutex> lk(m, std::defer_lock);
+  assert(lk.try_lock() == true);
+  assert(m.try_lock_called == true);
+  assert(lk.owns_lock() == true);
 #ifndef TEST_HAS_NO_EXCEPTIONS
-    try
-    {
-        TEST_IGNORE_NODISCARD lk.try_lock();
-        assert(false);
-    }
-    catch (std::system_error& e)
-    {
-        assert(e.code().value() == EDEADLK);
-    }
+  try {
+    TEST_IGNORE_NODISCARD lk.try_lock();
+    assert(false);
+  } catch (std::system_error& e) {
+    assert(e.code().value() == EDEADLK);
+  }
 #endif
-    lk.unlock();
-    assert(lk.try_lock() == false);
-    assert(try_lock_called == false);
-    assert(lk.owns_lock() == false);
-    lk.release();
+  lk.unlock();
+  assert(lk.try_lock() == false);
+  assert(m.try_lock_called == false);
+  assert(lk.owns_lock() == false);
+  lk.release();
 #ifndef TEST_HAS_NO_EXCEPTIONS
-    try
-    {
-        TEST_IGNORE_NODISCARD lk.try_lock();
-        assert(false);
-    }
-    catch (std::system_error& e)
-    {
-        assert(e.code().value() == EPERM);
-    }
+  try {
+    TEST_IGNORE_NODISCARD lk.try_lock();
+    assert(false);
+  } catch (std::system_error& e) {
+    assert(e.code().value() == EPERM);
+  }
 #endif
 
   return 0;
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_for.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_for.pass.cpp
index 8e7004e5eec85..01c3e34af113b 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_for.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_for.pass.cpp
@@ -21,57 +21,36 @@
 #include <system_error>
 
 #include "test_macros.h"
+#include "../types.h"
 
-bool try_lock_for_called = false;
+MyTimedMutex m;
 
-typedef std::chrono::milliseconds ms;
-
-struct mutex
-{
-    template <class Rep, class Period>
-        bool try_lock_for(const std::chrono::duration<Rep, Period>& rel_time)
-    {
-        assert(rel_time == ms(5));
-        try_lock_for_called = !try_lock_for_called;
-        return try_lock_for_called;
-    }
-    void unlock() {}
-};
-
-mutex m;
-
-int main(int, char**)
-{
-    std::unique_lock<mutex> lk(m, std::defer_lock);
-    assert(lk.try_lock_for(ms(5)) == true);
-    assert(try_lock_for_called == true);
-    assert(lk.owns_lock() == true);
+int main(int, char**) {
+  using ms = std::chrono::milliseconds;
+  std::unique_lock<MyTimedMutex> lk(m, std::defer_lock);
+  assert(lk.try_lock_for(ms(5)) == true);
+  assert(m.try_lock_for_called == true);
+  assert(lk.owns_lock() == true);
 #ifndef TEST_HAS_NO_EXCEPTIONS
-    try
-    {
-        TEST_IGNORE_NODISCARD lk.try_lock_for(ms(5));
-        assert(false);
-    }
-    catch (std::system_error& e)
-    {
-        assert(e.code().value() == EDEADLK);
-    }
+  try {
+    TEST_IGNORE_NODISCARD lk.try_lock_for(ms(5));
+    assert(false);
+  } catch (std::system_error& e) {
+    assert(e.code().value() == EDEADLK);
+  }
 #endif
-    lk.unlock();
-    assert(lk.try_lock_for(ms(5)) == false);
-    assert(try_lock_for_called == false);
-    assert(lk.owns_lock() == false);
-    lk.release();
+  lk.unlock();
+  assert(lk.try_lock_for(ms(5)) == false);
+  assert(m.try_lock_for_called == false);
+  assert(lk.owns_lock() == false);
+  lk.release();
 #ifndef TEST_HAS_NO_EXCEPTIONS
-    try
-    {
-        TEST_IGNORE_NODISCARD lk.try_lock_for(ms(5));
-        assert(false);
-    }
-    catch (std::system_error& e)
-    {
-        assert(e.code().value() == EPERM);
-    }
+  try {
+    TEST_IGNORE_NODISCARD lk.try_lock_for(ms(5));
+    assert(false);
+  } catch (std::system_error& e) {
+    assert(e.code().value() == EPERM);
+  }
 #endif
 
   return 0;
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_until.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_until.pass.cpp
index 077bc517399ab..01e70c32f50a9 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_until.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/try_lock_until.pass.cpp
@@ -21,57 +21,36 @@
 #include <system_error>
 
 #include "test_macros.h"
+#include "../types.h"
 
-bool try_lock_until_called = false;
+MyTimedMutex m;
 
-struct mutex
-{
-    template <class Clock, class Duration>
-        bool try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time)
-    {
-        typedef std::chrono::milliseconds ms;
-        assert(Clock::now() - abs_time < ms(5));
-        try_lock_until_called = !try_lock_until_called;
-        return try_lock_until_called;
-    }
-    void unlock() {}
-};
-
-mutex m;
-
-int main(int, char**)
-{
-    typedef std::chrono::steady_clock Clock;
-    std::unique_lock<mutex> lk(m, std::defer_lock);
-    assert(lk.try_lock_until(Clock::now()) == true);
-    assert(try_lock_until_called == true);
-    assert(lk.owns_lock() == true);
+int main(int, char**) {
+  typedef std::chrono::steady_clock Clock;
+  std::unique_lock<MyTimedMutex> lk(m, std::defer_lock);
+  assert(lk.try_lock_until(Clock::now()) == true);
+  assert(m.try_lock_until_called == true);
+  assert(lk.owns_lock() == true);
 #ifndef TEST_HAS_NO_EXCEPTIONS
-    try
-    {
-        TEST_IGNORE_NODISCARD lk.try_lock_until(Clock::now());
-        assert(false);
-    }
-    catch (std::system_error& e)
-    {
-        assert(e.code().value() == EDEADLK);
-    }
+  try {
+    TEST_IGNORE_NODISCARD lk.try_lock_until(Clock::now());
+    assert(false);
+  } catch (std::system_error& e) {
+    assert(e.code().value() == EDEADLK);
+  }
 #endif
-    lk.unlock();
-    assert(lk.try_lock_until(Clock::now()) == false);
-    assert(try_lock_until_called == false);
-    assert(lk.owns_lock() == false);
-    lk.release();
+  lk.unlock();
+  assert(lk.try_lock_until(Clock::now()) == false);
+  assert(m.try_lock_until_called == false);
+  assert(lk.owns_lock() == false);
+  lk.release();
 #ifndef TEST_HAS_NO_EXCEPTIONS
-    try
-    {
-        TEST_IGNORE_NODISCARD lk.try_lock_until(Clock::now());
-        assert(false);
-    }
-    catch (std::system_error& e)
-    {
-        assert(e.code().value() == EPERM);
-    }
+  try {
+    TEST_IGNORE_NODISCARD lk.try_lock_until(Clock::now());
+    assert(false);
+  } catch (std::system_error& e) {
+    assert(e.code().value() == EPERM);
+  }
 #endif
 
   return 0;
diff --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/unlock.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/unlock.pass.cpp
index 30c795150dace..af7761e196cc4 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/unlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.locking/unlock.pass.cpp
@@ -19,45 +19,30 @@
 #include <system_error>
 
 #include "test_macros.h"
+#include "../types.h"
 
-bool unlock_called = false;
+MyMutex m;
 
-struct mutex
-{
-    void lock() {}
-    void unlock() {unlock_called = true;}
-};
-
-mutex m;
-
-int main(int, char**)
-{
-    std::unique_lock<mutex> lk(m);
-    lk.unlock();
-    assert(unlock_called == true);
-    assert(lk.owns_lock() == false);
+int main(int, char**) {
+  std::unique_lock<MyMutex> lk(m);
+  lk.unlock();
+  assert(lk.owns_lock() == false);
 #ifndef TEST_HAS_NO_EXCEPTIONS
-    try
-    {
-        lk.unlock();
-        assert(false);
-    }
-    catch (std::system_error& e)
-    {
-        assert(e.code().value() == EPERM);
-    }
+  try {
+    lk.unlock();
+    assert(false);
+  } catch (std::system_error& e) {
+    assert(e.code().value() == EPERM);
+  }
 #endif
-    lk.rele...
[truncated]

@ilovepi ilovepi requested review from petrhosek and ldionne July 18, 2024 20:43
Created using spr 1.3.4
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically LGTM but I have a few comments.

@@ -5,34 +5,32 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: no-threads, c++03
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain why this was disabled in C++03 mode. Either way, if it works in C++03 mode we might as well test it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, jeez. I missed that it was more than no-threads in this case. I'm happy to go either way, though.

@@ -0,0 +1,88 @@
//===----------------------------------------------------------------------===//
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not attached to this file: The tests used to also exercise the interoperation between std::unique_lock and std::mutex because that's the only type we used. Now, they don't anymore.

So in theory std::unique_lock<std::mutex> could be broken and we wouldn't really notice anymore. Can you add a general-purpose test that is UNSUPPORTED: no-threads and that uses std::unique_lock<std::mutex> in a couple of different ways. No need to have 100% testing coverage for unique_lock's API, but let's at least make sure that it's not broken since that would be embarrassing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. As an FYI, I may not get to that till tomorrow, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, we still have several tests that still have no-threads

  • thread.lock.unique.locking/lock.pass.cpp
  • thread.lock.unique.cons/mutex.pass.cpp
  • thread.lock.unique.cons/mutex_duration.pass.cpp
  • thread.lock.unique.cons/mutex_try_to_lock.pass.cpp
  • thread.lock.unique.cons/mutex_time_point.pass.cpp

Of these 3 use std::mutex:

  • thread.lock.unique.cons/mutex.pass.cpp
  • thread.lock.unique.cons/mutex_try_to_lock.pass.cpp
  • thread.lock.unique.locking/lock.pass.cpp

I think most of the API surface is tested in those tests. I see couple of constructors/destructors, lock, unlock, release ... I think maybe we're only missing try_for/try_until, and maybe some of the other assertions on move construction.

In light of those tests still using std::mutex do you still want me to add more tests? I'm happy to, but I can't think of much outside the try* APIs that should get exercised.

@ilovepi
Copy link
Contributor Author

ilovepi commented Jul 29, 2024

The bots seem to be running into an error w/ missing definitions for error codes. From what I can tell, no-threads shouldn't affect the availability of error codes.

In my local build I can reproduce the same error when compiling the test w/ -D_LIBCPP_NO_THREADS=1. I checked the preprocessed output, and noticed it referenced a module. Removing module related flags -fmodules -fcxx-modules -fmodule-cache-path=... stops the error from occurring. I'm unclear on why that would happen though.

@ldionne have you encountered this type of issue before? Is there a way to avoid this behavior?

@ldionne
Copy link
Member

ldionne commented Jul 30, 2024

@ilovepi Instead of doing e.g. assert(e.code().value() == EPERM);, I would do assert(e.code() == std:::errc::operation_not_permitted);. The issue must be that you're missing an include of <cerrno> in that test but IMO it's better to instead include <system_error> and use a C++ construct.

ilovepi added 2 commits July 30, 2024 08:28
Created using spr 1.3.4
Created using spr 1.3.4
Copy link

github-actions bot commented Jul 30, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Created using spr 1.3.4
@ilovepi
Copy link
Contributor Author

ilovepi commented Jul 30, 2024

@ilovepi Instead of doing e.g. assert(e.code().value() == EPERM);, I would do assert(e.code() == std:::errc::operation_not_permitted);. The issue must be that you're missing an include of <cerrno> in that test but IMO it's better to instead include <system_error> and use a C++ construct.

Thanks for the suggestion. That seems to have satisfied my local build, so I've gone ahead and updated the other tests to match.

@ilovepi ilovepi merged commit e9d5842 into main Jul 31, 2024
54 checks passed
@ilovepi ilovepi deleted the users/ilovepi/spr/libc-make-stdunique_lock-available-with-_libcpp_has_no_threads branch July 31, 2024 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants