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