c++-gtk-utils: rw_lock.h Source File

c++-gtk-utils

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

rw_lock.h
Go to the documentation of this file.
1 /* Copyright (C) 2010 to 2014 Chris Vine
2
3 The library comprised in this file or of which this file is part is
4 distributed by Chris Vine under the GNU Lesser General Public
5 License as follows:
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public License
9 as published by the Free Software Foundation; either version 2.1 of
10 the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License, version 2.1, for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License, version 2.1, along with this library (see the file LGPL.TXT
19 which came with this source code package in the c++-gtk-utils
20 sub-directory); if not, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
23 However, it is not intended that the object code of a program whose
24 source code instantiates a template from this file or uses macros or
25 inline functions (of any length) should by reason only of that
26 instantiation or use be subject to the restrictions of use in the GNU
27 Lesser General Public License. With that in mind, the words "and
28 macros, inline functions and instantiations of templates (of any
29 length)" shall be treated as substituted for the words "and small
30 macros and small inline functions (ten lines or less in length)" in
31 the fourth paragraph of section 5 of that licence. This does not
32 affect any other reason why object code may be subject to the
33 restrictions in that licence (nor for the avoidance of doubt does it
34 affect the application of section 2 of that licence to modifications
35 of the source code in this file).
36
37 */
38
39 #ifndef CGU_RW_LOCK_H
40 #define CGU_RW_LOCK_H
41
42 #include <exception>
43 #include <pthread.h>
44
45 #include <c++-gtk-utils/mutex.h> // for Locked and DeferLock enumerations
46 #include <c++-gtk-utils/cgu_config.h>
47
48 /**
49 * @file rw_lock.h
50 * @brief Provides wrapper class for pthread read-write locks, and
51 * scoped locking classes for exception safe locking of read-write
52 * locks.
53 */
54
55 namespace Cgu {
56
57 namespace Thread {
58
59 struct RWLockError: public std::exception {
60 virtual const char* what() const throw() {return "Thread::RWLockError";}
61 };
62
63 /**
64 * @class RWLock rw_lock.h c++-gtk-utils/rw_lock.h
65 * @brief A wrapper class for pthread read-write locks.
66 * @sa Thread::Thread Thread::RWLock::ReaderLock Thread::RWLock::ReaderTrackLock Thread::RWLock::WriterLock Thread::RWLock::WriterTrackLock Thread::Mutex
67 *
68 * This class can be used interchangeably with threads started with
69 * GThread and by this library, as both glib and this library use
70 * pthreads underneath on POSIX and other unix-like OSes. It can also
71 * in practice be used interchangeably with those started by C++11/14
72 * std::thread, as in C++11/14 on unix-like OSes these facilities will
73 * be built on top of pthreads (for which purpose C++11/14 provides
74 * the std::native_handle_type type and std::thread::native_handle()
75 * function), or if they are not, they will use the same threading
76 * primitives provided by the kernel.
77 *
78 * RWLock objects can be constructed statically as well as dynamically
79 * and there is no need to call g_thread_init() before they are
80 * constructed, even if glib < 2.32 is used. (If created as a static
81 * object in global scope, it will not be possible to catch
82 * Thread::RWLockError thrown by its constructor, but if a static
83 * global read-write lock throws there is nothing that could be done
84 * anyway except abort, and it would show that the pthreads
85 * installation is seriously defective.)
86 *
87 * Read-write locks are similar to mutexes except that they allow more
88 * than one thread to hold the lock for reading at once. This can
89 * offer advantages over a mutex where a particular shared object is
90 * thread safe for lock-free reading by multiple threads
91 * simultaneously, is frequently read by different threads and is not
92 * often modified. However, the implementation of a read-write lock
93 * is more complex than that of a mutex, and unless the particular
94 * pthread read-write lock scheduling implementation favours
95 * already-blocking writers over later readers whenever a read-write
96 * lock object is unlocked, writer starvation can occur. Unless all
97 * the reads are of significant duration, might cause (if protected by
98 * a mutex) significant contention between each other and greatly
99 * exceed the number of times the write lock is held, then it is
100 * usually better to use an ordinary mutex.
101 */
102
103 class RWLock {
104 pthread_rwlock_t pthr_rwlock;
105
106 public:
107 class ReaderLock;
108 class ReaderTrackLock;
109 class WriterLock;
110 class WriterTrackLock;
111
112 /**
113 * This class cannot be copied. The copy constructor is deleted.
114 */
115 RWLock(const RWLock&) = delete;
116
117 /**
118 * This class cannot be copied. The assignment operator is deleted.
119 */
120 RWLock& operator=(const RWLock&) = delete;
121
122 /**
123 * Locks the read-write lock for reading. Blocks if already locked
124 * for writing until it becomes free. More than one thread may
125 * simultaneously hold a read lock, and a thread may lock for reading
126 * recursively provided that each call to this method is matched by a
127 * call to unlock(). It is not a cancellation point. It does not
128 * throw. It is thread safe.
129 * @return 0 if successful, otherwise the pthread read-write lock
130 * error number.
131 * @note With this library implementation, the only pthread error
132 * numbers which could be returned by this method are EDEADLK and
133 * EAGAIN. EDEADLK would be returned if the default pthread reader
134 * lock behaviour happens to return that error rather than deadlock
135 * where the thread calling this method already holds a write lock on
136 * this read-write lock. Most default implementations do not do this
137 * (they just deadlock) and hence the return value is usually not
138 * worth checking for except during debugging. EAGAIN would be
139 * returned if the maximum number of read locks for this read-write
140 * lock has been reached. Usually this number is at or around INT_MAX
141 * so it is also not usually useful to check for it except during
142 * debugging.
143 */
144 int reader_lock() noexcept {return pthread_rwlock_rdlock(&pthr_rwlock);}
145
146 /**
147 * Tries to lock the read-write lock for reading, but returns
148 * immediately with value EBUSY if it is already locked for writing.
149 * More than one thread may simultaneously hold a read lock, and a
150 * thread may lock for reading recursively provided that each
151 * successful call to this method is matched by a call to unlock().
152 * It is not a cancellation point. It does not throw. It is thread
153 * safe.
154 * @return 0 if successful, otherwise EBUSY or other pthread
155 * read-write lock error number.
156 * @note With this library implementation, apart from EBUSY, the only
157 * other pthread error number which could be returned by this method
158 * is EAGAIN, which would be returned if the maximum number of read
159 * locks for this read-write lock has been reached. Usually this
160 * number is at or around INT_MAX so it is not usually useful to check
161 * for it except during debugging.
162 */
163 int reader_trylock() noexcept {return pthread_rwlock_tryrdlock(&pthr_rwlock);}
164
165 /**
166 * Locks the read-write lock for writing and acquires ownership.
167 * Blocks if already locked for reading or writing until it becomes
168 * free. It is not a cancellation point. It does not throw. It is
169 * thread safe.
170 * @return 0 if successful, otherwise the pthread read-write lock
171 * error number.
172 * @note With this library implementation, the only pthread error
173 * number which could be returned by this method is EDEADLK, which it
174 * would do if the default pthread reader lock behaviour happens to
175 * return that error rather than deadlock where the thread calling
176 * this method already holds a read lock or write lock on this
177 * read-write lock. Most default implementations do not do this (they
178 * just deadlock) and hence the return value is usually not worth
179 * checking for except during debugging.
180 */
181 int writer_lock() noexcept {return pthread_rwlock_wrlock(&pthr_rwlock);}
182
183 /**
184 * Tries to lock the read-write lock for writing and acquire
185 * ownership, but returns immediately with value EBUSY if it is
186 * already locked for reading or writing. It is not a cancellation
187 * point. It does not throw. It is thread safe.
188 * @return 0 if successful, otherwise EBUSY.
189 * @note With this library implementation, the only pthread error
190 * number which could be returned by this method is EBUSY.
191 */
192 int writer_trylock() noexcept {return pthread_rwlock_trywrlock(&pthr_rwlock);}
193
194 /**
195 * Unlocks a read-write lock previously locked for reading or writing
196 * by the calling thread. If the calling thread has locked the
197 * read-write lock for writing, it relinquishes ownership. If it has
198 * previously locked the read-write lock for reading, it releases that
199 * particular lock, but the read-write lock may remain locked for
200 * reading if it has been locked for reading recursively or other
201 * threads hold a read lock and the particular implementation does not
202 * provide writer priority. It is not a cancellation point. It does
203 * not throw.
204 * @return 0 if successful, otherwise the pthread read-write lock
205 * error number.
206 * @note With this library implementation, the only pthread error
207 * number which could be returned by this method is EPERM because the
208 * calling thread does hold a lock on this read-write lock (however
209 * POSIX does not require that return value in that case and hence the
210 * return value is usually not worth checking for except during
211 * debugging).
212 */
213 int unlock() noexcept {return pthread_rwlock_unlock(&pthr_rwlock);}
214
215 /**
216 * Initialises the pthread read-write lock. It is not a cancellation
217 * point.
218 * @exception Cgu::Thread::RWLockError Throws this exception if
219 * initialisation of the read-write lock fails. (It is often not
220 * worth checking for this, as it means either memory is exhausted or
221 * pthread has run out of other resources to create new read-write
222 * locks.)
223 */
224 RWLock() {if (pthread_rwlock_init(&pthr_rwlock, 0)) throw RWLockError();}
225
226 /**
227 * Destroys the pthread read-write lock. It is not a cancellation
228 * point. It does not throw.
229 */
230 ~RWLock() {pthread_rwlock_destroy(&pthr_rwlock);}
231
232 /* Only has effect if --with-glib-memory-slices-compat or
233 * --with-glib-memory-slices-no-compat option picked */
234 CGU_GLIB_MEMORY_SLICES_FUNCS
235 };
236
237 /**
238 * @class RWLock::ReaderLock rw_lock.h c++-gtk-utils/rw_lock.h
239 * @brief A scoped locking class for exception safe RWLock read locking.
240 * @sa Thread::RWLock Thread::RWLock::ReaderTrackLock Thread::RWLock::WriterLock Thread::RWLock::WriterTrackLock Thread::Thread
241 */
242
243 class RWLock::ReaderLock {
244 RWLock& rw_lock;
245
246 public:
247 /**
248 * This class cannot be copied. The copy constructor is deleted.
249 */
250 ReaderLock(const RWLock::ReaderLock&) = delete;
251
252 /**
253 * This class cannot be copied. The assignment operator is deleted.
254 */
255 RWLock::ReaderLock& operator=(const RWLock::ReaderLock&) = delete;
256
257 /**
258 * Calls RWLock::reader_lock(), and so relocks the read-write lock for
259 * reading. It blocks if the read-write lock is already locked for
260 * writing until it becomes free. This method should normally only be
261 * called if a previous call has been made to
262 * RWLock::ReaderLock::unlock() (that is, where the thread owning the
263 * RWLock::ReaderLock object has temporarily allowed another thread to
264 * take the read-write lock concerned for writing if another thread
265 * does not hold a read lock or the read-write lock has not been
266 * recursively locked for reading). It is not a cancellation point.
267 * It does not throw.
268 * @return 0 if successful, otherwise the pthread read-write lock
269 * error number.
270 * @note With this library implementation, the only pthread error
271 * numbers which could be returned by this method are EDEADLK and
272 * EAGAIN. EDEADLK would be returned if the default pthread reader
273 * lock behaviour happens to return that error rather than deadlock
274 * where the thread calling this method already holds a write lock on
275 * the particular read-write lock in question. Most default
276 * implementations do not do this (they just deadlock) and hence the
277 * return value is usually not worth checking for except during
278 * debugging. EAGAIN would be returned if the maximum number of read
279 * locks for the read-write lock in question has been reached.
280 * Usually this number is at or around INT_MAX so it is also not
281 * usually useful to check for it except during debugging.
282 * @sa RWLock::ReaderTrackLock
283 */
284 int lock() noexcept {return rw_lock.reader_lock();}
285
286 /**
287 * Calls RWLock::reader_trylock(), and so tries to relock the
288 * read-write lock for reading, but returns immediately with value
289 * EBUSY if it is already locked for writing. This method should
290 * normally only be called if a previous call has been made to
291 * RWLock::ReaderLock::unlock() (that is, where the thread owning the
292 * RWLock::ReaderLock object has temporarily allowed another thread to
293 * take the read-write lock concerned for writing if another thread
294 * does not hold a read lock or the read-write lock has not been
295 * recursively locked for reading). It is not a cancellation point.
296 * It does not throw.
297 * @return 0 if successful, otherwise EBUSY or other pthread
298 * read-write lock error number.
299 * @note With this library implementation, apart from EBUSY, the only
300 * other pthread error number which could be returned by this method
301 * is EAGAIN, which would be returned if the maximum number of read
302 * locks for the particular read-write lock in question has been
303 * reached. Usually this number is at or around INT_MAX so it is not
304 * usually useful to check for it except during debugging.
305 * @sa RWLock::ReaderTrackLock
306 */
307 int trylock() noexcept {return rw_lock.reader_trylock();}
308
309 /**
310 * Calls RWLock::unlock(), and so unlocks a locked read-write lock
311 * held by the calling thread for reading (so temporarily allowing
312 * another thread to take the read-write lock for writing should no
313 * other read lock be held or the particular implementation provides
314 * writer priority). This method should normally only be called if it
315 * is to be followed by a call to RWLock::ReaderLock::lock() or a
316 * successful call to RWLock::ReaderLock::trylock() before the
317 * RWLock::ReaderLock object concerned goes out of scope (otherwise
318 * RWLock::ReaderLock's destructor will attempt to unlock an already
319 * unlocked read-write lock or a read-write lock of which another
320 * thread holds a lock - RWLock::ReaderLock objects do not maintain
321 * state). See RWLock::ReaderTrackLock::unlock() for a safe version
322 * of this method. It is not a cancellation point. It does not
323 * throw.
324 * @return 0 if successful, otherwise the pthread read-write lock
325 * error number.
326 * @note With this library implementation, the only pthread error
327 * number which could be returned by this method is EPERM because the
328 * calling thread does hold a lock on the particular read-write lock
329 * in question (however POSIX does not require that return value in
330 * that case and hence the return value is usually not worth checking
331 * for except during debugging).
332 * @sa RWLock::ReaderTrackLock
333 */
334 int unlock() noexcept {return rw_lock.unlock();}
335
336 /**
337 * This constructor locks for reading the read-write lock passed to
338 * it. It is not a cancellation point.
339 * @param rw_lock_ The read-write lock to be locked for reading.
340 * @exception Cgu::Thread::RWLockError Throws this exception if
341 * initialization of the read-write lock fails because the maximum
342 * number of read locks for the particular read-write lock in question
343 * has been reached. Usually this number is at or around INT_MAX so
344 * it is not usually useful to check for the exception except during
345 * debugging. This exception may also be thrown if the thread
346 * constructing this object already holds a write lock on the
347 * read-write lock in question. It will do this if the default pthread
348 * implementation returns EDEADLK in such a case instead of
349 * deadlocking. However as most default implementations will simply
350 * deadlock in such circumstances, it is usually not worth checking
351 * for this either except during debugging.
352 */
353 ReaderLock(RWLock& rw_lock_): rw_lock(rw_lock_) {if (rw_lock.reader_lock()) throw RWLockError();}
354
355 /**
356 * This constructor takes a read-write lock already locked for reading
357 * (say as a result of RWLock::reader_trylock()), and takes management
358 * of that read lock operation. It is not a cancellation point. It
359 * does not throw.
360 * @param rw_lock_ The read-write lock to be managed for reading by
361 * this object.
362 * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
363 */
364 ReaderLock(RWLock& rw_lock_, Locked tag) noexcept: rw_lock(rw_lock_) {}
365
366 /**
367 * This class requires initialisation with a RWLock. The default
368 * constructor is deleted.
369 */
370 ReaderLock() = delete;
371
372 /**
373 * The destructor unlocks the read-write lock which is managed for
374 * reading. It is not a cancellation point. It does not throw.
375 */
376 ~ReaderLock() {rw_lock.unlock();}
377
378 /* Only has effect if --with-glib-memory-slices-compat or
379 * --with-glib-memory-slices-no-compat option picked */
380 CGU_GLIB_MEMORY_SLICES_FUNCS
381 };
382
383 /**
384 * @class RWLock::ReaderTrackLock rw_lock.h c++-gtk-utils/rw_lock.h
385 * @brief A scoped locking class for exception safe RWLock read
386 * locking which tracks the status of its read-write lock.
387 * @sa Thread::RWLock Thread::RWLock::ReaderLock Thread::RWLock::WriterLock Thread::RWLock::WriterTrackLock Thread::Thread
388 *
389 * This class is similar to a RWLock::ReaderLock object, except that
390 * it tracks whether the read-write lock it manages is locked for
391 * reading by the thread creating the RWLock::ReaderTrackLock object
392 * with respect to the particular read-locking operation to be
393 * governed by the object (provided that, while the
394 * RWLock::ReaderTrackLock object exists, the thread creating it only
395 * accesses the managed read-write lock with respect that particular
396 * operation through that object). This enables
397 * RWLock::ReaderTrackLock::unlock() to be used without it being
398 * followed later by a call to RWLock::ReaderTrackLock::lock() or a
399 * successful call to RWLock::ReaderTrackLock::trylock(), and also
400 * permits locking to be deferred until after construction of the
401 * RWLock::ReaderTrackLock object. Note that only one thread may call
402 * the methods of any one RWLock::ReaderTrackLock object, including
403 * causing its destructor to be invoked.
404 */
405
406 class RWLock::ReaderTrackLock {
407 RWLock& rw_lock;
408 bool owner;
409
410 public:
411 /**
412 * This class cannot be copied. The copy constructor is deleted.
413 */
414 ReaderTrackLock(const RWLock::ReaderTrackLock&) = delete;
415
416 /**
417 * This class cannot be copied. The assignment operator is deleted.
418 */
419 RWLock::ReaderTrackLock& operator=(const RWLock::ReaderTrackLock&) = delete;
420
421 /**
422 * This calls RWLock::reader_lock(), and so locks the read-write lock
423 * for reading and acquires ownership (which may be shared with other
424 * read locks). It blocks if the read-write lock is already locked
425 * for writing until it becomes free. This method should normally
426 * only be called if a previous call has been made to
427 * RWLock::ReaderTrackLock::unlock() or this RWLock::ReaderTrackLock
428 * object has been constructed with the Thread::defer enum tag. It is
429 * not a cancellation point. It does not throw.
430 * @return 0 if successful, otherwise the pthread read-write lock
431 * error number.
432 * @note With this library implementation, the only pthread error
433 * numbers which could be returned by this method are EDEADLK and
434 * EAGAIN. EDEADLK would be returned if the default pthread reader
435 * lock behaviour happens to return that error rather than deadlock
436 * where the thread calling this method already holds a write lock on
437 * the particular read-write lock in question. Most default
438 * implementations do not do this (they just deadlock) and hence the
439 * return value is usually not worth checking for except during
440 * debugging. EAGAIN would be returned if the maximum number of read
441 * locks for the read-write lock in question has been reached.
442 * Usually this number is at or around INT_MAX so it is also not
443 * usually useful to check for it except during debugging.
444 */
445 int lock() noexcept {int ret = rw_lock.reader_lock(); if (!owner) owner = !ret; return ret;}
446
447 /**
448 * This calls RWLock::reader_trylock(), and so tries to lock the
449 * read-write lock for reading and acquire ownership (which may be
450 * shared with other read locks), but returns immediately with value
451 * EBUSY if it is already locked for writing. This method should
452 * normally only be called if a previous call has been made to
453 * RWLock::ReaderTrackLock::unlock() or this RWLock::ReaderTrackLock
454 * object has been constructed with the Thread::defer enum tag. It is
455 * not a cancellation point. It does not throw.
456 * @return 0 if successful, otherwise EBUSY or other pthread
457 * read-write lock error number.
458 * @note With this library implementation, apart from EBUSY, the only
459 * other pthread error number which could be returned by this method
460 * is EAGAIN, which would be returned if the maximum number of read
461 * locks for the particular read-write lock in question has been
462 * reached. Usually this number is at or around INT_MAX so it is not
463 * usually useful to check for it except during debugging.
464 */
465 int trylock() noexcept {int ret = rw_lock.reader_trylock(); if (!owner) owner = !ret; return ret;}
466
467 /**
468 * This calls RWLock::unlock(), and so unlocks a locked read-write
469 * lock held by the calling thread for reading and relinquishes
470 * ownership (whether it was sole or shared with other read locks).
471 * It will cause is_owner() to return false unless a subsequent call
472 * is made to lock() or a subsequent successful call is made to
473 * trylock(). It is not a cancellation point. It does not throw.
474 * @return 0 if successful, otherwise the pthread read-write lock
475 * error number.
476 * @note With this library implementation, the only pthread error
477 * number which could be returned by this method is EPERM because the
478 * calling thread does hold a lock on the particular read-write lock
479 * in question (however POSIX does not require that return value in
480 * that case and hence the return value is usually not worth checking
481 * for except during debugging).
482 */
483 int unlock() noexcept {int ret = rw_lock.unlock(); if (owner) owner = ret; return ret;}
484
485 /**
486 * Indicates whether the read-write lock managed by this
487 * RWLock::ReaderTrackLock object is locked for reading by it and so
488 * owned by it (whether solely or with other read locks). It does not
489 * throw.
490 * @return true if the read-write lock is owned by this object,
491 * otherwise false.
492 */
493 bool is_owner() const noexcept {return owner;}
494
495 /**
496 * This constructor locks for reading the read-write lock passed to
497 * it. It is not a cancellation point.
498 * @param rw_lock_ The read-write lock to be locked for reading.
499 * @exception Cgu::Thread::RWLockError Throws this exception if
500 * initialization of the read-write lock fails because the maximum
501 * number of read locks for the particular read-write lock in question
502 * has been reached. Usually this number is at or around INT_MAX so
503 * it is not usually useful to check for the exception except during
504 * debugging. This exception may also be thrown if the thread
505 * constructing this object already holds a write lock on the
506 * read-write lock in question. It will do this if the default pthread
507 * implementation returns EDEADLK in such a case instead of
508 * deadlocking. However as most default implementations will simply
509 * deadlock in such circumstances, it is usually not worth checking
510 * for this either except during debugging.
511 */
512 ReaderTrackLock(RWLock& rw_lock_): rw_lock(rw_lock_), owner(true) {if (rw_lock.reader_lock()) throw RWLockError();}
513
514 /**
515 * This constructor takes a read-write lock already locked for reading
516 * (say as a result of RWLock::reader_trylock()), and takes management
517 * of that read lock operation. It is not a cancellation point. It
518 * does not throw.
519 * @param rw_lock_ The read-write lock to be managed for reading by
520 * this object.
521 * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
522 */
523 ReaderTrackLock(RWLock& rw_lock_, Locked tag) noexcept: rw_lock(rw_lock_), owner(true) {}
524
525 /**
526 * This constructor defers locking of the read-write lock for reading
527 * until an explicit call to lock() or trylock() is made. It is not a
528 * cancellation point. It does not throw.
529 * @param rw_lock_ The read-write lock to be managed for reading by
530 * this object.
531 * @param tag Pass the Cgu::Thread::defer enum tag to this parameter.
532 */
533 ReaderTrackLock(RWLock& rw_lock_, DeferLock tag) noexcept: rw_lock(rw_lock_), owner(false) {}
534
535 /**
536 * This class requires initialisation with a RWLock. The default
537 * constructor is deleted.
538 */
539 ReaderTrackLock() = delete;
540
541 /**
542 * The destructor unlocks the read-write lock which is managed for
543 * reading if it is owned by this RWLock::ReaderTrackLock object
544 * (whether solely or with other read locks). It is not a
545 * cancellation point. It does not throw.
546 */
547 ~ReaderTrackLock() {if (owner) rw_lock.unlock();}
548
549 /* Only has effect if --with-glib-memory-slices-compat or
550 * --with-glib-memory-slices-no-compat option picked */
551 CGU_GLIB_MEMORY_SLICES_FUNCS
552 };
553
554 /**
555 * @class RWLock::WriterLock rw_lock.h c++-gtk-utils/rw_lock.h
556 * @brief A scoped locking class for exception safe RWLock write locking.
557 * @sa Thread::RWLock Thread::RWLock::WriterTrackLock Thread::RWLock::ReaderLock Thread::RWLock::ReaderTrackLock Thread::Thread
558 */
559
560 class RWLock::WriterLock {
561 RWLock& rw_lock;
562
563 public:
564 /**
565 * This class cannot be copied. The copy constructor is deleted.
566 */
567 WriterLock(const RWLock::WriterLock&) = delete;
568
569 /**
570 * This class cannot be copied. The assignment operator is deleted.
571 */
572 RWLock::WriterLock& operator=(const RWLock::WriterLock&) = delete;
573
574 /**
575 * Calls RWLock::writer_lock(), and so locks the read-write lock for
576 * writing and reacquires ownership. It blocks if the read-write lock
577 * is already locked for reading or writing until it becomes free.
578 * This method should normally only be called if a previous call has
579 * been made to RWLock::WriterLock::unlock() (that is, where the
580 * thread owning the RWLock::WriterLock object has temporarily allowed
581 * another thread to take the read-write lock concerned for reading or
582 * writing). It is not a cancellation point. It does not throw.
583 * @return 0 if successful, otherwise the pthread read-write lock
584 * error number.
585 * @note With this library implementation, the only pthread error
586 * number which could be returned by this method is EDEADLK, which it
587 * would do if the default pthread reader lock behaviour happens to
588 * return that error rather than deadlock where the thread calling
589 * this method already holds a read lock or write lock on the
590 * particular read-write lock in question. Most default
591 * implementations do not do this (they just deadlock) and hence the
592 * return value is usually not worth checking for except during
593 * debugging.
594 * @sa RWLock::WriterTrackLock
595 */
596 int lock() noexcept {return rw_lock.writer_lock();}
597
598 /**
599 * Calls RWLock::writer_trylock(), and so tries to lock the read-write
600 * lock for writing and reacquire ownership, but returns immediately
601 * with value EBUSY if it is already locked for reading or writing.
602 * This method should normally only be called if a previous call has
603 * been made to RWLock::WriterLock::unlock() (that is, where the
604 * thread owning the RWLock::WriterLock object has temporarily allowed
605 * another thread to take the read-write lock concerned for reading or
606 * writing). It is not a cancellation point. It does not throw.
607 * @return 0 if successful, otherwise EBUSY.
608 * @note With this library implementation, the only pthread error
609 * number which could be returned by this method is EBUSY.
610 * @sa RWLock::WriterTrackLock
611 */
612 int trylock() noexcept {return rw_lock.writer_trylock();}
613
614 /**
615 * Calls RWLock::unlock(), and so unlocks a locked read-write lock
616 * owned by the calling thread for writing and relinquishes ownership
617 * (so temporarily allowing another thread to take the read-write
618 * lock). This method should normally only be called if it is to be
619 * followed by a call to RWLock::WriterLock::lock() or a successful
620 * call to RWLock::WriterLock::trylock() before the RWLock::WriterLock
621 * object concerned goes out of scope (otherwise RWLock::WriterLock's
622 * destructor will attempt to unlock an already unlocked read-write
623 * lock or a read-write lock of which another thread has by then taken
624 * ownership - RWLock::WriterLock objects do not maintain state). See
625 * RWLock::WriterTrackLock::unlock() for a safe version of this
626 * method. It is not a cancellation point. It does not throw.
627 * @return 0 if successful, otherwise the pthread read-write lock
628 * error number.
629 * @note With this library implementation, the only pthread error
630 * number which could be returned by this method is EPERM because the
631 * calling thread does hold a lock on the particular read-write lock
632 * in question (however POSIX does not require that return value in
633 * that case and hence the return value is usually not worth checking
634 * for except during debugging).
635 * @sa RWLock::WriterTrackLock
636 */
637 int unlock() noexcept {return rw_lock.unlock();}
638
639 /**
640 * This constructor locks for writing the read-write lock passed to
641 * it. It is not a cancellation point. It does not throw.
642 * @param rw_lock_ The read-write lock to be locked for writing.
643 */
644 WriterLock(RWLock& rw_lock_) noexcept: rw_lock(rw_lock_) {rw_lock.writer_lock();}
645
646 /**
647 * This constructor takes a read-write lock already locked for writing
648 * (say as a result of RWLock::writer_trylock()), and takes ownership
649 * of it. It is not a cancellation point. It does not throw.
650 * @param rw_lock_ The read-write lock to be managed for writing by
651 * this object.
652 * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
653 */
654 WriterLock(RWLock& rw_lock_, Locked tag) noexcept: rw_lock(rw_lock_) {}
655
656 /**
657 * This class requires initialisation with a RWLock. The default
658 * constructor is deleted.
659 */
660 WriterLock() = delete;
661
662 /**
663 * The destructor unlocks the owned read-write lock. It is not a
664 * cancellation point. It does not throw.
665 */
666 ~WriterLock() {rw_lock.unlock();}
667
668 /* Only has effect if --with-glib-memory-slices-compat or
669 * --with-glib-memory-slices-no-compat option picked */
670 CGU_GLIB_MEMORY_SLICES_FUNCS
671 };
672
673 /**
674 * @class RWLock::WriterTrackLock rw_lock.h c++-gtk-utils/rw_lock.h
675 * @brief A scoped locking class for exception safe RWLock write
676 * locking which tracks the status of its read-write lock..
677 * @sa Thread::RWLock Thread::RWLock::WriterLock Thread::RWLock::ReaderLock Thread::RWLock::ReaderTrackLock Thread::Thread
678 *
679 * This class is similar to a RWLock::WriterLock object, except that
680 * it tracks whether the read-write lock it manages is locked for
681 * writing by the thread creating the RWLock::WriterTrackLock object
682 * (provided that, while the RWLock::WriterTrackLock object exists,
683 * the thread creating it only accesses the managed read-write lock
684 * for write-locking through that object). This enables
685 * RWLock::WriterTrackLock::unlock() to be used without it being
686 * followed later by a call to RWLock::WriterTrackLock::lock() or a
687 * successful call to RWLock::WriterTrackLock::trylock(), and also
688 * permits locking to be deferred until after construction of the
689 * RWLock::WriterTrackLock object. Note that only one thread may call
690 * the methods of any one RWLock::WriterTrackLock object, including
691 * causing its destructor to be invoked.
692 */
693
694 class RWLock::WriterTrackLock {
695 RWLock& rw_lock;
696 bool owner;
697
698 public:
699 /**
700 * This class cannot be copied. The copy constructor is deleted.
701 */
702 WriterTrackLock(const RWLock::WriterTrackLock&) = delete;
703
704 /**
705 * This class cannot be copied. The assignment operator is deleted.
706 */
707 RWLock::WriterTrackLock& operator=(const RWLock::WriterTrackLock&) = delete;
708
709 /**
710 * Calls RWLock::writer_lock(), and so locks the read-write lock for
711 * writing and acquires ownership. It blocks if the read-write lock
712 * is already locked for reading or writing until it becomes free.
713 * This method should normally only be called if a previous call has
714 * been made to RWLock::WriterTrackLock::unlock() or this
715 * RWLock::WriterTrackLock object has been constructed with the
716 * Thread::defer enum tag. It is not a cancellation point. It does
717 * not throw.
718 * @return 0 if successful, otherwise the pthread read-write lock
719 * error number.
720 * @note With this library implementation, the only pthread error
721 * number which could be returned by this method is EDEADLK, which it
722 * would do if the default pthread reader lock behaviour happens to
723 * return that error rather than deadlock where the thread calling
724 * this method already holds a read lock or write lock on the
725 * particular read-write lock in question. Most default
726 * implementations do not do this (they just deadlock) and hence the
727 * return value is usually not worth checking for except during
728 * debugging.
729 */
730 int lock() noexcept {int ret = rw_lock.writer_lock(); if (!owner) owner = !ret; return ret;}
731
732 /**
733 * Calls RWLock::writer_trylock(), and so tries to lock the read-write
734 * lock for writing and acquire ownership, but returns immediately
735 * with value EBUSY if it is already locked for reading or writing.
736 * This method should normally only be called if a previous call has
737 * been made to RWLock::WriterTrackLock::unlock() or this
738 * RWLock::WriterTrackLock object has been constructed with the
739 * Thread::defer enum tag. It is not a cancellation point. It does
740 * not throw.
741 * @return 0 if successful, otherwise EBUSY.
742 * @note With this library implementation, the only pthread error
743 * number which could be returned by this method is EBUSY.
744 */
745 int trylock() noexcept {int ret = rw_lock.writer_trylock(); if (!owner) owner = !ret; return ret;}
746
747 /**
748 * Calls RWLock::unlock(), and so unlocks a locked read-write lock
749 * owned by the calling thread for writing and relinquishes ownership.
750 * It will cause is_owner() to return false unless a subsequent call
751 * is made to lock() or a subsequent successful call is made to
752 * trylock(). It is not a cancellation point. It does not throw.
753 * @return 0 if successful, otherwise the pthread read-write lock
754 * error number.
755 * @note With this library implementation, the only pthread error
756 * number which could be returned by this method is EPERM because the
757 * calling thread does hold a lock on the particular read-write lock
758 * in question (however POSIX does not require that return value in
759 * that case and hence the return value is usually not worth checking
760 * for except during debugging).
761 */
762 int unlock() noexcept {int ret = rw_lock.unlock(); if (owner) owner = ret; return ret;}
763
764 /**
765 * Indicates whether the read-write lock managed by this
766 * RWLock::ReaderTrackLock object is locked for writing by it and so
767 * owned by it. It does not throw.
768 * @return true if the read-write lock is owned by this object,
769 * otherwise false.
770 */
771 bool is_owner() const noexcept {return owner;}
772
773 /**
774 * This constructor locks for writing the read-write lock passed to
775 * it. It is not a cancellation point. It does not throw.
776 * @param rw_lock_ The read-write lock to be locked for writing.
777 */
778 WriterTrackLock(RWLock& rw_lock_) noexcept: rw_lock(rw_lock_), owner(true) {rw_lock.writer_lock();}
779
780 /**
781 * This constructor takes a read-write lock already locked for writing
782 * (say as a result of RWLock::writer_trylock()), and takes ownership
783 * of it. It is not a cancellation point. It does not throw.
784 * @param rw_lock_ The read-write lock to be managed for writing by
785 * this object.
786 * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
787 */
788 WriterTrackLock(RWLock& rw_lock_, Locked tag) noexcept: rw_lock(rw_lock_), owner(true) {}
789
790 /**
791 * This constructor defers locking of the read-write lock for writing
792 * until an explicit call to lock() or trylock() is made. It is not a
793 * cancellation point. It does not throw.
794 * @param rw_lock_ The read-write lock to be managed for writing by
795 * this object.
796 * @param tag Pass the Cgu::Thread::defer enum tag to this parameter.
797 */
798 WriterTrackLock(RWLock& rw_lock_, DeferLock tag) noexcept: rw_lock(rw_lock_), owner(false) {}
799
800 /**
801 * This class requires initialisation with a RWLock. The default
802 * constructor is deleted.
803 */
804 WriterTrackLock() = delete;
805
806 /**
807 * The destructor unlocks the read-write lock which is managed for
808 * writing if it is owned by this RWLock::WriterTrackLock object. It
809 * is not a cancellation point. It does not throw.
810 */
811 ~WriterTrackLock() {if (owner) rw_lock.unlock();}
812
813 /* Only has effect if --with-glib-memory-slices-compat or
814 * --with-glib-memory-slices-no-compat option picked */
815 CGU_GLIB_MEMORY_SLICES_FUNCS
816 };
817
818 } // namespace Thread
819
820 } // namespace Cgu
821
822 #endif
Cgu::Thread::RWLock::WriterTrackLock::lock
int lock() noexcept
Definition: rw_lock.h:730
Cgu::Thread::RWLock::WriterTrackLock::WriterTrackLock
WriterTrackLock(RWLock &rw_lock_) noexcept
Definition: rw_lock.h:778
Cgu::Thread::RWLock::ReaderLock
A scoped locking class for exception safe RWLock read locking.
Definition: rw_lock.h:243
Cgu::Thread::RWLock::ReaderLock::~ReaderLock
~ReaderLock()
Definition: rw_lock.h:376
Cgu::Thread::RWLock::WriterTrackLock::WriterTrackLock
WriterTrackLock(RWLock &rw_lock_, DeferLock tag) noexcept
Definition: rw_lock.h:798
Cgu::Thread::RWLock::ReaderTrackLock::lock
int lock() noexcept
Definition: rw_lock.h:445
Cgu::Thread::RWLock::RWLock
RWLock()
Definition: rw_lock.h:224
Cgu::Thread::RWLock::ReaderLock::ReaderLock
ReaderLock(RWLock &rw_lock_)
Definition: rw_lock.h:353
Cgu::Thread::RWLock
A wrapper class for pthread read-write locks.
Definition: rw_lock.h:103
cgu_config.h
Cgu::Thread::RWLock::ReaderTrackLock::ReaderTrackLock
ReaderTrackLock()=delete
Cgu::Thread::RWLock::writer_trylock
int writer_trylock() noexcept
Definition: rw_lock.h:192
Cgu::Thread::RWLock::WriterTrackLock::~WriterTrackLock
~WriterTrackLock()
Definition: rw_lock.h:811
Cgu::Thread::RWLock::WriterTrackLock::is_owner
bool is_owner() const noexcept
Definition: rw_lock.h:771
Cgu::Thread::RWLock::unlock
int unlock() noexcept
Definition: rw_lock.h:213
Cgu::Thread::RWLock::WriterTrackLock::trylock
int trylock() noexcept
Definition: rw_lock.h:745
Cgu::Thread::RWLock::WriterLock::operator=
RWLock::WriterLock & operator=(const RWLock::WriterLock &)=delete
Cgu::Thread::RWLock::WriterLock::lock
int lock() noexcept
Definition: rw_lock.h:596
Cgu::Thread::RWLock::operator=
RWLock & operator=(const RWLock &)=delete
Cgu::Thread::RWLock::ReaderTrackLock
A scoped locking class for exception safe RWLock read locking which tracks the status of its read-wri...
Definition: rw_lock.h:406
Cgu::Thread::RWLock::WriterTrackLock::operator=
RWLock::WriterTrackLock & operator=(const RWLock::WriterTrackLock &)=delete
Cgu::Thread::RWLock::WriterTrackLock::WriterTrackLock
WriterTrackLock()=delete
Cgu::Thread::RWLock::ReaderTrackLock::ReaderTrackLock
ReaderTrackLock(RWLock &rw_lock_, Locked tag) noexcept
Definition: rw_lock.h:523
Cgu::Thread::RWLock::WriterLock::WriterLock
WriterLock(RWLock &rw_lock_, Locked tag) noexcept
Definition: rw_lock.h:654
Cgu::Thread::RWLock::~RWLock
~RWLock()
Definition: rw_lock.h:230
Cgu::Thread::RWLock::ReaderTrackLock::trylock
int trylock() noexcept
Definition: rw_lock.h:465
Cgu::Thread::RWLock::ReaderTrackLock::unlock
int unlock() noexcept
Definition: rw_lock.h:483
Cgu::Thread::RWLock::WriterTrackLock::unlock
int unlock() noexcept
Definition: rw_lock.h:762
Cgu::Thread::RWLock::ReaderLock::operator=
RWLock::ReaderLock & operator=(const RWLock::ReaderLock &)=delete
Cgu::Thread::RWLockError::what
virtual const char * what() const
Definition: rw_lock.h:60
Cgu::Thread::RWLock::ReaderLock::trylock
int trylock() noexcept
Definition: rw_lock.h:307
Cgu::Thread::RWLock::ReaderTrackLock::ReaderTrackLock
ReaderTrackLock(RWLock &rw_lock_, DeferLock tag) noexcept
Definition: rw_lock.h:533
Cgu::Thread::RWLock::ReaderTrackLock::operator=
RWLock::ReaderTrackLock & operator=(const RWLock::ReaderTrackLock &)=delete
Cgu::Thread::RWLock::writer_lock
int writer_lock() noexcept
Definition: rw_lock.h:181
Cgu::Thread::RWLock::ReaderLock::unlock
int unlock() noexcept
Definition: rw_lock.h:334
Cgu::Thread::RWLock::ReaderTrackLock::~ReaderTrackLock
~ReaderTrackLock()
Definition: rw_lock.h:547
Cgu::Thread::RWLockError
Definition: rw_lock.h:59
mutex.h
Provides wrapper classes for pthread mutexes and condition variables, and scoped locking classes for ...
Cgu
Definition: application.h:44
Cgu::Thread::RWLock::reader_lock
int reader_lock() noexcept
Definition: rw_lock.h:144
Cgu::Thread::Locked
Locked
Definition: mutex.h:196
Cgu::Thread::DeferLock
DeferLock
Definition: mutex.h:199
Cgu::Thread::RWLock::ReaderLock::ReaderLock
ReaderLock(RWLock &rw_lock_, Locked tag) noexcept
Definition: rw_lock.h:364
Cgu::Thread::RWLock::WriterLock::WriterLock
WriterLock(RWLock &rw_lock_) noexcept
Definition: rw_lock.h:644
Cgu::Thread::RWLock::WriterLock
A scoped locking class for exception safe RWLock write locking.
Definition: rw_lock.h:560
Cgu::Thread::RWLock::WriterTrackLock
A scoped locking class for exception safe RWLock write locking which tracks the status of its read-wr...
Definition: rw_lock.h:694
Cgu::Thread::RWLock::ReaderTrackLock::ReaderTrackLock
ReaderTrackLock(RWLock &rw_lock_)
Definition: rw_lock.h:512
Cgu::Thread::RWLock::WriterLock::trylock
int trylock() noexcept
Definition: rw_lock.h:612
Cgu::Thread::RWLock::ReaderLock::lock
int lock() noexcept
Definition: rw_lock.h:284
Cgu::Thread::RWLock::ReaderLock::ReaderLock
ReaderLock()=delete
Cgu::Thread::RWLock::ReaderTrackLock::is_owner
bool is_owner() const noexcept
Definition: rw_lock.h:493
Cgu::Thread::RWLock::reader_trylock
int reader_trylock() noexcept
Definition: rw_lock.h:163
Cgu::Thread::RWLock::WriterLock::WriterLock
WriterLock()=delete
CGU_GLIB_MEMORY_SLICES_FUNCS
#define CGU_GLIB_MEMORY_SLICES_FUNCS
Definition: cgu_config.h:84
Cgu::Thread::RWLock::WriterTrackLock::WriterTrackLock
WriterTrackLock(RWLock &rw_lock_, Locked tag) noexcept
Definition: rw_lock.h:788
Cgu::Thread::RWLock::WriterLock::~WriterLock
~WriterLock()
Definition: rw_lock.h:666
Cgu::Thread::RWLock::WriterLock::unlock
int unlock() noexcept
Definition: rw_lock.h:637