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() noexcept {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() noexcept {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() noexcept {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() noexcept {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() noexcept {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() noexcept {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() noexcept {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() noexcept {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) noexcept: 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() noexcept {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() noexcept {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() noexcept {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). It does not
488  * throw.
489  * @return true if the read-write lock is owned by this object,
490  * otherwise false.
491  */
492  bool is_owner() const noexcept {return owner;}
493 
494 /**
495  * This constructor locks for reading the read-write lock passed to
496  * it. It is not a cancellation point.
497  * @param rw_lock_ The read-write lock to be locked for reading.
498  * @exception Cgu::Thread::RWLockError Throws this exception if
499  * initialization of the read-write lock fails because the maximum
500  * number of read locks for the particular read-write lock in question
501  * has been reached. Usually this number is at or around INT_MAX so
502  * it is not usually useful to check for the exception except during
503  * debugging. This exception may also be thrown if the thread
504  * constructing this object already holds a write lock on the
505  * read-write lock in question. It will do this if the default pthread
506  * implementation returns EDEADLK in such a case instead of
507  * deadlocking. However as most default implementations will simply
508  * deadlock in such circumstances, it is usually not worth checking
509  * for this either except during debugging.
510  */
511  ReaderTrackLock(RWLock& rw_lock_): rw_lock(rw_lock_), owner(true) {if (rw_lock.reader_lock()) throw RWLockError();}
512 
513 /**
514  * This constructor takes a read-write lock already locked for reading
515  * (say as a result of RWLock::reader_trylock()), and takes management
516  * of that read lock operation. It is not a cancellation point. It
517  * does not throw.
518  * @param rw_lock_ The read-write lock to be managed for reading by
519  * this object.
520  * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
521  */
522  ReaderTrackLock(RWLock& rw_lock_, Locked tag) noexcept: rw_lock(rw_lock_), owner(true) {}
523 
524 /**
525  * This constructor defers locking of the read-write lock for reading
526  * until an explicit call to lock() or trylock() is made. It is not a
527  * cancellation point. It does not throw.
528  * @param rw_lock_ The read-write lock to be managed for reading by
529  * this object.
530  * @param tag Pass the Cgu::Thread::defer enum tag to this parameter.
531  */
532  ReaderTrackLock(RWLock& rw_lock_, DeferLock tag) noexcept: rw_lock(rw_lock_), owner(false) {}
533 
534 /**
535  * This class requires initialisation with a RWLock. The default
536  * constructor is deleted.
537  */
538  ReaderTrackLock() = delete;
539 
540 /**
541  * The destructor unlocks the read-write lock which is managed for
542  * reading if it is owned by this RWLock::ReaderTrackLock object
543  * (whether solely or with other read locks). It is not a
544  * cancellation point. It does not throw.
545  */
546  ~ReaderTrackLock() {if (owner) rw_lock.unlock();}
547 
548 /* Only has effect if --with-glib-memory-slices-compat or
549  * --with-glib-memory-slices-no-compat option picked */
551 };
552 
553 /**
554  * @class RWLock::WriterLock rw_lock.h c++-gtk-utils/rw_lock.h
555  * @brief A scoped locking class for exception safe RWLock write locking.
556  * @sa Thread::RWLock Thread::RWLock::WriterTrackLock Thread::RWLock::ReaderLock Thread::RWLock::ReaderTrackLock Thread::Thread
557  */
558 
560  RWLock& rw_lock;
561 
562 public:
563 /**
564  * This class cannot be copied. The copy constructor is deleted.
565  */
566  WriterLock(const RWLock::WriterLock&) = delete;
567 
568 /**
569  * This class cannot be copied. The assignment operator is deleted.
570  */
572 
573 /**
574  * Calls RWLock::writer_lock(), and so locks the read-write lock for
575  * writing and reacquires ownership. It blocks if the read-write lock
576  * is already locked for reading or writing until it becomes free.
577  * This method should normally only be called if a previous call has
578  * been made to RWLock::WriterLock::unlock() (that is, where the
579  * thread owning the RWLock::WriterLock object has temporarily allowed
580  * another thread to take the read-write lock concerned for reading or
581  * writing). It is not a cancellation point. It does not throw.
582  * @return 0 if successful, otherwise the pthread read-write lock
583  * error number.
584  * @note With this library implementation, the only pthread error
585  * number which could be returned by this method is EDEADLK, which it
586  * would do if the default pthread reader lock behaviour happens to
587  * return that error rather than deadlock where the thread calling
588  * this method already holds a read lock or write lock on the
589  * particular read-write lock in question. Most default
590  * implementations do not do this (they just deadlock) and hence the
591  * return value is usually not worth checking for except during
592  * debugging.
593  * @sa RWLock::WriterTrackLock
594  */
595  int lock() noexcept {return rw_lock.writer_lock();}
596 
597 /**
598  * Calls RWLock::writer_trylock(), and so tries to lock the read-write
599  * lock for writing and reacquire ownership, but returns immediately
600  * with value EBUSY if it is already locked for reading or writing.
601  * This method should normally only be called if a previous call has
602  * been made to RWLock::WriterLock::unlock() (that is, where the
603  * thread owning the RWLock::WriterLock object has temporarily allowed
604  * another thread to take the read-write lock concerned for reading or
605  * writing). It is not a cancellation point. It does not throw.
606  * @return 0 if successful, otherwise EBUSY.
607  * @note With this library implementation, the only pthread error
608  * number which could be returned by this method is EBUSY.
609  * @sa RWLock::WriterTrackLock
610  */
611  int trylock() noexcept {return rw_lock.writer_trylock();}
612 
613 /**
614  * Calls RWLock::unlock(), and so unlocks a locked read-write lock
615  * owned by the calling thread for writing and relinquishes ownership
616  * (so temporarily allowing another thread to take the read-write
617  * lock). This method should normally only be called if it is to be
618  * followed by a call to RWLock::WriterLock::lock() or a successful
619  * call to RWLock::WriterLock::trylock() before the RWLock::WriterLock
620  * object concerned goes out of scope (otherwise RWLock::WriterLock's
621  * destructor will attempt to unlock an already unlocked read-write
622  * lock or a read-write lock of which another thread has by then taken
623  * ownership - RWLock::WriterLock objects do not maintain state). See
624  * RWLock::WriterTrackLock::unlock() for a safe version of this
625  * method. It is not a cancellation point. It does not throw.
626  * @return 0 if successful, otherwise the pthread read-write lock
627  * error number.
628  * @note With this library implementation, the only pthread error
629  * number which could be returned by this method is EPERM because the
630  * calling thread does hold a lock on the particular read-write lock
631  * in question (however POSIX does not require that return value in
632  * that case and hence the return value is usually not worth checking
633  * for except during debugging).
634  * @sa RWLock::WriterTrackLock
635  */
636  int unlock() noexcept {return rw_lock.unlock();}
637 
638 /**
639  * This constructor locks for writing the read-write lock passed to
640  * it. It is not a cancellation point. It does not throw.
641  * @param rw_lock_ The read-write lock to be locked for writing.
642  */
643  WriterLock(RWLock& rw_lock_) noexcept: rw_lock(rw_lock_) {rw_lock.writer_lock();}
644 
645 /**
646  * This constructor takes a read-write lock already locked for writing
647  * (say as a result of RWLock::writer_trylock()), and takes ownership
648  * of it. It is not a cancellation point. It does not throw.
649  * @param rw_lock_ The read-write lock to be managed for writing by
650  * this object.
651  * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
652  */
653  WriterLock(RWLock& rw_lock_, Locked tag) noexcept: rw_lock(rw_lock_) {}
654 
655 /**
656  * This class requires initialisation with a RWLock. The default
657  * constructor is deleted.
658  */
659  WriterLock() = delete;
660 
661 /**
662  * The destructor unlocks the owned read-write lock. It is not a
663  * cancellation point. It does not throw.
664  */
665  ~WriterLock() {rw_lock.unlock();}
666 
667 /* Only has effect if --with-glib-memory-slices-compat or
668  * --with-glib-memory-slices-no-compat option picked */
670 };
671 
672 /**
673  * @class RWLock::WriterTrackLock rw_lock.h c++-gtk-utils/rw_lock.h
674  * @brief A scoped locking class for exception safe RWLock write
675  * locking which tracks the status of its read-write lock..
676  * @sa Thread::RWLock Thread::RWLock::WriterLock Thread::RWLock::ReaderLock Thread::RWLock::ReaderTrackLock Thread::Thread
677  *
678  * This class is similar to a RWLock::WriterLock object, except that
679  * it tracks whether the read-write lock it manages is locked for
680  * writing by the thread creating the RWLock::WriterTrackLock object
681  * (provided that, while the RWLock::WriterTrackLock object exists,
682  * the thread creating it only accesses the managed read-write lock
683  * for write-locking through that object). This enables
684  * RWLock::WriterTrackLock::unlock() to be used without it being
685  * followed later by a call to RWLock::WriterTrackLock::lock() or a
686  * successful call to RWLock::WriterTrackLock::trylock(), and also
687  * permits locking to be deferred until after construction of the
688  * RWLock::WriterTrackLock object. Note that only one thread may call
689  * the methods of any one RWLock::WriterTrackLock object, including
690  * causing its destructor to be invoked.
691  */
692 
694  RWLock& rw_lock;
695  bool owner;
696 
697 public:
698 /**
699  * This class cannot be copied. The copy constructor is deleted.
700  */
701  WriterTrackLock(const RWLock::WriterTrackLock&) = delete;
702 
703 /**
704  * This class cannot be copied. The assignment operator is deleted.
705  */
707 
708 /**
709  * Calls RWLock::writer_lock(), and so locks the read-write lock for
710  * writing and acquires ownership. It blocks if the read-write lock
711  * is already locked for reading or writing until it becomes free.
712  * This method should normally only be called if a previous call has
713  * been made to RWLock::WriterTrackLock::unlock() or this
714  * RWLock::WriterTrackLock object has been constructed with the
715  * Thread::defer enum tag. It is not a cancellation point. It does
716  * not throw.
717  * @return 0 if successful, otherwise the pthread read-write lock
718  * error number.
719  * @note With this library implementation, the only pthread error
720  * number which could be returned by this method is EDEADLK, which it
721  * would do if the default pthread reader lock behaviour happens to
722  * return that error rather than deadlock where the thread calling
723  * this method already holds a read lock or write lock on the
724  * particular read-write lock in question. Most default
725  * implementations do not do this (they just deadlock) and hence the
726  * return value is usually not worth checking for except during
727  * debugging.
728  */
729  int lock() noexcept {int ret = rw_lock.writer_lock(); if (!owner) owner = !ret; return ret;}
730 
731 /**
732  * Calls RWLock::writer_trylock(), and so tries to lock the read-write
733  * lock for writing and acquire ownership, but returns immediately
734  * with value EBUSY if it is already locked for reading or writing.
735  * This method should normally only be called if a previous call has
736  * been made to RWLock::WriterTrackLock::unlock() or this
737  * RWLock::WriterTrackLock object has been constructed with the
738  * Thread::defer enum tag. It is not a cancellation point. It does
739  * not throw.
740  * @return 0 if successful, otherwise EBUSY.
741  * @note With this library implementation, the only pthread error
742  * number which could be returned by this method is EBUSY.
743  */
744  int trylock() noexcept {int ret = rw_lock.writer_trylock(); if (!owner) owner = !ret; return ret;}
745 
746 /**
747  * Calls RWLock::unlock(), and so unlocks a locked read-write lock
748  * owned by the calling thread for writing and relinquishes ownership.
749  * It will cause is_owner() to return false unless a subsequent call
750  * is made to lock() or a subsequent successful call is made to
751  * trylock(). It is not a cancellation point. It does not throw.
752  * @return 0 if successful, otherwise the pthread read-write lock
753  * error number.
754  * @note With this library implementation, the only pthread error
755  * number which could be returned by this method is EPERM because the
756  * calling thread does hold a lock on the particular read-write lock
757  * in question (however POSIX does not require that return value in
758  * that case and hence the return value is usually not worth checking
759  * for except during debugging).
760  */
761  int unlock() noexcept {int ret = rw_lock.unlock(); if (owner) owner = ret; return ret;}
762 
763 /**
764  * Indicates whether the read-write lock managed by this
765  * RWLock::ReaderTrackLock object is locked for writing by it and so
766  * owned by it. It does not throw.
767  * @return true if the read-write lock is owned by this object,
768  * otherwise false.
769  */
770  bool is_owner() const noexcept {return owner;}
771 
772 /**
773  * This constructor locks for writing the read-write lock passed to
774  * it. It is not a cancellation point. It does not throw.
775  * @param rw_lock_ The read-write lock to be locked for writing.
776  */
777  WriterTrackLock(RWLock& rw_lock_) noexcept: rw_lock(rw_lock_), owner(true) {rw_lock.writer_lock();}
778 
779 /**
780  * This constructor takes a read-write lock already locked for writing
781  * (say as a result of RWLock::writer_trylock()), and takes ownership
782  * of it. It is not a cancellation point. It does not throw.
783  * @param rw_lock_ The read-write lock to be managed for writing by
784  * this object.
785  * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
786  */
787  WriterTrackLock(RWLock& rw_lock_, Locked tag) noexcept: rw_lock(rw_lock_), owner(true) {}
788 
789 /**
790  * This constructor defers locking of the read-write lock for writing
791  * until an explicit call to lock() or trylock() is made. It is not a
792  * cancellation point. It does not throw.
793  * @param rw_lock_ The read-write lock to be managed for writing by
794  * this object.
795  * @param tag Pass the Cgu::Thread::defer enum tag to this parameter.
796  */
797  WriterTrackLock(RWLock& rw_lock_, DeferLock tag) noexcept: rw_lock(rw_lock_), owner(false) {}
798 
799 /**
800  * This class requires initialisation with a RWLock. The default
801  * constructor is deleted.
802  */
803  WriterTrackLock() = delete;
804 
805 /**
806  * The destructor unlocks the read-write lock which is managed for
807  * writing if it is owned by this RWLock::WriterTrackLock object. It
808  * is not a cancellation point. It does not throw.
809  */
810  ~WriterTrackLock() {if (owner) rw_lock.unlock();}
811 
812 /* Only has effect if --with-glib-memory-slices-compat or
813  * --with-glib-memory-slices-no-compat option picked */
815 };
816 
817 } // namespace Thread
818 
819 } // namespace Cgu
820 
821 #endif