#ifndef ParksComputing_Threading_H #define ParksComputing_Threading_H #include #include #include namespace ParksComputing { namespace WindowLib { /////////////////////////////////////////////////////////////////////// // Forward declarations // Base template for mechanism policies, which provide fine control over // the creation of mechanisms. I should have named this // MechanismPolicy but I fear it is too late to change it. template class Policy; // Base template for lock policies, which provide control over timeouts, // etc. template class LockPolicy; // Forward declaration of classes used internally in Synch. template > class Lock; template > class MultiLock; // Did I just say n'Sync? /////////////////////////////////////////////////////////////////////// // Synchronization template classes // Standard template interface for all synchronization mechanisms // The M class is a synchronization mechanism class, and P is a policy // class to specify attibutes of the mechanism. template > class Synch { public: // Initialize the mechanism with an instance of the policy class Synch() : mechanism_(P()) { }; // Internal locking class simplifies locking in most instances. Use the // external class for finer control. The internal class only works with // objects of type Synch where M is the same type as the mechanism // instantiated by this template's instance class Lock { public: Lock(Synch& s) : lo_(s.mechanism_) { } ~Lock() { } private: WindowLib::Lock lo_; }; // As above, the internal MultiLock class only works with objects created by // this specific template. It is likely that the external MultiLock will be // more useful in multi-lock situations. class MultiLock { public: MultiLock() { } ~MultiLock() { } void Add(Synch& s) { lo_.Add(s); } void Lock() { lo_.Lock(); } private: WindowLib::MultiLock lo_; }; M mechanism_; }; // Lock an object of type M, which is a mechanism class. The LP // class is a lock policy specific to mechanism M. template > class Lock { private: // Hide the default constructor to force lock objects // to be associated with synch objects at instantiation Lock() {} public: template Lock(Synch& s) : mechanism_(s.mechanism_) { mechanism_.Acquire(lp_); } Lock(M& m) : mechanism_(m), lp_(LP()) { mechanism_.Acquire(lp_); } ~Lock() { mechanism_.Release(); } private: M& mechanism_; LP lp_; }; // Lock multiple objects of type M, which is a mechanism class. The LP // class is a lock policy specific to mechanism M. template > class MultiLock { public: MultiLock() : sa_(0), count_(0), lp_(LP()) { } template void Add(Synch& s) { vs_.push_back(&s.mechanism_.so_); } ~MultiLock() { M::Release(sa_, count_); } void Lock() { // It's a little pointless to try to lock with no objects assert(vs_.size() > 0); // I don't like. I don't, I don't, I don't. Still, at least // I came to my senses and added a __finally section to delete // the array. __try { sa_ = new M::SynchType[vs_.size()]; size_t index = vs_.size(); while (index > 0) { --index; sa_[index] = *(vs_[index]); } M::Acquire(sa_, count_, lp_); } __finally { delete[] sa_; sa_ = 0; } } private: std::vector vs_; M::SynchType* sa_; size_t count_; LP lp_; }; class CriticalSection; template<> class Policy { public: Policy(DWORD spinCount = 4000) : spinCount_(spinCount) { } DWORD spinCount_; }; template class CriticalSectionPolicy : public Policy { public: CriticalSectionPolicy() : Policy(spinCount) { } }; template<> class LockPolicy { }; // The Critical Section synchronization mechanism class CriticalSection { public: typedef CRITICAL_SECTION SynchType; friend Lock; friend MultiLock; CriticalSection(Policy& p) : spinCount_(p.spinCount_) { #if (_WIN32_WINNT >= 0x0500) CheckWin32(::InitializeCriticalSectionAndSpinCount(&so_, spinCount_)); #else ::InitializeCriticalSection(&so_); #endif } ~CriticalSection() { ::DeleteCriticalSection(&so_); } void Acquire(LockPolicy& lp) { ::EnterCriticalSection(&so_); } bool TryAcquire() { #if(_WIN32_WINNT >= 0x0400) return bool_cast(::TryEnterCriticalSection(&so_)); #else ::EnterCriticalSection(&so_); return true; #endif } void Release() { ::LeaveCriticalSection(&so_); } static void Acquire(SynchType* sa, size_t count, LockPolicy& lp) { while (count > 0) { --count; ::EnterCriticalSection(&sa[count]); } } static void Release(SynchType* sa, size_t count) { while (count > 0) { --count; ::LeaveCriticalSection(&sa[count]); } } private: CRITICAL_SECTION so_; DWORD spinCount_; }; // Ah, Mutexes. Here's where it starts getting fun. class Mutex; template<> class Policy { public: Policy( LPSECURITY_ATTRIBUTES psa = NULL, bool bInitOwner = false, LPCTSTR name = NULL, DWORD dwDesiredAccess = MUTEX_ALL_ACCESS, bool bInheritable = false ) : psa_(psa), bInitOwner_(bInitOwner), name_(name), dwDesiredAccess_(dwDesiredAccess), bInheritable_(bInheritable) { } LPSECURITY_ATTRIBUTES psa_; bool bInitOwner_; LPCTSTR name_; DWORD dwDesiredAccess_; bool bInheritable_; }; template< LPSECURITY_ATTRIBUTES psa = NULL, bool bInitOwner = false, LPCTSTR name = NULL, DWORD dwDesiredAccess = MUTEX_ALL_ACCESS, bool bInheritable = false > class MutexPolicy : public Policy { public: MutexPolicy() : Policy(psa, bInitOwner, name, dwDesiredAccess, bInheritable) { } }; template<> class LockPolicy { public: LockPolicy( bool waitAll = true, DWORD timeoutMillis = INFINITE, bool alertable = false ) : waitAll_(waitAll), timeoutMillis_(timeoutMillis), alertable_(alertable) { } bool waitAll_; DWORD timeoutMillis_; bool alertable_; }; template< bool waitAll = true, DWORD timeoutMillis = INFINITE, bool alertable = false > class MutexLockPolicy : public LockPolicy { public: MutexLockPolicy() : LockPolicy( waitAll, timeoutMillis, alertable ) { } }; class Mutex { public: typedef HANDLE SynchType; Mutex(Policy& p) : psa_(p.psa_), bInitOwner_(p.bInitOwner_), name_(p.name_), dwDesiredAccess_(p.dwDesiredAccess_), bInheritable_(p.bInheritable_) { // try // { so_ = ::CreateMutex( psa_, bInitOwner_, name_ ); // } /* catch (Win32Error& e) { if (e.GetErrorCode() == ERROR_ALREADY_EXISTS) { ::OpenMutex( dwDesiredAccess_, bInheritable_, name_ ); } } */ } ~Mutex() { ::CloseHandle(so_); } void Acquire(LockPolicy& lp) { ::WaitForSingleObject( so_, lp.timeoutMillis_ ); } bool TryAcquire() { DWORD waitStatus = ::WaitForSingleObject( so_, 0 ); if (waitStatus == WAIT_OBJECT_0) { return true; } return false; } void Release() { ::ReleaseMutex(so_); } static void Acquire(SynchType* sa, size_t count, LockPolicy& lp) { ::WaitForMultipleObjectsEx(count, sa, lp.waitAll_, lp.timeoutMillis_, lp.alertable_); } static void Release(SynchType* sa, size_t count) { while (count > 0) { --count; ::ReleaseMutex(sa[count]); } } SynchType so_; private: LPSECURITY_ATTRIBUTES psa_; bool bInitOwner_; LPCTSTR name_; DWORD dwDesiredAccess_; bool bInheritable_; }; }; // namespace Threading }; // namespace ParksComputing #endif // #ifndef ParksComputing_Threading_H