ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
smartlock.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_SMARTLOCK
9#define HPP_ALIB_THREADS_SMARTLOCK 1
10
11#if !defined (HPP_ALIB_THREADS_THREADLOCK)
13#endif
14
15#if !defined (_GLIBCXX_VECTOR) && !defined(_VECTOR_)
16 #include <vector>
17#endif
18
19namespace alib { namespace threads {
20
21/** ************************************************************************************************
22 * This class extends class ThreadLock, adding functionality to register 'acquirers' of type
23 * \b %ThreadLock. Only with the second \e acquirer added, the lock is activated using method
24 * \alib{threads;ThreadLock::SetSafeness}.
25 * The goal is to not use a mutex, when such use is not needed. In occasions with very high
26 * frequency of acquisition, this can provide a performance benefit.
27 *
28 * <b>The following rules apply:</b><br>
29 * - An instance of this type must not be acquired before an \e acquirer is registered.
30 * - The \e acquirers have to be in recursive mode.
31 * - If \e acquirers are locked in a nested fashion, then they have to be added
32 * in the same order they are locked and removed in reverse order
33 * - An \e acquirer must not be added twice. (This is not a technical restriction, but a chosen
34 * design. While a second addition is ignored, in debug versions of the code, an
35 * <em>ALib Error Report</em> is written (by default this triggers an assertion).
36 *
37 * <b>Using nulled acquirers:</b><br>
38 * Sometimes it is useful to add a \c nullptr as an \e acquirer. A sample for this is found and
39 * explained static field #StdOutputStreams.
40 * If the first acquirer is nullptr, the second should be added in a thread-safe way. This means,
41 * the code invoking #AddAcquirer needs to care for itself, that this object is not acquired
42 * during this process. E.g. it can be done in the bootstrap section of a process, when no parallel
43 * threads were started. For further acquirers, such care does not need to be taken.
44 * While an \e acquirer must not be attached twice, 'anonymous' (nullptr) \e acquirers may.
45 * For each anonymous invocation of #AddAcquirer, a corresponding call #RemoveAcquirer is
46 * needed, to get back to \b Safeness::Unsafe.
47 **************************************************************************************************/
48class SmartLock : public ThreadLock
49{
50 // #############################################################################################
51 // Protected fields
52 // #############################################################################################
53 protected:
54 /** A lock for acquirer management. */
56
57 /** The list of acquirers. */
58 std::vector<ThreadLock*> acquirers;
59
60 // #############################################################################################
61 // Constructors
62 // #############################################################################################
63 public:
64
65 /** ****************************************************************************************
66 * Constructs a SmartLock. Parent ThreadLock is initialized to Unsafe mode.
67 ******************************************************************************************/
68 SmartLock() : ThreadLock( lang::Safeness::Unsafe )
69 {}
70
71 /** ****************************************************************************************
72 * Overwriting ThreadLock::Acquire. With debug builds, asserts that at least one
73 * acquirer is set.
74 *
75 * \note
76 * In debug-compilations of the library, this method accepts three parameters,
77 * providing information about the caller. In the release version these parameters do not
78 * exist. Therefore use macro #ALIB_CALLER_PRUNED to provide the parameters:
79 *
80 * sample.Acquire( ALIB_CALLER_PRUNED );
81 *
82 * @param dbgFile Caller information. Available only with debug builds.
83 * @param dbgLine Caller information. Available only with debug builds.
84 * @param dbgFunc Caller information. Available only with debug builds.
85 ******************************************************************************************/
86 #if ALIB_DEBUG
87 void Acquire( const NCString& dbgFile, int dbgLine, const NCString& dbgFunc )
88 {
89 ALIB_ASSERT_ERROR( acquirers.size() > 0, "THREADS", "Must not be acquired without acquirers." )
90 ThreadLock::Acquire(dbgFile, dbgLine, dbgFunc);
91 }
92 #else
93 void Acquire()
94 {
95 ALIB_ASSERT_ERROR( acquirers.size() > 0, "THREADS", "Must not be acquired without acquirers." )
97 }
98 #endif
99
101
102 // #############################################################################################
103 // Interface
104 // #############################################################################################
105 public:
106 /** ****************************************************************************************
107 * Adds an acquirer. With the second acquirer added, this lock will be set into safe mode.
108 * @param newAcquirer The acquirer to add.
109 * @return The new number of \e acquirers set.
110 ******************************************************************************************/
112 int AddAcquirer( ThreadLock* newAcquirer );
113
114 /** ****************************************************************************************
115 * Removes an acquirer. If the amount of acquirers after removal equals \c 1, then
116 * this lock will be set to unsafe mode.
117 * @param acquirer The acquirer to remove.
118 * @return The new number of \e acquirers set.
119 ******************************************************************************************/
121 int RemoveAcquirer( ThreadLock* acquirer );
122
123 /** ****************************************************************************************
124 * Returns the number of \e acquirers. This is for debug and statistics purposes.
125 * @return The number of \e acquirers set.
126 ******************************************************************************************/
128 int CntAcquirers();
129
130 /** ****************************************************************************************
131 * This is a static singleton of this class that allows to lock an application's
132 * <em>standard output streams</em>.
133 *
134 * In multi-threaded processes, to protect the output streams from concurrent access,
135 * this smart lock might be used by any \e entity that writes data to the streams.
136 * Before it can be used (acquired and released), it is needed to register with the object
137 * using \alib{threads;SmartLock::AddAcquirer}.
138 * This has to be done once per thread that aims to write to the stream. Then, prior to
139 * writing, this object has to be acquired and after writing released.
140 *
141 * Because often, the standard \e output stream and standard \e error stream are identical,
142 * \alib provides one single lock for both, to protect also against interwoven
143 * standard output and error information.
144 *
145 * If the 'entity' that is registering is not of type \alib{threads;ThreadLock} it is allowed to
146 * provide \c nullptr in the parameter of method \b AddAcquirer. In this case, the process
147 * of adding and removing \e acquirers is not performed in a thread safe way.
148 * Therefore it is advised to register so called anonymous (\c nullptr) \e acquirers only at
149 * bootstrap time, when no parallel threads were started, yet.
150 *
151 * If an application is deemed to always write to the standard output streams from within
152 * multiple threads, an alternative to registering each writing entity, is to
153 * invoke \b AddAcquirer just two times in a row with \c nullptr at the start of a process
154 * and then never do this again (and never de-register). This way, no thread needs
155 * to register/de-register but threads may still \b Acquire and \b Release the lock without
156 * being registered. In other words, once a smart lock is enabled, subsequent registrations
157 * are just used to count and identify the de-registration.
158 *
159 * \note
160 * The advantage of the SmartLock is that if only one 'entity' registered, no
161 * system \e mutexes will be used with \b Acquire and \b Release, hence there is
162 * a performance gain. Such gain is not noticeable for the 'slow' terminal console output,
163 * but it is for fast, buffered output streams.
164 * <p>
165 * \note
166 * Logging module \alib_alox, which is built on \alib, will register whenever a \e Logger
167 * is used that writes to the standard output stream. Hence, applications that in
168 * parallel use, e.g. 'std::cout', should register at bootstrap and \e acquire this
169 * instance prior to writing. This way, log output and other application output is
170 * not mixed, but separated in different Lines.
171 * <p>
172 ******************************************************************************************/
173 ALIB_API static
175};
176
177
178
179} // namespace alib[::threads]
180
181/// Type alias in namespace \b alib.
183
184} // namespace [alib]
185
186#endif // HPP_ALIB_THREADS_SMARTLOCK
ALIB_API int RemoveAcquirer(ThreadLock *acquirer)
Definition smartlock.cpp:92
void Acquire(const NCString &dbgFile, int dbgLine, const NCString &dbgFunc)
Definition smartlock.hpp:87
std::vector< ThreadLock * > acquirers
Definition smartlock.hpp:58
ALIB_API int CntAcquirers()
Definition smartlock.cpp:23
ALIB_API int AddAcquirer(ThreadLock *newAcquirer)
Definition smartlock.cpp:29
static ALIB_API SmartLock StdOutputStreams
ALIB_API void Acquire(const NCString &dbgFile, int dbgLine, const NCString &dbgFunc)
ALIB_API void Release()
defined(ALIB_DOX)
#define ALIB_API
Definition alib.hpp:538
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:984
Definition alib.cpp:57