/* * Copyright (c) 2005, Eric Crahen * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished * to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ #ifndef __ZTGUARD_H__ #define __ZTGUARD_H__ #include "zthread/NonCopyable.h" #include "zthread/Exceptions.h" namespace ZThread { // // GuardLockingPolicyContract { // // createScope(lock_type&) // bool createScope(lock_type&, unsigned long) // destroyScope(lock_type&) // // } // /** * @class LockHolder * @author Eric Crahen * @date <2003-07-16T17:55:42-0400> * @version 2.2.0 * * This is a simple base class for Guards class. It allows Guards * that have compatible targets to refer to each others targets * allowing for the construction of Guards that share the same lock * but have different locking policies. */ template class LockHolder { LockType &_lock; bool _enabled; public: template LockHolder(T& t) : _lock(extract(t)._lock), _enabled(true) { } LockHolder(LockHolder& holder) : _lock(holder._lock), _enabled(true) { } LockHolder(LockType& lock) : _lock(lock), _enabled(true) { } void disable() { _enabled = false; } bool isDisabled() { return !_enabled; } LockType& getLock() { return _lock; } protected: template static LockHolder& extract(T& t) { // Design and Evolution of C++, page 328 return (LockHolder&)(t); } }; /** * @class CompoundScope * @author Eric Crahen * @date <2003-07-16T17:55:42-0400> * @version 2.2.0 * * Locking policy that aggregates two policies that share a target. * It is not appropriate to use with any type of OverlappedScope */ template class CompoundScope { public: template static void createScope(LockHolder& l) { Scope1::createScope(l); Scope2::createScope(l); } template static void createScope(LockHolder& l, unsigned long ms) { if(Scope1::createScope(l, ms)) if(!Scope2::createScope(l, ms)) { Scope1::destroyScope(l); return false; } return true; } template static void destroyScope(LockHolder& l) { Scope1::destroyScope(l); Scope2::destroyScope(l); } }; /** * @class LockedScope * @author Eric Crahen * @date <2003-07-16T17:55:42-0400> * @version 2.2.0 * * Locking policy for Lockable objects. This policy acquire()s a Lockable * when the protection scope is created, and it release()s a Lockable * when the scope is destroyed. */ class LockedScope { public: /** * A new protection scope is being created by l2, using an existing scope * created by l1. * * @param lock1 LockType1& is the LockHolder that holds the desired lock * @param lock2 LockType1& is the LockHolder that wants to share template static void shareScope(LockHolder& l1, LockHolder& l2) { l2.getLock().acquire(); } */ /** * A new protection scope is being created. * * @param lock LockType& is a type of LockHolder. */ template static bool createScope(LockHolder& l, unsigned long ms) { return l.getLock().tryAcquire(ms); } /** * A new protection scope is being created. * * @param lock LockType& is a type of LockHolder. */ template static void createScope(LockHolder& l) { l.getLock().acquire(); } /** * A protection scope is being destroyed. * * @param lock LockType& is a type of LockHolder. */ template static void destroyScope(LockHolder& l) { l.getLock().release(); } }; /** * @class UnlockedScope * @author Eric Crahen * @date <2003-07-16T17:55:42-0400> * @version 2.2.0 * * Locking policy for Lockable objects. This policy release()s a Lockable * when the protection scope is created, and it acquire()s a Lockable * when the scope is destroyed. */ class UnlockedScope { public: /** * A new protection scope is being created by l2, using an existing scope * created by l1. * * @param lock1 LockType1& is the LockHolder that holds the desired lock * @param lock2 LockType1& is the LockHolder that wants to share */ template static void shareScope(LockHolder& l1, LockHolder& l2) { l2.getLock().release(); } /** * A new protection scope is being created. * * @param lock LockType& is a type of LockHolder. template static void createScope(LockHolder& l) { l.getLock().release(); } */ /** * A protection scope is being destroyed. * * @param lock LockType& is a type of LockHolder. */ template static void destroyScope(LockHolder& l) { l.getLock().acquire(); } }; /** * @class TimedLockedScope * @author Eric Crahen * @date <2003-07-16T17:55:42-0400> * @version 2.2.0 * * Locking policy that attempts to enterScope some resource * in a certain amount of time using an tryEnterScope-relase protocol. */ template class TimedLockedScope { public: /** * Try to enterScope the given LockHolder. * * @param lock LockType& is a type of LockHolder. */ template static void shareScope(LockHolder& l1, LockHolder& l2) { if(!l2.getLock().tryAcquire(TimeOut)) throw Timeout_Exception(); } template static void createScope(LockHolder& l) { if(!l.getLock().tryAcquire(TimeOut)) throw Timeout_Exception(); } template static void destroyScope(LockHolder& l) { l.getLock().release(); } }; /** * @class OverlappedScope * @author Eric Crahen * @date <2003-07-16T17:55:42-0400> * @version 2.2.0 * * Locking policy allows the effective scope of two locks to overlap * by releasing and disabling one lock before its Guard does so. */ class OverlappedScope { public: template static void transferScope(LockHolder& l1, LockHolder& l2) { l1.getLock().acquire(); l2.getLock().release(); l2.disable(); } template static void destroyScope(LockHolder& l) { l.getLock().release(); } }; /** * @class Guard * @author Eric Crahen * @date <2003-07-16T17:55:42-0400> * @version 2.2.0 * * Scoped locking utility. This template class can be given a Lockable * synchronization object and can 'Guard' or serialize access to * that method. * * For instance, consider a case in which a class or program have a * Mutex object associated with it. Access can be serialized with a * Guard as shown below. * * @code * * Mutex _mtx; * void guarded() { * * Guard g(_mtx); * * } * * @endcode * * The Guard will lock the synchronization object when it is created and * automatically unlock it when it goes out of scope. This eliminates * common mistakes like forgetting to unlock your mutex. * * An alternative to the above example would be * * @code * * void guarded() { * * (Guard)(_mtx); * * } * * @endcode * * HOWEVER; using a Guard in this method is dangerous. Depending on your * compiler an anonymous variable like this can go out of scope immediately * which can result in unexpected behavior. - This is the case with MSVC * and was the reason for introducing assertions into the Win32_MutexImpl * to track this problem down * */ template class Guard : private LockHolder, private NonCopyable { friend class LockHolder; public: /** * Create a Guard that enforces a the effective protection scope * throughout the lifetime of the Guard object or until the protection * scope is modified by another Guard. * * @param lock LockType the lock this Guard will use to enforce its * protection scope. * @post the protection scope may be ended prematurely */ Guard(LockType& lock) : LockHolder(lock) { LockingPolicy::createScope(*this); }; /** * Create a Guard that enforces a the effective protection scope * throughout the lifetime of the Guard object or until the protection * scope is modified by another Guard. * * @param lock LockType the lock this Guard will use to enforce its * protection scope. * @post the protection scope may be ended prematurely */ Guard(LockType& lock, unsigned long timeout) : LockHolder(lock) { if(!LockingPolicy::createScope(*this, timeout)) throw Timeout_Exception(); }; /** * Create a Guard that shares the effective protection scope * from the given Guard to this Guard. * * @param g Guard guard that is currently enabled * @param lock LockType the lock this Guard will use to enforce its * protection scope. */ template Guard(Guard& g) : LockHolder(g) { LockingPolicy::shareScope(*this, extract(g)); } /** * Create a Guard that shares the effective protection scope * from the given Guard to this Guard. * * @param g Guard guard that is currently enabled * @param lock LockType the lock this Guard will use to enforce its * protection scope. */ Guard(Guard& g) : LockHolder(g) { LockingPolicy::shareScope(*this, g); } /** * Create a Guard that transfers the effective protection scope * from the given Guard to this Guard. * * @param g Guard guard that is currently enabled * @param lock LockType the lock this Guard will use to enforce its * protection scope. */ template Guard(Guard& g, LockType& lock) : LockHolder(lock) { LockingPolicy::transferScope(*this, extract(g)); } /** * Create a Guard that transfers the effective protection scope * from the given Guard to this Guard. * * @param g Guard guard that is currently enabled * @param lock LockType the lock this Guard will use to enforce its * protection scope. */ Guard(Guard& g, LockType& lock) : LockHolder(lock) { LockingPolicy::transferScope(*this, g); } /** * Unlock a given Lockable object with the destruction of this Guard */ ~Guard() throw(); }; /* Guard */ template Guard::~Guard() throw() { try { if(!LockHolder::isDisabled()) LockingPolicy::destroyScope(*this); } catch (...) { /* ignore */ } } }; #endif // __ZTGUARD_H__