[libcxx-commits] [libcxx] [libc++] Adds shared resource helper. (PR #72529)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Thu Nov 16 08:27:00 PST 2023


https://github.com/mordante created https://github.com/llvm/llvm-project/pull/72529

This is based on the __wrapped_streambuf_mutex class used in <syncstream>. This is a generic version set to replace __wrapped_streambuf_mutex.

>From 9486adb7b90fdf8c5a2c7bef0792aff48f394de4 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 11 Nov 2023 11:29:59 +0100
Subject: [PATCH] [libc++] Adds shared resource helper.

This is based on the __wrapped_streambuf_mutex class used in
<syncstream>. This is a generic version set to replace
__wrapped_streambuf_mutex.
---
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__availability                 |  9 ++
 libcxx/include/__utility/shared_resource.h    | 59 +++++++++++++
 libcxx/include/module.modulemap.in            |  1 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  3 +
 libcxx/src/CMakeLists.txt                     |  1 +
 libcxx/src/shared_resource.cpp                | 83 +++++++++++++++++++
 .../utility/shared_resource.pass.cpp          | 44 ++++++++++
 libcxx/utils/libcxx/test/features.py          |  9 ++
 9 files changed, 210 insertions(+)
 create mode 100644 libcxx/include/__utility/shared_resource.h
 create mode 100644 libcxx/src/shared_resource.cpp
 create mode 100644 libcxx/test/libcxx/utilities/utility/shared_resource.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 889d7fedbf2965f..75d47979645c7bd 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -854,6 +854,7 @@ set(files
   __utility/piecewise_construct.h
   __utility/priority_tag.h
   __utility/rel_ops.h
+  __utility/shared_resource.h
   __utility/small_buffer.h
   __utility/swap.h
   __utility/to_underlying.h
diff --git a/libcxx/include/__availability b/libcxx/include/__availability
index 99a16c968de3c60..66985dbc974b633 100644
--- a/libcxx/include/__availability
+++ b/libcxx/include/__availability
@@ -139,6 +139,12 @@
 // #   define _LIBCPP_AVAILABILITY_HAS_NO_TZDB
 #   define _LIBCPP_AVAILABILITY_TZDB
 
+    // This controls the availability of the shared resource code.
+    // This is used in syncstream to synchronize write operations,
+	// but the code is generic and can be used for similar purposes.
+// #   define _LIBCPP_AVAILABILITY_HAS_NO_SHARED_RESOURCE
+#   define _LIBCPP_AVAILABILITY_SHARED_RESOURCE
+
 // Enable additional explicit instantiations of iostreams components. This
 // reduces the number of weak definitions generated in programs that use
 // iostreams by providing a single strong definition in the shared library.
@@ -241,6 +247,9 @@
 #  define _LIBCPP_AVAILABILITY_HAS_NO_TZDB
 #  define _LIBCPP_AVAILABILITY_TZDB __attribute__((unavailable))
 
+#   define _LIBCPP_AVAILABILITY_HAS_NO_SHARED_RESOURCE
+#   define _LIBCPP_AVAILABILITY_SHARED_RESOURCE __attribute__((unavailable))
+
 #  if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 120000)   || \
       (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 150000) || \
       (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 150000)         || \
diff --git a/libcxx/include/__utility/shared_resource.h b/libcxx/include/__utility/shared_resource.h
new file mode 100644
index 000000000000000..ee5182007a9c65f
--- /dev/null
+++ b/libcxx/include/__utility/shared_resource.h
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___UTILITY_SHARED_RESOURCE_H
+#define _LIBCPP___UTILITY_SHARED_RESOURCE_H
+
+#include <__availability>
+#include <__config>
+#include <__mutex/lock_guard.h>
+#include <__mutex/mutex.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP_HAS_NO_THREADS
+
+// Contains helper functions for a reference counted resource.
+// The resources are identified by their address. The reference counting is
+// done by calling increasing and decreasing the reference count at the
+// appropriate time.
+//
+// There are two ways to guard the resource against concurrent access:
+// - Call the lock function. The function locks two mutexes
+//   - The internal map's mutex
+//   - The mutex of the resource
+//   This approach does not require to store a pointer/reference to the mutex,
+//   reducing the size of the object.
+// - Store the mutex of the inc function. Then the caller can lock the mutex at
+//   the appropriate time.
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// std::mutex is available as an extension since C++03, but returning a
+// lock_guard requires C++17. Since the current use-cases require newer C++
+// versions the older versions are not supported.
+#  if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_RESOURCE mutex&
+__shared_resource_inc_reference(const void* __ptr);
+
+// precondition: __ptr's reference count > 0
+_LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_RESOURCE void __shared_resource_dec_reference(const void* __ptr);
+
+// precondition: __ptr's reference count > 0
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_RESOURCE lock_guard<mutex>
+__shared_resource_get_lock(const void* __ptr);
+
+#  endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_HAS_NO_THREADS
+#endif // _LIBCPP___UTILITY_SHARED_RESOURCE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 17ebe48f329963d..607caea93ce2f23 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2084,6 +2084,7 @@ module std_private_utility_piecewise_construct    [system] { header "__utility/p
 module std_private_utility_priority_tag           [system] { header "__utility/priority_tag.h" }
 module std_private_utility_rel_ops                [system] { header "__utility/rel_ops.h" }
 module std_private_utility_small_buffer           [system] { header "__utility/small_buffer.h" }
+module std_private_utility_shared_resource        [system] { header "__utility/shared_resource.h" }
 module std_private_utility_swap                   [system] {
   header "__utility/swap.h"
   export std_private_type_traits_is_swappable
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 49e3579614ee8c1..0e6661dbb6df9bb 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1227,7 +1227,10 @@
 {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__126__shared_resource_get_lockEPKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__shared_resource_dec_referenceEPKv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__shared_resource_inc_referenceEPKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 168, 'type': 'OBJECT'}
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 156dbe8a4c2f92e..c9671d76e48380c 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -72,6 +72,7 @@ if (LIBCXX_ENABLE_THREADS)
     mutex_destructor.cpp
     mutex.cpp
     shared_mutex.cpp
+    shared_resource.cpp
     thread.cpp
     )
 endif()
diff --git a/libcxx/src/shared_resource.cpp b/libcxx/src/shared_resource.cpp
new file mode 100644
index 000000000000000..6c4fb442a2c1c82
--- /dev/null
+++ b/libcxx/src/shared_resource.cpp
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <map>
+#include <mutex>
+#include <shared_mutex>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class __shared_resource {
+public:
+  _LIBCPP_HIDE_FROM_ABI __shared_resource()              = default;
+  __shared_resource(const __shared_resource&)            = delete;
+  __shared_resource& operator=(const __shared_resource&) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI mutex& __inc_reference(const void* __ptr) {
+    _LIBCPP_ASSERT_NON_NULL(__ptr != nullptr, "not a valid resource");
+    unique_lock __lock{__mutex_};
+
+    auto& __resource = __lut_[reinterpret_cast<uintptr_t>(__ptr)];
+    ++__resource.__count;
+    return __resource.__mutex;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void __dec_reference(const void* __ptr) {
+    unique_lock __lock{__mutex_};
+
+    auto __it = __get_it(__ptr);
+    if (__it->second.__count == 1)
+      __lut_.erase(__it);
+    else
+      --__it->second.__count;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex> __get_lock(const void* __ptr) {
+    shared_lock __lock{__mutex_};
+    return lock_guard{__get_it(__ptr)->second.__mutex};
+  }
+
+  [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI __shared_resource& __instance() {
+    static __shared_resource __result;
+    return __result;
+  }
+
+private:
+  struct __value {
+    mutex __mutex;
+    size_t __count{0};
+  };
+
+  shared_mutex __mutex_;
+  map<uintptr_t, __value> __lut_;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI map<uintptr_t, __value>::iterator __get_it(const void* __ptr) {
+    _LIBCPP_ASSERT_NON_NULL(__ptr != nullptr, "not a valid resource");
+
+    auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
+    _LIBCPP_ASSERT_INTERNAL(__it != __lut_.end(), "the resource is not registered");
+    _LIBCPP_ASSERT_INTERNAL(__it->second.__count > 0, "the resouce is not active");
+    return __it;
+  }
+};
+
+_LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_RESOURCE mutex&
+__shared_resource_inc_reference(const void* __ptr) {
+  return __shared_resource::__instance().__inc_reference(__ptr);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_RESOURCE void __shared_resource_dec_reference(const void* __ptr) {
+  __shared_resource::__instance().__dec_reference(__ptr);
+}
+
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_RESOURCE lock_guard<mutex>
+__shared_resource_get_lock(const void* __ptr) {
+  return __shared_resource::__instance().__get_lock(__ptr);
+}
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/utilities/utility/shared_resource.pass.cpp b/libcxx/test/libcxx/utilities/utility/shared_resource.pass.cpp
new file mode 100644
index 000000000000000..32a46d1c6a1ee12
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/shared_resource.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: no-threads
+// XFAIL: availability-shared_resource-missing
+
+#include <__utility/shared_resource.h>
+#include <mutex>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  int a, b, c;
+
+  std::mutex& a_mutex = std::__shared_resource_inc_reference(&a);
+  std::mutex& b_mutex = std::__shared_resource_inc_reference(&b);
+  std::__shared_resource_inc_reference(&c);
+
+  { // lock all three objects, all objects have an unique address so no dead locks.
+    std::lock_guard a_lock{a_mutex};
+    std::lock_guard b_lock{b_mutex};
+    std::lock_guard c_lock = std::__shared_resource_get_lock(&c);
+  }
+  { // Test the lock of a locks the mutex returned by inc.
+    std::lock_guard a_lock = std::__shared_resource_get_lock(&a);
+    assert(!a_mutex.try_lock());
+
+    std::lock_guard b_lock{b_mutex};
+    std::lock_guard c_lock = std::__shared_resource_get_lock(&c);
+  }
+
+  std::__shared_resource_dec_reference(&a);
+  std::__shared_resource_dec_reference(&b);
+  std::__shared_resource_dec_reference(&c);
+
+  return 0;
+}
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 29822f55521360b..f933a0b0e7112aa 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -533,6 +533,15 @@ def check_gdb(cfg):
             cfg.available_features,
         ),
     ),
+    # Tests that require the shared_resource in the built library
+    Feature(
+        name="availability-shared_resource-missing",
+        when=lambda cfg: BooleanExpression.evaluate(
+            # TODO(ldionne) Please provide the correct value.
+            "(stdlib=apple-libc++ && target={{.+}}-apple-macosx{{(10.13|10.14|10.15|11.0|12.0|13.0)(.0)?}})",
+            cfg.available_features,
+        ),
+    ),
     # Tests that require 64-bit architecture
     Feature(
         name="32-bit-pointer",



More information about the libcxx-commits mailing list