1 ////////////////////////////////////////////////////////////////////////////////
3 // LevelMutex facility for the Loki Library
4 // Copyright (c) 2008 Richard Sposato
5 // The copyright on this file is protected under the terms of the MIT license.
7 // Permission to use, copy, modify, distribute and sell this software for any
8 // purpose is hereby granted without fee, provided that the above copyright
9 // notice appear in all copies and that both that copyright notice and this
10 // permission notice appear in supporting documentation.
12 // The author makes no representations about the suitability of this software
13 // for any purpose. It is provided "as is" without express or implied warranty.
15 ////////////////////////////////////////////////////////////////////////////////
19 /// @file LevelMutex.h Defines classes and functions for LevelMutex facility.
21 #ifndef LOKI_LEVEL_MUTEX_H_INCLUDED
22 #define LOKI_LEVEL_MUTEX_H_INCLUDED
25 // ----------------------------------------------------------------------------
31 #if defined( _MSC_VER )
37 #if !defined(_WIN32) && !defined(_WIN64)
38 #include <unistd.h> // declares sleep under Linux
41 /** @par thread_local Keyword
42 The mutexes require compilers to provide thread local storage - meaning each
43 thread gets its own copy of the data. The next version of C++ will have a
44 new keyword, thread_local for that purpose. Some existing compilers already
45 provide thread local storage using different syntax, so these lines use
46 thread_local to mimic that syntax. If your compiler provides thread local
47 storage but using different syntax besides "thread_local", you may want to
48 modify these lines. If your compiler does not support thread local storage,
49 you can't use LevelMutex.
51 #ifndef LOKI_THREAD_LOCAL
52 #if defined( _MSC_VER )
53 #if ( _MSC_VER >= 1300 )
54 #define LOKI_THREAD_LOCAL __declspec( thread )
56 #error "Only Visual Studio versions 7.0 and after supported."
60 #define LOKI_THREAD_LOCAL __thread
63 #warning "Check if your compiler provides thread local storage."
64 #define LOKI_THREAD_LOCAL thread_local
68 #if defined( DEBUG ) || defined( _DEBUG )
69 #define LOKI_MUTEX_DEBUG_CODE( x ) x
71 #define LOKI_MUTEX_DEBUG_CODE( x )
79 // ----------------------------------------------------------------------------
85 /// @enum Type Possible error conditions detected by LevelMutex functions.
88 Success = 0, ///< Operation occurred correctly.
89 NoProblem, ///< Pre-lock and pre-unlock checks passed.
90 WrongLevel, ///< All mutexes in container must have same level.
91 LevelTooLow, ///< Trying to unlock a mutex lower than current level.
92 LevelTooHigh, ///< Trying to lock a mutex higher than current level.
93 TryFailed, ///< TryLock call failed to lock mutex.
94 NullMutexPointer, ///< Container has a NULL pointer in it.
95 DuplicateMutex, ///< Container must have unique pointers - no duplicates.
96 EmptyContainer, ///< Container must have at least 1 pointer in it.
97 AlreadyLocked, ///< TryLock call failed because mutex already locked.
98 WasntLocked, ///< Unlock failed because mutex was not even locked.
99 NotRecentLock, ///< Mutex in container was not recently locked by this thread.
100 NotLockedByThread, ///< Can't unlock a mutex not locked by this thread.
101 MultiUnlockFailed, ///< MultiUnlock can't unlock at least 1 mutex in container.
102 TimedOut, ///< Wait time elapsed without locking mutex.
103 TooMuchRecursion, ///< Tried to relock a PThread mutex which is not re-entrant.
104 NotInitialized, ///< Tried to lock a PThread mutex which did not get setup.
105 AlreadyInitialized, ///< PThread mutex initialized before ctor called.
106 InvalidAttribute, ///< PThread mutex improperly initialized.
107 InvalidAddress, ///< Bad pointer used to initialize a PThread mutex.
108 ExceptionThrown, ///< Exception caught in mutex operation.
109 MayDeadlock, ///< Locking this mutex may cause a deadlock.
110 OtherError ///< Unknown error occurred.
114 // ----------------------------------------------------------------------------
116 /** @class LevelMutexInfo
117 This monolithic base class stores common info for a template class used to
118 control mutexes. The template class, LevelMutex, is policy-based class.
121 Each thread has a list of mutexes it locked. When a mutex first gets locked, it
122 gets added to the head of the list. If locked again, LevelMutex merely increments
123 a count. When unlocked, the count gets decremented until it reaches zero, and
124 then it gets removed from the list. Each mutex has a pointer to the mutex most
125 recently locked by the current thread. The current level of a thread is always
126 the level of the most recently locked mutex, or UnlockedLevel if the thread does
127 not have any mutexes locked now. A mutex is considered "recently" locked if it is at
128 the head of the list, or the same level as the current mutex and also locked by the
131 @par Class Invariants
132 This class maintains invariants for each LevelMutexInfo so that no function
133 calls corrupt a mutex. Each function makes a call to IsValid at the start so
134 that LevelMutex knows it acts on valid internal data. Many functions call
135 IsValid again when they return to insure the function did not leave any data in
136 an invalid state. The exit call to IsValid occurs through a tiny helper class
137 called Checker to insure all data remain valid even when exceptions occur.
138 Another helper class, MutexUndoer, unlocks mutexes in a container if an
139 exception occurs during calls to MultiLock.
142 Many functions return an enum value to indicate an error status. Many enum values
143 indicate errors detected within LevelMutex, but some indicate errors found in policy
144 classes, SpinLevelMutex and SleepLevelMutex.
151 /** Level for thread that has not locked any mutex. Maximum possible level
152 for a mutex is UnlockedLevel-1; No mutex may have a level of UnlockedLevel.
154 static const unsigned int UnlockedLevel = 0xFFFFFFFF;
156 /// Container for locking multiple mutexes at once.
157 typedef ::std::vector< volatile LevelMutexInfo * > MutexContainer;
158 typedef MutexContainer::iterator LevelMutexContainerIter;
159 typedef MutexContainer::const_iterator LevelMutexContainerCIter;
160 typedef MutexContainer::reverse_iterator LevelMutexContainerRIter;
161 typedef MutexContainer::const_reverse_iterator LevelMutexContainerCRIter;
163 /** Locks several mutexes at once. Requires O(m + n*n) actions where m is the
164 number of mutexes currently locked by the thread and n is the number of mutexes
165 in the container. This provides strong exception safety. If an exception occurs,
166 any mutexes that were locked during this call will get unlocked.
167 @param mutexes Container of pointers to mutexes. Container must have at
168 least 1 mutex, all mutexes must have the same level, no NULL pointers, and all
169 mutexes must not exceed the thread's current level. This sorts the container
171 @return Enum value indicating success or error.
173 static MutexErrors::Type MultiLock( MutexContainer &mutexes );
175 /** Locks several mutexes at once. Requires O(m + n*n + n*t) actions where m is
176 the number of mutexes currently locked by the thread, n is the number of mutexes
177 in the container, and t is the wait time for each mutex. This provides strong
178 exception safety. If an exception occurs, any mutexes that were locked during
179 this call will ge unlocked.
180 @param mutexes Container of pointers to mutexes. Container must have at
181 least 1 mutex, all mutexes must have the same level, no NULL pointers, and all
182 mutexes must not exceed the thread's current level. This sorts the container
184 @param milliSeconds Amount of time to wait for each mutex.
185 @return Enum value indicating success or error.
187 static MutexErrors::Type MultiLock( MutexContainer &mutexes,
188 unsigned int milliSeconds );
190 /** Unlocks several mutexes at once. Requires O(m) actions where m is the number of
191 mutexes in the container. This provides strong exception safety. If an exception
192 occurs when unlocking one mutex, other mutexes in the container get unlocked anyway.
193 @param mutexes Container of pointers to mutexes. Container must have at least 1
194 mutex, all mutexes must have the same level, no NULL pointers, and all mutexes must
195 be locked by the current thread. This sorts the container dby address order.
196 @return Enum value indicating success or error.
198 static MutexErrors::Type MultiUnlock( MutexContainer &mutexes );
200 /** Gives pointer to most recently locked mutex, or NULL if nothing locked.
201 The pointer is for a const mutex so the mutex can't be modified inappropriately.
202 The pointer is for a volatile mutex so callers can call volatile member
203 functions to get info about the mutex.
205 static const volatile LevelMutexInfo *GetCurrentMutex( void );
207 /// Returns the level of this mutex.
208 inline unsigned int GetLevel( void ) const volatile
213 /// Returns true if this mutex was locked at least once.
214 inline bool IsLocked( void ) const volatile
216 return ( 0 < m_count );
219 /// Returns count of how many times this mutex got locked.
220 inline unsigned int GetLockCount( void ) const volatile
225 /// Returns pointer to mutex previously locked by the thread which locked this.
226 inline const volatile LevelMutexInfo *GetPrevious( void ) const volatile
231 /** Tries to lock mutex, and returns immediately if mutex already locked by
232 another thread. It will return immediately with a value of AlreadyLocked
233 if the mutex was locked by a different thread. It may throw an exception
234 or assert when errors occur if the ErrorPolicy class implements that behavior.
235 @return An error condition if any occurred, else Success.
237 virtual MutexErrors::Type TryLock( void ) volatile = 0;
239 /** Blocking call will attempt to lock mutex and wait until it can lock.
240 This may throw an exception if the lock failed or an error occurred - if
241 that is what the error policy specifies.
242 @return An error condition if any occurred, else Success.
244 virtual MutexErrors::Type Lock( void ) volatile = 0;
246 /** Attempts to lock mutex, but only waits for a limited amount of time
247 before it gives up. Will return quickly if an error occurs before any
248 attempt to lock. This may throw an exception if the lock failed or an
249 error occurred - if that is what the error policy specifies.
250 @param milliSeconds How long to wait.
251 @return An error condition if any occurred, else Success.
253 virtual MutexErrors::Type Lock( unsigned int milliSeconds ) volatile = 0;
255 /** Unlocks the mutex, or returns an error condition. This may throw an
256 exception if the lock failed or an error occurred - if that is what the
257 error policy specifies.
258 @return An error condition if any occurred, else Success.
260 virtual MutexErrors::Type Unlock( void ) volatile = 0;
262 /** Returns true if this mutex was locked by current thread, and level is the same
263 as the current thread's level. Which means this was the most recently locked
264 mutex, or it was locked along with several others of the same level recently.
266 bool IsRecentLock( void ) const volatile;
268 /** Returns true if this mutex was locked within the last count mutexes.
269 @param count How many recent mutexes to look through to find this mutex.
271 bool IsRecentLock( unsigned int count ) const volatile;
273 /// Returns true if this was locked by current thread.
274 bool IsLockedByCurrentThread( void ) const volatile;
276 /// Returns true if this was locked by another thread.
277 bool IsLockedByAnotherThread( void ) const volatile;
281 /** @class Checker Performs validity check on mutex to insure no class invariants
282 were violated inside any member function. This class only gets used in debug
283 builds, and any instance of it gets optimized away in release builds. A checker
284 is created inside many of member functions so that it's destructor gets called
285 when the function exits. It determines if any class invariants were violated
286 during the function call.
291 inline explicit Checker( const volatile LevelMutexInfo *mutex ) :
293 inline ~Checker( void )
299 Checker( const Checker & );
300 Checker &operator = ( const Checker & );
301 const volatile LevelMutexInfo *m_mutex;
304 /** @class MutexUndoer
305 Undoes actions by MultiLock if an exception occurs. It keeps track of
306 which mutexes in a container got locked, and if an exception occurs, then
307 the destructor unlocks them. If MultiLock succeeds, then it cancels the
308 undoer so nothing gets unlocked inadvertently.
314 explicit MutexUndoer( MutexContainer &mutexes );
315 ~MutexUndoer( void );
316 void SetPlace( LevelMutexContainerIter &here );
322 MutexUndoer( const MutexUndoer & );
323 MutexUndoer &operator = ( const MutexUndoer & );
325 MutexContainer &m_mutexes;
326 LevelMutexContainerIter m_here;
329 /** Returns true if linked-list of locked mutexes in this thread is valid.
330 Which means the list has no loops, and each previous mutex on the list has a
331 higher or same level as the current mutex. Called by IsValid.
333 static bool IsValidList( void );
335 /** This is the only available constructor, and it forces any derived class to set
336 a level for each mutex.
338 explicit LevelMutexInfo( unsigned int level );
340 /// The destructor only gets called by the derived class.
341 virtual ~LevelMutexInfo( void );
343 MutexErrors::Type PreLockCheck( bool forTryLock ) volatile;
345 MutexErrors::Type PreUnlockCheck( void ) volatile;
347 /** This gets called after each call to DoLock and DoTryLock to make sure the data
348 members in this object get set correctly.
350 void PostLock( void ) volatile;
352 /// Gets called just before an attempt to unlock a mutex.
353 void PreUnlock( void ) volatile;
355 /// Called to relock a mutex already locked by the current thread.
356 void IncrementCount( void ) volatile;
358 /// Called to unlock a mutex locked multiple times by the current thread.
359 void DecrementCount( void ) volatile;
361 /** Returns true if no class invariant broken, otherwise asserts. This function
362 only gets called in debug builds.
364 bool IsValid( void ) const volatile;
368 /// Copy constructor is not implemented.
369 LevelMutexInfo( const LevelMutexInfo & );
370 /// Copy-assignment operator is not implemented.
371 LevelMutexInfo &operator = ( const LevelMutexInfo & );
373 /** Called only by MultiLock & MultiUnlock to pass a result through an
374 error checking policy.
375 @param result What error condition to check.
376 @return Result or assertion or an exception - depending on error policy.
378 virtual MutexErrors::Type DoErrorCheck( MutexErrors::Type result ) const volatile = 0;
380 /// Called only by MultiLock to Lock each particular mutex within a container.
381 virtual MutexErrors::Type LockThis( void ) volatile = 0;
383 /** Called only by MultiLock to lock each particular mutex within a container.
384 @param milliSeconds How much time to wait before giving up on locking a mutex.
386 virtual MutexErrors::Type LockThis( unsigned int milliSeconds ) volatile = 0;
388 /// Called only by MultiUnlock to unlock each particular mutex within a container.
389 virtual MutexErrors::Type UnlockThis( void ) volatile = 0;
391 /// Pointer to singly-linked list of mutexes locked by the current thread.
392 static LOKI_THREAD_LOCAL volatile LevelMutexInfo *s_currentMutex;
394 /// Level of this mutex.
395 const unsigned int m_level;
397 /// How many times this mutex got locked.
398 unsigned int m_count;
400 /// Pointer to mutex locked before this one.
401 volatile LevelMutexInfo *m_previous;
405 // ----------------------------------------------------------------------------
407 /** @class ThrowOnAnyMutexError
408 Implements the ErrorPolicy for LevelMutex and throws an exception for any
409 error condition. Only allows MutexErrors::Success and MutexErrors::NoProblem
410 to get through. Useful for release builds.
412 class ThrowOnAnyMutexError
415 static MutexErrors::Type CheckError( MutexErrors::Type error,
416 unsigned int level );
419 // ----------------------------------------------------------------------------
421 /** @class ThrowOnBadDesignMutexError
422 Implements the ErrorPolicy for LevelMutex and throws an exception if the error
423 indicates the programmer did not levelize the calls to mutexes. Otherwise
424 returns the error result. Useful for release builds.
426 class ThrowOnBadDesignMutexError
429 static MutexErrors::Type CheckError( MutexErrors::Type error,
430 unsigned int level );
433 // ----------------------------------------------------------------------------
435 /** @class AssertAnyMutexError
436 Implements the ErrorPolicy for LevelMutex and asserts for any error condition.
437 Only allows MutexErrors::Success and MutexErrors::NoProblem to get through.
438 Useful for testing mutexes in debug builds.
440 class AssertAnyMutexError
443 static inline MutexErrors::Type CheckError( MutexErrors::Type error,
447 assert( ( error == MutexErrors::Success )
448 || ( error == MutexErrors::NoProblem ) );
453 // ----------------------------------------------------------------------------
455 /** @class AssertBadDesignMutexError
456 Implements the ErrorPolicy for LevelMutex and asserts if the error
457 indicates the programmer did not levelize the calls to mutexes. Otherwise
458 returns the error result. Useful for testing mutexes in debug builds.
460 class AssertBadDesignMutexError
463 static inline MutexErrors::Type CheckError( MutexErrors::Type error,
467 assert( ( error != MutexErrors::LevelTooHigh )
468 && ( error != MutexErrors::LevelTooLow ) );
473 // ----------------------------------------------------------------------------
475 /** @class JustReturnMutexError
476 Implements the ErrorPolicy for LevelMutex and does nothing no matter how bad
477 the error condition. Only recommended use is for automated unit-testing of
480 class JustReturnMutexError
483 static inline MutexErrors::Type CheckError( MutexErrors::Type error,
491 // ----------------------------------------------------------------------------
493 /** @class NoMutexWait
494 Implements the WaitPolicy for LevelMutex. Does nothing at all so it turns
495 all wait loops into spin loops. Useful for low-level mutexes.
500 static inline void Wait( void ) {}
503 // ----------------------------------------------------------------------------
505 /** @class MutexSleepWaits
506 Implements the WaitPolicy for LevelMutex. Sleeps for a moment so thread won't
507 consume idle CPU cycles. Useful for high-level mutexes.
509 class MutexSleepWaits
512 static void Wait( void );
513 static unsigned int sleepTime;
516 // ----------------------------------------------------------------------------
518 /** @class SpinLevelMutex
519 Implements a spin-loop to wait for the mutex to unlock. Since this class makes
520 the thread wait in a tight spin-loop, it can cause the thread to remain busy
521 while waiting and thus consume CPU cycles. For that reason, this mutex is best
522 used only for very low-level resources - especially resources which do not
523 require much CPU time to exercise. Rule of thumb: Use this only if all actions
524 on the resource consume a very small number of CPU cycles. Otherwise, use the
525 SleepLevelMutex instead.
531 /// Constructs a spin-level mutex.
532 explicit SpinLevelMutex( unsigned int level );
534 /// Destructs the mutex.
535 virtual ~SpinLevelMutex( void );
537 virtual MutexErrors::Type Lock( void ) volatile;
539 virtual MutexErrors::Type TryLock( void ) volatile;
541 virtual MutexErrors::Type Unlock( void ) volatile;
543 inline unsigned int GetLevel( void ) const volatile
550 /// Copy constructor is not implemented.
551 SpinLevelMutex( const SpinLevelMutex & );
552 /// Copy-assignment operator is not implemented.
553 SpinLevelMutex &operator = ( const SpinLevelMutex & );
555 #if defined( _MSC_VER )
556 #if ( _MSC_VER >= 1300 )
557 /// The actual mutex.
558 CRITICAL_SECTION m_mutex;
560 #error "Only Visual Studio versions 7.0 and after supported."
564 /// The actual mutex.
565 pthread_mutex_t m_mutex;
568 #error "Check if any mutex libraries are compatible with your compiler."
571 /// Keep a copy of the mutex level around for error reporting.
572 const unsigned int m_level;
574 }; // end class SpinLevelMutex
576 // ----------------------------------------------------------------------------
578 /** @class SleepLevelMutex
579 Implements a sleeping loop to wait for the mutex to unlock.
582 Since this class puts the thread to sleep for short intervals, you can use this
583 class for most of your mutexes. Especially for locking any high level resources
584 where any one operation on the resouce consumes many CPU cycles. The purpose of
585 this mutex is to reduce the number of CPU cycles spent in idle loops. All
586 SleepLevelMutex's should have higher levels than all your SpinLevelMutex's.
588 @par Dependence on SpinLevelMutex
589 This utilizes SpinLevelMutex so it does not have to re-implement the DoTryLock
590 and DoUnlock functions the same way. All it really needs is a DoLock function
591 and the amount of time it should sleep if an attempt to lock a function fails.
593 class SleepLevelMutex : public SpinLevelMutex
597 /** Constructs a levelized mutex that puts threads to sleep while they wait
598 for another thread to unlock the mutex.
599 @param level Level of this mutex.
601 explicit SleepLevelMutex( unsigned int level );
603 SleepLevelMutex( unsigned int level, unsigned int sleepTime );
605 /// Destructs the mutex.
606 virtual ~SleepLevelMutex( void );
608 inline unsigned int GetSleepTime( void ) const volatile
613 inline void SetSleepTime( unsigned int sleepTime ) volatile
615 if ( 0 != sleepTime )
616 m_sleepTime = sleepTime;
619 #if defined( _MSC_VER )
620 inline bool GetWakable( void ) const volatile
624 inline void SetWakable( bool wakable ) volatile { m_wakable = wakable; }
627 /** Attempts to lock a mutex, and if it fails, then sleeps for a while
628 before attempting again.
630 virtual MutexErrors::Type Lock( void ) volatile;
634 /// Default constructor is not implemented.
635 SleepLevelMutex( void );
636 /// Copy constructor is not implemented.
637 SleepLevelMutex( const SleepLevelMutex & );
638 /// Copy-assignment operator is not implemented.
639 SleepLevelMutex &operator = ( const SleepLevelMutex & );
641 #if defined( _MSC_VER )
642 #if ( _MSC_VER >= 1300 )
643 /// True if operating system may wake thread to respond to events.
646 #error "Only Visual Studio versions 7.0 and after supported."
650 /// How many milli-seconds to sleep before trying to lock mutex again.
651 unsigned int m_sleepTime;
653 }; // end class SleepLevelMutex
655 // ----------------------------------------------------------------------------
657 /** @class LevelMutex
658 Levelized mutex class prevents deadlocks by requiring programs to lock mutexes in
659 the same order, and unlock them in reverse order. This is accomplished by forcing
660 each mutex to have a level and forcing code to lock mutexes with higher levels
661 before locking mutexes at lower levels. If you want to lock several mutexes, they
662 must be locked in decreasing order by level, or if they are all of the same level,
663 then locked by LevelMutex::MultiLock.
666 - Immune: Very unlikely to deadlock since all mutexes are locked in the same
667 order and unlocked in reverse order.
668 - Scalable: Can handle any number of mutexes.
669 - Efficient: Many operations occur in constant time, and most operations require
670 no more than O(m) steps.
671 - Exception safe: All operations provide strong safety or don't throw.
672 - Extendable: Can work with existing mutexes through policy-based design.
673 - Easily Extended: Derived classes only need to implement 5 functions and a mutex
674 to get all the features of this class.
675 - Re-Entrant: Allows for re-entrancy even if mutexes in policy classes don't.
676 - Cost-Free: No resource allocations occur in LevelMutex - although user-defined
677 policy classes may allocate resources.
678 - Compact: Each LevelMutex object is small.
679 - Portable: As long as your compiler and libraries can meet the requirements.
680 - Robust: Maintains data integrity even if exceptions occur in policy classes.
681 - Affording: Several functions provide information about a mutex which allows
682 client code to easily choose correct actions.
685 - Your compiler must allow for thread-specific data.
686 - You must have a threading or mutex library.
688 @par Policy-Based Design
689 This class hosts 3 policies and a default level. The policy-based design allows
690 users to write their own policies to extend the behaviors of LevelMutex. The
691 paragraphs below say how to design a class for each policy.
692 - MutexPolicy The mutex policy class.
693 - defaultLevel A level for existing client code that calls a default constructor.
694 - ErrorPolicy How the mutex should handle error conditions.
695 - WaitPolicy Whether a thread should wait, and how long in some internal loops.
698 A policy class that wraps a low-level mutex. Loki provides two policy classes
699 for the actual mutex (SpinLevelMutex and SleepLevelMutex), both of which wrap
700 either pthreads or the Windows CRITICAL_SECTION. If you want to use a mutex
701 mechanism besides one of those, then all you have to do is provide a class
702 which wraps the mutex and implements these functions.
703 explicit SpinLevelMutex( unsigned int level );
704 virtual ~SpinLevelMutex( void );
705 virtual MutexErrors::Type Lock( void ) volatile;
706 virtual MutexErrors::Type TryLock( void ) volatile;
707 virtual MutexErrors::Type Unlock( void ) volatile;
708 Indeed, since the base class does most of the work, and provides all the interace
709 and functionality to client classes, a derived class has very few requirements.
710 It only needs to implement a single constructor, the destructor, some virtual
711 functions, and whatever data members it requires. You don't actually need to
712 declare those functions as virtual if the policy class is not a base or child
713 class. In the parlance of design patterns, LevelMutex is a Template, and the
714 MutexPolicy is a Strategy.
717 The template class requires a default level to use inside the default constructor.
718 Some existing code calls instantiates mutexes with a default constructor, so the
719 mutex must know what level to use there. Please do not use zero or UnlockedLevel
720 as the default level.
723 This policy specifies how to handle error conditions. The mutexes can return
724 errors, assert, or throw exceptions. I recommend that debug code use asserts,
725 release code use exceptions, and unit-testing code just return errors. The
726 error policy class only needs to implement one function:
727 static MutexErrors::Type CheckError( MutexErrors::Type error, unsigned int level );
730 This states whether the mutex should wait within some tight internal loops,
731 how the waiting is done, and for how long. A wait policy class could sleep,
732 do nothing, check if other objects need attention, or check if the program
733 received events or notices from the operating system. It only needs to
734 implement one function:
735 static void Wait( void );
737 @par Per-Function Usage
738 If you implement a function with a static local mutex, then you have to insure
739 the function is not called from a lower level via call-backs, virtual functions in
740 interface classes. If the function does get called from a lower level, you are
741 setting up a potential deadlock. LevelMutex will detect that by checking the
742 current level and the local mutex's level, so it will refuse to lock the local mutex.
744 @par Per-Object Usage
745 If you use a mutex as a data member of an object to protect that object, then I
746 recommend specifying which functions are volatile and which are not, and then only
747 use the mutex within the volatile functions. You may also want to provide accessor
748 functions so that client code can lock and unlock the mutex either to allow for
749 calling multiple operations without having to lock and unlock before and after each
750 operation, or so they can lock it along with several other objects at the same
754 If you make a static data member within a class, you can use that to lock any
755 resources shared by those objects, or to require threads to act on only one object
756 at a time. You may also want to provide static accessor functions so that client
757 code can lock several other resources at the same level.
763 unsigned int DefaultLevel,
764 class ErrorPolicy = ::Loki::ThrowOnBadDesignMutexError,
765 class WaitPolicy = ::Loki::NoMutexWait
767 class LevelMutex : public LevelMutexInfo
771 typedef ErrorPolicy EP;
772 typedef WaitPolicy WP;
773 typedef MutexPolicy MP;
775 /** This constructor allows callers to replace the default level with another
776 value. It also acts as the default constructor for existing code which uses
777 default construction for mutexes. This is the only time the DefaultLevel
778 template parameter gets used.
780 explicit LevelMutex( unsigned int level = DefaultLevel ) :
781 LevelMutexInfo( level ),
793 /** These functions allow callers to access the mutex in case they need to
794 modify specific values in the MutexPolicy (e.g. - sleep time, functors to
795 call as tasks, etc...) There is one function for every combination of
796 const and volatile qualifiers so callers get a reference to a MutexPolicy
797 with the proper qualifiers.
799 inline const volatile MutexPolicy &GetMutexPolicy( void ) const volatile
803 inline volatile MutexPolicy &GetMutexPolicy( void ) volatile { return m_mutex; }
804 inline const MutexPolicy &GetMutexPolicy( void ) const
808 inline MutexPolicy &GetMutexPolicy( void )
813 virtual MutexErrors::Type TryLock( void ) volatile
816 LOKI_MUTEX_DEBUG_CODE( Checker checker( this ); (void)checker; )
818 MutexErrors::Type result = LevelMutexInfo::PreLockCheck( true );
819 if ( MutexErrors::Success == result )
820 return MutexErrors::Success;
821 else if ( MutexErrors::AlreadyLocked == result )
823 else if ( MutexErrors::NoProblem != result )
824 return EP::CheckError( result, GetLevel() );
826 assert( 0 == LevelMutexInfo::GetLockCount() );
827 result = m_mutex.TryLock();
828 if ( MutexErrors::Success != result )
829 return EP::CheckError( result, GetLevel() );
830 LevelMutexInfo::PostLock();
832 return MutexErrors::Success;
835 virtual MutexErrors::Type Lock( void ) volatile
838 LOKI_MUTEX_DEBUG_CODE( Checker checker( this ); (void)checker; )
840 MutexErrors::Type result = LevelMutexInfo::PreLockCheck( false );
841 if ( MutexErrors::Success == result )
842 return MutexErrors::Success;
843 else if ( MutexErrors::NoProblem != result )
844 return EP::CheckError( result, GetLevel() );
846 assert( !LevelMutexInfo::IsLockedByCurrentThread() );
847 result = m_mutex.Lock();
848 if ( MutexErrors::Success != result )
849 return EP::CheckError( result, GetLevel() );
852 return MutexErrors::Success;
855 virtual MutexErrors::Type Lock( unsigned int milliSeconds ) volatile
858 LOKI_MUTEX_DEBUG_CODE( Checker checker( this ); (void)checker; )
860 MutexErrors::Type result = LevelMutexInfo::PreLockCheck( false );
861 if ( MutexErrors::Success == result )
862 return MutexErrors::Success;
863 else if ( MutexErrors::NoProblem != result )
864 return EP::CheckError( result, GetLevel() );
866 assert( !LevelMutexInfo::IsLockedByCurrentThread() );
867 clock_t timeOut = clock() + milliSeconds;
868 while ( clock() < timeOut )
871 result = m_mutex.TryLock();
874 case MutexErrors::Success:
877 return MutexErrors::Success;
879 case MutexErrors::AlreadyLocked:
880 return MutexErrors::AlreadyLocked;
881 case MutexErrors::TryFailed:
884 return EP::CheckError( result, GetLevel() );
888 return MutexErrors::TimedOut;
891 virtual MutexErrors::Type Unlock( void ) volatile
894 LOKI_MUTEX_DEBUG_CODE( Checker checker( this ); (void)checker; )
896 MutexErrors::Type result = LevelMutexInfo::PreUnlockCheck();
897 if ( MutexErrors::Success == result )
898 return MutexErrors::Success;
899 else if ( MutexErrors::NoProblem != result )
900 return EP::CheckError( result, GetLevel() );
902 LevelMutexInfo::PreUnlock();
903 result = MutexErrors::OtherError;
906 result = m_mutex.Unlock();
907 if ( MutexErrors::Success != result )
913 result = MutexErrors::ExceptionThrown;
921 /// Copy constructor is not implemented since mutexes don't get copied.
922 LevelMutex( const LevelMutex & );
923 /// Copy-assignment operator is not implemented since mutexes don't get copied.
924 LevelMutex &operator = ( const LevelMutex & );
926 virtual MutexErrors::Type DoErrorCheck( MutexErrors::Type result ) const volatile
928 return EP::CheckError( result, GetLevel() );
931 /** Called only by MultiLock to lock each particular mutex within a container.
932 This does not do pre-lock error checking since MultiLock does that. Since
933 this skips the error checking, that means that callers of LevelMutex should
934 not call this function directly, and so this will not be publicly available.
935 @return Error status indicating success or reason for failure.
937 virtual MutexErrors::Type LockThis( void ) volatile
940 LOKI_MUTEX_DEBUG_CODE( Checker checker( this ); (void)checker; )
941 assert( this != LevelMutexInfo::GetCurrentMutex() );
943 const MutexErrors::Type result = m_mutex.Lock();
944 if ( MutexErrors::Success != result )
948 return MutexErrors::Success;
951 /** Called only by MultiLock to lock each particular mutex within a container.
952 This does not do pre-lock error checking since MultiLock does that. Since
953 this skips the error checking, callers of LevelMutex should not call this
954 function directly, and so this will not be publicly available.
955 @param milliSeconds How much time to wait before giving up on locking a mutex.
956 @return Error status indicating success or reason for failure.
958 virtual MutexErrors::Type LockThis( unsigned int milliSeconds ) volatile
961 LOKI_MUTEX_DEBUG_CODE( Checker checker( this ); (void)checker; )
963 clock_t timeOut = clock() + milliSeconds;
964 while ( clock() < timeOut )
967 const bool locked = ( MutexErrors::Success == m_mutex.TryLock() );
971 return MutexErrors::Success;
975 return MutexErrors::TimedOut;
978 /** Called only by MultiUnlock to unlock each mutex within a container.
979 This does not do pre-unlock error checking since MultiLock does that. Since
980 this skips the error checking, callers of LevelMutex should not call this
981 function directly, and so this will not be publicly available.
982 @return Error status indicating success or reason for failure.
984 virtual MutexErrors::Type UnlockThis( void ) volatile
987 assert( NULL != LevelMutexInfo::GetCurrentMutex() );
988 LOKI_MUTEX_DEBUG_CODE( Checker checker( this ); (void)checker; )
990 if ( 1 < LevelMutexInfo::GetLockCount() )
992 LevelMutexInfo::DecrementCount();
993 return MutexErrors::Success;
996 LevelMutexInfo::PreUnlock();
997 MutexErrors::Type result = m_mutex.Unlock();
1002 /// An instance of an unleveled mutex wrapped to match LevelMutex's needs.
1003 MutexPolicy m_mutex;
1005 }; // end class LevelMutex
1007 // ----------------------------------------------------------------------------
1009 /** Returns level of most recently locked mutex by this thread, or UnlockedLevel
1010 if no mutexes are locked. Runs in constant time, and never throws exceptions.
1012 unsigned int GetCurrentThreadsLevel( void );
1014 /** Returns count of how mutexes the current thread locked. Requires O(m)
1015 actions where m is the number of mutexes in the thread. Never throws exceptions.
1017 unsigned int CountMutexesInCurrentThread( void );
1019 /** Returns count of how mutexes the current thread locked. The lock count
1020 exceeds the number of mutexes locked by current thread if any mutex got locked
1021 more than once. Requires O(m) actions where m is the number of mutexes in the
1022 thread. Never throws exceptions.
1024 unsigned int CountLocksInCurrentThread( void );
1026 /** Returns count of mutexes locked by current thread which have the same level
1027 as GetCurrentThreadsLevel. Requires O(m) actions where m is the number of
1028 mutexes in the thread at current level. Never throws exceptions.
1030 unsigned int CountMutexesAtCurrentLevel( void );
1032 /** Determines if container of mutexes matches the recently locked mutexes.
1033 If they do match, it returns success, otherwise an error condition.
1035 MutexErrors::Type DoMutexesMatchContainer( const LevelMutexInfo::MutexContainer &mutexes );
1037 // ----------------------------------------------------------------------------
1039 /** @class MutexException
1040 Exception class used to throw error statuses about LevelMutex's up to code that
1041 can respond to mutex problems. This class exists because it conveys more info
1042 about the error condition than just ::std::exception.
1044 class MutexException : public ::std::exception
1048 /** Constructs an exception which stores information about a mutex and the
1049 reason an attempt to use a mutex failed.
1051 MutexException( const char *message, unsigned int level, MutexErrors::Type reason );
1053 /// Copy constructor performs a member-by-member copy of an exception.
1054 MutexException( const MutexException &that ) throw ();
1056 /// Copy-assignment operator performs a member-by-member copy of an exception.
1057 MutexException &operator = ( const MutexException &that ) throw ();
1059 /// Destroys the exception.
1060 virtual ~MutexException( void ) throw();
1062 /// Returns a simple message about which operation failed.
1063 virtual const char *what( void ) const throw();
1065 /// Returns level of mutex(es) used when problem occurred.
1066 unsigned int GetLevel( void ) const
1071 /// Returns an error status for why operation failed.
1072 MutexErrors::Type GetReason( void ) const
1079 /// Default constructor is not implemented.
1080 MutexException( void ) throw ();
1082 /// Simple message about operation that failed.
1083 const char *m_message;
1084 /// Level of mutex(es) used when problem occurred.
1085 unsigned int m_level;
1086 /// Error status for why operation failed.
1087 MutexErrors::Type m_reason;
1089 }; // end class MutexException
1091 // ----------------------------------------------------------------------------
1093 /** @class MutexLocker
1094 You can place an instance of this as a local variable inside a function to lock
1095 a single mutex. It will lock the mutex if no error occurs, or throw if one
1096 does happen. When the function ends, the destructor will determine if it needs
1097 to unlock the mutex. This RAII technique insures the mutex gets unlocked even
1098 when exceptions occur.
1104 /** Creates an object to lock an unlock a mutex for a function. This
1105 will throw if an attempt to lock the mutex fails.
1106 @param mutex Reference to the mutex.
1107 @param lock True if function wants to lock the mutex as this gets
1110 explicit MutexLocker( volatile LevelMutexInfo &mutex, bool lock = true );
1112 /** Creates an object to lock an unlock a mutex for a function. This waits
1113 a specified amount of time for another thread to unlock the mutex if it is
1114 locked. This will throw if an attempt to lock the mutex fails.
1115 @param mutex Reference to the mutex.
1116 @param milliSeconds Amount of time to wait for another thread to unlock
1118 @param lock True if function wants to lock the mutex as this gets
1121 MutexLocker( volatile LevelMutexInfo &mutex, unsigned int milliSeconds,
1124 /// Destructs the locker, and determines if it needs to unlock the mutex.
1125 ~MutexLocker( void );
1127 /** You can call this to lock (or relock) a mutex. In theory, you can lock
1128 and unlock a mutex several times within a function in order to give other
1129 threads access to a resource while this function does not need it.
1130 @return True if mutex is locked by this, else false if not locked.
1134 /** You can call this to unlock a mutex before the destructor does it.
1135 By unlocking the mutexes before returning, the function can do other
1136 operations without making other threads wait too long.
1137 @return True if unlocked by this, else false if not unlocked by this.
1138 (Which is not the same as whether the mutex itself is locked or not by
1141 bool Unlock( void );
1143 /// Returns true if the mutex is locked by this object.
1144 inline bool IsLocked( void ) const
1149 /// Provides access to mutex controlled by this.
1150 const volatile LevelMutexInfo &GetMutex( void ) const
1157 /// Default constructor is not implemented.
1158 MutexLocker( void );
1159 /// Copy constructor is not implemented.
1160 MutexLocker( const MutexLocker & );
1161 /// Copy-assignment operator is not implemented.
1162 MutexLocker &operator = ( const MutexLocker & );
1164 /// True if mutex got locked.
1167 /// Reference to mutex.
1168 volatile LevelMutexInfo &m_mutex;
1171 // ----------------------------------------------------------------------------
1173 /** @class MultiMutexLocker
1174 You can place an instance of this as a local variable inside a function to lock
1175 a collection of mutexes. It locks them if no error occurs, or throws an
1176 exception if one does happen. When the function ends, the destructor determines
1177 if it needs to unlock the mutexes. This RAII technique insures the mutexes get
1178 unlocked even when exceptions occur. You will also have to construct a
1179 MutexContainer as a local object within the same function.
1181 class MultiMutexLocker
1185 /** Creates an object to lock and unlock a collection of mutexes for a function.
1186 This will throw if an attempt to lock any mutex fails. If an exception occurs,
1187 it unlocks mutexes it previously locked.
1188 @param mutex Reference to a collection of mutexes.
1189 @param lock True if function wants to lock the mutex as this gets
1192 explicit MultiMutexLocker( LevelMutexInfo::MutexContainer &mutexes,
1195 /** Creates an object to lock and unlock a collection of mutexes for a function.
1196 This waits a specified amount of time for other threads to unlock each mutex
1197 that is locked. This will throw if an attempt to lock any mutex fails. If an
1198 exception occurs, it unlocks mutexes it previously locked.
1199 @param mutexes Reference to a collection of mutexes.
1200 @param milliSeconds Amount of time to wait for another thread to unlock
1202 @param lock True if function wants to lock the mutexes as this gets
1205 MultiMutexLocker( LevelMutexInfo::MutexContainer &mutexes,
1206 unsigned int milliSeconds, bool lock = true );
1208 /// Destructs the locker, and determines if it needs to unlock the mutexes.
1209 ~MultiMutexLocker( void );
1211 /** You can call this to lock (or relock) the mutexes. In theory, you can lock
1212 and unlock mutexes several times within a function in order to give other
1213 threads access to resources while this function does not need them.
1214 @return True if mutex is locked by this, else false if not locked.
1218 /** You can call this to unlock the mutexes before the destructor does it.
1219 By unlocking the mutexes before returning, the function can do other
1220 operations without making other threads wait too long.
1221 @return True if unlocked by this, else false if not unlocked by this.
1222 (Which is not the same as whether the mutex itself is locked or not by
1225 bool Unlock( void );
1227 /// Returns true if the mutexes are locked by this object.
1228 inline bool IsLocked( void ) const
1233 /// Provides access to the collection of mutexes controlled by this.
1234 const LevelMutexInfo::MutexContainer &GetMutexes( void ) const
1241 /// Default constructor is not implemented.
1242 MultiMutexLocker( void );
1243 /// Copy constructor is not implemented.
1244 MultiMutexLocker( const MultiMutexLocker & );
1245 /// Copy-assignment operator is not implemented.
1246 MultiMutexLocker &operator = ( const MultiMutexLocker & );
1248 /// True if mutexes got locked.
1251 /// Reference to external container of mutexes;
1252 LevelMutexInfo::MutexContainer &m_mutexes;
1255 // ----------------------------------------------------------------------------
1257 } // end namespace Loki
1259 #endif // end file guardian