AutoLock.h revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/** @file
*
* AutoLock: smart critical section wrapper
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
#ifndef ____H_AUTOLOCK
#define ____H_AUTOLOCK
#include <iprt/critsect.h>
#if defined(DEBUG)
#endif
{
/**
* Smart class to safely manage critical sections. Also provides ecplicit
* lock, unlock leave and enter operations.
*
* When constructing an instance, it enters the given critical
* section. This critical section will be exited automatically when the
* instance goes out of scope (i.e. gets destroyed).
*/
{
#if defined(DEBUG)
# define ___CritSectEnter(cs) \
RTCritSectEnterDebug ((cs), \
"AutoLock::lock()/enter() return address >>>", 0, \
#else
#endif
/**
* Lock (critical section) handle. An auxiliary base class for structures
* that need locking. Instances of classes inherited from it can be passed
* as arguments to the AutoLock constructor.
*/
{
/** Returns |true| if this handle is locked on the current thread. */
bool isLockedOnCurrentThread() const
{
return RTCritSectIsOwner (&mCritSect);
}
/** Returns a tag to lock this handle for reading by AutoMultiLock */
/** Returns a tag to lock this handle for writing by AutoMultiLock */
};
/**
* Lockable interface. An abstract base for classes that need locking.
* Unlike Handle that makes the lock handle a part of class data, this
* class allows subclasses to decide which lock handle to use.
*/
{
/**
* Returns a pointer to a Handle used by AutoLock for locking.
* Subclasses are allowed to return |NULL| -- in this case,
* the AutoLock object constructed using an instance of such
* subclass will simply turn into no-op.
*/
/**
* Equivalent to |#lockHandle()->isLockedOnCurrentThread()|.
* Returns |false| if lockHandle() returns NULL.
*/
bool isLockedOnCurrentThread()
{
Handle *h = lockHandle();
return h ? h->isLockedOnCurrentThread() : false;
}
/**
* Returns a tag to lock this handle for reading by AutoMultiLock.
* Shortcut to |lockHandle()->rlock()|.
* The returned tag is a no-op, when lockHandle() returns |NULL|.
*/
/**
* Returns a tag to lock this handle for writing by AutoMultiLock.
* Shortcut to |lockHandle()->wlock()|.
* The returned tag is a no-op, when lockHandle() returns |NULL|.
*/
};
~AutoLock()
{
if (mCritSect)
{
if (mLeftLevel)
{
mLeftLevel -= mLevel;
mLevel = 0;
for (; mLeftLevel; -- mLeftLevel)
}
}
}
/**
* Tries to acquire the lock or increases the lock level
* if the lock is already owned by this thread.
*/
void lock()
{
if (mCritSect)
{
++ mLevel;
}
}
/**
* Decreases the lock level. If the level goes to zero, the lock
* is released by the current thread.
*/
void unlock()
{
if (mCritSect)
{
-- mLevel;
}
}
/**
* Causes the current thread to completely release the lock
* (including locks acquired by all other instances of this class
* referring to the same object or handle). #enter() must be called
* to acquire the lock back and restore all lock levels.
*/
void leave()
{
if (mCritSect)
{
}
}
/**
* Causes the current thread to acquire the lock again and restore
* all lock levels after calling #leave().
*/
void enter()
{
if (mCritSect)
{
for (; mLeftLevel; -- mLeftLevel)
}
}
/**
* Current level of the nested lock. 1 means the lock is not currently
* nested.
*/
/** Returns |true| if this instance manages the given lock handle. */
{
}
/** Returns |true| if this instance manages the given lock handle. */
{
}
/** Returns |true| if this instance manages the given lockable object. */
{
}
/** Returns |true| if this instance manages the given lockable object. */
{
}
{
Assert (l);
Handle *h = l->lockHandle();
}
};
/**
* Prototype. Later will be used to acquire a read-only lock
* (read-only locks differ from regular (write) locks so that more than one
* read lock can be acquired simultaneously provided that there are no
* active write locks).
* @todo Implement it!
*/
{
};
{
/**
* @internal
* Special struct to differentiate between read and write locks
* in AutoMultiLock constructors.
*/
struct LockableTag
{
const char mode;
};
}
{
}
{
}
{
}
{
}
/**
* Smart template class to safely enter and leave a list of critical sections.
*
* When constructing an instance, it enters all given critical sections
* (in an "atomic", "all or none" fashion). These critical sections will be
* exited automatically when the instance goes out of scope (i.e. gets destroyed).
*
* It is possible to lock different critical sections in two different modes:
* for writing (as AutoLock does) or for reading (as AutoReaderLock does).
* The lock mode is determined by the method called on an AutoLock::Handle or
* AutoLock::Lockable instance when passing it to the AutoMultiLock constructor:
* |rlock()| to lock for reading or |wlock()| to lock for writing.
*
* Instances of this class are constructed as follows:
* <code>
* ...
* AutoLock::Handle data1, data2;
* ...
* {
* AutoMultiLock <2> multiLock (data1.wlock(), data2.rlock());
* // all locks are entered here:
* // data1 is entered in write mode (like AutoLock)
* // data2 is entered in read mode (like AutoReaderLock),
* }
* // all locks are exited here
* </code>
*
* The number of critical sections passed to the constructor must exactly
* match the number specified as the @a tCnt parameter of the template.
*
* @param tCnt number of critical sections to manage
*/
{
#if defined(DEBUG)
# define ___CritSectEnterMulti(n, acs) \
RTCritSectEnterMultipleDebug ((n), (acs), \
"AutoMultiLock instantiation address >>>", 0, \
#else
#endif
#define A(n) internal::LockableTag l##n
#define B(n) if (l##n.handle) { /* skip NULL tags */ \
} else
#define C(n) \
mLocked = true; \
mM [0] = 0; /* for safety in case of early return */ \
("This AutoMultiLock is for %d locks, but %d were passed!\n", tCnt, n)); \
if (tCnt != n) return; \
int i = 0
/// @todo (dmik) this will change when we switch to RTSemRW*
#define D() mM [i] = 0; /* end of array */ \
AutoMultiLock (A(0), A(1))
{ C(2); B(0); B(1); D(); }
{ C(3); B(0); B(1); B(2); D(); }
{ C(4); B(0); B(1); B(2); B(3); D(); }
{ C(5); B(0); B(1); B(2); B(3); B(4); D(); }
{ C(6); B(0); B(1); B(2); B(3); B(4); B(5); D(); }
{ C(7); B(0); B(1); B(2); B(3); B(4); B(5); B(6); D(); }
{ C(8); B(0); B(1); B(2); B(3); B(4); B(5); B(6); B(7); D(); }
{ C(9); B(0); B(1); B(2); B(3); B(4); B(5); B(6); B(7); B(8); D(); }
{ C(10); B(0); B(1); B(2); B(3); B(4); B(5); B(6); B(7); B(8); B(9); D(); }
#undef D
#undef C
#undef B
#undef A
/**
* Releases all locks if not yet released by #leave() and
* destroys the instance.
*/
{
/// @todo (dmik) this will change when we switch to RTSemRW*
if (mLocked)
}
/**
* Releases all locks temporarily in order to enter them again using
* #enter().
*
* @note There is no need to call this method unless you want later call
* #enter(). The destructor calls it automatically when necessary.
*
* @note Calling this method twice without calling #enter() in between will
* definitely fail.
*
* @note Unlike AutoLock::leave(), this method doesn't cause a complete
* release of all involved locks; if any of the locks was entered on the
* current thread prior constructing the AutoMultiLock instanve, they will
* remain acquired after this call! For this reason, using this method in
* the custom code doesn't make any practical sense.
*
* @todo Rename this method to unlock() and rename #enter() to lock()
* for similarity with AutoLock.
*/
void leave()
{
/// @todo (dmik) this will change when we switch to RTSemRW*
mLocked = false;
}
/**
* Tries to enter all locks temporarily released by #unlock().
*
* @note This method succeeds only after #unlock() and always fails
* otherwise.
*/
void enter()
{
mLocked = true;
}
bool mLocked;
};
/**
* Disable instantiations of AutoMultiLock for zero and one
* number of locks.
*/
template<>
template<>
} // namespace util
#endif // ____H_AUTOLOCK