Line data Source code
1 : // Copyright (c) 2013-2021 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/cppthread
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : /** \file
21 : * \brief Implementation of the Thread Runner and Managers.
22 : *
23 : * This file includes the implementation used by the cppthread environment.
24 : */
25 :
26 :
27 : // self
28 : //
29 : #include "cppthread/guard.h"
30 :
31 : #include "cppthread/exception.h"
32 : #include "cppthread/log.h"
33 : #include "cppthread/mutex.h"
34 :
35 :
36 : // last include
37 : //
38 : #include <snapdev/poison.h>
39 :
40 :
41 :
42 : namespace cppthread
43 : {
44 :
45 :
46 :
47 : /** \class guard
48 : * \brief Lock a mutex in an RAII manner.
49 : *
50 : * This class is used to lock mutexes in a safe manner in regard to
51 : * exceptions. It is extremely important to unlock all mutexes before
52 : * a thread quits otherwise the application will lock up.
53 : *
54 : * \code
55 : * {
56 : * cppthread::guard lock(my_mutex);
57 : * ... // atomic work
58 : * }
59 : * \endcode
60 : *
61 : * \warning
62 : * This guard implementation assumes that the guard itself is used in one
63 : * single thread. In other words, even though you could add a guard inside
64 : * an object as a variable member, the lock() and is_locked() functions are
65 : * not safe in that situation. The constructor and destructor are, so you
66 : * could still do it that way. It is still preferred to push a guard on
67 : * the stack as shown in the example above, rather than as a variable member
68 : * of a class you then create on the stack.
69 : */
70 :
71 :
72 :
73 :
74 :
75 :
76 : /** \brief Lock a mutex.
77 : *
78 : * This function locks the specified mutex and keep track of the lock
79 : * until the destructor is called.
80 : *
81 : * The mutex parameter cannot be a reference to a nullptr pointer.
82 : *
83 : * \param[in] m The Snap! mutex to lock.
84 : */
85 137 : guard::guard(mutex & m)
86 137 : : f_mutex(&m)
87 : {
88 137 : if(f_mutex == nullptr)
89 : {
90 : // mutex is mandatory
91 : //
92 0 : throw cppthread_logic_error("mutex missing in guard() constructor");
93 : }
94 137 : f_mutex->lock();
95 137 : f_locked = true;
96 137 : }
97 :
98 :
99 : /** \brief Ensure that the mutex was unlocked.
100 : *
101 : * The destructor ensures that the mutex gets unlocked. Note that it is
102 : * written to avoid exceptions, however, if an exception occurs it ends
103 : * up calling exit(1).
104 : *
105 : * \note
106 : * If a function throws it logs information using the Snap! logger.
107 : */
108 274 : guard::~guard()
109 : {
110 : try
111 : {
112 137 : unlock();
113 : }
114 0 : catch(std::exception const & e)
115 : {
116 : // a log was already printed, we do not absolutely need another one
117 0 : log << log_level_t::fatal
118 0 : << "mutex::unlock() threw an exception while in the ~guard() function."
119 0 : << end;
120 0 : std::terminate();
121 : }
122 137 : }
123 :
124 :
125 : /** \brief Unlock this mutex.
126 : *
127 : * This function can be called any number of times. If the mutex is currently
128 : * locked, the function unlocks it, otherwise nothing happens.
129 : *
130 : * If necessary, you can relock the mutex using the lock() function.
131 : *
132 : * This function may throw an exception if the mutex::unlock() call fails.
133 : *
134 : * \param[in] done Whether you are done with this guard, if so, the pointer
135 : * will be set to null and you can then destroy the mutex (this is the
136 : * default). If instead you want to be able to re-lock the mutex, then set
137 : * this parameter to false. The mutex cannot be destroyed if this parameter
138 : * is set to false.
139 : *
140 : * \sa lock()
141 : */
142 137 : void guard::unlock(bool done)
143 : {
144 137 : if(f_locked)
145 : {
146 137 : mutex * m(f_mutex);
147 137 : f_locked = false;
148 137 : if(done)
149 : {
150 137 : f_mutex = nullptr;
151 : }
152 137 : m->unlock();
153 : }
154 137 : }
155 :
156 :
157 : /** \brief Relock this mutex.
158 : *
159 : * This function can be called any number of times. If called while the
160 : * mutex is not locked, then it gets relocked, otherwise nothing happens.
161 : *
162 : * Note that when creating the guard, the mutex is automatically locked,
163 : * so you rarely need to call this function.
164 : *
165 : * This is most often used when a mutex needs to be unlocked within a
166 : * guarded block:
167 : *
168 : * \code
169 : * {
170 : * cppthread::guard lock(f_mutex);
171 : *
172 : * ...do some things...
173 : *
174 : * if(this_or_that)
175 : * {
176 : * lock.unlock();
177 : * do_special_thing_while_unlocked();
178 : * lock.lock();
179 : * }
180 : *
181 : * ...do more things...
182 : * }
183 : * \endcode
184 : *
185 : * This example shows how one can run do_special_thing_while_unlocked()
186 : * while the lock is not being held. The way it is written is still
187 : * RAII safe. If the do_special_thing_while_unlocked() throws, the mutex
188 : * is in a known state (i.e. unlocked when exiting the guarded block).
189 : *
190 : * \note
191 : * This implementation always attempts a mutex::lock(), checks whether it
192 : * was necessary (i.e. is the f_locked flag false?) and if not, it unlocks
193 : * the mutex (since the guard already had the lock to itself).
194 : *
195 : * \warning
196 : * It is not 100% safe to call this function when the guard::unlock()
197 : * function is called with its done parameter set to true, which is the
198 : * default if you don't specify false in your call. It is safe if the
199 : * guard is only used on the stack.
200 : *
201 : * \sa unlock()
202 : */
203 0 : void guard::lock()
204 : {
205 0 : if(f_mutex == nullptr)
206 : {
207 0 : return;
208 : }
209 :
210 0 : f_mutex->lock();
211 :
212 0 : if(f_locked)
213 : {
214 0 : f_mutex->unlock();
215 : }
216 : else
217 : {
218 0 : f_locked = true;
219 : }
220 : }
221 :
222 :
223 : /** \brief This function returns whether the guard is current locked.
224 : *
225 : * This function returns the f_locked flag of the guard object. If true,
226 : * then the guard is expected to have its mutex locked. If false, then
227 : * the guard mutex is not currently locked.
228 : *
229 : * \warning
230 : * This function is not 100% safe if you call unlock() with its done
231 : * parameter set to true, which is the default. It is safe if the guard
232 : * is only used by on the stack.
233 : *
234 : * \return true if the guard currently holds the lock.
235 : */
236 0 : bool guard::is_locked() const
237 : {
238 0 : bool result(f_mutex != nullptr);
239 0 : if(result)
240 : {
241 0 : f_mutex->lock();
242 0 : result = f_locked;
243 0 : f_mutex->unlock();
244 : }
245 :
246 0 : return result;
247 : }
248 :
249 :
250 :
251 :
252 : /** \fn guard::guard(guard const & rhs)
253 : * \brief The copy operator is deleted.
254 : *
255 : * The guard class saves a bare pointer to the mutex it is guarding.
256 : * Because of that, a copy is not really possible and it's also not
257 : * useful. Plus Effective C++ wants it this way (which is great).
258 : *
259 : * \param[in] rhs The right hand side.
260 : */
261 :
262 :
263 : /** \fn guard & guard::operator = (guard const & rhs)
264 : * \brief The assignment operator is deleted.
265 : *
266 : * The guard class saves a bare pointer to the mutex it is guarding.
267 : * Because of that, an assignment is not really possible and it's also
268 : * not useful. Plus Effective C++ wants it this way (which is great).
269 : *
270 : * \param[in] rhs The right hand side.
271 : *
272 : * \return A reference to this object.
273 : */
274 :
275 :
276 :
277 : /** \var guard::f_mutex
278 : * \brief The mutex used by the guard class.
279 : *
280 : * Whenever you want to lock a part of your code so only one thread
281 : * runs it at any given time, you want to use a guard. This guard
282 : * makes use of a mutex that you pass to it on construction.
283 : *
284 : * The guard object keeps a reference to your mutex and uses
285 : * it to lock on construction and unlock on destruction. This
286 : * generates a perfect safe guard around your code. Safe guard
287 : * which is exception safe since it will still get unlocked when
288 : * an exception occurs.
289 : *
290 : * \warning
291 : * Note that it is not safe if you get a Unix signal. The lock
292 : * will very likely still be in place if such a signal happens
293 : * while within the lock.
294 : */
295 :
296 :
297 : /** \var guard::f_locked
298 : * \brief Whether the guard is currently in effect.
299 : *
300 : * This flag is used to know whether the mutex is currently considered locked
301 : * by the guard object.
302 : */
303 :
304 :
305 :
306 6 : } // namespace cppthread
307 : // vim: ts=4 sw=4 et
|