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