https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118960

            Bug ID: 118960
           Summary: Define std::mutex unconditionally
           Product: gcc
           Version: 15.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: redi at gcc dot gnu.org
  Target Milestone: ---

We have a few cases of code like this in src/c++20/format.cc

#ifndef _GLIBCXX_HAS_GTHREADS
// Dummy mutex
struct mutex
{
  void lock() const { }
  void unlock() const { }
};
#endif


This is actually incorrect, as it means we'll use a dummy mutex on a target
that supports parallelism but doesn't support all the gthreads features needed
for full C++11 support (e.g. nvptx).

It should be something like:

#ifndef __GTHREADS
// Dummy mutex
struct mutex
{
  void lock() const { }
  void unlock() const { }
};
#elif ! defined _GLIBCXX_HAS_GTHREADS
// Use __gnu_cxx::__mutex if std::mutex isn't available.
using mutex = __gnu_cxx::__mutex;
#endif

But a better solution would be to just make <bits/std_mutex.h> define
std::mutex unconditionally (at least for C++11 and later).

std::mutex doesn't actually need any of the features included in the
__GTHREAD_CXX0X set, only the basic ones from the __GTHREADS API. This means we
can define a fully functional std::mutex for any target that defines __GTHREADS
(including nvptx) and we can define a dummy no-op std::mutex otherwise:

--- a/libstdc++-v3/include/bits/std_mutex.h
+++ b/libstdc++-v3/include/bits/std_mutex.h
@@ -54,7 +54,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    * @{
    */

-#ifdef _GLIBCXX_HAS_GTHREADS
+#ifdef __GTHREADS // std::mutex only needs __GTHREADS, not __GTHREADS_CXX0X
   /// @cond undocumented

   // Common base class for std::mutex and std::timed_mutex
@@ -138,7 +138,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     native_handle() noexcept
     { return &_M_mutex; }
   };
+#else  // ! __GTHREADS
+  // No-op mutex type for single-threaded targets.
+  struct mutex
+  {
+    constexpr mutex() noexcept { }
+    mutex(const mutex&) = delete;
+    mutex& operator=(const mutex&) = delete;
+    void lock() { }
+    void unlock() { }
+    bool try_lock() noexcept { return true; }
+  };
+#endif // __GTHREADS

+#ifdef _GLIBCXX_HAS_GTHREADS
   /// @cond undocumented

   // Implementation details for std::condition_variable



By providing the dummy std:::mutex here, we don't need to keep redefining
equivalents in src/c++20/format.cc and src/c++20/tzdb.cc and elsewhere.

The downside of this is that it would break any user code which provides its
own polyfill mutex defined in namespace std. Such code is not allowed and so
undefined, but it's the kind of thing people do, e.g.

namespace std {
 using mutex = ::acme::alternative_mutex;
}


So we could do:

+#elif ! defined _GLIBCXX_NO_SINGLE_THREADED_MUTEX
+  // No-op mutex type for single-threaded targets.
+  struct mutex

So that the library can rely on that being defined for src/c++20/format.cc etc.
but user code can suppress that definition if it's not wanted.

Reply via email to