// This may look like C code, but it's really -*- C++ -*- /* * Copyright (C) 2010 Emweb bvba, Kessel-Lo, Belgium. * * See the LICENSE file for terms of use. */ #ifndef WT_SYNC_LOCK_H_ #define WT_SYNC_LOCK_H_ #include #include #include namespace Wt { /*! \class SyncLock Wt/SyncLock Wt/SyncLock * \brief An dead-lock avoidance adaptor for a Boost mutex lock. * * A %SyncLock adapts a Boost mutex lock (such as boost::mutex::scoped_lock), * and provides the same API as the original lock (it derives from it). * * Just as can be expected by a lock, a call to lock() will block until the * thread has exclusive access to the mutex. * * While waiting to acquire the lock, however, the current * Wt::WApplication lock, may be temporarily released in favor of * another helper thread trying to acquire the application lock. Thus, * you need to be aware that when the lock is taken, some other thread * may have modified the current application state. * * A sync lock is useful in the context of a deployment where multiple * (or all) WApplication instances are running in a single process and * communicating with each other (such as the built-in httpd). Then, * this lock adaptor helps in avoiding a dead-lock situation when * applications wants to access a shared resource protected by a * mutex, which communicates to other applications while taking their * update lock. In that case, it is likely that you will also want to * iterate over all "registered" applications while holding the global * mutex, and this creates a natural dead-lock scenario because you * have two mutexes (global mutex, application mutex) which are * sequentially aquired in a different order: * - application mutex -> global mutex, during an application request * - global mutex -> application mutex, while propagating events to applications * * The altered behaviour of a call by application A to lock() * is that this application A's state may be updated by another * thread (e.g. serving application B), which tries to take * A's update lock. * * The following conventional locking code: * \code * boost::recursive_mutex::scoped_lock lock(mutex_); * \endcode * is updated to a sync lock, by doing: * \code * Wt::SyncLock lock(mutex_); * \endcode */ template class SyncLock : public Lock { public: /*! \brief Creates a RIIA lock. * * Initializes and acquires exclusive access to the \p mutex. */ template SyncLock(Mutex& mutex) : Lock(mutex, boost::defer_lock) { lock(); } /*! \brief Creates a RIIA lock, but defers taking the lock. * * Initialize the lock but defers taking the lock. * * \sa lock() */ template SyncLock(Mutex& mutex, boost::defer_lock_t) : Lock(mutex, boost::defer_lock) { } /*! \brief Tries to acquire the lock, blocking while waiting. * * While an application A is waiting for the lock, its state * may be updated by another application: another application * B can succesfully take A's \link * WApplication::UpdateLock update lock\endlink. * * Therefore you need to be prepared to deal with application state * changes while waiting for the lock. */ void lock() { WApplication *app = WApplication::instance(); if (app) { int id = app->startWaitingAtLock(); Lock::lock(); app->endWaitingAtLock(id); } else Lock::lock(); } }; } #endif // WT_SYNC_LOCK_H_