Fawkes API  Fawkes Development Version
scoped_rwlock.cpp
00001 
00002 /***************************************************************************
00003  *  scoped_rwlock.cpp - Scoped read/write lock
00004  *
00005  *  Created: Mon Jan 10 11:45:49 2011
00006  *  Copyright  2006-2011  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <core/threading/scoped_rwlock.h>
00025 #include <core/threading/read_write_lock.h>
00026 
00027 namespace fawkes {
00028 #if 0 /* just to make Emacs auto-indent happy */
00029 }
00030 #endif
00031 
00032 /** @class ScopedRWLock <core/threading/scoped_rwlock.h>
00033  * Scoped read/write lock.
00034  * This class is a convenience class which can help you prevent quite
00035  * a few headaches. Consider the following code.
00036  * @code
00037  * void my_function()
00038  * {
00039  *   rwlock->lock_for_write();
00040  *   for (int i = 0; i < LIMIT; ++i) {
00041  *     if ( failure ) {
00042  *       rwlock->unlock();
00043  *     }
00044  *   }
00045  *
00046  *   switch ( someval ) {
00047  *     VALA:
00048  *       rwlock->unlock();
00049  *       return;
00050  *     VALB:
00051  *       do_something();
00052  *   }
00053  *
00054  *   try {
00055  *     do_function_that_throws_exceptions();
00056  *   } catch (Exception &e) {
00057  *     rwlock->unlock();
00058  *     throw;
00059  *   }
00060  *   rwlock->unlock();
00061  * }
00062  * @endcode
00063  * This is not a complete list of examples but as you see if you have many
00064  * exit points in a function it becomes more and more work to have correct
00065  * locking behavior.
00066  *
00067  * This is a lot simpler with the ScopedRWLock. The ScopedRWLock locks the
00068  * given ReadWriteLock on creation, and unlocks it in the destructor. If you now
00069  * have a read/write locker on the stack as integral type the destructor is
00070  * called automagically on function exit and thus the lock is appropriately
00071  * unlocked.
00072  * The code would look like this:
00073  * @code
00074  * void my_function()
00075  * {
00076  *   ScopedRWLock lock(rwlock);
00077  *   // do anything, no need to call rwlock->lock_*()/unlock() if only has to be
00078  *   // called on entering and exiting the function.
00079  * }
00080  * @endcode
00081  *
00082  * @ingroup Threading
00083  * @ingroup FCL
00084  *
00085  * @author Tim Niemueller
00086  */
00087 
00088 
00089 /** Constructor.
00090  * @param rwlock ReadWriteLock to lock/unlock appropriately.
00091  * @param initially_lock true to lock the rwlock in the constructor,
00092  * false to not lock
00093  * @param lock_type locking type, lock either for writing or for reading
00094  */
00095 ScopedRWLock::ScopedRWLock(RefPtr<ReadWriteLock> rwlock, ScopedRWLock::LockType lock_type,
00096                            bool initially_lock)
00097 {
00098   __rawrwlock = 0;
00099   __refrwlock = rwlock;
00100   __lock_type = lock_type;
00101   if ( initially_lock ) {
00102     if (__lock_type == LOCK_WRITE) {
00103       __refrwlock->lock_for_write();
00104     } else {
00105       __refrwlock->lock_for_read();
00106     }
00107   }
00108   __locked = initially_lock;
00109 }
00110 
00111 
00112 /** Constructor.
00113  * @param rwlock ReadWriteLock to lock/unlock appropriately.
00114  * @param initially_lock true to lock the rwlock in the constructor,
00115  * false to not lock
00116  * @param lock_type locking type, lock either for writing or for reading
00117  */
00118 ScopedRWLock::ScopedRWLock(ReadWriteLock *rwlock, ScopedRWLock::LockType lock_type,
00119                            bool initially_lock)
00120 {
00121   __rawrwlock = rwlock;
00122   __lock_type = lock_type;
00123   if ( initially_lock ) {
00124     if (__lock_type == LOCK_WRITE) {
00125       __rawrwlock->lock_for_write();
00126     } else {
00127       __rawrwlock->lock_for_read();
00128     }
00129   }
00130   __locked = initially_lock;
00131 }
00132 
00133 
00134 /** Destructor */
00135 ScopedRWLock::~ScopedRWLock()
00136 {
00137   if ( __locked ) {
00138     if ( __rawrwlock) {
00139       __rawrwlock->unlock();
00140     } else {
00141       __refrwlock->unlock();
00142     }
00143   }
00144 }
00145 
00146 
00147 /** Lock this rwlock, again.
00148  * Use this if you unlocked the rwlock from the outside.
00149  */
00150 void
00151 ScopedRWLock::relock()
00152 {
00153   if ( __rawrwlock ) {
00154     if (__lock_type == LOCK_WRITE) {
00155       __rawrwlock->lock_for_write();
00156     } else {
00157       __rawrwlock->lock_for_read();
00158     }
00159   } else {
00160     if (__lock_type == LOCK_WRITE) {
00161       __refrwlock->lock_for_write();
00162     } else {
00163       __refrwlock->lock_for_read();
00164     }
00165   }
00166   __locked = true;
00167 }
00168 
00169 
00170 /** Unlock the rwlock. */
00171 void
00172 ScopedRWLock::unlock()
00173 {
00174   __locked = false;
00175   if ( __rawrwlock ) {
00176     __rawrwlock->unlock();
00177   } else {
00178     __refrwlock->unlock();
00179   }
00180 }
00181 
00182 
00183 } // end namespace fawkes