c++-gtk-utils
|
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