// (C) Copyright 2001 // Craig Henderson // // cdm.henderson@virgin.net // http://homepage.virgin.net/cdm.henderson // // SpinLock.h #ifndef __SPINLOCK_H_ #define __SPINLOCK_H_ // if we haven't got a DBG_SCOPE macro defined for debug output, then // define it to be a simple TRACE #ifndef DBG_SCOPE # define DBG_SCOPE TRACE #endif // if we haven't got a TRACE macro defined for debug output, then // define a dummy to compile to a null statement #ifndef TRACE # define TRACE (void)0 #endif // TRACE // if we haven't got an ASSERT macro defined for debugging, then // define a dummy to compile to a null statement #ifndef ASSERT # define ASSERT (void)0 #endif // ASSERT // scope the class inside a namespace // this is always good practice to avoid clashes // with classes from external sources namespace cdmh { // we use a trick with a numeric template parameter to enable multiple locks // and to avoid the need for a .cpp implementation file just to instantiate // the static m_lock member // each object that is instantiated with the same template parameter value // will share the lock, and different values of the template parameter will // identify independent locks // For example, an if an application has two classes of worker threads, each // class of thread requires exclusive access to a shared resource, but these // resources are independent of each other, then one class of thread can use // spin_lock<1> and the other can use spin_lock<2>. template class spin_lock { private: // this is the value that will spin around the threads. the thread // with this value in it's m_plock member is the one that holds the // lock. this simplifies debugging as it is easy to see if a lock is // held by a lock object by casting the m_plock member to a char * in // the watch window enum { LockValue = 'kcol' }; // we declare a static member which will provide us with a cross-thread // value storing the actual value of the lock. each object that is // instantiated with the same template value will share the lock, and // different values of the template parameter will identify independent // locks static long m_lock; // this is the actual lock pointer that is used in the class. // it can be set through a parameter to the ctor, or by default, // uses the above member long *m_plock; protected: // if these methods are not defined, then the compiler // will generate them if we try to use them. // we declare them but do not implement them as they are unused, // but if they are called in error, then we will get a linker // error. if we did not do this, the compiler will generate a basic // memcpy version, but that will be incorrect as the m_data vector // will not get copied correctly spin_lock(const spin_lock &); spin_lock &operator=(const spin_lock &); public: // this is the only public ctor // the parameter is optional, and can be omitted in most circumstances // to provide the default functionality. if the outer algorithm wants to // control the lock object more closely, then this parameter can be used explicit spin_lock(long *plock = 0) { // if we haven't been supplied a lock variable, then use our own if (plock == NULL) m_plock = &m_lock; else m_plock = plock; } void lock(void) const { // output to the debugger DBG_SCOPE("0x%08x waiting for lock\n", this); // this is why it is called a spin lock; we simply sit in a loop // until the lock is released by the holding object. to make the // algorithm more efficient, we reliquish the processor to another // thread if we fail to get the lock // note that this is inefficient for anything more than the smallest // interval, as the execution thread does not block while the lock // is held while (::InterlockedExchange(m_plock, LockValue) != 0) ::Sleep(0); } // release the lock void unlock(void) const { // reset the lock value ::InterlockedExchange(m_plock, 0); // output to the debugger TRACE("0x%08x released lock\n", this); } // returns true/false to signify if the lock is held by any thread bool is_locked(void) const { return (*m_plock == LockValue); } }; // instantiate the static memory and initialise it // to zero - the unlocked state template long spin_lock::m_lock = 0; } // namespace cdmh #endif __SPINLOCK_H_