ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
sharedlock.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header file is part of module \alib_threads of the \aliblong.
4///
5/// \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
6/// Published under \ref mainpage_license "Boost Software License".
7//==================================================================================================
8#ifndef HPP_ALIB_THREADS_SHAREDLOCK
9#define HPP_ALIB_THREADS_SHAREDLOCK 1
10#pragma once
11#if !defined(DOXYGEN)
12# include "alib/alib.hpp"
13#endif
14ALIB_ASSERT_MODULE(THREADS)
15#if ALIB_DEBUG
17#endif
18#if ALIB_DEBUG_CRITICAL_SECTIONS
20#endif
21
22#include <shared_mutex>
23#include <atomic>
24
25namespace alib { namespace threads {
26// =================================================================================================
27/// This class is a simple wrapper around C++ standard library type \c std::shared_mutex.
28/// Thus, it is used to implement <em>mutual exclusive access</em> to resources by protecting
29/// critical code sections from being executed in parallel in concurrent threads, while
30/// allowing typical read-operations to continue to be executed in parallel.
31///
32/// When a pair of #Acquire and #Release invocations is performed within the same code block, then
33/// it is recommended to use a stack instantiation of class \alib{lang;Owner} to acquire and release
34/// objects of this class.
35/// Such a use is highly simplified with macros \ref ALIB_LOCK and \ref ALIB_LOCK_WITH.<br>
36/// The same is recommended for paired invocations of #AcquireShared and #ReleaseShared.
37/// Here, class \alib{lang;OwnerShared} is to be used, best using macros \ref ALIB_LOCK_SHARED and
38/// \ref ALIB_LOCK_SHARED_WITH.
39///
40/// This class does not allow nested calls to the method #Acquire - method #Release has to be
41/// invoked (from within the same thread that acquired this mutex), before any other thread can
42/// again gain access. Nested acquisitions constitute undefined behavior.
43///
44/// Typically macros \ref ALIB_LOCK and \ref ALIB_LOCK_WITH should be used to acquire
45/// this lock in non-shared mode and macros ALIB_LOCK_SHARED and \ref ALIB_LOCK_SHARED_WITH
46/// for shared acquisitions.
47///
48/// \par Debug-Features
49/// Public member #Dbg is available with debug-compilations. It offers the following features:
50/// - An assertion is raised when nested use is performed.
51/// - The object stores the actual owning thread and the source code position of the last
52/// acquirement.
53/// Likewise the last shared acquirement's caller information is stored.
54/// - Releasing non-acquired instances, as well as destructing acquired one, raise an assertion.
55/// - Field \alib{threads;DbgLockAsserter;WaitTimeLimit} enables the raise of \alib warnings in case a
56/// certain wait time is exceeded.
57/// Note that instead of wrapping \c std::shared_mutex, with debug-compilations class
58/// \c std::shared_timed_mutex is wrapped.
59/// - Field #DbgWarningMaximumShared enables the raise of \alib warnings in the case that
60/// the number of parallel shared acquirements reaches the limit given with this member.
61///
62/// @see
63/// - Chapter \ref alib_threads_locks of the Programmer's Manual of the module \alib_threads_nl.
64/// - Chapter \ref alib_manual_appendix_callerinfo of the General Programmer's Manual.
65// =================================================================================================
69#endif
70{
71 protected:
72 #if !ALIB_DEBUG && !DOXYGEN
73 std::shared_mutex mutex; // the only member in release compilations
74
75 #else
76 #if DOXYGEN
77 /// The internal object to lock on.
78 /// \note With debug-compilations, this is of type <c>std::timed_mutex</c>.
79 std::shared_mutex mutex;
80 #else
81 std::shared_timed_mutex mutex;
82 #endif
83
84 public:
85 /// The debug tool instance.
87
88 /// Warning-threshold of maximum number of parallel shared acquisitions.<br>
89 /// Defaults to 10.
90 std::atomic<int> DbgWarningMaximumShared =10;
91 #endif
92 public:
93
94 #if ALIB_DEBUG_CRITICAL_SECTIONS
95 /// Destructor. With debug-compilations, asserts that this lock is not acquired.
96 ~SharedLock() override
97 { Dbg.AssertNotOwned( ALIB_CALLER, ALIB_CALLER, "Destructing acquired lock" ); }
98
99 /// @return \c true if the lock is acquired (in non-shared mode), \c false otherwise.
100 ALIB_API virtual bool DCSIsAcquired() const override;
101
102 /// @return \c true if the lock is shared-acquired (by at least any thread).
103 /// Otherwise, returns \c false.
104 ALIB_API virtual bool DCSIsSharedAcquired() const override;
105 #elif ALIB_DEBUG
107 { Dbg.AssertNotOwned( ALIB_CALLER, ALIB_CALLER, "Destructing acquired lock" ); }
108 #endif
109
110 // ===============================================================================================
111 // ==== Standard Acquire/Release (Writer)
112 // ===============================================================================================
113
114 #if ALIB_DEBUG || DOXYGEN
115 /// Acquires this lock.
116 /// In the case that this object is already owned by another thread, the invoking thread is
117 /// suspended until ownership can be gained.
118 /// Multiple (nested) calls to this method are not supported and lead to undefined behavior.
119 ///
120 /// \par Debug Parameter:
121 /// Pass macro \ref ALIB_CALLER_PRUNED with invocations.
124
125 /// Tries to acquire this lock.
126 /// Multiple (nested) successful calls to this method or method #Acquire are not supported and
127 /// lead to undefined behavior.
128 ///
129 /// \par Debug Parameter:
130 /// Pass macro \ref ALIB_CALLER_PRUNED with invocations.
131 /// @return \c true if the lock was not acquired by a different thread and thus, this
132 /// call was successful. \c false otherwise.
134 [[nodiscard]]
136
137 /// Releases ownership of this object.
138 /// If this method is invoked on an object that is not acquired, in debug-compilations an
139 /// assertion is raised. In release compilations, this leads to undefined behavior.
140 /// \par Debug Parameter:
141 /// Pass macro \ref ALIB_COMMA_CALLER_PRUNED with invocations.
144
145 #else
146 void Acquire() { mutex.lock(); }
147 [[nodiscard]] bool TryAcquire() { return mutex.try_lock(); }
148 void Release() { mutex.unlock(); }
149
150 #endif
151
152
153 // ===============================================================================================
154 // ==== Shared Acquire/Release (Reader)
155 // ===============================================================================================
156 #if ALIB_DEBUG || DOXYGEN
157 /// Acquires this lock in shared mode.
158 /// In the case that this object is already owned (not shared) by another thread, the invoking
159 /// thread is suspended until ownership can be gained.
160 /// Multiple (nested) calls to this method are not supported and lead to undefined behavior.
161 ///
162 /// \par Debug Parameter:
163 /// Pass macro \ref ALIB_CALLER_PRUNED with invocations.
165 void
167
168 /// Tries to acquire this lock.
169 /// Multiple (nested) calls to this method or method #AcquireShared from within the same thread
170 /// are not supported and lead to undefined behavior.
171 ///
172 /// \par Debug Parameter:
173 /// Pass macro \ref ALIB_CALLER_PRUNED with invocations.
174 /// @return \c true if the lock was not acquired by a different thread and thus, this
175 /// call was successful. \c false otherwise.
177 [[nodiscard]]
179
180 /// Releases shared ownership of this object.
181 /// Invoking this method on an object that is not "shared acquired" by this thread constitutes
182 /// undefined behavior.<br>
183 /// In debug-compilations the overall sum (of any thread) of shared acquirements and releases
184 /// is counted, and an assertion is raised if more releases than acquisitions are performed.
185 /// \par Debug Parameter:
186 /// Pass macro \ref ALIB_CALLER_PRUNED with invocations.
189 #else
190 void AcquireShared() { mutex.lock_shared(); }
191 [[nodiscard]] bool TryAcquireShared() { return mutex.try_lock_shared(); }
192 void ReleaseShared() { mutex.unlock_shared(); }
193 #endif
194
195};
196
197
198} // namespace alib[::threads]
199
200/// Type alias in namespace \b alib.
202
203} // namespace [alib]
204
205#endif // HPP_ALIB_THREADS_SHAREDLOCK
206
virtual ALIB_API bool DCSIsAcquired() const override
Definition locks.cpp:282
ALIB_API bool TryAcquireShared(ALIB_DBG_TAKE_CI)
Definition locks.cpp:751
ALIB_API void AcquireShared(ALIB_DBG_TAKE_CI)
Definition locks.cpp:713
virtual ALIB_API bool DCSIsSharedAcquired() const override
Definition locks.cpp:284
DbgSharedLockAsserter Dbg
The debug tool instance.
std::shared_mutex mutex
ALIB_API void ReleaseShared(ALIB_DBG_TAKE_CI)
Definition locks.cpp:769
ALIB_API bool TryAcquire(ALIB_DBG_TAKE_CI)
Definition locks.cpp:687
ALIB_API void Acquire(ALIB_DBG_TAKE_CI)
Definition locks.cpp:653
ALIB_API void Release(ALIB_DBG_TAKE_CI)
Definition locks.cpp:700
std::atomic< int > DbgWarningMaximumShared
~SharedLock() override
Destructor. With debug-compilations, asserts that this lock is not acquired.
#define ALIB_DBG_TAKE_CI
Definition alib.hpp:1176
#define ALIB_ASSERT_MODULE(modulename)
Definition alib.hpp:223
#define ALIB_CALLER
Definition alib.hpp:1164
#define ALIB_API
Definition alib.hpp:639
#define ALIB_DEBUG_CRITICAL_SECTIONS
Definition prepro.md:44
Definition alib.cpp:69
void AssertNotOwned(const CallerInfo &assertCI, const CallerInfo &ci, const NString &headline)