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