[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