c++-gtk-utils
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
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 */
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 
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  */
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 */
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 
407  RWLock& rw_lock;
408  bool owner;
409 
410 public:
411 /**
412  * This class cannot be copied. The copy constructor is deleted.
413  */
415 
416 /**
417  * This class cannot be copied. The assignment operator is deleted.
418  */
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 */
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 
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  */
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 */
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 
695  RWLock& rw_lock;
696  bool owner;
697 
698 public:
699 /**
700  * This class cannot be copied. The copy constructor is deleted.
701  */
703 
704 /**
705  * This class cannot be copied. The assignment operator is deleted.
706  */
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 */
816 };
817 
818 } // namespace Thread
819 
820 } // namespace Cgu
821 
822 #endif
Cgu::Thread::RWLock::WriterLock::lock
int lock() noexcept
Definition: rw_lock.h:596
Cgu::Thread::RWLock::ReaderLock::unlock
int unlock() noexcept
Definition: rw_lock.h:334
Cgu::Thread::RWLock::unlock
int unlock() noexcept
Definition: rw_lock.h:213
Cgu::Thread::RWLock::WriterLock::WriterLock
WriterLock()=delete
Cgu::Thread::RWLock::ReaderTrackLock::ReaderTrackLock
ReaderTrackLock()=delete
Cgu
Definition: application.h:44
Cgu::Thread::RWLock::reader_trylock
int reader_trylock() noexcept
Definition: rw_lock.h:163
Cgu::Thread::RWLock::WriterTrackLock::WriterTrackLock
WriterTrackLock()=delete
Cgu::Thread::RWLock::ReaderTrackLock::~ReaderTrackLock
~ReaderTrackLock()
Definition: rw_lock.h:547
Cgu::Thread::RWLock::WriterLock::WriterLock
WriterLock(RWLock &rw_lock_, Locked tag) noexcept
Definition: rw_lock.h:654
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::RWLock
RWLock()
Definition: rw_lock.h:224
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::ReaderTrackLock::trylock
int trylock() noexcept
Definition: rw_lock.h:465
Cgu::Thread::RWLock::ReaderTrackLock::ReaderTrackLock
ReaderTrackLock(RWLock &rw_lock_)
Definition: rw_lock.h:512
Cgu::Thread::RWLock::WriterTrackLock::lock
int lock() noexcept
Definition: rw_lock.h:730
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::operator=
RWLock & operator=(const RWLock &)=delete
Cgu::Thread::RWLock::writer_trylock
int writer_trylock() noexcept
Definition: rw_lock.h:192
Cgu::Thread::RWLock::WriterLock::~WriterLock
~WriterLock()
Definition: rw_lock.h:666
Cgu::Thread::RWLock::WriterTrackLock::is_owner
bool is_owner() const noexcept
Definition: rw_lock.h:771
Cgu::Thread::RWLock::ReaderLock::ReaderLock
ReaderLock(RWLock &rw_lock_)
Definition: rw_lock.h:353
Cgu::Thread::RWLock::ReaderLock::ReaderLock
ReaderLock(const RWLock::ReaderLock &)=delete
Cgu::Thread::RWLock::ReaderTrackLock::operator=
RWLock::ReaderTrackLock & operator=(const RWLock::ReaderTrackLock &)=delete
Cgu::Thread::RWLock::WriterLock::WriterLock
WriterLock(const RWLock::WriterLock &)=delete
Cgu::Thread::RWLockError
Definition: rw_lock.h:59
Cgu::Thread::RWLock::~RWLock
~RWLock()
Definition: rw_lock.h:230
Cgu::Thread::RWLock::WriterTrackLock::WriterTrackLock
WriterTrackLock(RWLock &rw_lock_) noexcept
Definition: rw_lock.h:778
Cgu::Thread::RWLock::ReaderLock::ReaderLock
ReaderLock()=delete
Cgu::Thread::RWLock::WriterTrackLock::WriterTrackLock
WriterTrackLock(RWLock &rw_lock_, Locked tag) noexcept
Definition: rw_lock.h:788
Cgu::Thread::RWLock::WriterLock::WriterLock
WriterLock(RWLock &rw_lock_) noexcept
Definition: rw_lock.h:644
Cgu::Thread::RWLock::writer_lock
int writer_lock() noexcept
Definition: rw_lock.h:181
Cgu::Thread::RWLock::WriterLock::trylock
int trylock() noexcept
Definition: rw_lock.h:612
Cgu::Thread::RWLock::RWLock
RWLock(const RWLock &)=delete
Cgu::Thread::RWLock::WriterTrackLock::WriterTrackLock
WriterTrackLock(const RWLock::WriterTrackLock &)=delete
Cgu::Thread::RWLock::WriterLock::unlock
int unlock() noexcept
Definition: rw_lock.h:637
Cgu::Thread::RWLock::ReaderTrackLock::ReaderTrackLock
ReaderTrackLock(RWLock &rw_lock_, Locked tag) noexcept
Definition: rw_lock.h:523
Cgu::Thread::DeferLock
DeferLock
Definition: mutex.h:199
Cgu::Thread::RWLockError::what
virtual const char * what() const
Definition: rw_lock.h:60
Cgu::Thread::RWLock::reader_lock
int reader_lock() noexcept
Definition: rw_lock.h:144
CGU_GLIB_MEMORY_SLICES_FUNCS
#define CGU_GLIB_MEMORY_SLICES_FUNCS
Definition: cgu_config.h:84
Cgu::Thread::RWLock::ReaderLock::~ReaderLock
~ReaderLock()
Definition: rw_lock.h:376
Cgu::Thread::RWLock::WriterTrackLock::unlock
int unlock() noexcept
Definition: rw_lock.h:762
Cgu::Thread::Locked
Locked
Definition: mutex.h:196
mutex.h
Provides wrapper classes for pthread mutexes and condition variables, and scoped locking classes for ...
Cgu::Thread::RWLock::ReaderLock::trylock
int trylock() noexcept
Definition: rw_lock.h:307
Cgu::Thread::RWLock::ReaderLock::ReaderLock
ReaderLock(RWLock &rw_lock_, Locked tag) noexcept
Definition: rw_lock.h:364
Cgu::Thread::RWLock
A wrapper class for pthread read-write locks.
Definition: rw_lock.h:103
Cgu::Thread::RWLock::ReaderLock::operator=
RWLock::ReaderLock & operator=(const RWLock::ReaderLock &)=delete
Cgu::Thread::RWLock::ReaderLock
A scoped locking class for exception safe RWLock read locking.
Definition: rw_lock.h:243
Cgu::Thread::RWLock::ReaderLock::lock
int lock() noexcept
Definition: rw_lock.h:284
Cgu::Thread::RWLock::WriterTrackLock::trylock
int trylock() noexcept
Definition: rw_lock.h:745
Cgu::Thread::RWLock::WriterLock
A scoped locking class for exception safe RWLock write locking.
Definition: rw_lock.h:560
Cgu::Thread::RWLock::ReaderTrackLock::ReaderTrackLock
ReaderTrackLock(const RWLock::ReaderTrackLock &)=delete
Cgu::Thread::RWLock::WriterLock::operator=
RWLock::WriterLock & operator=(const RWLock::WriterLock &)=delete
Cgu::Thread::RWLock::ReaderTrackLock::unlock
int unlock() noexcept
Definition: rw_lock.h:483
Cgu::Thread::RWLock::ReaderTrackLock::is_owner
bool is_owner() const noexcept
Definition: rw_lock.h:493
Cgu::Thread::RWLock::WriterTrackLock::operator=
RWLock::WriterTrackLock & operator=(const RWLock::WriterTrackLock &)=delete
cgu_config.h
Cgu::Thread::RWLock::ReaderTrackLock::ReaderTrackLock
ReaderTrackLock(RWLock &rw_lock_, DeferLock tag) noexcept
Definition: rw_lock.h:533
Cgu::Thread::RWLock::WriterTrackLock::~WriterTrackLock
~WriterTrackLock()
Definition: rw_lock.h:811