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