c++-gtk-utils
rw_lock.h
Go to the documentation of this file.
00001 /* Copyright (C) 2010 and 2011 Chris Vine
00002 
00003 The library comprised in this file or of which this file is part is
00004 distributed by Chris Vine under the GNU Lesser General Public
00005 License as follows:
00006 
00007    This library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Lesser General Public License
00009    as published by the Free Software Foundation; either version 2.1 of
00010    the License, or (at your option) any later version.
00011 
00012    This library is distributed in the hope that it will be useful, but
00013    WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Lesser General Public License, version 2.1, for more details.
00016 
00017    You should have received a copy of the GNU Lesser General Public
00018    License, version 2.1, along with this library (see the file LGPL.TXT
00019    which came with this source code package in the c++-gtk-utils
00020    sub-directory); if not, write to the Free Software Foundation, Inc.,
00021    59 Temple Place - Suite 330, Boston, MA, 02111-1307, USA.
00022 
00023 However, it is not intended that the object code of a program whose
00024 source code instantiates a template from this file or uses macros or
00025 inline functions (of any length) should by reason only of that
00026 instantiation or use be subject to the restrictions of use in the GNU
00027 Lesser General Public License.  With that in mind, the words "and
00028 macros, inline functions and instantiations of templates (of any
00029 length)" shall be treated as substituted for the words "and small
00030 macros and small inline functions (ten lines or less in length)" in
00031 the fourth paragraph of section 5 of that licence.  This does not
00032 affect any other reason why object code may be subject to the
00033 restrictions in that licence (nor for the avoidance of doubt does it
00034 affect the application of section 2 of that licence to modifications
00035 of the source code in this file).
00036 
00037 */
00038 
00039 #ifndef CGU_RW_LOCK_H
00040 #define CGU_RW_LOCK_H
00041 
00042 #include <exception>
00043 #include <pthread.h>
00044 
00045 #include <c++-gtk-utils/mutex.h> // for Locked and DeferLock enumerations
00046 #include <c++-gtk-utils/cgu_config.h>
00047 
00048 /**
00049  * @file rw_lock.h
00050  * @brief Provides wrapper class for pthread read-write locks, and
00051  * scoped locking classes for exception safe locking of read-write
00052  * locks.
00053  */
00054 
00055 namespace Cgu {
00056 
00057 namespace Thread {
00058 
00059 struct RWLockError: public std::exception {
00060   virtual const char* what() const throw() {return "Thread::RWLockError";}
00061 };
00062 
00063 /**
00064  * @class RWLock rw_lock.h c++-gtk-utils/rw_lock.h
00065  * @brief A wrapper class for pthread read-write locks.
00066  * @sa Thread::Thread Thread::RWLock::ReaderLock Thread::RWLock::ReaderTrackLock Thread::RWLock::WriterLock Thread::RWLock::WriterTrackLock Thread::Mutex
00067  *
00068  * This class can be used interchangeably with threads started with
00069  * GThread and by this library, as both glib and this library use
00070  * pthreads underneath on POSIX and other unix-like OSes.  It can also
00071  * be used interchangeably with those started by C++11, as in C++11 on
00072  * unix-like OSes these facilities will be built on top of pthreads
00073  * (for which purpose C++11 provides the std::native_handle_type type
00074  * and std::thread::native_handle() function), or if they are not,
00075  * they will use the same threading primitives provided by the kernel.
00076  *
00077  * There is no separate class for static read-write locks.  That is
00078  * not necessary, as these RWLock objects can be constructed
00079  * statically as well as dynamically and there is no need to call
00080  * g_thread_init() before they are constructed.  (If created as a
00081  * static object in global scope, it will not be possible to catch
00082  * Thread::RWLockError thrown by its constructor, but if a static
00083  * global read-write lock throws there is nothing that could be done
00084  * anyway except abort, and it would show that the pthreads
00085  * installation is seriously defective.)
00086  *
00087  * Read-write locks are similar to mutexes except that they allow more
00088  * than one thread to hold the lock for reading at once.  This can
00089  * offer advantages over a mutex where a particular shared object is
00090  * thread safe for lock-free reading by multiple threads
00091  * simultaneously, is frequently read by different threads and is not
00092  * often modified.  However, the implementation of a read-write lock
00093  * is more complex than that of a mutex, and unless the particular
00094  * pthread read-write lock scheduling implementation favours
00095  * already-blocking writers over later readers whenever a read-write
00096  * lock object is unlocked, writer starvation can occur.  Unless all
00097  * the reads are of significant duration, might cause (if protected by
00098  * a mutex) significant contention between each other and greatly
00099  * exceed the number of times the write lock is held, then it is
00100  * usually better to use an ordinary mutex.
00101  */
00102 
00103 class RWLock {
00104   pthread_rwlock_t pthr_rwlock;
00105   
00106 public:
00107   class ReaderLock;
00108   class ReaderTrackLock;
00109   class WriterLock;
00110   class WriterTrackLock;
00111 
00112 /**
00113  * This class cannot be copied.  The copy constructor is deleted.
00114  */
00115   RWLock(const RWLock&) = delete;
00116 
00117 /**
00118  * This class cannot be copied.  The assignment operator is deleted.
00119  */
00120   RWLock& operator=(const RWLock&) = delete;
00121 
00122 /**
00123  * Locks the read-write lock for reading.  Blocks if already locked
00124  * for writing until it becomes free.  More than one thread may
00125  * simultaneously hold a read lock, and a thread may lock for reading
00126  * recursively provided that each call to this method is matched by a
00127  * call to unlock().  It is not a cancellation point.  It does not
00128  * throw.  It is thread safe.
00129  * @return 0 if successful, otherwise the pthread read-write lock
00130  * error number.
00131  * @note With this library implementation, the only pthread error
00132  * numbers which could be returned by this method are EDEADLK and
00133  * EAGAIN.  EDEADLK would be returned if the default pthread reader
00134  * lock behaviour happens to return that error rather than deadlock
00135  * where the thread calling this method already holds a write lock on
00136  * this read-write lock.  Most default implementations do not do this
00137  * (they just deadlock) and hence the return value is usually not
00138  * worth checking for except during debugging.  EAGAIN would be
00139  * returned if the maximum number of read locks for this read-write
00140  * lock has been reached.  Usually this number is at or around INT_MAX
00141  * so it is also not usually useful to check for it except during
00142  * debugging.
00143  */
00144   int reader_lock() {return pthread_rwlock_rdlock(&pthr_rwlock);}
00145 
00146 /**
00147  * Tries to lock the read-write lock for reading, but returns
00148  * immediately with value EBUSY if it is already locked for writing.
00149  * More than one thread may simultaneously hold a read lock, and a
00150  * thread may lock for reading recursively provided that each call to
00151  * this method is matched by a call to unlock().  It is not a
00152  * cancellation point.  It does not throw.  It is thread safe.
00153  * @return 0 if successful, otherwise EBUSY or other pthread
00154  * read-write lock error number.
00155  * @note With this library implementation, apart from EBUSY, the only
00156  * other pthread error number which could be returned by this method
00157  * is EAGAIN, which would be returned if the maximum number of read
00158  * locks for this read-write lock has been reached.  Usually this
00159  * number is at or around INT_MAX so it is not usually useful to check
00160  * for it except during debugging.
00161  */
00162   int reader_trylock() {return pthread_rwlock_tryrdlock(&pthr_rwlock);}
00163 
00164 /**
00165  * Locks the read-write lock for writing and acquires ownership.
00166  * Blocks if already locked for reading or writing until it becomes
00167  * free.  It is not a cancellation point.  It does not throw.  It is
00168  * thread safe.
00169  * @return 0 if successful, otherwise the pthread read-write lock
00170  * error number.
00171  * @note With this library implementation, the only pthread error
00172  * number which could be returned by this method is EDEADLK, which it
00173  * would do if the default pthread reader lock behaviour happens to
00174  * return that error rather than deadlock where the thread calling
00175  * this method already holds a read lock or write lock on this
00176  * read-write lock.  Most default implementations do not do this (they
00177  * just deadlock) and hence the return value is usually not worth
00178  * checking for except during debugging.
00179  */
00180   int writer_lock() {return pthread_rwlock_wrlock(&pthr_rwlock);}
00181 
00182 /**
00183  * Tries to lock the read-write lock for writing and acquire
00184  * ownership, but returns immediately with value EBUSY if it is
00185  * already locked for reading or writing.  It is not a cancellation
00186  * point.  It does not throw.  It is thread safe.
00187  * @return 0 if successful, otherwise EBUSY.
00188  * @note With this library implementation, the only pthread error
00189  * number which could be returned by this method is EBUSY.
00190  */
00191   int writer_trylock() {return pthread_rwlock_trywrlock(&pthr_rwlock);}
00192 
00193 /**
00194  * Unlocks a read-write lock previously locked for reading or writing
00195  * by the calling thread.  If the calling thread has locked the
00196  * read-write lock for writing, it relinquishes ownership.  If it has
00197  * previously locked the read-write lock for reading, it releases that
00198  * particular lock, but the read-write lock may remain locked for
00199  * reading if it has been locked for reading recursively or other
00200  * threads hold a read lock and the particular implementation does not
00201  * provide writer priority.  It is not a cancellation point.  It does
00202  * not throw.
00203  * @return 0 if successful, otherwise the pthread read-write lock
00204  * error number.
00205  * @note With this library implementation, the only pthread error
00206  * number which could be returned by this method is EPERM because the
00207  * calling thread does hold a lock on this read-write lock (however
00208  * POSIX does not require that return value in that case and hence the
00209  * return value is usually not worth checking for except during
00210  * debugging).
00211  */
00212   int unlock() {return pthread_rwlock_unlock(&pthr_rwlock);}
00213 
00214 /**
00215  * Initialises the pthread read-write lock.  It is not a cancellation
00216  * point.
00217  * @exception Cgu::Thread::RWLockError Throws this exception if
00218  * initialisation of the read-write lock fails.  (It is often not
00219  * worth checking for this, as it means either memory is exhausted or
00220  * pthread has run out of other resources to create new read-write
00221  * locks.)
00222  */
00223   RWLock() {if (pthread_rwlock_init(&pthr_rwlock, 0)) throw RWLockError();}
00224 
00225 /**
00226  * Destroys the pthread read-write lock.  It is not a cancellation
00227  * point.  It does not throw.
00228  */
00229   ~RWLock() {pthread_rwlock_destroy(&pthr_rwlock);}
00230 
00231 /* Only has effect if --with-glib-memory-slices-compat or
00232  * --with-glib-memory-slices-no-compat option picked */
00233   CGU_GLIB_MEMORY_SLICES_FUNCS
00234 };
00235 
00236 /**
00237  * @class RWLock::ReaderLock rw_lock.h c++-gtk-utils/rw_lock.h
00238  * @brief A scoped locking class for exception safe RWLock read locking.
00239  * @sa Thread::RWLock Thread::RWLock::ReaderTrackLock Thread::RWLock::WriterLock Thread::RWLock::WriterTrackLock Thread::Thread
00240  */
00241 
00242 class RWLock::ReaderLock {
00243   RWLock& rw_lock;
00244 
00245 public:
00246 /**
00247  * This class cannot be copied.  The copy constructor is deleted.
00248  */
00249   ReaderLock(const RWLock::ReaderLock&) = delete;
00250 
00251 /**
00252  * This class cannot be copied.  The assignment operator is deleted.
00253  */
00254   RWLock::ReaderLock& operator=(const RWLock::ReaderLock&) = delete;
00255 
00256 /**
00257  * Calls RWLock::reader_lock(), and so relocks the read-write lock for
00258  * reading.  It blocks if the read-write lock is already locked for
00259  * writing until it becomes free.  This method should normally only be
00260  * called if a previous call has been made to
00261  * RWLock::ReaderLock::unlock() (that is, where the thread owning the
00262  * RWLock::ReaderLock object has temporarily allowed another thread to
00263  * take the read-write lock concerned for writing if another thread
00264  * does not hold a read lock or the read-write lock has not been
00265  * recursively locked for reading).  It is not a cancellation point.
00266  * It does not throw.
00267  * @return 0 if successful, otherwise the pthread read-write lock
00268  * error number.
00269  * @note With this library implementation, the only pthread error
00270  * numbers which could be returned by this method are EDEADLK and
00271  * EAGAIN.  EDEADLK would be returned if the default pthread reader
00272  * lock behaviour happens to return that error rather than deadlock
00273  * where the thread calling this method already holds a write lock on
00274  * the particular read-write lock in question.  Most default
00275  * implementations do not do this (they just deadlock) and hence the
00276  * return value is usually not worth checking for except during
00277  * debugging.  EAGAIN would be returned if the maximum number of read
00278  * locks for the read-write lock in question has been reached.
00279  * Usually this number is at or around INT_MAX so it is also not
00280  * usually useful to check for it except during debugging.
00281  * @sa RWLock::ReaderTrackLock
00282  */
00283   int lock() {return rw_lock.reader_lock();}
00284 
00285 /**
00286  * Calls RWLock::reader_trylock(), and so tries to relock the
00287  * read-write lock for reading, but returns immediately with value
00288  * EBUSY if it is already locked for writing.  This method should
00289  * normally only be called if a previous call has been made to
00290  * RWLock::ReaderLock::unlock() (that is, where the thread owning the
00291  * RWLock::ReaderLock object has temporarily allowed another thread to
00292  * take the read-write lock concerned for writing if another thread
00293  * does not hold a read lock or the read-write lock has not been
00294  * recursively locked for reading).  It is not a cancellation point.
00295  * It does not throw.
00296  * @return 0 if successful, otherwise EBUSY or other pthread
00297  * read-write lock error number.
00298  * @note With this library implementation, apart from EBUSY, the only
00299  * other pthread error number which could be returned by this method
00300  * is EAGAIN, which would be returned if the maximum number of read
00301  * locks for the particular read-write lock in question has been
00302  * reached.  Usually this number is at or around INT_MAX so it is not
00303  * usually useful to check for it except during debugging.
00304  * @sa RWLock::ReaderTrackLock
00305  */
00306   int trylock() {return rw_lock.reader_trylock();}
00307 
00308 /**
00309  * Calls RWLock::unlock(), and so unlocks a locked read-write lock
00310  * held by the calling thread for reading (so temporarily allowing
00311  * another thread to take the read-write lock for writing should no
00312  * other read lock be held or the particular implementation provides
00313  * writer priority).  This method should normally only be called if it
00314  * is to be followed by a call to RWLock::ReaderLock::lock() or a
00315  * successful call to RWLock::ReaderLock::trylock() before the
00316  * RWLock::ReaderLock object concerned goes out of scope (otherwise
00317  * RWLock::ReaderLock's destructor will attempt to unlock an already
00318  * unlocked read-write lock or a read-write lock of which another
00319  * thread holds a lock - RWLock::ReaderLock objects do not maintain
00320  * state).  See RWLock::ReaderTrackLock::unlock() for a safe version
00321  * of this method.  It is not a cancellation point.  It does not
00322  * throw.
00323  * @return 0 if successful, otherwise the pthread read-write lock
00324  * error number.
00325  * @note With this library implementation, the only pthread error
00326  * number which could be returned by this method is EPERM because the
00327  * calling thread does hold a lock on the particular read-write lock
00328  * in question (however POSIX does not require that return value in
00329  * that case and hence the return value is usually not worth checking
00330  * for except during debugging).
00331  * @sa RWLock::ReaderTrackLock
00332  */
00333   int unlock() {return rw_lock.unlock();}
00334 
00335 /**
00336  * This constructor locks for reading the read-write lock passed to
00337  * it.  It is not a cancellation point.
00338  * @param rw_lock_ The read-write lock to be locked for reading.
00339  * @exception Cgu::Thread::RWLockError Throws this exception if
00340  * initialization of the read-write lock fails because the maximum
00341  * number of read locks for the particular read-write lock in question
00342  * has been reached.  Usually this number is at or around INT_MAX so
00343  * it is not usually useful to check for the exception except during
00344  * debugging.  This exception may also be thrown if the thread
00345  * constructing this object already holds a write lock on the
00346  * read-write lock in question. It will do this if the default pthread
00347  * implementation returns EDEADLK in such a case instead of
00348  * deadlocking.  However as most default implementations will simply
00349  * deadlock in such circumstances, it is usually not worth checking
00350  * for this either except during debugging.
00351  */
00352   ReaderLock(RWLock& rw_lock_): rw_lock(rw_lock_) {if (rw_lock.reader_lock()) throw RWLockError();}
00353 
00354 /**
00355  * This constructor takes a read-write lock already locked for reading
00356  * (say as a result of RWLock::reader_trylock()), and takes management
00357  * of that read lock operation.  It is not a cancellation point.  It
00358  * does not throw.
00359  * @param rw_lock_ The read-write lock to be managed for reading by
00360  * this object.
00361  * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
00362  */
00363   ReaderLock(RWLock& rw_lock_, Locked tag): rw_lock(rw_lock_) {}
00364 
00365 /**
00366  * This class requires initialisation with a RWLock.  The default
00367  * constructor is deleted.
00368  */
00369   ReaderLock() = delete;
00370 
00371 /**
00372  * The destructor unlocks the read-write lock which is managed for
00373  * reading.  It is not a cancellation point.  It does not throw.
00374  */
00375   ~ReaderLock() {rw_lock.unlock();}
00376 
00377 /* Only has effect if --with-glib-memory-slices-compat or
00378  * --with-glib-memory-slices-no-compat option picked */
00379   CGU_GLIB_MEMORY_SLICES_FUNCS
00380 };
00381 
00382 /**
00383  * @class RWLock::ReaderTrackLock rw_lock.h c++-gtk-utils/rw_lock.h
00384  * @brief A scoped locking class for exception safe RWLock read
00385  * locking which tracks the status of its read-write lock.
00386  * @sa Thread::RWLock Thread::RWLock::ReaderLock Thread::RWLock::WriterLock Thread::RWLock::WriterTrackLock Thread::Thread
00387  *
00388  * This class is similar to a RWLock::ReaderLock object, except that
00389  * it tracks whether the read-write lock it manages is locked for
00390  * reading by the thread creating the RWLock::ReaderTrackLock object
00391  * with respect to the particular read-locking operation to be
00392  * governed by the object (provided that, while the
00393  * RWLock::ReaderTrackLock object exists, the thread creating it only
00394  * accesses the managed read-write lock with respect that particular
00395  * operation through that object).  This enables
00396  * RWLock::ReaderTrackLock::unlock() to be used without it being
00397  * followed later by a call to RWLock::ReaderTrackLock::lock() or a
00398  * successful call to RWLock::ReaderTrackLock::trylock(), and also
00399  * permits locking to be deferred until after construction of the
00400  * RWLock::ReaderTrackLock object.  Note that only one thread may call
00401  * the methods of any one RWLock::ReaderTrackLock object, including
00402  * causing its destructor to be invoked.
00403  */
00404 
00405 class RWLock::ReaderTrackLock {
00406   RWLock& rw_lock;
00407   bool owner;
00408 
00409 public:
00410 /**
00411  * This class cannot be copied.  The copy constructor is deleted.
00412  */
00413   ReaderTrackLock(const RWLock::ReaderTrackLock&) = delete;
00414 
00415 /**
00416  * This class cannot be copied.  The assignment operator is deleted.
00417  */
00418   RWLock::ReaderTrackLock& operator=(const RWLock::ReaderTrackLock&) = delete;
00419 
00420 /**
00421  * This calls RWLock::reader_lock(), and so locks the read-write lock
00422  * for reading and acquires ownership (which may be shared with other
00423  * read locks).  It blocks if the read-write lock is already locked
00424  * for writing until it becomes free.  This method should normally
00425  * only be called if a previous call has been made to
00426  * RWLock::ReaderTrackLock::unlock() or this RWLock::ReaderTrackLock
00427  * object has been constructed with the Thread::defer enum tag.  It is
00428  * not a cancellation point.  It does not throw.
00429  * @return 0 if successful, otherwise the pthread read-write lock
00430  * error number.
00431  * @note With this library implementation, the only pthread error
00432  * numbers which could be returned by this method are EDEADLK and
00433  * EAGAIN.  EDEADLK would be returned if the default pthread reader
00434  * lock behaviour happens to return that error rather than deadlock
00435  * where the thread calling this method already holds a write lock on
00436  * the particular read-write lock in question.  Most default
00437  * implementations do not do this (they just deadlock) and hence the
00438  * return value is usually not worth checking for except during
00439  * debugging.  EAGAIN would be returned if the maximum number of read
00440  * locks for the read-write lock in question has been reached.
00441  * Usually this number is at or around INT_MAX so it is also not
00442  * usually useful to check for it except during debugging.
00443  */
00444   int lock() {int ret = rw_lock.reader_lock(); if (!owner) owner = !ret; return ret;}
00445 
00446 /**
00447  * This calls RWLock::reader_trylock(), and so tries to lock the
00448  * read-write lock for reading and acquire ownership (which may be
00449  * shared with other read locks), but returns immediately with value
00450  * EBUSY if it is already locked for writing.  This method should
00451  * normally only be called if a previous call has been made to
00452  * RWLock::ReaderTrackLock::unlock() or this RWLock::ReaderTrackLock
00453  * object has been constructed with the Thread::defer enum tag.  It is
00454  * not a cancellation point.  It does not throw.
00455  * @return 0 if successful, otherwise EBUSY or other pthread
00456  * read-write lock error number.
00457  * @note With this library implementation, apart from EBUSY, the only
00458  * other pthread error number which could be returned by this method
00459  * is EAGAIN, which would be returned if the maximum number of read
00460  * locks for the particular read-write lock in question has been
00461  * reached.  Usually this number is at or around INT_MAX so it is not
00462  * usually useful to check for it except during debugging.
00463  */
00464   int trylock() {int ret = rw_lock.reader_trylock(); if (!owner) owner = !ret; return ret;}
00465 
00466 /**
00467  * This calls RWLock::unlock(), and so unlocks a locked read-write
00468  * lock held by the calling thread for reading and relinquishes
00469  * ownership (whether it was sole or shared with other read locks).
00470  * It will cause is_owner() to return false unless a subsequent call
00471  * is made to lock() or a subsequent successful call is made to
00472  * trylock().  It is not a cancellation point.  It does not throw.
00473  * @return 0 if successful, otherwise the pthread read-write lock
00474  * error number.
00475  * @note With this library implementation, the only pthread error
00476  * number which could be returned by this method is EPERM because the
00477  * calling thread does hold a lock on the particular read-write lock
00478  * in question (however POSIX does not require that return value in
00479  * that case and hence the return value is usually not worth checking
00480  * for except during debugging).
00481  */
00482   int unlock() {int ret = rw_lock.unlock(); if (owner) owner = ret; return ret;}
00483 
00484 /**
00485  * Indicates whether the read-write lock managed by this
00486  * RWLock::ReaderTrackLock object is locked for reading by it and so
00487  * owned by it (whether solely or with other read locks).
00488  * @return true if the read-write lock is owned by this object,
00489  * otherwise false.
00490  */
00491   bool is_owner() const {return owner;}
00492 
00493 /**
00494  * This constructor locks for reading the read-write lock passed to
00495  * it.  It is not a cancellation point.
00496  * @param rw_lock_ The read-write lock to be locked for reading.
00497  * @exception Cgu::Thread::RWLockError Throws this exception if
00498  * initialization of the read-write lock fails because the maximum
00499  * number of read locks for the particular read-write lock in question
00500  * has been reached.  Usually this number is at or around INT_MAX so
00501  * it is not usually useful to check for the exception except during
00502  * debugging.  This exception may also be thrown if the thread
00503  * constructing this object already holds a write lock on the
00504  * read-write lock in question. It will do this if the default pthread
00505  * implementation returns EDEADLK in such a case instead of
00506  * deadlocking.  However as most default implementations will simply
00507  * deadlock in such circumstances, it is usually not worth checking
00508  * for this either except during debugging.
00509  */
00510   ReaderTrackLock(RWLock& rw_lock_): rw_lock(rw_lock_), owner(true) {if (rw_lock.reader_lock()) throw RWLockError();}
00511 
00512 /**
00513  * This constructor takes a read-write lock already locked for reading
00514  * (say as a result of RWLock::reader_trylock()), and takes management
00515  * of that read lock operation.  It is not a cancellation point.  It
00516  * does not throw.
00517  * @param rw_lock_ The read-write lock to be managed for reading by
00518  * this object.
00519  * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
00520  */
00521   ReaderTrackLock(RWLock& rw_lock_, Locked tag): rw_lock(rw_lock_), owner(true) {}
00522 
00523 /**
00524  * This constructor defers locking of the read-write lock for reading
00525  * until an explicit call to lock() or trylock() is made.  It is not a
00526  * cancellation point.  It does not throw.
00527  * @param rw_lock_ The read-write lock to be managed for reading by
00528  * this object.
00529  * @param tag Pass the Cgu::Thread::defer enum tag to this parameter.
00530  */
00531   ReaderTrackLock(RWLock& rw_lock_, DeferLock tag): rw_lock(rw_lock_), owner(false) {}
00532 
00533 /**
00534  * This class requires initialisation with a RWLock.  The default
00535  * constructor is deleted.
00536  */
00537   ReaderTrackLock() = delete;
00538 
00539 /**
00540  * The destructor unlocks the read-write lock which is managed for
00541  * reading if it is owned by this RWLock::ReaderTrackLock object
00542  * (whether solely or with other read locks).  It is not a
00543  * cancellation point.  It does not throw.
00544  */
00545   ~ReaderTrackLock() {if (owner) rw_lock.unlock();}
00546 
00547 /* Only has effect if --with-glib-memory-slices-compat or
00548  * --with-glib-memory-slices-no-compat option picked */
00549   CGU_GLIB_MEMORY_SLICES_FUNCS
00550 };
00551 
00552 /**
00553  * @class RWLock::WriterLock rw_lock.h c++-gtk-utils/rw_lock.h
00554  * @brief A scoped locking class for exception safe RWLock write locking.
00555  * @sa Thread::RWLock Thread::RWLock::WriterTrackLock Thread::RWLock::ReaderLock Thread::RWLock::ReaderTrackLock Thread::Thread
00556  */
00557 
00558 class RWLock::WriterLock {
00559   RWLock& rw_lock;
00560 
00561 public:
00562 /**
00563  * This class cannot be copied.  The copy constructor is deleted.
00564  */
00565   WriterLock(const RWLock::WriterLock&) = delete;
00566 
00567 /**
00568  * This class cannot be copied.  The assignment operator is deleted.
00569  */
00570   RWLock::WriterLock& operator=(const RWLock::WriterLock&) = delete;
00571 
00572 /**
00573  * Calls RWLock::writer_lock(), and so locks the read-write lock for
00574  * writing and reacquires ownership.  It blocks if the read-write lock
00575  * is already locked for reading or writing until it becomes free.
00576  * This method should normally only be called if a previous call has
00577  * been made to RWLock::WriterLock::unlock() (that is, where the
00578  * thread owning the RWLock::WriterLock object has temporarily allowed
00579  * another thread to take the read-write lock concerned for reading or
00580  * writing).  It is not a cancellation point.  It does not throw.
00581  * @return 0 if successful, otherwise the pthread read-write lock
00582  * error number.
00583  * @note With this library implementation, the only pthread error
00584  * number which could be returned by this method is EDEADLK, which it
00585  * would do if the default pthread reader lock behaviour happens to
00586  * return that error rather than deadlock where the thread calling
00587  * this method already holds a read lock or write lock on the
00588  * particular read-write lock in question.  Most default
00589  * implementations do not do this (they just deadlock) and hence the
00590  * return value is usually not worth checking for except during
00591  * debugging.
00592  * @sa RWLock::WriterTrackLock
00593  */
00594   int lock() {return rw_lock.writer_lock();}
00595 
00596 /**
00597  * Calls RWLock::writer_trylock(), and so tries to lock the read-write
00598  * lock for writing and reacquire ownership, but returns immediately
00599  * with value EBUSY if it is already locked for reading or writing.
00600  * This method should normally only be called if a previous call has
00601  * been made to RWLock::WriterLock::unlock() (that is, where the
00602  * thread owning the RWLock::WriterLock object has temporarily allowed
00603  * another thread to take the read-write lock concerned for reading or
00604  * writing).  It is not a cancellation point.  It does not throw.
00605  * @return 0 if successful, otherwise EBUSY.
00606  * @note With this library implementation, the only pthread error
00607  * number which could be returned by this method is EBUSY.
00608  * @sa RWLock::WriterTrackLock
00609  */
00610   int trylock() {return rw_lock.writer_trylock();}
00611 
00612 /**
00613  * Calls RWLock::unlock(), and so unlocks a locked read-write lock
00614  * owned by the calling thread for writing and relinquishes ownership
00615  * (so temporarily allowing another thread to take the read-write
00616  * lock).  This method should normally only be called if it is to be
00617  * followed by a call to RWLock::WriterLock::lock() or a successful
00618  * call to RWLock::WriterLock::trylock() before the RWLock::WriterLock
00619  * object concerned goes out of scope (otherwise RWLock::WriterLock's
00620  * destructor will attempt to unlock an already unlocked read-write
00621  * lock or a read-write lock of which another thread has by then taken
00622  * ownership - RWLock::WriterLock objects do not maintain state).  See
00623  * RWLock::WriterTrackLock::unlock() for a safe version of this
00624  * method.  It is not a cancellation point.  It does not throw.
00625  * @return 0 if successful, otherwise the pthread read-write lock
00626  * error number.
00627  * @note With this library implementation, the only pthread error
00628  * number which could be returned by this method is EPERM because the
00629  * calling thread does hold a lock on the particular read-write lock
00630  * in question (however POSIX does not require that return value in
00631  * that case and hence the return value is usually not worth checking
00632  * for except during debugging).
00633  * @sa RWLock::WriterTrackLock
00634  */
00635   int unlock() {return rw_lock.unlock();}
00636 
00637 /**
00638  * This constructor locks for writing the read-write lock passed to
00639  * it.  It is not a cancellation point.  It does not throw.
00640  * @param rw_lock_ The read-write lock to be locked for writing.
00641  */
00642   WriterLock(RWLock& rw_lock_): rw_lock(rw_lock_) {rw_lock.writer_lock();}
00643 
00644 /**
00645  * This constructor takes a read-write lock already locked for writing
00646  * (say as a result of RWLock::writer_trylock()), and takes ownership
00647  * of it.  It is not a cancellation point.  It does not throw.
00648  * @param rw_lock_ The read-write lock to be managed for writing by
00649  * this object.
00650  * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
00651  */
00652   WriterLock(RWLock& rw_lock_, Locked tag): rw_lock(rw_lock_) {}
00653 
00654 /**
00655  * This class requires initialisation with a RWLock.  The default
00656  * constructor is deleted.
00657  */
00658   WriterLock() = delete;
00659 
00660 /**
00661  * The destructor unlocks the owned read-write lock.  It is not a
00662  * cancellation point.  It does not throw.
00663  */
00664   ~WriterLock() {rw_lock.unlock();}
00665 
00666 /* Only has effect if --with-glib-memory-slices-compat or
00667  * --with-glib-memory-slices-no-compat option picked */
00668   CGU_GLIB_MEMORY_SLICES_FUNCS
00669 };
00670 
00671 /**
00672  * @class RWLock::WriterTrackLock rw_lock.h c++-gtk-utils/rw_lock.h
00673  * @brief A scoped locking class for exception safe RWLock write
00674  * locking which tracks the status of its read-write lock..
00675  * @sa Thread::RWLock Thread::RWLock::WriterLock Thread::RWLock::ReaderLock Thread::RWLock::ReaderTrackLock Thread::Thread
00676  *
00677  * This class is similar to a RWLock::WriterLock object, except that
00678  * it tracks whether the read-write lock it manages is locked for
00679  * writing by the thread creating the RWLock::WriterTrackLock object
00680  * (provided that, while the RWLock::WriterTrackLock object exists,
00681  * the thread creating it only accesses the managed read-write lock
00682  * for write-locking through that object).  This enables
00683  * RWLock::WriterTrackLock::unlock() to be used without it being
00684  * followed later by a call to RWLock::WriterTrackLock::lock() or a
00685  * successful call to RWLock::WriterTrackLock::trylock(), and also
00686  * permits locking to be deferred until after construction of the
00687  * RWLock::WriterTrackLock object.  Note that only one thread may call
00688  * the methods of any one RWLock::WriterTrackLock object, including
00689  * causing its destructor to be invoked.
00690  */
00691 
00692 class RWLock::WriterTrackLock {
00693   RWLock& rw_lock;
00694   bool owner;
00695 
00696 public:
00697 /**
00698  * This class cannot be copied.  The copy constructor is deleted.
00699  */
00700   WriterTrackLock(const RWLock::WriterTrackLock&);
00701 
00702 /**
00703  * This class cannot be copied.  The assignment operator is deleted.
00704  */
00705   RWLock::WriterTrackLock& operator=(const RWLock::WriterTrackLock&);
00706 
00707 /**
00708  * Calls RWLock::writer_lock(), and so locks the read-write lock for
00709  * writing and acquires ownership.  It blocks if the read-write lock
00710  * is already locked for reading or writing until it becomes free.
00711  * This method should normally only be called if a previous call has
00712  * been made to RWLock::WriterTrackLock::unlock() or this
00713  * RWLock::WriterTrackLock object has been constructed with the
00714  * Thread::defer enum tag.  It is not a cancellation point.  It does
00715  * not throw.
00716  * @return 0 if successful, otherwise the pthread read-write lock
00717  * error number.
00718  * @note With this library implementation, the only pthread error
00719  * number which could be returned by this method is EDEADLK, which it
00720  * would do if the default pthread reader lock behaviour happens to
00721  * return that error rather than deadlock where the thread calling
00722  * this method already holds a read lock or write lock on the
00723  * particular read-write lock in question.  Most default
00724  * implementations do not do this (they just deadlock) and hence the
00725  * return value is usually not worth checking for except during
00726  * debugging.
00727  */
00728   int lock() {int ret = rw_lock.writer_lock(); if (!owner) owner = !ret; return ret;}
00729 
00730 /**
00731  * Calls RWLock::writer_trylock(), and so tries to lock the read-write
00732  * lock for writing and acquire ownership, but returns immediately
00733  * with value EBUSY if it is already locked for reading or writing.
00734  * This method should normally only be called if a previous call has
00735  * been made to RWLock::WriterTrackLock::unlock() or this
00736  * RWLock::WriterTrackLock object has been constructed with the
00737  * Thread::defer enum tag.  It is not a cancellation point.  It does
00738  * not throw.
00739  * @return 0 if successful, otherwise EBUSY.
00740  * @note With this library implementation, the only pthread error
00741  * number which could be returned by this method is EBUSY.
00742  */
00743   int trylock() {int ret = rw_lock.writer_trylock(); if (!owner) owner = !ret; return ret;}
00744 
00745 /**
00746  * Calls RWLock::unlock(), and so unlocks a locked read-write lock
00747  * owned by the calling thread for writing and relinquishes ownership.
00748  * It will cause is_owner() to return false unless a subsequent call
00749  * is made to lock() or a subsequent successful call is made to
00750  * trylock().  It is not a cancellation point.  It does not throw.
00751  * @return 0 if successful, otherwise the pthread read-write lock
00752  * error number.
00753  * @note With this library implementation, the only pthread error
00754  * number which could be returned by this method is EPERM because the
00755  * calling thread does hold a lock on the particular read-write lock
00756  * in question (however POSIX does not require that return value in
00757  * that case and hence the return value is usually not worth checking
00758  * for except during debugging).
00759  */
00760   int unlock() {int ret = rw_lock.unlock(); if (owner) owner = ret; return ret;}
00761 
00762 /**
00763  * Indicates whether the read-write lock managed by this
00764  * RWLock::ReaderTrackLock object is locked for writing by it and so
00765  * owned by it.
00766  * @return true if the read-write lock is owned by this object,
00767  * otherwise false.
00768  */
00769   bool is_owner() const {return owner;}
00770 
00771 /**
00772  * This constructor locks for writing the read-write lock passed to
00773  * it.  It is not a cancellation point.  It does not throw.
00774  * @param rw_lock_ The read-write lock to be locked for writing.
00775  */
00776   WriterTrackLock(RWLock& rw_lock_): rw_lock(rw_lock_), owner(true) {rw_lock.writer_lock();}
00777 
00778 /**
00779  * This constructor takes a read-write lock already locked for writing
00780  * (say as a result of RWLock::writer_trylock()), and takes ownership
00781  * of it.  It is not a cancellation point.  It does not throw.
00782  * @param rw_lock_ The read-write lock to be managed for writing by
00783  * this object.
00784  * @param tag Pass the Cgu::Thread::locked enum tag to this parameter.
00785  */
00786   WriterTrackLock(RWLock& rw_lock_, Locked tag): rw_lock(rw_lock_), owner(true) {}
00787 
00788 /**
00789  * This constructor defers locking of the read-write lock for writing
00790  * until an explicit call to lock() or trylock() is made.  It is not a
00791  * cancellation point.  It does not throw.
00792  * @param rw_lock_ The read-write lock to be managed for writing by
00793  * this object.
00794  * @param tag Pass the Cgu::Thread::defer enum tag to this parameter.
00795  */
00796   WriterTrackLock(RWLock& rw_lock_, DeferLock tag): rw_lock(rw_lock_), owner(false) {}
00797 
00798 /**
00799  * This class requires initialisation with a RWLock.  The default
00800  * constructor is deleted.
00801  */
00802   WriterTrackLock() = delete;
00803 
00804 /**
00805  * The destructor unlocks the read-write lock which is managed for
00806  * writing if it is owned by this RWLock::WriterTrackLock object.  It
00807  * is not a cancellation point.  It does not throw.
00808  */
00809   ~WriterTrackLock() {if (owner) rw_lock.unlock();}
00810 
00811 /* Only has effect if --with-glib-memory-slices-compat or
00812  * --with-glib-memory-slices-no-compat option picked */
00813   CGU_GLIB_MEMORY_SLICES_FUNCS
00814 };
00815 
00816 } // namespace Thread
00817 
00818 } // namespace Cgu
00819 
00820 #endif