diff options
author | Neo2003 <none@none> | 2008-10-02 16:23:55 -0500 |
---|---|---|
committer | Neo2003 <none@none> | 2008-10-02 16:23:55 -0500 |
commit | 9b1c0e006f20091f28f3f468cfcab1feb51286bd (patch) | |
tree | b5d1ba94a656e6679f8737f9ea6bed1239b73b14 /dep/src/zthread |
[svn] * Proper SVN structureinit
--HG--
branch : trunk
Diffstat (limited to 'dep/src/zthread')
81 files changed, 11151 insertions, 0 deletions
diff --git a/dep/src/zthread/AtomicCount.cxx b/dep/src/zthread/AtomicCount.cxx new file mode 100644 index 00000000000..ac0d0773b12 --- /dev/null +++ b/dep/src/zthread/AtomicCount.cxx @@ -0,0 +1,57 @@ +/* + * 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 __ZTATOMICCOUNTSELECT_H__ +#define __ZTATOMICCOUNTSELECT_H__ + +#include "zthread/AtomicCount.h" +#include "zthread/Config.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* +// Select the correct AtomicCount implementation based on +// what the compilation environment has defined + +#ifndef ZT_VANILLA + +#if defined(HAVE_ATOMIC_LINUX) +# include "linux/AtomicCount.cxx" +#elif defined(ZT_WIN32) +# include "win32/AtomicCount.cxx" +#elif defined(ZT_WIN9X) +# include "win9x/AtomicCount.cxx" +#endif + +#endif + +// Default to an AtomicCount that just uses a FastLock +#ifndef __ZTATOMICCOUNTIMPL_H__ +# include "vanilla/SimpleAtomicCount.cxx" +#endif +*/ + +# include "vanilla/SimpleAtomicCount.cxx" + +#endif // __ZTATOMICCOUNTSELECT_H__ diff --git a/dep/src/zthread/ConcurrentExecutor.cxx b/dep/src/zthread/ConcurrentExecutor.cxx new file mode 100644 index 00000000000..a65e9c5e909 --- /dev/null +++ b/dep/src/zthread/ConcurrentExecutor.cxx @@ -0,0 +1,54 @@ +/* + * 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. + * + */ + +#include "zthread/ConcurrentExecutor.h" + +namespace ZThread { + + ConcurrentExecutor::ConcurrentExecutor() + : _executor(1) {} + + void ConcurrentExecutor::interrupt() { + _executor.interrupt(); + } + + void ConcurrentExecutor::execute(const Task& task) { + _executor.execute(task); + } + + void ConcurrentExecutor::cancel() { + _executor.cancel(); + } + + bool ConcurrentExecutor::isCanceled() { + return _executor.isCanceled(); + } + + void ConcurrentExecutor::wait() { + _executor.wait(); + } + + bool ConcurrentExecutor::wait(unsigned long timeout) { + return _executor.wait(timeout); + } + +} diff --git a/dep/src/zthread/Condition.cxx b/dep/src/zthread/Condition.cxx new file mode 100644 index 00000000000..39485fb5ca4 --- /dev/null +++ b/dep/src/zthread/Condition.cxx @@ -0,0 +1,80 @@ +/* + * 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. + * + */ + +#include "zthread/Condition.h" +#include "ConditionImpl.h" + +namespace ZThread { + + class FifoConditionImpl : public ConditionImpl<fifo_list> { + public: + FifoConditionImpl(Lockable& l) : ConditionImpl<fifo_list>(l) {} + + }; + + Condition::Condition(Lockable& lock) { + + _impl = new FifoConditionImpl(lock); + + } + + + Condition::~Condition() { + + if(_impl != 0) + delete _impl; + + } + + + + void Condition::wait() { + + _impl->wait(); + + } + + + + bool Condition::wait(unsigned long ms) { + + return _impl->wait(ms); + + } + + + + void Condition::signal() { + + _impl->signal(); + + } + + + void Condition::broadcast() { + + _impl->broadcast(); + + } + +} // namespace ZThread + diff --git a/dep/src/zthread/ConditionImpl.h b/dep/src/zthread/ConditionImpl.h new file mode 100644 index 00000000000..eeeaba10bbc --- /dev/null +++ b/dep/src/zthread/ConditionImpl.h @@ -0,0 +1,377 @@ +/* + * 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 __ZTCONDITIONIMPL_H__ +#define __ZTCONDITIONIMPL_H__ + +#include "zthread/Guard.h" + +#include "Debug.h" +#include "Scheduling.h" +#include "DeferredInterruptionScope.h" + +namespace ZThread { + +/** + * @class ConditionImpl + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-18T08:15:37-0400> + * @version 2.2.11 + * + * The ConditionImpl template allows how waiter lists are sorted + * to be parameteized + */ +template <typename List> +class ConditionImpl { + + //! Waiters currently blocked + List _waiters; + + //! Serialize access to this object + FastLock _lock; + + //! External lock + Lockable& _predicateLock; + + public: + + /** + * Create a new ConditionImpl. + * + * @exception Initialization_Exception thrown if resources could not be + * allocated + */ + ConditionImpl(Lockable& predicateLock) : _predicateLock(predicateLock) { + + } + + /** + * Destroy this ConditionImpl, release its resources + */ + ~ConditionImpl(); + + void signal(); + + void broadcast(); + + void wait(); + + bool wait(unsigned long timeout); + +}; + + +template <typename List> +ConditionImpl<List>::~ConditionImpl() { + +#ifndef NDEBUG + + // It is an error to destroy a condition with threads waiting on it. + if(!_waiters.empty()) { + + ZTDEBUG("** You are destroying a condition variable which still has waiting threads. **\n"); + assert(0); + + } + +#endif + + } + + +/** + * Signal the condition variable, waking one thread if any. + */ +template <typename List> +void ConditionImpl<List>::signal() { + + Guard<FastLock> g1(_lock); + + // Try to find a waiter with a backoff & retry scheme + for(;;) { + + // Go through the list, attempt to notify() a waiter. + for(typename List::iterator i = _waiters.begin(); i != _waiters.end();) { + + // Try the monitor lock, if it cant be locked skip to the next waiter + ThreadImpl* impl = *i; + Monitor& m = impl->getMonitor(); + + if(m.tryAcquire()) { + + // Notify the monitor & remove from the waiter list so time isn't + // wasted checking it again. + i = _waiters.erase(i); + + // If notify() is not sucessful, it is because the wait() has already + // been ended (killed/interrupted/notify'd) + bool woke = m.notify(); + + m.release(); + + // Once notify() succeeds, return + if(woke) + return; + + } else ++i; + + } + + if(_waiters.empty()) + return; + + { // Backoff and try again + + Guard<FastLock, UnlockedScope> g2(g1); + ThreadImpl::yield(); + + } + + } + + } + +/** + * Broadcast to the condition variable, waking all threads waiting at the time of + * the broadcast. + */ +template <typename List> +void ConditionImpl<List>::broadcast() { + + Guard<FastLock> g1(_lock); + + // Try to find a waiter with a backoff & retry scheme + for(;;) { + + // Go through the list, attempt to notify() a waiter. + for(typename List::iterator i = _waiters.begin(); i != _waiters.end();) { + + // Try the monitor lock, if it cant be locked skip to the next waiter + ThreadImpl* impl = *i; + Monitor& m = impl->getMonitor(); + + if(m.tryAcquire()) { + + // Notify the monitor & remove from the waiter list so time isn't + // wasted checking it again. + i = _waiters.erase(i); + + // Try to wake the waiter, it doesn't matter if this is successful + // or not (only fails when the monitor is already going to stop waiting). + m.notify(); + + m.release(); + + } else ++i; + + } + + if(_waiters.empty()) + return; + + { // Backoff and try again + + Guard<FastLock, UnlockedScope> g2(g1); + ThreadImpl::yield(); + + } + + } + + } + +/** + * Cause the currently executing thread to block until this ConditionImpl has + * been signaled, the threads state changes. + * + * @param predicate Lockable& + * + * @exception Interrupted_Exception thrown when the caller status is interrupted + * @exception Synchronization_Exception thrown if there is some other error. + */ +template <typename List> +void ConditionImpl<List>::wait() { + + // Get the monitor for the current thread + ThreadImpl* self = ThreadImpl::current(); + Monitor& m = self->getMonitor(); + + Monitor::STATE state; + + { + + Guard<FastLock> g1(_lock); + + // Release the _predicateLock + _predicateLock.release(); + + // Stuff the waiter into the list + _waiters.insert(self); + + // Move to the monitor's lock + m.acquire(); + + { + + Guard<FastLock, UnlockedScope> g2(g1); + state = m.wait(); + + } + + // Move back to the Condition's lock + m.release(); + + // Remove from waiter list, regarless of weather release() is called or + // not. The monitor is sticky, so its possible a state 'stuck' from a + // previous operation and will leave the wait() w/o release() having + // been called. + typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self); + if(i != _waiters.end()) + _waiters.erase(i); + + } + + // Defer interruption until the external lock is acquire()d + Guard<Monitor, DeferredInterruptionScope> g3(m); + { + +#if !defined(NDEBUG) + try { +#endif + _predicateLock.acquire(); // Should not throw +#if !defined(NDEBUG) + } catch(...) { assert(0); } +#endif + + } + + switch(state) { + + case Monitor::SIGNALED: + break; + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + default: + throw Synchronization_Exception(); + } + + } + + +/** + * Cause the currently executing thread to block until this ConditionImpl has + * been signaled, or the timeout expires or the threads state changes. + * + * @param _predicateLock Lockable& + * @param timeout maximum milliseconds to block. + * + * @return bool + * + * @exception Interrupted_Exception thrown when the caller status is interrupted + * @exception Synchronization_Exception thrown if there is some other error. + */ +template <typename List> +bool ConditionImpl<List>::wait(unsigned long timeout) { + + // Get the monitor for the current thread + ThreadImpl* self = ThreadImpl::current(); + Monitor& m = self->getMonitor(); + + Monitor::STATE state; + + { + + Guard<FastLock> g1(_lock); + + // Release the _predicateLock + _predicateLock.release(); + + // Stuff the waiter into the list + _waiters.insert(self); + + state = Monitor::TIMEDOUT; + + // Don't bother waiting if the timeout is 0 + if(timeout) { + + m.acquire(); + + { + + Guard<FastLock, UnlockedScope> g2(g1); + state = m.wait(timeout); + + } + + m.release(); + + } + + // Remove from waiter list, regarless of weather release() is called or + // not. The monitor is sticky, so its possible a state 'stuck' from a + // previous operation and will leave the wait() w/o release() having + // been called. + typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self); + if(i != _waiters.end()) + _waiters.erase(i); + + } + + + // Defer interruption until the external lock is acquire()d + Guard<Monitor, DeferredInterruptionScope> g3(m); + { + +#if !defined(NDEBUG) + try { +#endif + _predicateLock.acquire(); // Should not throw +#if !defined(NDEBUG) + } catch(...) { assert(0); } +#endif + + } + + switch(state) { + + case Monitor::SIGNALED: + break; + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + case Monitor::TIMEDOUT: + return false; + + default: + throw Synchronization_Exception(); + } + + return true; + + } + +} // namespace ZThread + +#endif // __ZTCONDITIONIMPL_H__ diff --git a/dep/src/zthread/CountingSemaphore.cxx b/dep/src/zthread/CountingSemaphore.cxx new file mode 100644 index 00000000000..43e8b8cfba7 --- /dev/null +++ b/dep/src/zthread/CountingSemaphore.cxx @@ -0,0 +1,92 @@ +/* + * 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. + * + */ + +#include "zthread/CountingSemaphore.h" +#include "SemaphoreImpl.h" + +using namespace ZThread; + +namespace ZThread { + + + CountingSemaphore::CountingSemaphore(int initialCount) { + + _impl = new FifoSemaphoreImpl(initialCount, 0 , false); + + } + + + CountingSemaphore::~CountingSemaphore() { + + try { + + if(_impl != 0) + delete _impl; + + } catch(...) { } + + } + + + void CountingSemaphore::wait() { + _impl->acquire(); + } + + + bool CountingSemaphore::tryWait(unsigned long ms) { + + return _impl->tryAcquire(ms); + + } + + + void CountingSemaphore::post() { + + _impl->release(); + + } + + int CountingSemaphore::count() { + + return _impl->count(); + + } + + void CountingSemaphore::acquire() { + + _impl->acquire(); + + } + + bool CountingSemaphore::tryAcquire(unsigned long ms) { + + return _impl->tryAcquire(ms); + + } + + void CountingSemaphore::release() { + + _impl->release(); + + } + +} // namespace ZThread diff --git a/dep/src/zthread/Debug.h b/dep/src/zthread/Debug.h new file mode 100644 index 00000000000..484b37f7d6f --- /dev/null +++ b/dep/src/zthread/Debug.h @@ -0,0 +1,32 @@ +/* + * 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 ZTDEBUG + +#ifndef NDEBUG +# include <stdio.h> +# define ZTDEBUG printf +#else +# define ZTDEBUG(x) +#endif + +#endif diff --git a/dep/src/zthread/DeferredInterruptionScope.h b/dep/src/zthread/DeferredInterruptionScope.h new file mode 100644 index 00000000000..041d1e427f6 --- /dev/null +++ b/dep/src/zthread/DeferredInterruptionScope.h @@ -0,0 +1,63 @@ +/* + * 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 __ZTDEFERREDINTERRUPTIONSCOPE_H__ +#define __ZTDEFERREDINTERRUPTIONSCOPE_H__ + +#include "ThreadImpl.h" +#include <cassert> + +namespace ZThread { + +/** + * @class DeferredInterruptionScope + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T19:45:18-0400> + * @version 2.3.0 + * + * Locking policy for a Guard that will defer any state reported + * for the reported Status of a thread except SIGNALED until the + * scope has ended. This allows a Guard to be used to create an + * uninterruptible region in code. + */ +class DeferredInterruptionScope { + public: + + template <class LockType> + static void createScope(LockHolder<LockType>& l) { + + l.getLock().interest(Monitor::SIGNALED); + + } + + template <class LockType> + static void destroyScope(LockHolder<LockType>& l) { + + l.getLock().interest(Monitor::ANYTHING); + + } + +}; + +} + +#endif // __ZTDEFERREDINTERRUPTIONSCOPE_H__ diff --git a/dep/src/zthread/FastLock.h b/dep/src/zthread/FastLock.h new file mode 100644 index 00000000000..4d7f34a086d --- /dev/null +++ b/dep/src/zthread/FastLock.h @@ -0,0 +1,71 @@ +/* + * 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 __ZTFASTLOCKSELECT_H__ +#define __ZTFASTLOCKSELECT_H__ + +#include "zthread/Config.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// Select the correct FastLock implementation based on +// what the compilation environment has defined + +#if defined(ZT_POSIX) + +# if defined(HAVE_ATOMIC_LINUX) + +# if defined(ZTHREAD_USE_SPIN_LOCKS) +# include "linux/AtomicFastLock.h" +# endif + +# endif + +# include "posix/FastLock.h" + +// Use spin locks +#elif defined(ZTHREAD_USE_SPIN_LOCKS) + +# if defined(ZT_WIN9X) +# include "win9x/AtomicFastLock.h" +# elif defined(ZT_WIN32) +# include "win32/AtomicFastLock.h" +# endif + +// Use normal Mutex objects +#elif defined(ZT_WIN9X) || defined(ZT_WIN32) + +# include "win32/FastLock.h" + +#elif defined(ZT_MACOS) + +# include "macos/FastLock.h" + +#endif + +#ifndef __ZTFASTLOCK_H__ +#error "No FastLock implementation could be selected" +#endif + +#endif // __ZTFASTLOCKSELECT_H__ diff --git a/dep/src/zthread/FastMutex.cxx b/dep/src/zthread/FastMutex.cxx new file mode 100644 index 00000000000..464dd83e5e0 --- /dev/null +++ b/dep/src/zthread/FastMutex.cxx @@ -0,0 +1,53 @@ +/* + * 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. + * + */ + +#include "zthread/FastMutex.h" +#include "FastLock.h" + +namespace ZThread { + + FastMutex::FastMutex() : _lock(new FastLock) { } + + FastMutex::~FastMutex() { + delete _lock; + } + + + void FastMutex::acquire() { + + _lock->acquire(); + + } + + bool FastMutex::tryAcquire(unsigned long timeout) { + + return _lock->tryAcquire(timeout); + + } + + void FastMutex::release() { + + _lock->release(); + + } + +} // namespace ZThread diff --git a/dep/src/zthread/FastRecursiveLock.h b/dep/src/zthread/FastRecursiveLock.h new file mode 100644 index 00000000000..0a36f62d5f8 --- /dev/null +++ b/dep/src/zthread/FastRecursiveLock.h @@ -0,0 +1,74 @@ +/* + * 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 __ZTFASTRECURSIVELOCKSELECT_H__ +#define __ZTFASTRECURSIVELOCKSELECT_H__ + +#include "zthread/Config.h" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +// Select the correct FastRecusriveLock implementation based on +// what the compilation environment has defined + +#if defined(ZTHREAD_DUAL_LOCKS) +# include "vanilla/DualMutexRecursiveLock.h" +#else + +# ifndef ZT_VANILLA + +# if defined(ZT_POSIX) + +// Linux and Solaris have working pthreads recursive locks. These +// are created differently, and there are some system don't seem to +// include recursive locks at all. Several recursive implementations +// are provided + +# if defined(__linux__) +# include "linux/FastRecursiveLock.h" +# elif defined(HAVE_MUTEXATTR_SETTYPE) +# include "solaris/FastRecursiveLock.h" +# elif defined(ZTHREAD_CONDITION_LOCKS) +# include "posix/ConditionRecursiveLock.h" +# endif + +// Use spin locks +# elif defined(ZT_WIN32) && defined(ZTHREAD_USE_SPIN_LOCKS) +# include "win32/AtomicFastRecursiveLock.h" + +// Use normal Mutex objects +# elif defined(ZT_WIN32) || defined(ZT_WIN9X) +# include "win32/FastRecursiveLock.h" +# endif + +# endif + +#endif + +#ifndef __ZTFASTRECURSIVELOCK_H__ +#include "vanilla/SimpleRecursiveLock.h" +#endif + +#endif // __ZTFASTRECURSIVELOCKSELECT_H__ diff --git a/dep/src/zthread/FastRecursiveMutex.cxx b/dep/src/zthread/FastRecursiveMutex.cxx new file mode 100644 index 00000000000..5ca677a654d --- /dev/null +++ b/dep/src/zthread/FastRecursiveMutex.cxx @@ -0,0 +1,53 @@ +/* + * 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. + * + */ + +#include "zthread/FastRecursiveMutex.h" +#include "FastRecursiveLock.h" + +namespace ZThread { + + FastRecursiveMutex::FastRecursiveMutex() + : _lock(new FastRecursiveLock) { } + + FastRecursiveMutex::~FastRecursiveMutex() + { delete _lock; } + + + void FastRecursiveMutex::acquire() { + + _lock->acquire(); + + } + + bool FastRecursiveMutex::tryAcquire(unsigned long timeout) { + + return _lock->tryAcquire(timeout); + + } + + void FastRecursiveMutex::release() { + + _lock->release(); + + } + +} // namespace ZThread diff --git a/dep/src/zthread/IntrusivePtr.h b/dep/src/zthread/IntrusivePtr.h new file mode 100644 index 00000000000..47d5afbfcb8 --- /dev/null +++ b/dep/src/zthread/IntrusivePtr.h @@ -0,0 +1,99 @@ +/* + * 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 __ZTINTRUSIVEPTR_H__ +#define __ZTINTRUSIVEPTR_H__ + +#include "zthread/Guard.h" +#include <cstdlib> + +namespace ZThread { + +/** + * @class IntrusivePtr + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T17:54:23-0400> + * @version 2.2.0 + * + * This template creates an intrusively reference counted object + * an IntrusivePtr starts out with a 1 count, which is updated as references are + * added and removed. When the reference count drops to 0, the + * IntrusivePtr will delete itself. + */ +template <typename T, class LockType> +class IntrusivePtr : NonCopyable { + + //! Intrusive reference count + size_t _count; + + //! Synchornization object + LockType _lock; + +public: + + /** + * Create an IntrusivePtr with a count. + */ + IntrusivePtr(size_t InitialCount=1) : _count(InitialCount) { } + + /** + * Destroy an IntrusivePtr + */ + virtual ~IntrusivePtr() {} + + /** + * Add a reference to this object, it will take one more + * call to delReference() for it to be deleted. + */ + void addReference() { + + Guard<LockType, LockedScope> g(_lock); + _count++; + + } + + /** + * Remove a reference from this object, if the reference count + * drops to 0 as a result, the object deletes itself. + */ + void delReference() { + + bool result = false; + + { + + Guard<LockType, LockedScope> g(_lock); + result = (--_count == 0); + + } + + if(result) + delete this; + + } + +}; + + +}; + +#endif diff --git a/dep/src/zthread/Makefile.am b/dep/src/zthread/Makefile.am new file mode 100644 index 00000000000..a3c91195d51 --- /dev/null +++ b/dep/src/zthread/Makefile.am @@ -0,0 +1,132 @@ +## Copyright (c) 2005, Eric Crahen +## Modified for MaNGOS project <http://www.mangosproject.org> +## +## 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. + +## Process this file with automake to produce Makefile.in + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = -I$(srcdir)/../../include -I$(srcdir)/../../include/zthread + +## Build ZThread as shared library. +# libZThread shared library will later be reused by realm list daemon +# and world server daemon. +lib_LTLIBRARIES = libZThread.la +libZThread_la_SOURCES = \ + AtomicCount.cxx \ + Condition.cxx \ + ConcurrentExecutor.cxx \ + CountingSemaphore.cxx \ + FastMutex.cxx \ + FastRecursiveMutex.cxx \ + Mutex.cxx \ + RecursiveMutexImpl.cxx \ + RecursiveMutex.cxx \ + Monitor.cxx \ + PoolExecutor.cxx \ + PriorityCondition.cxx \ + PriorityInheritanceMutex.cxx \ + PriorityMutex.cxx \ + PrioritySemaphore.cxx \ + Semaphore.cxx \ + SynchronousExecutor.cxx \ + Thread.cxx \ + ThreadedExecutor.cxx \ + ThreadImpl.cxx \ + ThreadLocalImpl.cxx \ + ThreadQueue.cxx \ + Time.cxx \ + ThreadOps.cxx + +## libtool settings +# API versioning +# Link against dependencies +# How to increase version info: +# - only bug fixes implemented: +# bump the version to LTZTHREAD_CURRENT:LTZTHREAD_REVISION+1:LTZTHREAD_AGE +# - augmented the interface: +# bump the version to LTZTHREAD_CURRENT+1:0:LTZTHREAD_AGE+1 +# - broken old interface: +# bump the version to LTZTHREAD_CURRENT+1:0:0 +LTZTHREAD_CURRENT = 2 +LTZTHREAD_REVISION = 3 +LTZTHREAD_AGE = 2 +libZThread_la_LDFLAGS = -version-info $(LTZTHREAD_CURRENT):$(LTZTHREAD_REVISION):$(LTZTHREAD_AGE) + +## Additional files to include when running 'make dist' +# Header files. +EXTRA_DIST = \ + ConditionImpl.h \ + Debug.h \ + DeferredInterruptionScope.h \ + FastLock.h \ + FastRecursiveLock.h \ + IntrusivePtr.h \ + Monitor.h \ + MutexImpl.h \ + RecursiveMutexImpl.h \ + Scheduling.h \ + SemaphoreImpl.h \ + State.h \ + Status.h \ + TSS.h \ + ThreadImpl.h \ + ThreadOps.h \ + ThreadQueue.h \ + TimeStrategy.h \ + config.h + +# Implementation specific files. +EXTRA_DIST += \ + linux/AtomicCount.cxx \ + linux/AtomicFastLock.h \ + linux/FastRecursiveLock.h \ + macos/FastLock.h \ + macos/Monitor.cxx \ + macos/Monitor.h \ + macos/TSS.h \ + macos/ThreadOps.cxx \ + macos/ThreadOps.h \ + macos/UpTimeStrategy.h \ + posix/ConditionRecursiveLock.h \ + posix/FastLock.h \ + posix/FtimeStrategy.h \ + posix/GetTimeOfDayStrategy.h \ + posix/Monitor.cxx \ + posix/Monitor.h \ + posix/PriorityOps.h \ + posix/TSS.h \ + posix/ThreadOps.cxx \ + posix/ThreadOps.h \ + solaris/FastRecursiveLock.h \ + vanilla/DualMutexRecursiveLock.h \ + vanilla/SimpleAtomicCount.cxx \ + vanilla/SimpleRecursiveLock.h \ + win32/AtomicCount.cxx \ + win32/AtomicFastLock.h \ + win32/AtomicFastRecursiveLock.h \ + win32/FastLock.h \ + win32/FastRecursiveLock.h \ + win32/Monitor.cxx \ + win32/Monitor.h \ + win32/PerformanceCounterStrategy.h \ + win32/TSS.h \ + win32/ThreadOps.cxx \ + win32/ThreadOps.h \ + win9x/AtomicCount.cxx \ + win9x/AtomicFastLock.h diff --git a/dep/src/zthread/Monitor.cxx b/dep/src/zthread/Monitor.cxx new file mode 100644 index 00000000000..9a578e796ed --- /dev/null +++ b/dep/src/zthread/Monitor.cxx @@ -0,0 +1,39 @@ +/* + * 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 __ZTMONITORIMPLSELECT_CXX__ +#define __ZTMONITORIMPLSELECT_CXX__ + +#include "Monitor.h" + +// This file will select an implementation for a Monitor based on +// what Monitor.h selects. This method is for selecting the +// source files, to improve portability. Currently, the project is +// based on the autoconf tool-set, which doesn't support conditional +// compilation well. Additionally, this should make the library +// easier to port since its working around conditional compilation +// by using C++ features and people won't have to fiddle around with +// their make tool as much to compile the source + +#include ZT_MONITOR_IMPLEMENTATION + +#endif diff --git a/dep/src/zthread/Monitor.h b/dep/src/zthread/Monitor.h new file mode 100644 index 00000000000..6f9492fe32c --- /dev/null +++ b/dep/src/zthread/Monitor.h @@ -0,0 +1,60 @@ +/* + * 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 __ZTMONITORSELECT_H__ +#define __ZTMONITORSELECT_H__ + +#include "zthread/Config.h" + +#if defined(ZT_MONITOR_IMPLEMENTATION) +# error "Reserved symbol defined" +#endif + +// Include the dependencies for a Montior +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +// Select the correct Monitor implementation based on +// what the compilation environment has defined +#if defined(ZT_POSIX) + +# include "posix/Monitor.h" +# define ZT_MONITOR_IMPLEMENTATION "posix/Monitor.cxx" + +#elif defined(ZT_WIN32) || defined(ZT_WIN9X) + +# include "win32/Monitor.h" +# define ZT_MONITOR_IMPLEMENTATION "win32/Monitor.cxx" + +#elif defined(ZT_MACOS) + +# include "macos/Monitor.h" +# define ZT_MONITOR_IMPLEMENTATION "macos/Monitor.cxx" + +#endif + +#ifndef __ZTMONITOR_H__ +#error "No Monitor implementation could be selected" +#endif + +#endif // __ZTMONITORSELECT_H__ diff --git a/dep/src/zthread/Mutex.cxx b/dep/src/zthread/Mutex.cxx new file mode 100644 index 00000000000..eca38ba89c6 --- /dev/null +++ b/dep/src/zthread/Mutex.cxx @@ -0,0 +1,68 @@ +/* + * 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. + * + */ + +#include "zthread/Mutex.h" +#include "MutexImpl.h" + +namespace ZThread { + + class FifoMutexImpl : public MutexImpl<fifo_list, NullBehavior> { }; + + + Mutex::Mutex() { + + _impl = new FifoMutexImpl(); + + } + + Mutex::~Mutex() { + + if(_impl != 0) + delete _impl; + } + + // P + void Mutex::acquire() { + + _impl->acquire(); + + } + + + // P + bool Mutex::tryAcquire(unsigned long ms) { + + return _impl->tryAcquire(ms); + + } + + // V + void Mutex::release() { + + _impl->release(); + + } + + + +} // namespace ZThread + diff --git a/dep/src/zthread/MutexImpl.h b/dep/src/zthread/MutexImpl.h new file mode 100644 index 00000000000..10a9160ce5a --- /dev/null +++ b/dep/src/zthread/MutexImpl.h @@ -0,0 +1,377 @@ +/* + * 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. + * + */ + +#include "zthread/Exceptions.h" +#include "zthread/Guard.h" + +#include "Debug.h" +#include "FastLock.h" +#include "Scheduling.h" + +#include <assert.h> +#include <errno.h> + +namespace ZThread { + + +/** + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T19:52:12-0400> + * @version 2.2.11 + * @class NullBehavior + */ +class NullBehavior { +protected: + + inline void waiterArrived(ThreadImpl*) { } + + inline void waiterDeparted(ThreadImpl*) { } + + inline void ownerAcquired(ThreadImpl*) { } + + inline void ownerReleased(ThreadImpl*) { } + +}; + +/** + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T19:52:12-0400> + * @version 2.2.11 + * @class MutexImpl + * + * The MutexImpl template allows how waiter lists are sorted, and + * what actions are taken when a thread interacts with the mutex + * to be parametized. + */ +template <typename List, typename Behavior> +class MutexImpl : Behavior { + + //! List of Events that are waiting for notification + List _waiters; + + //! Serialize access to this Mutex + FastLock _lock; + + //! Current owner + volatile ThreadImpl* _owner; + + public: + + + /** + * Create a new MutexImpl + * + * @exception Initialization_Exception thrown if resources could not be + * properly allocated + */ + MutexImpl() : _owner(0) { } + + ~MutexImpl(); + + void acquire(); + + void release(); + + bool tryAcquire(unsigned long timeout); + +}; + + /** + * Destroy this MutexImpl and release its resources + */ +template<typename List, typename Behavior> +MutexImpl<List, Behavior>::~MutexImpl() { + +#ifndef NDEBUG + + // It is an error to destroy a mutex that has not been released + if(_owner != 0) { + + ZTDEBUG("** You are destroying a mutex which was never released. **\n"); + assert(0); // Destroyed mutex while in use + + } + + if(!_waiters.empty()) { + + ZTDEBUG("** You are destroying a mutex which is blocking %d threads. **\n", _waiters.size()); + assert(0); // Destroyed mutex while in use + + } + +#endif + + } + + + /** + * Acquire a lock on the mutex. If this operation succeeds the calling + * thread holds an exclusive lock on this mutex, otherwise it is blocked + * until the lock can be acquired. + * + * @exception Deadlock_Exception thrown when the caller attempts to acquire() more + * than once, If the checking flag is set. + * @exception Interrupted_Exception thrown when the caller status is interrupted + * @exception Synchronization_Exception thrown if there is some other error. + */ +template<typename List, typename Behavior> +void MutexImpl<List, Behavior>::acquire() { + + ThreadImpl* self = ThreadImpl::current(); + Monitor& m = self->getMonitor(); + + Monitor::STATE state; + + Guard<FastLock> g1(_lock); + + // Deadlock will occur if the current thread is the owner + // and there is no entry count. + if(_owner == self) + throw Deadlock_Exception(); + + // Acquire the lock if it is free and there are no waiting threads + if(_owner == 0 && _waiters.empty()) { + + _owner = self; + + this->ownerAcquired(self); + + } + + // Otherwise, wait for a signal from a thread releasing its + // ownership of the lock + else { + + _waiters.insert(self); + m.acquire(); + + this->waiterArrived(self); + + { + + Guard<FastLock, UnlockedScope> g2(g1); + state = m.wait(); + + } + + this->waiterDeparted(self); + + m.release(); + + // Remove from waiter list, regardless of wether release() is called or + // not. The monitor is sticky, so its possible a state 'stuck' from a + // previous operation and will leave the wait() w/o release() having + // been called (e.g. interrupted) + typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self); + if(i != _waiters.end()) + _waiters.erase(i); + + // If awoke due to a notify(), take ownership. + switch(state) { + case Monitor::SIGNALED: + + assert(_owner == 0); + _owner = self; + + this->ownerAcquired(self); + + break; + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + default: + throw Synchronization_Exception(); + } + + } + + } + + + /** + * Acquire a lock on the mutex. If this operation succeeds the calling + * thread holds an exclusive lock on this mutex. If the lock cannot be + * obtained before the timeout expires, the caller returns false. + * + * @exception Deadlock_Exception thrown when the caller attempts to acquire() more + * than once, If the checking flag is set. + * @exception Interrupted_Exception thrown when the caller status is interrupted + * @exception Synchronization_Exception thrown if there is some other error. + */ +template<typename List, typename Behavior> +bool MutexImpl<List, Behavior>::tryAcquire(unsigned long timeout) { + + ThreadImpl* self = ThreadImpl::current(); + Monitor& m = self->getMonitor(); + + Guard<FastLock> g1(_lock); + + // Deadlock will occur if the current thread is the owner + // and there is no entry count. + if(_owner == self) + throw Deadlock_Exception(); + + // Acquire the lock if it is free and there are no waiting threads + if(_owner == 0 && _waiters.empty()) { + + _owner = self; + + this->ownerAcquired(self); + + } + + // Otherwise, wait for a signal from a thread releasing its + // ownership of the lock + else { + + _waiters.insert(self); + + Monitor::STATE state = Monitor::TIMEDOUT; + + // Don't bother waiting if the timeout is 0 + if(timeout) { + + m.acquire(); + + this->waiterArrived(self); + + { + + Guard<FastLock, UnlockedScope> g2(g1); + state = m.wait(timeout); + + } + + this->waiterDeparted(self); + + m.release(); + + } + + + // Remove from waiter list, regarless of weather release() is called or + // not. The monitor is sticky, so its possible a state 'stuck' from a + // previous operation and will leave the wait() w/o release() having + // been called. + typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self); + if(i != _waiters.end()) + _waiters.erase(i); + + // If awoke due to a notify(), take ownership. + switch(state) { + case Monitor::SIGNALED: + + assert(0 == _owner); + _owner = self; + + this->ownerAcquired(self); + + break; + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + case Monitor::TIMEDOUT: + return false; + + default: + throw Synchronization_Exception(); + } + + } + + return true; + + } + + /** + * Release a lock on the mutex. If this operation succeeds the calling + * thread no longer holds an exclusive lock on this mutex. If there are + * waiting threads, one will be selected, assigned ownership and specifically + * awakened. + * + * @exception InvalidOp_Exception - thrown if an attempt is made to + * release a mutex not owned by the calling thread. + */ +template<typename List, typename Behavior> +void MutexImpl<List, Behavior>::release() { + + ThreadImpl* impl = ThreadImpl::current(); + + Guard<FastLock> g1(_lock); + + // Make sure the operation is valid + if(_owner != impl) + throw InvalidOp_Exception(); + + _owner = 0; + + this->ownerReleased(impl); + + // Try to find a waiter with a backoff & retry scheme + for(;;) { + + // Go through the list, attempt to notify() a waiter. + for(typename List::iterator i = _waiters.begin(); i != _waiters.end();) { + + // Try the monitor lock, if it cant be locked skip to the next waiter + impl = *i; + Monitor& m = impl->getMonitor(); + + if(m.tryAcquire()) { + + // If notify() is not sucessful, it is because the wait() has already + // been ended (killed/interrupted/notify'd) + bool woke = m.notify(); + + m.release(); + + // Once notify() succeeds, return + if(woke) + return; + + } else ++i; + + } + + if(_waiters.empty()) + return; + + { // Backoff and try again + + Guard<FastLock, UnlockedScope> g2(g1); + ThreadImpl::yield(); + + } + + } + + } + +} // namespace ZThread + + + + + + diff --git a/dep/src/zthread/PoolExecutor.cxx b/dep/src/zthread/PoolExecutor.cxx new file mode 100644 index 00000000000..cf84e145453 --- /dev/null +++ b/dep/src/zthread/PoolExecutor.cxx @@ -0,0 +1,629 @@ +/* + * 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. + * + */ + +#include "ThreadImpl.h" +#include "zthread/PoolExecutor.h" +#include "zthread/MonitoredQueue.h" +#include "zthread/FastMutex.h" +#include "ThreadImpl.h" +#include "ThreadQueue.h" + +#include <algorithm> +#include <deque> +#include <utility> + +using namespace ZThread; + +namespace ZThread { + + namespace { + + /** + */ + class WaiterQueue { + + typedef std::deque<ThreadImpl*> ThreadList; + + typedef struct group_t { + size_t id; + size_t count; + ThreadList waiters; + group_t(size_t n) : id(n), count(0) {} + } Group; + + typedef std::deque<Group> GroupList; + + //! Predicate to find a specific group + struct by_id : public std::unary_function<bool, Group> { + size_t id; + by_id(size_t n) : id(n) {} + bool operator()(const Group& grp) { + return grp.id == id; + } + }; + + //! Functor to count groups + struct counter : public std::unary_function<void, Group> { + size_t count; + counter() : count(0) {} + void operator()(const Group& grp) { count += grp.count; } + operator size_t() { return count; } + }; + + FastMutex _lock; + GroupList _list; + size_t _id; + size_t _generation; + + public: + + WaiterQueue() : _id(0), _generation(0) { + // At least one empty-group exists + _list.push_back( Group(_id++) ); + } + + /** + * Insert the current thread into the current waiter list + * + * @pre At least one empty group exists + * @post At least one empty group exists + */ + bool wait(unsigned long timeout) { + + ThreadImpl* current = ThreadImpl::current(); + Monitor& m = current->getMonitor(); + + Monitor::STATE state; + + Guard<FastMutex> g1(_lock); + + // At least one empty-group exists + assert(!_list.empty()); + + // Return w/o waiting if there are no executing tasks + if((size_t)std::for_each(_list.begin(), _list.end(), counter()) < 1) + return true; + + // Update the waiter list for the active group + _list.back().waiters.push_back(current); + size_t n = _list.back().id; + + m.acquire(); + + { + + Guard<FastMutex, UnlockedScope> g2(g1); + state = timeout == 0 ? m.wait() : m.wait(timeout); + + } + + m.release(); + + // If awoke due to a reason other than the last task in the group 'n' completing, + // then then find the group 'current' is waiting in + GroupList::iterator i = std::find_if(_list.begin(), _list.end(), by_id(n)); + if(i != _list.end()) { + + // Remove 'current' from that list if it is still a member + ThreadList::iterator j = std::find(i->waiters.begin(), i->waiters.end(), current); + if(j != i->waiters.end()) + i->waiters.erase(j); + + } + + // At least one empty-group exists + assert(!_list.empty()); + + switch(state) { + case Monitor::SIGNALED: + break; + case Monitor::TIMEDOUT: + return false; + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + default: + throw Synchronization_Exception(); + } + + return true; + + } + + /** + * Increment the active group count + * + * @pre at least 1 empty group exists + * @post at least 1 non-empty group exists + */ + std::pair<size_t, size_t> increment() { + + Guard<FastMutex> g(_lock); + + // At least one empty-group exists + assert(!_list.empty()); + + GroupList::iterator i = --_list.end(); + size_t n = i->id; + + if(i == _list.end()) { + + // A group should never have been removed until + // the final task in that group completed + assert(0); + + } + + i->count++; + + // When the active group is being incremented, insert a new active group + // to replace it if there were waiting threads + if(i == --_list.end() && !i->waiters.empty()) + _list.push_back(Group(_id++)); + + // At least 1 non-empty group exists + assert((size_t)std::for_each(_list.begin(), _list.end(), counter()) > 0); + + return std::make_pair(n, _generation); + + } + + + /** + * Decrease the count for the group with the given id. + * + * @param n group id + * + * @pre At least 1 non-empty group exists + * @post At least 1 empty group exists + */ + void decrement(size_t n) { + + Guard<FastMutex> g1(_lock); + + // At least 1 non-empty group exists + assert((size_t)std::for_each(_list.begin(), _list.end(), counter()) > 0); + + // Find the requested group + GroupList::iterator i = std::find_if(_list.begin(), _list.end(), by_id(n)); + if(i == _list.end()) { + + // A group should never have been removed until + // the final task in that group completed + assert(0); + + } + + // Decrease the count for tasks in this group, + if(--i->count == 0 && i == _list.begin()) { + + do { + + // When the first group completes, wake all waiters for every + // group, starting from the first until a group that is not + // complete is reached + + /* + // Don't remove the empty active group + if(i == --_list.end() && i->waiters.empty()) + break; + */ + + if( awaken(*i) ) { + + // If all waiters were awakened, remove the group + i = _list.erase(i); + + } else { + + { + + // Otherwise, unlock and yield allowing the waiter + // lists to be updated if other threads are busy + Guard<FastLock, UnlockedScope> g2(g1); + ThreadImpl::yield(); + + } + + i = _list.begin(); + + } + + } while(i != _list.end() && i->count == 0); + + // Ensure that an active group exists + if(_list.empty()) + _list.push_back( Group(++_id) ); + + } + + // At least one group exists + assert(!_list.empty()); + + } + + /** + */ + size_t generation(bool next = false) { + + Guard<FastMutex> g(_lock); + return next ? _generation++ : _generation; + + } + + private: + + /** + * Awaken all the waiters remaining in the given group + * + * @return + * - true if all the waiting threads were successfully awakened. + * - false if there were one or more threads that could not be awakened. + */ + bool awaken(Group& grp) { + + // Go through the waiter list in the given group; + for(ThreadList::iterator i = grp.waiters.begin(); i != grp.waiters.end();) { + + ThreadImpl* impl = *i; + Monitor& m = impl->getMonitor(); + + // Try the monitor lock, if it cant be locked skip to the next waiter + if(m.tryAcquire()) { + + // Notify the monitor & remove from the waiter list so time isn't + // wasted checking it again. + i = grp.waiters.erase(i); + + // Try to wake the waiter, it doesn't matter if this is successful + // or not (only fails when the monitor is already going to stop waiting). + m.notify(); + m.release(); + + } else ++i; + + } + + return grp.waiters.empty(); + + } + + }; + + /** + * @class GroupedRunnable + * + * Wrap a task with group and generation information. + * + * - 'group' allows tasks to be grouped together so that lists of waiting + * threads can be managed. + * + * - 'generation' allows tasks to be interrupted + */ + class GroupedRunnable : public Runnable { + + Task _task; + WaiterQueue& _queue; + + size_t _group; + size_t _generation; + + public: + + GroupedRunnable(const Task& task, WaiterQueue& queue) + : _task(task), _queue(queue) { + + std::pair<size_t, size_t> pr( _queue.increment() ); + + _group = pr.first; + _generation = pr.second; + + } + + size_t group() const { + return _group; + } + + size_t generation() const { + return _generation; + } + + void run() { + + try { + + _task->run(); + + } catch(...) { + + } + + _queue.decrement( group() ); + + } + + }; + + typedef CountedPtr<GroupedRunnable, size_t> ExecutorTask; + + /** + * + */ + class ExecutorImpl { + + typedef MonitoredQueue<ExecutorTask, FastMutex> TaskQueue; + typedef std::deque<ThreadImpl*> ThreadList; + + TaskQueue _taskQueue; + WaiterQueue _waitingQueue; + + ThreadList _threads; + volatile size_t _size; + + + public: + + ExecutorImpl() : _size(0) {} + + + void registerThread() { + + Guard<TaskQueue> g(_taskQueue); + + ThreadImpl* impl = ThreadImpl::current(); + _threads.push_back(impl); + + // current cancel if too many threads are being created + if(_threads.size() > _size) + impl->cancel(); + + } + + void unregisterThread() { + + Guard<TaskQueue> g(_taskQueue); + std::remove(_threads.begin(), _threads.end(), ThreadImpl::current()); + + } + + void execute(const Task& task) { + + // Wrap the task with a grouped task + GroupedRunnable* runnable = new GroupedRunnable(task, _waitingQueue); + + try { + + _taskQueue.add( ExecutorTask(runnable) ); + + } catch(...) { + + // Incase the queue is canceled between the time the WaiterQueue is + // updated and the task is added to the TaskQueue + _waitingQueue.decrement( runnable->group() ); + throw; + + } + + } + + void interrupt() { + + // Bump the generation number + _waitingQueue.generation(true); + + Guard<TaskQueue> g(_taskQueue); + + // Interrupt all threads currently running, thier tasks would be + // from an older generation + for(ThreadList::iterator i = _threads.begin(); i != _threads.end(); ++i) + (*i)->interrupt(); + + } + + //! Adjust the number of desired workers and return the number of Threads needed + size_t workers(size_t n) { + + Guard<TaskQueue> g(_taskQueue); + + size_t m = (_size < n) ? (n - _size) : 0; + _size = n; + + return m; + + } + + size_t workers() { + + Guard<TaskQueue> g(_taskQueue); + return _size; + + } + + ExecutorTask next() { + + ExecutorTask task; + + // Draw the task from the queue + for(;;) { + + try { + + task = _taskQueue.next(); + break; + + } catch(Interrupted_Exception&) { + + // Ignore interruption here, it can only come from + // another thread interrupt()ing the executor. The + // thread was interrupted in the hopes it was busy + // with a task + + } + + } + + // Interrupt the thread running the tasks when the generation + // does not match the current generation + if( task->generation() != _waitingQueue.generation() ) + ThreadImpl::current()->interrupt(); + + // Otherwise, clear the interrupted status for the thread and + // give it a clean slate to start with + else + ThreadImpl::current()->isInterrupted(); + + return task; + + } + + bool isCanceled() { + return _taskQueue.isCanceled(); + } + + void cancel() { + _taskQueue.cancel(); + } + + bool wait(unsigned long timeout) { + return _waitingQueue.wait(timeout); + } + + }; + + //! Executor job + class Worker : public Runnable { + + CountedPtr< ExecutorImpl > _impl; + + public: + + //! Create a Worker that draws upon the given Queue + Worker(const CountedPtr< ExecutorImpl >& impl) + : _impl(impl) { } + + //! Run until Thread or Queue are canceled + void run() { + + _impl->registerThread(); + + // Run until the Queue is canceled + while(!Thread::canceled()) { + + // Draw tasks from the queue + ExecutorTask task( _impl->next() ); + task->run(); + + } + + _impl->unregisterThread(); + + } + + }; /* Worker */ + + + //! Helper + class Shutdown : public Runnable { + + CountedPtr< ExecutorImpl > _impl; + + public: + + Shutdown(const CountedPtr< ExecutorImpl >& impl) + : _impl(impl) { } + + void run() { + _impl->cancel(); + } + + }; /* Shutdown */ + + } + + PoolExecutor::PoolExecutor(size_t n) + : _impl( new ExecutorImpl() ), _shutdown( new Shutdown(_impl) ) { + + size(n); + + // Request cancelation when main() exits + ThreadQueue::instance()->insertShutdownTask(_shutdown); + + } + + PoolExecutor::~PoolExecutor() { + + try { + + /** + * If the shutdown task for this executor has not already been + * selected to run, then run it locally + */ + if(ThreadQueue::instance()->removeShutdownTask(_shutdown)) + _shutdown->run(); + + } catch(...) { } + + } + + void PoolExecutor::interrupt() { + _impl->interrupt(); + } + + void PoolExecutor::size(size_t n) { + + if(n < 1) + throw InvalidOp_Exception(); + + for(size_t m = _impl->workers(n); m > 0; --m) + Thread t(new Worker(_impl)); + + } + + size_t PoolExecutor::size() { + return _impl->workers(); + } + + + void PoolExecutor::execute(const Task& task) { + + // Enqueue the task, the Queue will reject it with a + // Cancelation_Exception if the Executor has been canceled + _impl->execute(task); + + } + + void PoolExecutor::cancel() { + _impl->cancel(); + } + + bool PoolExecutor::isCanceled() { + return _impl->isCanceled(); + } + + void PoolExecutor::wait() { + _impl->wait(0); + } + + bool PoolExecutor::wait(unsigned long timeout) { + return _impl->wait(timeout == 0 ? 1 : timeout); + } + +} diff --git a/dep/src/zthread/PriorityCondition.cxx b/dep/src/zthread/PriorityCondition.cxx new file mode 100644 index 00000000000..c43953ff73b --- /dev/null +++ b/dep/src/zthread/PriorityCondition.cxx @@ -0,0 +1,80 @@ +/* + * 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. + * + */ + +#include "zthread/PriorityCondition.h" +#include "ConditionImpl.h" + +namespace ZThread { + + class PriorityConditionImpl : public ConditionImpl<priority_list> { + public: + PriorityConditionImpl(Lockable& l) : ConditionImpl<priority_list>(l) {} + + }; + + PriorityCondition::PriorityCondition(Lockable& lock) { + + _impl = new PriorityConditionImpl(lock); + + } + + + PriorityCondition::~PriorityCondition() { + + if(_impl != 0) + delete _impl; + + } + + + + void PriorityCondition::wait() { + + _impl->wait(); + + } + + + + bool PriorityCondition::wait(unsigned long ms) { + + return _impl->wait(ms); + + } + + + + void PriorityCondition::signal() { + + _impl->signal(); + + } + + + void PriorityCondition::broadcast() { + + _impl->broadcast(); + + } + +} // namespace ZThread + diff --git a/dep/src/zthread/PriorityInheritanceMutex.cxx b/dep/src/zthread/PriorityInheritanceMutex.cxx new file mode 100644 index 00000000000..108e4a74370 --- /dev/null +++ b/dep/src/zthread/PriorityInheritanceMutex.cxx @@ -0,0 +1,109 @@ +/* + * 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. + * + */ + +#include "zthread/PriorityInheritanceMutex.h" +#include "MutexImpl.h" +#include "ThreadOps.h" + + +namespace ZThread { + + class InheritPriorityBehavior : public NullBehavior { + + ThreadImpl* owner; + Priority p; + + protected: + + // Temporarily raise the effective priority of the owner + inline void waiterArrived(ThreadImpl* impl) { + + Priority q = impl->getPriority(); + if((int)q > (int)p) { + + ThreadOps::setPriority(impl, p); + p = q; + + } + + } + + + // Note the owners priority + inline void ownerAcquired(ThreadImpl* impl) { + + p = impl->getPriority(); + owner = impl; + + } + + // Restore its original priority + inline void ownerReleased(ThreadImpl* impl) { + + if(p > owner->getPriority()) + ThreadOps::setPriority(impl, impl->getPriority()); + + } + + }; + + class PriorityInheritanceMutexImpl : + public MutexImpl<priority_list, InheritPriorityBehavior> { }; + + PriorityInheritanceMutex::PriorityInheritanceMutex() { + + _impl = new PriorityInheritanceMutexImpl(); + + } + + PriorityInheritanceMutex::~PriorityInheritanceMutex() { + + if(_impl != 0) + delete _impl; + + } + + // P + void PriorityInheritanceMutex::acquire() { + + _impl->acquire(); + + } + + + // P + bool PriorityInheritanceMutex::tryAcquire(unsigned long ms) { + + return _impl->tryAcquire(ms); + + } + + // V + void PriorityInheritanceMutex::release() { + + _impl->release(); + + } + + +} // namespace ZThread + diff --git a/dep/src/zthread/PriorityMutex.cxx b/dep/src/zthread/PriorityMutex.cxx new file mode 100644 index 00000000000..c25eaebc46c --- /dev/null +++ b/dep/src/zthread/PriorityMutex.cxx @@ -0,0 +1,69 @@ +/* + * 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. + * + */ + +#include "zthread/PriorityMutex.h" +#include "MutexImpl.h" +#include "ThreadOps.h" + + +namespace ZThread { + + class PriorityMutexImpl : public MutexImpl<priority_list, NullBehavior> { }; + + PriorityMutex::PriorityMutex() { + + _impl = new PriorityMutexImpl(); + + } + + PriorityMutex::~PriorityMutex() { + + if(_impl != 0) + delete _impl; + + } + + // P + void PriorityMutex::acquire() { + + _impl->acquire(); + + } + + + // P + bool PriorityMutex::tryAcquire(unsigned long ms) { + + return _impl->tryAcquire(ms); + + } + + // V + void PriorityMutex::release() { + + _impl->release(); + + } + + +} // namespace ZThread + diff --git a/dep/src/zthread/PrioritySemaphore.cxx b/dep/src/zthread/PrioritySemaphore.cxx new file mode 100644 index 00000000000..15138b5f426 --- /dev/null +++ b/dep/src/zthread/PrioritySemaphore.cxx @@ -0,0 +1,104 @@ +/* + * 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. + * + */ + +#include "Debug.h" +#include "zthread/PrioritySemaphore.h" +#include "SemaphoreImpl.h" + +namespace ZThread { + + class PrioritySemaphoreImpl : public SemaphoreImpl<priority_list> { + public: + + PrioritySemaphoreImpl(int count, unsigned int maxCount) + : SemaphoreImpl<priority_list>(count, maxCount, true) { } + + }; + + /** + * Create a new semaphore of a given size with a given count + * + * @param initialCount initial count to assign this semaphore + * @param maxCount maximum size of the semaphore count + */ + PrioritySemaphore::PrioritySemaphore(int count, unsigned int maxCount) { + + _impl = new PrioritySemaphoreImpl(count, maxCount); + + } + + PrioritySemaphore::~PrioritySemaphore() { + + if(_impl != 0) + delete _impl; + + } + + void PrioritySemaphore::wait() { + + _impl->acquire(); + + } + + + bool PrioritySemaphore::tryWait(unsigned long ms) { + + return _impl->tryAcquire(ms); + + } + + void PrioritySemaphore::post() { + + _impl->release(); + + } + + int PrioritySemaphore::count() { + + return _impl->count(); + + } + + /////////////////////////////////////////////////////////////////////////////// + // Locakable compatibility + // + + void PrioritySemaphore::acquire() { + + _impl->acquire(); + + } + + bool PrioritySemaphore::tryAcquire(unsigned long ms) { + + return _impl->tryAcquire(ms); + + + } + + void PrioritySemaphore::release() { + + _impl->release(); + + } + +} // namespace ZThread diff --git a/dep/src/zthread/RecursiveMutex.cxx b/dep/src/zthread/RecursiveMutex.cxx new file mode 100644 index 00000000000..57994f55b81 --- /dev/null +++ b/dep/src/zthread/RecursiveMutex.cxx @@ -0,0 +1,61 @@ +/* + * 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. + * + */ + +#include "zthread/RecursiveMutex.h" +#include "RecursiveMutexImpl.h" + +namespace ZThread { + + RecursiveMutex::RecursiveMutex() { + + _impl = new RecursiveMutexImpl(); + + } + + RecursiveMutex::~RecursiveMutex() { + + if(_impl != (RecursiveMutexImpl*)0 ) + delete _impl; + + } + + + void RecursiveMutex::acquire() { + + _impl->acquire(); + + } + + + bool RecursiveMutex::tryAcquire(unsigned long ms) { + + return _impl->tryAcquire(ms); + + } + + void RecursiveMutex::release() { + + _impl->release(); + + } + +} // namespace ZThread diff --git a/dep/src/zthread/RecursiveMutexImpl.cxx b/dep/src/zthread/RecursiveMutexImpl.cxx new file mode 100644 index 00000000000..41ca03547f8 --- /dev/null +++ b/dep/src/zthread/RecursiveMutexImpl.cxx @@ -0,0 +1,286 @@ +/* + * 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. + * + */ + +#include "Debug.h" + +#include "RecursiveMutexImpl.h" +#include "ThreadImpl.h" + +#include "zthread/Guard.h" + +#include <assert.h> +#include <errno.h> +#include <algorithm> + +namespace ZThread { + + /** + * Create a new RecursiveMutexImpl + * + * @exception Initialization_Exception thrown if resources could not be + * properly allocated + */ + RecursiveMutexImpl::RecursiveMutexImpl() + : _owner(0), _count(0) { + + } + + /** + * Destroy this RecursiveMutexImpl and release its resources + */ + RecursiveMutexImpl::~RecursiveMutexImpl() { + +#ifndef NDEBUG + + // It is an error to destroy a mutex that has not been released + if(_owner != 0) { + + ZTDEBUG("** You are destroying a mutex which was never released. **\n"); + assert(0); // Destroyed mutex while in use + + } + + if(!_waiters.empty()) { + + ZTDEBUG("** You are destroying a mutex which is blocking %d threads. **\n", _waiters.size()); + assert(0); // Destroyed mutex while in use + + } + +#endif + + } + + + void RecursiveMutexImpl::acquire() { + + // Get the monitor for the current thread + Monitor& m = ThreadImpl::current()->getMonitor(); + Monitor::STATE state; + + Guard<FastLock> g1(_lock); + + // If there is an entry count and the current thread is + // the owner, increment the count and continue. + if(_owner == &m) + _count++; + + else { + + // Acquire the lock if it is free and there are no waiting threads + if(_owner == 0 && _waiters.empty()) { + + assert(_count == 0); + + _owner = &m; + _count++; + + } else { // Otherwise, wait() + + _waiters.push_back(&m); + + m.acquire(); + + { + + Guard<FastLock, UnlockedScope> g2(g1); + state = m.wait(); + + } + + m.release(); + + // Remove from waiter list, regarless of weather release() is called or + // not. The monitor is sticky, so its possible a state 'stuck' from a + // previous operation and will leave the wait() w/o release() having + // been called. + List::iterator i = std::find(_waiters.begin(), _waiters.end(), &m); + if(i != _waiters.end()) + _waiters.erase(i); + + // If awoke due to a notify(), take ownership. + switch(state) { + case Monitor::SIGNALED: + + assert(_owner == 0); + assert(_count == 0); + + _owner = &m; + _count++; + + break; + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + default: + throw Synchronization_Exception(); + } + + } + + } + + } + + bool RecursiveMutexImpl::tryAcquire(unsigned long timeout) { + + // Get the monitor for the current thread + Monitor& m = ThreadImpl::current()->getMonitor(); + + Guard<FastLock> g1(_lock); + + // If there is an entry count and the current thread is + // the owner, increment the count and continue. + if(_owner == &m) + _count++; + + else { + + // Acquire the lock if it is free and there are no waiting threads + if(_owner == 0 && _waiters.empty()) { + + assert(_count == 0); + + _owner = &m; + _count++; + + } else { // Otherwise, wait() + + _waiters.push_back(&m); + + Monitor::STATE state = Monitor::TIMEDOUT; + + // Don't bother waiting if the timeout is 0 + if(timeout) { + + m.acquire(); + + { + + Guard<FastLock, UnlockedScope> g2(g1); + state = m.wait(timeout); + + } + + m.release(); + + } + + // Remove from waiter list, regarless of weather release() is called or + // not. The monitor is sticky, so its possible a state 'stuck' from a + // previous operation and will leave the wait() w/o release() having + // been called. + List::iterator i = std::find(_waiters.begin(), _waiters.end(), &m); + if(i != _waiters.end()) + _waiters.erase(i); + + // If awoke due to a notify(), take ownership. + switch(state) { + case Monitor::SIGNALED: + + assert(_count == 0); + assert(_owner == 0); + + _owner = &m; + _count++; + + break; + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + case Monitor::TIMEDOUT: + return false; + + default: + throw Synchronization_Exception(); + } + + } + + } + + return true; + + } + + void RecursiveMutexImpl::release() { + + // Get the monitor for the current thread + Monitor& m = ThreadImpl::current()->getMonitor(); + + Guard<FastLock> g1(_lock); + + // Make sure the operation is valid + if(!(_owner == &m)) + throw InvalidOp_Exception(); + + // Update the count, if it has reached 0, wake another waiter. + if(--_count == 0) { + + _owner = 0; + + // Try to find a waiter with a backoff & retry scheme + for(;;) { + + // Go through the list, attempt to notify() a waiter. + for(List::iterator i = _waiters.begin(); i != _waiters.end();) { + + // Try the monitor lock, if it cant be locked skip to the next waiter + Monitor* n = *i; + if(n->tryAcquire()) { + + // If notify() is not sucessful, it is because the wait() has already + // been ended (killed/interrupted/notify'd) + bool woke = n->notify(); + n->release(); + + // Once notify() succeeds, return + if(woke) + return; + + } else ++i; + + } + + if(_waiters.empty()) + return; + + { // Backoff and try again + + Guard<FastLock, UnlockedScope> g2(g1); + ThreadImpl::yield(); + + } + + } + + } + + } + +} // namespace ZThread + + + + diff --git a/dep/src/zthread/RecursiveMutexImpl.h b/dep/src/zthread/RecursiveMutexImpl.h new file mode 100644 index 00000000000..9e1ae050c5b --- /dev/null +++ b/dep/src/zthread/RecursiveMutexImpl.h @@ -0,0 +1,78 @@ +/* + * 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 __ZTRECURSIVEMUTEXIMPL_H__ +#define __ZTRECURSIVEMUTEXIMPL_H__ + +#include "zthread/Exceptions.h" + +#include "FastLock.h" + +#include <vector> + +namespace ZThread { + + class Monitor; + + /** + * @class RecursiveMutexImpl + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T19:58:26-0400> + * @version 2.1.6 + * + * This synchronization object provides serialized access + * through an acquire/release protocol. + */ + class ZTHREAD_API RecursiveMutexImpl { + + typedef std::vector<Monitor*> List; + + //! List of Events that are waiting for notification + List _waiters; + + //! Serialize access to this Mutex + FastLock _lock; + + //! Current owning Event object + Monitor* _owner; + + //! Entry count + size_t _count; + + public: + + RecursiveMutexImpl(); + + virtual ~RecursiveMutexImpl(); + + void acquire(); + + bool tryAcquire(unsigned long); + + void release(); + + }; /* RecursiveMutexImpl */ + + +}; + +#endif diff --git a/dep/src/zthread/Scheduling.h b/dep/src/zthread/Scheduling.h new file mode 100644 index 00000000000..b12f7fff0b6 --- /dev/null +++ b/dep/src/zthread/Scheduling.h @@ -0,0 +1,96 @@ +/* + * 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 __ZTSCHEDULING_H__ +#define __ZTSCHEDULING_H__ + +#include "ThreadImpl.h" + +#include <algorithm> +#include <functional> +#include <deque> +#include <utility> + +namespace ZThread { + + /** + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T20:01:18-0400> + * @version 2.2.0 + * @class fifo_list + */ + class fifo_list : public std::deque<ThreadImpl*> { + public: + + void insert(const value_type& val) { push_back(val); } + + }; + + /** + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T20:01:18-0400> + * @version 2.2.0 + * @struct priority_order + */ + struct priority_order : public std::binary_function<ThreadImpl*, ThreadImpl*, bool> { + + std::less<const ThreadImpl*> id; + + bool operator()(const ThreadImpl* t0, const ThreadImpl* t1) const { + + if(t0->getPriority() > t1->getPriority()) + return true; + + else if (t0->getPriority() < t1->getPriority()) + return false; + + return id(t0, t1); + + } + + }; + + + /** + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T20:01:18-0400> + * @version 2.2.0 + * @class priority_list + */ + class priority_list : public std::deque<ThreadImpl*> { + + priority_order comp; + + public: + + void insert(const value_type& val) { + + push_back(val); + std::sort(begin(), end(), comp); + + } + + }; + +} // namespace ZThread + +#endif // __ZTSCHEDULING_H__ diff --git a/dep/src/zthread/Semaphore.cxx b/dep/src/zthread/Semaphore.cxx new file mode 100644 index 00000000000..b9fb8d0f613 --- /dev/null +++ b/dep/src/zthread/Semaphore.cxx @@ -0,0 +1,101 @@ +/* + * 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. + * + */ + +#include "zthread/Semaphore.h" +#include "SemaphoreImpl.h" + +namespace ZThread { + + /** + * Create a new semaphore of a given size with a given count + * + * @param initialCount initial count to assign this semaphore + * @param maxCount maximum size of the semaphore count + */ + Semaphore::Semaphore(int count, unsigned int maxCount) { + + _impl = new FifoSemaphoreImpl(count, maxCount, true); + + } + + Semaphore::~Semaphore() { + + if(_impl != 0) + delete _impl; + + } + + void Semaphore::wait() { + + _impl->acquire(); + + } + + + bool Semaphore::tryWait(unsigned long ms) { + + return _impl->tryAcquire(ms); + + } + + void Semaphore::post() { + + _impl->release(); + + } + + int Semaphore::count() { + + return _impl->count(); + + } + + /////////////////////////////////////////////////////////////////////////////// + // Locakable compatibility + // + + void Semaphore::acquire() { + + _impl->acquire(); + + } + + bool Semaphore::tryAcquire(unsigned long ms) { + + return _impl->tryAcquire(ms); + + + } + + void Semaphore::release() { + + _impl->release(); + + } + +} // namespace ZThread + + + + + + diff --git a/dep/src/zthread/SemaphoreImpl.h b/dep/src/zthread/SemaphoreImpl.h new file mode 100644 index 00000000000..086c4333fd0 --- /dev/null +++ b/dep/src/zthread/SemaphoreImpl.h @@ -0,0 +1,348 @@ +/* + * 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 __ZTSEMAPHOREIMPL_H__ +#define __ZTSEMAPHOREIMPL_H__ + +#include "zthread/Guard.h" + +#include "Debug.h" +#include "FastLock.h" +#include "Scheduling.h" + +#include <assert.h> + +namespace ZThread { + + class Monitor; + + /** + * @class SemaphoreImpl + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T20:03:20-0400> + * @version 2.2.11 + * + * The SemaphoreImpl template allows how waiter lists are sorted + * to be parameteized + */ + template <typename List> + class SemaphoreImpl { + + //! List of waiting events + List _waiters; + + //! Serialize access to this object + FastLock _lock; + + //! Current count + volatile int _count; + + //! Maximum count if any + volatile int _maxCount; + + //! Flag for bounded or unbounded count + volatile bool _checked; + + //! Entry count + volatile int _entryCount; + + public: + + + /** + * Create a new SemaphoreImpl. Initialzes one pthreads mutex for + * internal use. + * + * @exception Initialization_Exception thrown if resources could not be + * properly allocated + */ + SemaphoreImpl(int count, unsigned int maxCount, bool checked) + : _count(count), _maxCount(maxCount), _checked(checked), _entryCount(0) { } + + + ~SemaphoreImpl(); + + void acquire(); + + void release(); + + bool tryAcquire(unsigned long timeout); + + int count(); + + }; + + + /** + * Destroy this SemaphoreImpl and release its resources. + */ + template <typename List> + SemaphoreImpl<List>::~SemaphoreImpl() { + +#ifndef NDEBUG + + if(!_waiters.empty()) { + + ZTDEBUG("** You are destroying a semaphore which is blocking %d threads. **\n", _waiters.size()); + assert(0); // Destroyed semaphore while in use + + } + +#endif + + } + + + /** + * Get the count for the Semaphore + * + * @return int + */ + template <typename List> + int SemaphoreImpl<List>::count() { + + Guard<FastLock> g(_lock); + return _count; + + } + + /** + * Decrement the count, blocking when that count becomes 0 or less. + * + * @exception Interrupted_Exception thrown when the caller status is interrupted + * @exception Synchronization_Exception thrown if there is some other error. + */ + template <typename List> + void SemaphoreImpl<List>::acquire() { + + // Get the monitor for the current thread + ThreadImpl* self = ThreadImpl::current(); + Monitor& m = self->getMonitor(); + + Monitor::STATE state; + + Guard<FastLock> g1(_lock); + + // Update the count without waiting if possible. + if(_count > 0 && _entryCount == 0) + _count--; + + // Otherwise, wait() for the lock by placing the waiter in the list + else { + + ++_entryCount; + _waiters.insert(self); + + m.acquire(); + + { + + Guard<FastLock, UnlockedScope> g2(g1); + state = m.wait(); + + } + + m.release(); + + // Remove from waiter list, regarless of weather release() is called or + // not. The monitor is sticky, so its possible a state 'stuck' from a + // previous operation and will leave the wait() w/o release() having + // been called. + typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self); + if(i != _waiters.end()) + _waiters.erase(i); + + --_entryCount; + + switch(state) { + // If awoke due to a notify(), update the count + case Monitor::SIGNALED: + + _count--; + break; + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + default: + throw Synchronization_Exception(); + } + + } + + } + + /** + * Decrement the count, blocking when it that count is 0 or less. If the timeout + * expires before the count is raise above 0, the thread will stop blocking + * and return. + * + * @exception Interrupted_Exception thrown when the caller status is interrupted + * @exception Synchronization_Exception thrown if there is some other error. + */ + template <typename List> + bool SemaphoreImpl<List>::tryAcquire(unsigned long timeout) { + + // Get the monitor for the current thread + ThreadImpl* self = ThreadImpl::current(); + Monitor& m = self->getMonitor(); + + Guard<FastLock> g1(_lock); + + // Update the count without waiting if possible. + if(_count > 0 && _entryCount == 0) + _count--; + + // Otherwise, wait() for the lock by placing the waiter in the list + else { + + ++_entryCount; + _waiters.push_back(self); + + Monitor::STATE state = Monitor::TIMEDOUT; + + // Don't bother waiting if the timeout is 0 + if(timeout) { + + m.acquire(); + + { + + Guard<FastLock, UnlockedScope> g2(g1); + state = m.wait(timeout); + + } + + m.release(); + + } + + // Remove from waiter list, regarless of weather release() is called or + // not. The monitor is sticky, so its possible a state 'stuck' from a + // previous operation and will leave the wait() w/o release() having + // been called. + typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self); + if(i != _waiters.end()) + _waiters.erase(i); + + --_entryCount; + + switch(state) { + // If awoke due to a notify(), update the count + case Monitor::SIGNALED: + + _count--; + break; + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + case Monitor::TIMEDOUT: + return false; + + default: + throw Synchronization_Exception(); + } + + } + + return true; + + } + + /** + * Increment the count and release a waiter if there are any. If the semaphore + * is checked, then an exception will be raised if the maximum count is about to + * be exceeded. + * + * @exception InvalidOp_Exception thrown if the maximum count is exceeded while + * the checked flag is set. + */ + template <typename List> + void SemaphoreImpl<List>::release() { + + Guard<FastLock> g1(_lock); + + // Make sure the operation is valid + if(_checked && _count == _maxCount) + throw InvalidOp_Exception(); + + // Increment the count + _count++; + + // Try to find a waiter with a backoff & retry scheme + for(;;) { + + // Go through the list, attempt to notify() a waiter. + for(typename List::iterator i = _waiters.begin(); i != _waiters.end();) { + + // Try the monitor lock, if it cant be locked skip to the next waiter + ThreadImpl* impl = *i; + Monitor& m = impl->getMonitor(); + + if(m.tryAcquire()) { + + // Notify the monitor & remove from the waiter list so time isn't + // wasted checking it again. + i = _waiters.erase(i); + + // If notify() is not sucessful, it is because the wait() has already + // been ended (killed/interrupted/notify'd) + bool woke = m.notify(); + + m.release(); + + // Once notify() succeeds, return + if(woke) + return; + + } else ++i; + + } + + if(_waiters.empty()) + return; + + { // Backoff and try again + + Guard<FastLock, UnlockedScope> g2(g1); + ThreadImpl::yield(); + + } + + } + + } + + class FifoSemaphoreImpl : public SemaphoreImpl<fifo_list> { + public: + + FifoSemaphoreImpl(int count, unsigned int maxCount, bool checked) + /* throw(Synchronization_Exception) */ + : SemaphoreImpl<fifo_list>(count, maxCount, checked) { } + + }; + + +} // namespace ZThread + +#endif // __ZTSEMAPHOREIMPL_H__ diff --git a/dep/src/zthread/State.h b/dep/src/zthread/State.h new file mode 100644 index 00000000000..85279f4bde8 --- /dev/null +++ b/dep/src/zthread/State.h @@ -0,0 +1,151 @@ +/* + * 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 __ZTSTATE_H__ +#define __ZTSTATE_H__ + +namespace ZThread { + +/** + * @class State + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T20:04:01-0400> + * @version 2.2.1 + * + * Class to encapsulate the current state of the threads life-cycle. + */ +class State { + public: + + //! Various states + typedef enum { REFERENCE, IDLE, RUNNING, JOINED } STATE; + + /** + * Create State with the given flag set. + */ + State(STATE initialState) : _state(initialState) {} + + /** + * Test for the IDLE state. No task has yet run. + */ + bool isIdle() const { + return _state == IDLE; + } + + /** + * Test for the JOINED state. A task has completed and + * the thread is join()ed. + * + * @return bool + */ + bool isJoined() const { + return _state == JOINED; + } + + /** + * Test for the RUNNING state. A task is in progress. + * + * @return bool + */ + bool isRunning() const { + return _state == RUNNING; + } + + /** + * Test for the REFERENCE state. A task is in progress but not + * under control of this library. + * + * @return bool + */ + bool isReference() const { + return _state == REFERENCE; + } + + /** + * Transition to the IDLE state. + * + * @return bool true if successful + */ + bool setIdle() { + + if(_state != RUNNING) + return false; + + _state = IDLE; + return true; + + } + + /** + * Transition to the RUNNING state. + * + * @return bool true if successful + */ + bool setRunning() { + + if(_state != IDLE) + return false; + + _state = RUNNING; + return true; + + } + + /** + * Transition to the REFERENCE state. + * + * @return bool true if successful + */ + bool setReference() { + + if(_state != IDLE) + return false; + + _state = REFERENCE; + return true; + + } + + + /** + * Transition to the JOINED state. + * + * @return bool true if successful + */ + bool setJoined() { + + _state = JOINED; + return true; + + } + + private: + + //! Current state + STATE _state; + +}; + + +}; + +#endif diff --git a/dep/src/zthread/Status.h b/dep/src/zthread/Status.h new file mode 100644 index 00000000000..4735e352861 --- /dev/null +++ b/dep/src/zthread/Status.h @@ -0,0 +1,179 @@ +/* + * 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 __ZTBLOCKINGSTATE_H__ +#define __ZTBLOCKINGSTATE_H__ + +#include <assert.h> + +namespace ZThread { + + /** + * @class Status + * @version 2.3.0 + * + * A Status is associated with each Thread's Monitor. Monitors rely on a + * Status object for providing information that will affect a blocking operations. + */ + class Status { + public: + //! Aggregate of pending status changes + volatile unsigned short _pending; + + //! Interest mask + volatile unsigned short _mask; + + public: + + //! State for the monitor + typedef enum { + + // Default + INVALID = 0x00, + + // Valid states + SIGNALED = 0x01, + INTERRUPTED = 0x02, + TIMEDOUT = 0x04, + CANCELED = 0x08, + + // Mask + ANYTHING = (~INVALID & ~CANCELED) + + } STATE; + + Status() : _pending((unsigned short)INVALID), _mask((unsigned short)ANYTHING) { } + + /** + * Set the mask for the STATE's that next() will report. + * STATE's not covered by the interest mask can still be + * set, they just aren't reported until the mask is changed + * to cover that STATE. + * + * @param STATE + * @pre accessed ONLY by the owning thread. + */ + void interest(STATE mask) { + _mask = static_cast<unsigned short>(mask); + } + + bool masked(STATE mask) { + return (_mask & static_cast<unsigned short>(mask)) == 0; + } + + /** + * Return true if next() will return a STATE covered + * by the current interest mask and by the mask given + * to this function. + * + * @param unsigned short + * @pre accessed ONLY by the owning thread. + */ + bool pending(unsigned short mask) { + + assert(mask != INVALID); + return ((_pending & _mask) & mask) != INVALID; + + } + + /** + * Check the state without the interest mask. + * + * @param state + * @return true if the flag is set + * @pre access must be serial + */ + bool examine(STATE state) { + return (_pending & static_cast<unsigned short>(state)) != INVALID; + } + + /** + * Add the flags to the current state. + * + * @param interest - the flags to add to the current state. + * @pre access must be serial + */ + void push(STATE interest) { + _pending |= interest; + } + + /** + * Clear the flags from the current state + * + * @param interest - the flags to clear from the current state. + * @pre access must be serial + */ + void clear(STATE interest) { + + assert(interest != INVALID); + assert(interest != ANYTHING); + assert(interest != CANCELED); + + _pending &= ~interest; + + } + + /** + * Get the next state from set that has accumulated. The order STATES are + * reported in is SIGNALED, TIMEOUT, or INTERRUPTED. Setting the + * intrest mask allows certain state to be selectively ignored for + * a time - but not lost. The states will become visible again as soon + * as the interest mask is changed appropriately. The interest mask is + * generally used to create uninterruptable waits (waiting for threads + * to start, reacquiring a conditions predicate lock, etc) + * + * @return STATE + * @pre access must be serial + */ + STATE next() { + + STATE state = INVALID; + + if(((_pending & _mask) & SIGNALED) != 0) { + + // Absorb the timeout if it happens when a signal + // is available at the same time + _pending &= ~(SIGNALED|TIMEDOUT); + state = SIGNALED; + + } else if(((_pending & _mask) & TIMEDOUT) != 0) { + + _pending &= ~TIMEDOUT; + state = TIMEDOUT; + + } else if(((_pending & _mask) & INTERRUPTED) != 0) { + + _pending &= ~INTERRUPTED; + state = INTERRUPTED; + + } + + assert(state != INVALID); + return state; + + } + + }; + +}; // namespace ZThread + +#endif diff --git a/dep/src/zthread/SynchronousExecutor.cxx b/dep/src/zthread/SynchronousExecutor.cxx new file mode 100644 index 00000000000..0dc75b5f676 --- /dev/null +++ b/dep/src/zthread/SynchronousExecutor.cxx @@ -0,0 +1,90 @@ +/* + * 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. + * + */ + +#include "zthread/SynchronousExecutor.h" + +namespace ZThread { + + SynchronousExecutor::SynchronousExecutor() + : _canceled(false) {} + + SynchronousExecutor::~SynchronousExecutor() { + } + + void SynchronousExecutor::cancel() { + + Guard<Mutex> g(_lock); + _canceled = true; + + } + + bool SynchronousExecutor::isCanceled() { + + Guard<Mutex> g(_lock); + return _canceled; + + } + + void SynchronousExecutor::interrupt() { + } + + void SynchronousExecutor::execute(const Task& task) { + + // Canceled Executors will not accept new tasks, quick + // check to avoid excessive locking in the canceled state + if(_canceled) + throw Cancellation_Exception(); + + Guard<Mutex> g(_lock); + + if(_canceled) // Double check + throw Cancellation_Exception(); + + // Run the task. + Task(task)->run(); + + } + + void SynchronousExecutor::wait() { + + if(Thread::interrupted()) + throw Interrupted_Exception(); + + Guard<Mutex> g(_lock); + + } + + /** + * @see Executor::wait(unsigned long) + */ + bool SynchronousExecutor::wait(unsigned long) { + + if(Thread::interrupted()) + throw Interrupted_Exception(); + + Guard<Mutex> g(_lock); + return true; + + } + + +} diff --git a/dep/src/zthread/TSS.h b/dep/src/zthread/TSS.h new file mode 100644 index 00000000000..ed29230ec57 --- /dev/null +++ b/dep/src/zthread/TSS.h @@ -0,0 +1,52 @@ +/* + * 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 __ZTTSSSELECT_H__ +#define __ZTTSSSELECT_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// Select the correct TSS implementation based on +// what the compilation environment has defined + +#if defined(ZT_POSIX) + +#include "posix/TSS.h" + +#elif defined(ZT_WIN32) || defined(ZT_WIN9X) + +#include "win32/TSS.h" + +#elif defined(ZT_MACOS) + +#include "macos/TSS.h" + +#endif + + +#ifndef __ZTTSS_H__ +#error "No TSS implementation could be selected" +#endif + +#endif // __ZTTSSSELECT_H__ diff --git a/dep/src/zthread/Thread.cxx b/dep/src/zthread/Thread.cxx new file mode 100644 index 00000000000..25cde79969c --- /dev/null +++ b/dep/src/zthread/Thread.cxx @@ -0,0 +1,127 @@ +/* + * 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. + * + */ + +#include "zthread/Runnable.h" +#include "zthread/Thread.h" +#include "ThreadImpl.h" + +namespace ZThread { + + + Thread::Thread() + : _impl( ThreadImpl::current() ) { + + // ThreadImpl's start out life with a reference count + // of one, and the they are added to the ThreadQueue. + _impl->addReference(); + + } + + Thread::Thread(const Task& task, bool autoCancel) + : _impl( new ThreadImpl(task, autoCancel) ) { + + _impl->addReference(); + + } + + bool Thread::operator==(const Thread& t) const { + return (t._impl == _impl); + } + + Thread::~Thread() { + + _impl->delReference(); + + } + + void Thread::wait() { + _impl->join(0); + } + + bool Thread::wait(unsigned long timeout) { + + return _impl->join(timeout == 0 ? 1 : timeout); + + } + + bool Thread::interrupted() { + + return ThreadImpl::current()->isInterrupted(); + + } + + + bool Thread::canceled() { + + return ThreadImpl::current()->isCanceled(); + + } + + void Thread::setPriority(Priority n) { + + _impl->setPriority(n); + + } + + + Priority Thread::getPriority() { + + return _impl->getPriority(); + + } + + bool Thread::interrupt() { + + return _impl->interrupt(); + + } + + void Thread::cancel() { + + if(ThreadImpl::current() == _impl) + throw InvalidOp_Exception(); + + _impl->cancel(); + + } + + bool Thread::isCanceled() { + + return _impl->isCanceled(); + + } + + + void Thread::sleep(unsigned long ms) { + + ThreadImpl::sleep(ms); + + } + + + void Thread::yield() { + + ThreadImpl::yield(); + + } + +} // namespace ZThread diff --git a/dep/src/zthread/ThreadImpl.cxx b/dep/src/zthread/ThreadImpl.cxx new file mode 100644 index 00000000000..c7c22883b5e --- /dev/null +++ b/dep/src/zthread/ThreadImpl.cxx @@ -0,0 +1,470 @@ +/* + * 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. + * + */ + +#include "Debug.h" + +#include "zthread/Runnable.h" +#include "ThreadImpl.h" +#include "ThreadQueue.h" +#include "DeferredInterruptionScope.h" + +#include <assert.h> + +namespace ZThread { + + TSS<ThreadImpl*> ThreadImpl::_threadMap; + + namespace { + + class Launcher : public Runnable { + + ThreadImpl* x; + ThreadImpl* y; + Task z; + + public: + + Launcher(ThreadImpl* a, ThreadImpl* b, const Task& c) : x(a), y(b), z(c) {} + + void run() { + y->dispatch(x,y,z); + } + + }; + + } + + ThreadImpl::ThreadImpl() + : _state(State::REFERENCE), _priority(Medium), _autoCancel(false) { + + ZTDEBUG("Reference thread created.\n"); + + } + + ThreadImpl::ThreadImpl(const Task& task, bool autoCancel) + : _state(State::IDLE), _priority(Medium), _autoCancel(autoCancel) { + + ZTDEBUG("User thread created.\n"); + + start(task); + + } + + + ThreadImpl::~ThreadImpl() { + + _tls.clear(); + + if(isActive()) { + + ZTDEBUG("You are destroying an executing thread!\n"); + abort(); + + } + + ZTDEBUG("Thread destroyed.\n"); + + } + + Monitor& ThreadImpl::getMonitor() { + return _monitor; + } + + void ThreadImpl::cancel(bool autoCancel) { + if(!autoCancel || _autoCancel) + _monitor.cancel(); + } + + bool ThreadImpl::interrupt() { + return _monitor.interrupt(); + } + + bool ThreadImpl::isInterrupted() { + return _monitor.isInterrupted(); + } + + bool ThreadImpl::isCanceled() { + return _monitor.isCanceled(); + } + + Priority ThreadImpl::getPriority() const { + return _priority; + } + + + + bool ThreadImpl::isReference() { + return _state.isReference(); + } + + /** + * Join the thread, blocking the caller until it is interrupted or until + * the thread represented by this object exits. + * + * Reference threads are not under the control of ZThreads and cannot be + * joined. + */ + bool ThreadImpl::join(unsigned long timeout) { + + // Serial access to this ThreadImpl's state + Guard<Monitor> g1(_monitor); + + // Make sure a thread is not trying to join() itself. + if(ThreadOps::isCurrent(this)) + throw Deadlock_Exception("Cannot join self."); + + // Reference threads can't be joined. + if(_state.isReference()) + throw InvalidOp_Exception("Can not join this thread."); + + /* + + TODO: Insert cyclic join check. + + */ + + // If the task has not completed yet, wait for completion + if(!_state.isJoined()) { + + // Add the current thread to the joiner list + ThreadImpl* impl = current(); + _joiners.push_back(impl); + + Monitor::STATE result; + + { // Release this ThreadImpl's lock while the joiner sleeps + + _monitor.release(); + Guard<Monitor> g3(impl->getMonitor()); + + result = impl->_monitor.wait(timeout); + + _monitor.acquire(); + + } + + // Update the joiner list + List::iterator i = std::find(_joiners.begin(), _joiners.end(), impl); + if(i != _joiners.end()) + _joiners.erase(i); + + + switch(result) { + + case Monitor::TIMEDOUT: + return false; + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + default: + break; + + } + + } + + return true; + + } + + + /** + * Translate the priority into a pthread value, and update the thread priority. + * + * This is not available on all platforms, and probably works differently + * the platforms that do support it. Pthreads does not have very portable + * priority support as far I am aware. + * + * If SCHED_OTHER is not supported priority values are still set but + * dont not actually in affect anything. + * + * @param prio PRIORITY value + * + * @exception Killed_Exception thrown by KILLED threads. + * @exception InvalidOp_Exception thrown by IDLE, JOINING or JOINED threads. + */ + void ThreadImpl::setPriority(Priority p) { + + Guard<Monitor> g(_monitor); + + // Only set the native priority when the thread is running + if(_state.isRunning()) + ThreadOps::setPriority(this, p); + + _priority = p; + + } + + + /** + * Test the state Monitor of this thread to determine if the thread + * is an active thread created by zthreads. + * + * @return bool indicating the activity of the thread. + */ + bool ThreadImpl::isActive() { + + Guard<Monitor> g(_monitor); + return _state.isRunning(); + + } + + + /** + * Get a reference to an implmenetation that maps to the current thread. + * Accomplished by checking the TLS map. This will always return a valid + * ThreadImpl instance. + * + * @return ThreadImpl* current implementation that maps to the + * executing thread. + */ + ThreadImpl* ThreadImpl::current() { + + // Get the ThreadImpl previously mapped onto the executing thread. + ThreadImpl* impl = _threadMap.get(); + + // Create a reference thread for any threads that have been 'discovered' + // because they are not created by ZThreads. + if(impl == 0) { + + // Create a ThreadImpl to represent this thread. + impl = new ThreadImpl(); + impl->_state.setReference(); + + ThreadOps::activate(impl); + + // Map a reference thread and insert it into the queue + _threadMap.set(impl); + + ThreadQueue::instance()->insertReferenceThread(impl); + + } + + assert(impl != 0); + return impl; + + } + + /** + * Make current thread sleep for the given number of milliseconds. + * This sleep can be interrupt()ed. + * + * @param ms timeout for the sleep. + * + * @post the calling thread is blocked by waiting on the internal condition + * variable. This can be signaled in the monitor of an interrupt + */ + void ThreadImpl::sleep(unsigned long ms) { + + // Make sleep()ing for 0 milliseconds equivalent to a yield. + if(ms == 0) { + + yield(); + return; + + } + + // Get the monitor for the current thread + Monitor& monitor = current()->getMonitor(); + + // Acquire that threads Monitor with a Guard + Guard<Monitor> g(monitor); + + for(;;) { + + switch(monitor.wait(ms)) { + + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + + default: + return; + + } + + } + + } + + + /** + * Yield the current timeslice to another thread. + * If sched_yield() is available it is used. + * Otherwise, the state Monitor for this thread is used to simiulate a + * yield by blocking for 1 millisecond, which should give the + * scheduler a chance to schedule another thread. + */ + void ThreadImpl::yield() { + + // Try to yield with the native operation. If it fails, then + // simulate with a short wait() on the monitor. + if(!ThreadOps::yield()) { + + // Get the monitor for the current thread + Monitor& monitor = current()->getMonitor(); + + // Attempt a wait(). + Guard<Monitor> g(monitor); + monitor.wait(1); + + } + + } + + void ThreadImpl::start(const Task& task) { + + Guard<Monitor> g1(_monitor); + + // A Thread must be idle in order to be eligable to run a task. + if(!_state.isIdle()) + throw InvalidOp_Exception("Thread is not idle."); + + _state.setRunning(); + + // Spawn a new thread, blocking the parent (current) thread until + // the child starts. + + ThreadImpl* parent = current(); + Launcher launch(parent, this, task); + + // Attempt to start the child thread + Guard<Monitor> g2(parent->_monitor); + + if(!spawn(&launch)) { + + // Return to the idle state & report the error if it doesn't work out. + _state.setIdle(); + throw Synchronization_Exception(); + + + } + + // Wait, uninterruptably, for the child's signal. The parent thread + // still can be interrupted and killed; it just won't take effect + // until the child has started. + + Guard<Monitor, DeferredInterruptionScope> g3(parent->_monitor); + + if(parent->_monitor.wait() != Monitor::SIGNALED) { + assert(0); + } + + + } + + + void ThreadImpl::dispatch(ThreadImpl* parent, ThreadImpl* impl, Task task) { + + // Map the implementation object onto the running thread. + _threadMap.set(impl); + + // Update the reference count on a ThreadImpl before the 'Thread' + // that owns it can go out of scope (by signaling the parent) + impl->addReference(); + + // Update the priority of the thread + if(parent->_state.isReference()) + ThreadOps::setPriority(impl, + parent->_state.isReference() ? impl->_priority : parent->_priority); + + // Inherit ThreadLocal values from the parent + typedef ThreadLocalMap::const_iterator It; + + for(It i = parent->getThreadLocalMap().begin(); i != parent->getThreadLocalMap().end(); ++i) + if( (i->second)->isInheritable() ) + impl->getThreadLocalMap()[ i->first ] = (i->second)->clone(); + + // Insert a user-thread mapping + ThreadQueue::instance()->insertUserThread(impl); + // Wake the parent once the thread is setup + parent->_monitor.notify(); + + ZTDEBUG("Thread starting...\n"); + + // not catch exceptions, let program terminate + //try { + + task->run(); + + //} catch(...) { + + // Result of running a task that threw an exception. + // ZTDEBUG("The task has thrown an unhandled exception\n"); + //assert(0); // UQ1: Go to debugger... + + //} + + ZTDEBUG("Thread joining...\n"); + + { // Update the state of the thread + + Guard<Monitor> g(impl->_monitor); + impl->_state.setJoined(); + + // Wake the joiners that will be easy to join first + for(List::iterator i = impl->_joiners.begin(); i != impl->_joiners.end();) { + + ThreadImpl* joiner = *i; + Monitor& m = joiner->getMonitor(); + + if(m.tryAcquire()) { + + m.notify(); + m.release(); + + i = impl->_joiners.erase(i); + + } else + ++i; + + } + + // Wake the joiners that might take a while next + for(List::iterator i = impl->_joiners.begin(); i != impl->_joiners.end(); ++i) { + + ThreadImpl* joiner = *i; + Monitor& m = joiner->getMonitor(); + + m.acquire(); + m.notify(); + m.release(); + + } + + } + + ZTDEBUG("Thread exiting...\n"); + + // Insert a pending-thread mapping, allowing the resources to be reclaimed + ThreadQueue::instance()->insertPendingThread(impl); + + // Cleanup ThreadLocal values + impl->getThreadLocalMap().clear(); + + // Update the reference count allowing it to be destroyed + impl->delReference(); + + } + + +} // namespace ZThread diff --git a/dep/src/zthread/ThreadImpl.h b/dep/src/zthread/ThreadImpl.h new file mode 100644 index 00000000000..ae2c8f23960 --- /dev/null +++ b/dep/src/zthread/ThreadImpl.h @@ -0,0 +1,122 @@ +/* + * 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 __ZTTHREADIMPL_H__ +#define __ZTTHREADIMPL_H__ + +#include "zthread/ThreadLocalImpl.h" +#include "zthread/Thread.h" +#include "zthread/Exceptions.h" +#include "IntrusivePtr.h" + +#include "Monitor.h" +#include "TSS.h" +#include "ThreadOps.h" +#include "State.h" + +#include <map> +#include <deque> + +namespace ZThread { + +/** + * @class ThreadImpl + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-27T13:39:03-0400> + * @version 2.3.0 + */ +class ThreadImpl : public IntrusivePtr<ThreadImpl, FastLock>, public ThreadOps { + + typedef std::deque<ThreadImpl*> List; + + //! TSS to store implementation to current thread mapping. + static TSS<ThreadImpl*> _threadMap; + + //! The Monitor for controlling this thread + Monitor _monitor; + + //! Current state for the thread + State _state; + + //! Joining threads + List _joiners; + + public: + + typedef std::map<const ThreadLocalImpl*, ThreadLocalImpl::ValuePtr > ThreadLocalMap; + + private: + + ThreadLocalMap _tls; + + //! Cached thread priority + Priority _priority; + + //! Request cancel() when main() goes out of scope + bool _autoCancel; + + void start(const Task& task); + + public: + + ThreadImpl(); + + ThreadImpl(const Task&, bool); + + ~ThreadImpl(); + + Monitor& getMonitor(); + + void cancel(bool autoCancel = false); + + bool interrupt(); + + bool isInterrupted(); + + bool isCanceled(); + + Priority getPriority() const; + + // ThreadLocalMap& getThreadLocalMap(); + ThreadLocalMap& getThreadLocalMap() { return _tls; } + + bool join(unsigned long); + + void setPriority(Priority); + + bool isActive(); + + bool isReference(); + + static void sleep(unsigned long); + + static void yield(); + + static ThreadImpl* current(); + + static void dispatch(ThreadImpl*, ThreadImpl*, Task); + +}; + +} // namespace ZThread + +#endif // __ZTTHREADIMPL_H__ diff --git a/dep/src/zthread/ThreadLocalImpl.cxx b/dep/src/zthread/ThreadLocalImpl.cxx new file mode 100644 index 00000000000..25682e66325 --- /dev/null +++ b/dep/src/zthread/ThreadLocalImpl.cxx @@ -0,0 +1,66 @@ +/* + * 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. + * + */ + +#include "zthread/ThreadLocalImpl.h" +#include "ThreadImpl.h" + +namespace ZThread { + + ThreadLocalImpl::ThreadLocalImpl() {} + + ThreadLocalImpl::~ThreadLocalImpl() {} + + void ThreadLocalImpl::clearAll() { + + typedef ThreadImpl::ThreadLocalMap Map; + Map& m = ThreadImpl::current()->getThreadLocalMap(); + + m.clear(); + + } + + void ThreadLocalImpl::clear() const { + + typedef ThreadImpl::ThreadLocalMap Map; + Map& m = ThreadImpl::current()->getThreadLocalMap(); + + Map::iterator i = m.find(this); + if(i != m.end()) + m.erase(i); + + } + + ThreadLocalImpl::ValuePtr ThreadLocalImpl::value( ValuePtr(*pfn)() ) const { + + typedef ThreadImpl::ThreadLocalMap Map; + Map& m = ThreadImpl::current()->getThreadLocalMap(); + + Map::iterator i = m.find(this); + if(i != m.end()) + return i->second; + + m[ this ] = ValuePtr( pfn() ); + return m[ this ]; + + } + +} // namespace ZThread diff --git a/dep/src/zthread/ThreadOps.cxx b/dep/src/zthread/ThreadOps.cxx new file mode 100644 index 00000000000..53a3e4457bc --- /dev/null +++ b/dep/src/zthread/ThreadOps.cxx @@ -0,0 +1,54 @@ +/* + * 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 __ZTTHREADOPSIMPLSELECT_CXX__ +#define __ZTTHREADOPSIMPLSELECT_CXX__ + +#include "ThreadOps.h" + +// This file will select an implementation for a ThreadOps based on +// what ThreadOps.h selects. This method is for selecting the +// source files, to improve portability. Currently, the project is +// based on the autoconf tool-set, which doesn't support conditional +// compilation well. Additionally, this should make the library +// easier to port since its working around conditional compilation +// by using C++ features and people won't have to fiddle around with +// their make tool as much to compile the source + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +// Check for sched_yield() + +#if !defined(HAVE_SCHED_YIELD) +# if defined(HAVE_UNISTD_H) +# include <unistd.h> +# if defined(_POSIX_PRIORITY_SCHEDULING) +# define HAVE_SCHED_YIELD 1 +# endif +# endif +#endif + +#include ZT_THREADOPS_IMPLEMENTATION + +#endif diff --git a/dep/src/zthread/ThreadOps.h b/dep/src/zthread/ThreadOps.h new file mode 100644 index 00000000000..eef9f3c6e31 --- /dev/null +++ b/dep/src/zthread/ThreadOps.h @@ -0,0 +1,67 @@ +/* + * 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 __ZTTHREADOPSSELECT_H__ +#define __ZTTHREADOPSSELECT_H__ + +#include "zthread/Config.h" + +#if defined(ZT_THREADOPS_IMPLEMENTATION) +# error "Reserved symbol defined" +#endif + + +// Select the correct implementation +#if defined(ZT_POSIX) + +# include "posix/ThreadOps.h" +# define ZT_THREADOPS_IMPLEMENTATION "posix/ThreadOps.cxx" + +#elif defined(ZT_WIN32) || defined(ZT_WIN9X) + +// Visual C provides the _beginthreadex function, other compilers +// might not have this if they don't use Microsoft's C runtime. +// _beginthreadex is similar to in effect defining REENTRANT on a +// POSIX system. CreateThreadEx doesn't use reentrant parts of the +// Microsfot C runtime, but if your not using that runtime, no problem. + +# if !defined(HAVE_BEGINTHREADEX) +# if defined(_MSC_VER) +# define HAVE_BEGINTHREADEX +# endif +# endif + +# include "win32/ThreadOps.h" +# define ZT_THREADOPS_IMPLEMENTATION "win32/ThreadOps.cxx" + +#elif defined(ZT_MACOS) + +# include "macos/ThreadOps.h" +# define ZT_THREADOPS_IMPLEMENTATION "macos/ThreadOps.cxx" + +#endif + +#ifndef __ZTTHREADOPS_H__ +#error "No ThreadOps implementation could be selected" +#endif + +#endif // __ZTTHREADOPSSELECT_H__ diff --git a/dep/src/zthread/ThreadQueue.cxx b/dep/src/zthread/ThreadQueue.cxx new file mode 100644 index 00000000000..02349504641 --- /dev/null +++ b/dep/src/zthread/ThreadQueue.cxx @@ -0,0 +1,266 @@ +/* + * 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. + * + */ + +#include "DeferredInterruptionScope.h" +#include "Debug.h" +#include "ThreadImpl.h" +#include "ThreadQueue.h" + +#include <algorithm> +#include <deque> + +namespace ZThread { + + ThreadQueue::ThreadQueue() + : _waiter(0) { + + ZTDEBUG("ThreadQueue created\n"); + + } + + ThreadQueue::~ThreadQueue() { + + ZTDEBUG("ThreadQueue waiting on remaining threads...\n"); + + // Ensure the current thread is mapped. + ThreadImpl* impl = ThreadImpl::current(); + + bool threadsWaiting = false; + bool waitRequired = false; + + { + + TaskList shutdownTasks; + + { // Check the queue to for pending user threads + + Guard<FastLock> g(_lock); + + waitRequired = (_waiter != (ThreadImpl*)1); + _waiter = impl; + + threadsWaiting = !_userThreads.empty() || !_pendingThreads.empty(); + + //ZTDEBUG("Wait required: %d\n", waitRequired); + //ZTDEBUG("Threads waiting: %d\n", threadsWaiting); + + // Auto-cancel any active threads at the time main() goes out of scope + // "force" a gentle exit from the executing tasks; eventually the user- + // threads will transition into pending-threads + pollUserThreads(); + + // Remove all the tasks about to be run from the task list so an indication + // can be given to threads calling removeShutdownTask() too late. + std::remove_copy(_shutdownTasks.begin(), + _shutdownTasks.end(), + std::back_inserter(shutdownTasks), + Task((Runnable*)0)); + + //ZTDEBUG("Threads waiting: %d\n", threadsWaiting); + + } + + // Execute the shutdown tasks + for(TaskList::iterator i = shutdownTasks.begin(); i != shutdownTasks.end(); ++i) { + try { + (*i)->run(); + } catch(...) { } + } + + } + + // Wait for all the users threads to get into the appropriate state + if(threadsWaiting) { + + + Monitor& m = _waiter->getMonitor(); + + // Defer interruption while this thread waits for a signal from + // the last pending user thread + Guard<Monitor, CompoundScope<DeferredInterruptionScope, LockedScope> > g(m); + //ZTDEBUG("Threads waiting: %d %d\n", _userThreads.size(), _pendingThreads.size()); + + // Avoid race-condition where the last threads are done with thier tasks, but + // only begin the final part of the clean up phase after this destructor begins + // to run. Takes advantage of the fact that if all remaining threads have transitioned + // into a pending state by the time execution reaches this point, then there is no + // need to wait. + waitRequired = waitRequired && !(_userThreads.empty() && !_pendingThreads.empty()); + + // Reference threads can't be interrupted or otherwise + // manipulated. The only signal this monitor will receive + // at this point will be from the last pending thread. + if(waitRequired && m.wait() != Monitor::SIGNALED) { + assert(0); + } + + // Join those pending threads + pollPendingThreads(); + + } + + // Clean up the reference threads + pollReferenceThreads(); + + ZTDEBUG("ThreadQueue destroyed\n"); + + } + + + void ThreadQueue::insertPendingThread(ThreadImpl* impl) { + ZTDEBUG("insertPendingThread()\n"); + Guard<FastLock> g(_lock); + + // Move from the user-thread list to the pending-thread list + ThreadList::iterator i = std::find(_userThreads.begin(), _userThreads.end(), impl); + if(i != _userThreads.end()) + _userThreads.erase(i); + + _pendingThreads.push_back(impl); + + // Wake the main thread,if its waiting, when the last pending-thread becomes available; + // Otherwise, take note that no wait for pending threads to finish is needed + if(_userThreads.empty()) + if(_waiter && _waiter != (ThreadImpl*)1) + _waiter->getMonitor().notify(); + else + _waiter = (ThreadImpl*)!_waiter; + + ZTDEBUG("1 pending-thread added.\n"); + + } + + void ThreadQueue::insertReferenceThread(ThreadImpl* impl) { + + Guard<FastLock> g(_lock); + _referenceThreads.push_back(impl); + + ZTDEBUG("1 reference-thread added.\n"); + + } + + void ThreadQueue::insertUserThread(ThreadImpl* impl) { + + Guard<FastLock> g(_lock); + _userThreads.push_back(impl); + + // Reclaim pending-threads + pollPendingThreads(); + + // Auto-cancel threads that are started when main() is out of scope + if(_waiter) + impl->cancel(true); + + ZTDEBUG("1 user-thread added.\n"); + + } + + + void ThreadQueue::pollPendingThreads() { + + ZTDEBUG("pollPendingThreads()\n"); + + for(ThreadList::iterator i = _pendingThreads.begin(); i != _pendingThreads.end();) { + + ThreadImpl* impl = (ThreadImpl*)*i; + ThreadOps::join(impl); + + impl->delReference(); + + i = _pendingThreads.erase(i); + + ZTDEBUG("1 pending-thread reclaimed.\n"); + + } + + } + + void ThreadQueue::pollReferenceThreads() { + + ZTDEBUG("pollReferenceThreads()\n"); + + for(ThreadList::iterator i = _referenceThreads.begin(); i != _referenceThreads.end(); ++i) { + + ThreadImpl* impl = (ThreadImpl*)*i; + impl->delReference(); + + ZTDEBUG("1 reference-thread reclaimed.\n"); + + } + + } + + void ThreadQueue::pollUserThreads() { + + ZTDEBUG("pollUserThreads()\n"); + + for(ThreadList::iterator i = _userThreads.begin(); i != _userThreads.end(); ++i) { + + ThreadImpl* impl = *i; + impl->cancel(true); + + ZTDEBUG("1 user-thread reclaimed.\n"); + + } + + } + + void ThreadQueue::insertShutdownTask(Task& task) { + + bool hasWaiter = false; + + { + + Guard<FastLock> g(_lock); + + // Execute later when the ThreadQueue is destroyed + if( !(hasWaiter = (_waiter != 0)) ) { + + _shutdownTasks.push_back(task); + //ZTDEBUG("1 shutdown task added. %d\n", _shutdownTasks.size()); + + } + + } + + // Execute immediately if things are shutting down + if(hasWaiter) + task->run(); + + } + + bool ThreadQueue::removeShutdownTask(const Task& task) { + + Guard<FastLock> g(_lock); + + TaskList::iterator i = std::find(_shutdownTasks.begin(), _shutdownTasks.end(), task); + bool removed = (i != _shutdownTasks.end()); + if(removed) + _shutdownTasks.erase(i); + + //ZTDEBUG("1 shutdown task removed (%d)-%d\n", removed, _shutdownTasks.size()); + + return removed; + + } + +}; diff --git a/dep/src/zthread/ThreadQueue.h b/dep/src/zthread/ThreadQueue.h new file mode 100644 index 00000000000..044f8263026 --- /dev/null +++ b/dep/src/zthread/ThreadQueue.h @@ -0,0 +1,134 @@ +/* + * 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 __ZTTHREADQUEUE_H__ +#define __ZTTHREADQUEUE_H__ + +#include "zthread/Singleton.h" +#include "zthread/Guard.h" +#include "FastLock.h" + + +namespace ZThread { + + class ThreadImpl; + + /** + * @class ThreadQueue + * @version 2.3.0 + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-27T20:52:05-0400> + * + * A ThreadQueue accumulates references to user and reference threads. + * These are threads that are running outside the scope of the Thread + * object that created them. ZThreads doesn't have a central manager for + * all threads (partly why I renamed the ThreadManager to someting more + * appropriate). Instead, ZThreads will discover threads it did not create + * and create a reference thread that allows ZThreads to interact with it. + * Non user threads that are created by the user never have to touch the + * ThreadQueue. + */ + class ThreadQueue : public Singleton<ThreadQueue, StaticInstantiation> { + + typedef std::deque<ThreadImpl*> ThreadList; + typedef std::deque<Task> TaskList; + + //! Managed thread lists + ThreadList _pendingThreads; + ThreadList _referenceThreads; + ThreadList _userThreads; + + //! Shutdown handlers + TaskList _shutdownTasks; + + //! Serilize access to the thread list + FastLock _lock; + + //! Reference thread waiting to cleanup any user & reference threads + ThreadImpl* _waiter; + + public: + + ThreadQueue(); + + /** + * The thread destroys a ThreadQueue will be a reference thread, + * probably the main thread; but it could be another thread that + * started and loaded the library. + */ + ~ThreadQueue(); + + /** + * Insert a user-thread into the queue. User-threads are inserted as they + * begin thier task. Once that task completes, user-threads are automatically + * transitioned to pending-threads via <i>insertPendingThread()</i>. + * + * User-threads are known to be executing thier tasks and will be cancel()ed + * as the ThreadQueue is destroyed when main() goes out of scope. This sends + * a request to the task to complete soon. Once the task exits, the thread is + * transitioned to pending-thread status. + */ + void insertUserThread(ThreadImpl*); + + /** + * Insert a pending-thread into the queue. + * + * Pending-threads are known to have completed thier tasks and thier + * resources are reclaimed (lazily) as more threads are started or as the + * ThreadQueue is destroyed. + */ + void insertPendingThread(ThreadImpl*); + + + /** + * Insert reference thread. Reference threads are not removed until + * the ThreadQueue goes out of scope. + */ + void insertReferenceThread(ThreadImpl*); + + /** + * Insert a task to be run before threads are joined. + * Any items inserted after the ThreadQueue desctructor has begun to + * execute will be run() immediately. + */ + void insertShutdownTask(Task&); + + /** + * Remove an existing shutdown task. + */ + bool removeShutdownTask(const Task&); + + private: + + void pollPendingThreads(); + + void pollUserThreads(); + + void pollReferenceThreads(); + + }; + + +} // namespace ZThread + + +#endif // __ZTTHREADQUEUE_H__ diff --git a/dep/src/zthread/ThreadedExecutor.cxx b/dep/src/zthread/ThreadedExecutor.cxx new file mode 100644 index 00000000000..44a213e8daa --- /dev/null +++ b/dep/src/zthread/ThreadedExecutor.cxx @@ -0,0 +1,464 @@ +/* + * 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. + * + */ + +#include "zthread/ThreadedExecutor.h" +#include "zthread/Guard.h" +#include "zthread/FastMutex.h" +#include "zthread/Time.h" + +#include "ThreadImpl.h" + +namespace ZThread { + + namespace { + + //! + class WaiterQueue { + + typedef std::deque<ThreadImpl*> ThreadList; + + typedef struct group_t { + size_t id; + size_t count; + ThreadList waiters; + group_t(size_t n) : id(n), count(0) {} + } Group; + + typedef std::deque<Group> GroupList; + + //! Predicate to find a specific group + struct by_id : public std::unary_function<bool, Group> { + size_t id; + by_id(size_t n) : id(n) {} + bool operator()(const Group& grp) { + return grp.id == id; + } + }; + + //! Functor to count groups + struct counter : public std::unary_function<void, Group> { + size_t count; + counter() : count(0) {} + void operator()(const Group& grp) { count += grp.count; } + operator size_t() { return count; } + }; + + FastMutex _lock; + GroupList _list; + size_t _id; + size_t _generation; + + public: + + WaiterQueue() : _id(0), _generation(0) { + // At least one empty-group exists + _list.push_back( Group(_id++) ); + } + + /** + * Insert the current thread into the current waiter list + * + * @pre At least one empty group exists + * @post At least one empty group exists + */ + bool wait(unsigned long timeout) { + + ThreadImpl* self = ThreadImpl::current(); + Monitor& m = self->getMonitor(); + + Monitor::STATE state; + + Guard<Lockable> g1(_lock); + + // At least one empty-group exists + assert(!_list.empty()); + + // Return w/o waiting if there are no executing tasks + if((size_t)std::for_each(_list.begin(), _list.end(), counter()) < 1) + return true; + + // Update the waiter list for the active group + _list.back().waiters.push_back(self); + size_t n = _list.back().id; + + m.acquire(); + + { + + Guard<Lockable, UnlockedScope> g2(g1); + state = timeout == 0 ? m.wait() : m.wait(timeout); + + } + + m.release(); + + // If awoke due to a reason other than the last task in the group 'n' completing, + // then then find the group 'self' is waiting in + GroupList::iterator i = std::find_if(_list.begin(), _list.end(), by_id(n)); + if(i != _list.end()) { + + // Remove 'self' from that list if it is still a member + ThreadList::iterator j = std::find(i->waiters.begin(), i->waiters.end(), self); + if(j != i->waiters.end()) + i->waiters.erase(j); + + } + + // At least one empty-group exists + assert(!_list.empty()); + + switch(state) { + case Monitor::SIGNALED: + break; + case Monitor::TIMEDOUT: + return false; + case Monitor::INTERRUPTED: + throw Interrupted_Exception(); + default: + throw Synchronization_Exception(); + } + + return true; + + } + + /** + * Increment the active group count + * + * @pre at least 1 empty group exists + * @post at least 1 non-empty group exists + */ + std::pair<size_t, size_t> increment() { + + Guard<FastMutex> g(_lock); + + // At least one empty-group exists + assert(!_list.empty()); + + GroupList::iterator i = --_list.end(); + size_t n = i->id; + + if(i == _list.end()) { + + // A group should never have been removed until + // the final task in that group completed + assert(0); + + } + + i->count++; + + // When the active group is being incremented, insert a new active group + // to replace it if there were waiting threads + if(i == --_list.end() && !i->waiters.empty()) + _list.push_back(Group(_id++)); + + // At least 1 non-empty group exists + assert((size_t)std::for_each(_list.begin(), _list.end(), counter()) > 0); + + return std::make_pair(n, _generation); + + } + + + /** + * Decrease the count for the group with the given id. + * + * @param n group id + * + * @pre At least 1 non-empty group exists + * @post At least 1 empty group exists + */ + void decrement(size_t n) { + + Guard<FastMutex> g1(_lock); + + // At least 1 non-empty group exists + assert((size_t)std::for_each(_list.begin(), _list.end(), counter()) > 0); + + // Find the requested group + GroupList::iterator i = std::find_if(_list.begin(), _list.end(), by_id(n)); + if(i == _list.end()) { + + // A group should never have been removed until + // the final task in that group completed + assert(0); + + } + + // Decrease the count for tasks in this group, + if(--i->count == 0 && i == _list.begin()) { + + do { + + // When the first group completes, wake all waiters for every + // group, starting from the first until a group that is not + // complete is reached + + /* + // Don't remove the empty active group + if(i == --_list.end() && i->waiters.empty()) + break; + */ + + if( awaken(*i) ) { + + // If all waiters were awakened, remove the group + i = _list.erase(i); + + } else { + + { + + // Otherwise, unlock and yield allowing the waiter + // lists to be updated if other threads are busy + Guard<FastLock, UnlockedScope> g2(g1); + ThreadImpl::yield(); + + } + + i = _list.begin(); + + } + + } while(i != _list.end() && i->count == 0); + + // Ensure that an active group exists + if(_list.empty()) + _list.push_back( Group(++_id) ); + + } + + // At least one group exists + assert(!_list.empty()); + + } + + /** + */ + size_t generation(bool next = false) { + + Guard<FastMutex> g(_lock); + return next ? _generation++ : _generation; + + } + + private: + + /** + * Awaken all the waiters remaining in the given group + * + * @return + * - true if all the waiting threads were successfully awakened. + * - false if there were one or more threads that could not be awakened. + */ + bool awaken(Group& grp) { + + // Go through the waiter list in the given group; + for(ThreadList::iterator i = grp.waiters.begin(); i != grp.waiters.end();) { + + ThreadImpl* impl = *i; + Monitor& m = impl->getMonitor(); + + // Try the monitor lock, if it cant be locked skip to the next waiter + if(m.tryAcquire()) { + + // Notify the monitor & remove from the waiter list so time isn't + // wasted checking it again. + i = grp.waiters.erase(i); + + // Try to wake the waiter, it doesn't matter if this is successful + // or not (only fails when the monitor is already going to stop waiting). + m.notify(); + m.release(); + + } else ++i; + + } + + return grp.waiters.empty(); + + } + + }; + + //! Synchronization point for the Executor + class ExecutorImpl { + + typedef std::deque<ThreadImpl*> ThreadList; + + bool _canceled; + FastMutex _lock; + + //! Worker threads + ThreadList _threads; + + WaiterQueue _queue; + + public: + + ExecutorImpl() : _canceled(false) {} + + WaiterQueue& getWaiterQueue() { + return _queue; + } + + void registerThread(size_t generation) { + + // Interrupt slow starting threads + if(getWaiterQueue().generation() != generation) + ThreadImpl::current()->interrupt(); + + // Enqueue for possible future interrupt() + else { + + Guard<FastMutex> g(_lock); + _threads.push_back( ThreadImpl::current() ); + + } + + } + + void unregisterThread() { + + Guard<FastMutex> g(_lock); + std::remove(_threads.begin(), _threads.end(), ThreadImpl::current() ); + + } + + void cancel() { + + Guard<FastMutex> g(_lock); + _canceled = true; + + } + + bool isCanceled() { + + if(_canceled) + return true; + + Guard<FastMutex> g(_lock); + return _canceled; + + } + + void interrupt() { + + Guard<FastMutex> g(_lock); + + // Interrupt all the registered threads + for(ThreadList::iterator i = _threads.begin(); i != _threads.end(); ++i) + (*i)->interrupt(); + + // Bump the generation up, ensuring slow starting threads get this interrupt + getWaiterQueue().generation( true ); + + } + + }; /* ExecutorImpl */ + + //! Wrap a generation and a group around a task + class Worker : public Runnable { + + CountedPtr< ExecutorImpl > _impl; + Task _task; + + size_t _generation; + size_t _group; + + public: + + Worker(const CountedPtr< ExecutorImpl >& impl, const Task& task) + : _impl(impl), _task(task) { + + std::pair<size_t, size_t> pr( _impl->getWaiterQueue().increment() ); + + _group = pr.first; + _generation = pr.second; + + } + + size_t group() const { + return _group; + } + + size_t generation() const { + return _generation; + } + + void run() { + + // Register this thread once its begun; the generation is used to ensure + // threads that are slow starting are properly interrupted + + _impl->registerThread( generation() ); + + try { + _task->run(); + } catch(...) { + /* consume the exceptions the work propogates */ + } + + _impl->getWaiterQueue().decrement( group() ); + + // Unregister this thread + + _impl->unregisterThread(); + + } + + }; /* Worker */ + + } + + ThreadedExecutor::ThreadedExecutor() : _impl(new ExecutorImpl) {} + + ThreadedExecutor::~ThreadedExecutor() {} + + void ThreadedExecutor::execute(const Task& task) { + + Thread t( new Worker(_impl, task) ); + + } + + void ThreadedExecutor::interrupt() { + _impl->interrupt(); + } + + void ThreadedExecutor::cancel() { + _impl->cancel(); + } + + bool ThreadedExecutor::isCanceled() { + return _impl->isCanceled(); + } + + void ThreadedExecutor::wait() { + _impl->getWaiterQueue().wait(0); + } + + bool ThreadedExecutor::wait(unsigned long timeout) { + return _impl->getWaiterQueue().wait(timeout == 0 ? 1 : timeout); + } + +} diff --git a/dep/src/zthread/Time.cxx b/dep/src/zthread/Time.cxx new file mode 100644 index 00000000000..2409d93cb79 --- /dev/null +++ b/dep/src/zthread/Time.cxx @@ -0,0 +1,45 @@ +/* + * 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. + * + */ + +#include "zthread/Time.h" +#include "TimeStrategy.h" + + +using namespace ZThread; + +Time::Time() { + + // System startup time + static TimeStrategy firstHelper; + TimeStrategy helper; + + Time then(firstHelper.seconds(), firstHelper.milliseconds()); + Time now(helper.seconds(), helper.milliseconds()); + + now -= then; + + _seconds = now.seconds(); + _milliseconds = now.milliseconds(); + +} + + diff --git a/dep/src/zthread/TimeStrategy.h b/dep/src/zthread/TimeStrategy.h new file mode 100644 index 00000000000..0b9ad1e22ba --- /dev/null +++ b/dep/src/zthread/TimeStrategy.h @@ -0,0 +1,86 @@ +/* + * 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 __ZTTIMESELECT_H__ +#define __ZTTIMESELECT_H__ + +#include "zthread/Config.h" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +// Select the correct TimeOps implementation based on +// what the complilation environment has defined + +#ifndef HAVE_FTIME + +# if defined(ZT_WIN32) || defined(ZT_WIN9X) + +# if !defined(__MWERKS__) + +# ifndef HAVE_FTIME +# define HAVE_FTIME +# endif + +# elif defined(__MWERKS__) + +# ifndef HAVE_PERFORMANCECOUNTER +# define HAVE_PERFORMANCECOUNTER +# endif + +# endif + +# endif + +#endif + +// Some systems require this to complete the definition of timespec +// which is needed by pthreads. +#if defined(HAVE_SYS_TYPES_H) +# include <sys/types.h> +#endif + +#if defined(ZT_MACOS) + +# include "macos/UpTimeStrategy.h" + +#elif defined(HAVE_PERFORMANCECOUNTER) + +# include "win32/PerformanceCounterStrategy.h" + +#elif defined(HAVE_FTIME) + +# include "posix/FtimeStrategy.h" + +#else + +# include "posix/GetTimeOfDayStrategy.h" + +#endif + + +#ifndef __ZTTIMESTRATEGY_H__ +#error "No TimeStrategy implementation could be selected" +#endif + +#endif // __ZTTIMESELECT_H__ diff --git a/dep/src/zthread/config.h b/dep/src/zthread/config.h new file mode 100644 index 00000000000..0b630dcc36c --- /dev/null +++ b/dep/src/zthread/config.h @@ -0,0 +1,95 @@ +/* src/config.h. Generated by configure. */ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Defined if <bits/atomicity.h> is usable */ +/* #undef HAVE_ATOMIC_GCC */ + +/* Defined if <asm/atomic.h> is usable */ +/* #undef HAVE_ATOMIC_LINUX */ + +/* _beginthreadex() */ +/* #undef HAVE_BEGINTHREADEX */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <errno.h> header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* defined when pthreads is available */ +#define HAVE_POSIX_THREADS + +/* Defined if pthread_keycreate() is available */ +/* #undef HAVE_PTHREADKEYCREATE */ + +/* Defined if pthread_key_create() is available */ +#define HAVE_PTHREADKEY_CREATE + +/* Defined if pthread_yield() is available */ +#define HAVE_PTHREAD_YIELD + +/* Defined if -lrt is needed for RT scheduling */ +#define HAVE_SCHED_RT + +/* Defined if sched_yield() is available */ +#define HAVE_SCHED_YIELD + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +//#define PACKAGE "ZThread" + +/* Define to the address where bug reports for this package should be sent. */ +//#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +//#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +//#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +//#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +//#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Defined if ftime()/_ftime() is usable */ +#define SYSTEM_FTIME ftime + +/* Version number of package */ +//#define VERSION "2.3.2" + +/* No interrupt() hooks */ +/* #undef ZTHREAD_DISABLE_INTERRUPT */ + +/* No OS priority support */ +/* #undef ZTHREAD_DISABLE_PRIORITY */ diff --git a/dep/src/zthread/linux/AtomicCount.cxx b/dep/src/zthread/linux/AtomicCount.cxx new file mode 100644 index 00000000000..28c2381c3b4 --- /dev/null +++ b/dep/src/zthread/linux/AtomicCount.cxx @@ -0,0 +1,73 @@ +/* + * 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 __ZTATOMICCOUNTIMPL_H__ +#define __ZTATOMICCOUNTIMPL_H__ + +#include <asm/atomic.h> +#include <assert.h> + +namespace ZThread { + +typedef struct atomic_count_t { + + atomic_t count; + + atomic_count_t() { + atomic_t init = ATOMIC_INIT(0); + count = init; + } + + ~atomic_count_t() { + assert(atomic_read(&count) == 0); + } + +} ATOMIC_COUNT; + + +AtomicCount::AtomicCount() { + + _value = reinterpret_cast<void*>(new ATOMIC_COUNT); + +} + +AtomicCount::~AtomicCount() { + + delete reinterpret_cast<ATOMIC_COUNT*>(_value); + +} + +void AtomicCount::increment() { + + atomic_inc(&reinterpret_cast<ATOMIC_COUNT*>(_value)->count); + +} + +bool AtomicCount::decrement() { + + return atomic_dec_and_test(&reinterpret_cast<ATOMIC_COUNT*>(_value)->count); + +} + +}; + +#endif // __ZTATOMICCOUNTIMPL_H__ diff --git a/dep/src/zthread/linux/AtomicFastLock.h b/dep/src/zthread/linux/AtomicFastLock.h new file mode 100644 index 00000000000..b9aa1babcd6 --- /dev/null +++ b/dep/src/zthread/linux/AtomicFastLock.h @@ -0,0 +1,117 @@ +/* + * 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 __ZTFASTLOCK_H__ +#define __ZTFASTLOCK_H__ + +#include "zthread/NonCopyable.h" +#include "../ThreadOps.h" +#include <assert.h> +#include <asm/atomic.h> + +#if !defined(NDEBUG) +# include <pthread.h> +#endif + +namespace ZThread { + +/** + * @class FastLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:27:03-0400> + * @version 2.2.0 + * + * This implementation of a FastLock uses the atomic operations that + * linux provides with its kernel sources. This demonstrates how to implement + * a spinlock with a decrement and test primative. + */ +class FastLock : private NonCopyable { + + atomic_t _value; + +#if !defined(NDEBUG) + pthread_t _owner; +#endif + +public: + + inline FastLock() { + + atomic_t tmp = ATOMIC_INIT(1); + _value = tmp; + + } + + inline ~FastLock() { + + assert(atomic_read(&_value) == 1); + assert(_owner == 0); + + } + + inline void acquire() { + + while(!atomic_dec_and_test(&_value)) { + + atomic_inc(&_value); + ThreadOps::yield(); + + } + +#if !defined(NDEBUG) + _owner = pthread_self(); +#endif + } + + inline void release() { + +#if !defined(NDEBUG) + assert(pthread_equal(_owner, pthread_self()) != 0); +#endif + + atomic_inc(&_value); + _owner = 0; + + } + + inline bool tryAcquire(unsigned long timeout=0) { + + bool wasLocked = atomic_dec_and_test(&_value); + if(!wasLocked) + atomic_inc(&_value); + +#if !defined(NDEBUG) + if(wasLocked) + _owner = pthread_self(); +#endif + + return wasLocked; + + } + +}; /* FastLock */ + + +} // namespace ZThread + +#endif diff --git a/dep/src/zthread/linux/FastRecursiveLock.h b/dep/src/zthread/linux/FastRecursiveLock.h new file mode 100644 index 00000000000..d253652cb53 --- /dev/null +++ b/dep/src/zthread/linux/FastRecursiveLock.h @@ -0,0 +1,83 @@ +/* + * 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 __ZTFASTRECURSIVELOCK_H__ +#define __ZTFASTRECURSIVELOCK_H__ + +#include "zthread/NonCopyable.h" +#include <pthread.h> + +namespace ZThread { + +/** + * @class FastRecursiveLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:27:14-0400> + * @version 2.2.0 + * + * This implementation of a FastRecursiveLock uses the recursive mutex + * that linux pthreads provides. + */ +class FastRecursiveLock : private NonCopyable { + + pthread_mutex_t _mtx; + +public: + + inline FastRecursiveLock() { + + static const pthread_mutexattr_t attr = { PTHREAD_MUTEX_RECURSIVE_NP }; + pthread_mutex_init(&_mtx, &attr); + + } + + inline ~FastRecursiveLock() { + + pthread_mutex_destroy(&_mtx); + + } + + inline void acquire() { + + pthread_mutex_lock(&_mtx); + + } + + inline void release() { + + pthread_mutex_unlock(&_mtx); + + } + + inline bool tryAcquire(unsigned long timeout=0) { + + return (pthread_mutex_trylock(&_mtx) == 0); + + } + +}; /* FastRecursiveLock */ + + +} // namespace ZThread + +#endif diff --git a/dep/src/zthread/macos/FastLock.h b/dep/src/zthread/macos/FastLock.h new file mode 100644 index 00000000000..bae5c482903 --- /dev/null +++ b/dep/src/zthread/macos/FastLock.h @@ -0,0 +1,139 @@ +/* + * 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 __ZTFASTLOCK_H__ +#define __ZTFASTLOCK_H__ + +#include "zthread/NonCopyable.h" +#include "zthread/Exceptions.h" + +#include <assert.h> +#include <CoreServices/CoreServices.h> +//#include <Multiprocessing.h> + +namespace ZThread { + +/** + * @class FastLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:25:31-0400> + * @version 2.1.6 + * + */ +class FastLock : private NonCopyable { + + MPCriticalRegionID _mtx; + + public: + + /** + * Create a new FastLock. No safety or state checks are performed. + * + * @exception Initialization_Exception - not thrown + */ + inline FastLock() { + + // Apple TN1071 + static bool init = MPLibraryIsLoaded(); + + if(!init || MPCreateCriticalRegion(&_mtx) != noErr) { + assert(0); + throw Initialization_Exception(); + } + + } + + /** + * Destroy a FastLock. No safety or state checks are performed. + */ + inline ~FastLock() throw () { + + OSStatus status = MPDeleteCriticalRegion(_mtx); + if(status != noErr) + assert(false); + + } + + /** + * Acquire an exclusive lock. No safety or state checks are performed. + * + * @exception Synchronization_Exception - not thrown + */ + inline void acquire() { + + if(MPEnterCriticalRegion(_mtx, kDurationForever) != noErr) + throw Synchronization_Exception(); + + } + + /** + * Try to acquire an exclusive lock. No safety or state checks are performed. + * This function returns immediately regardless of the value of the timeout + * + * @param timeout Unused + * @return bool + * @exception Synchronization_Exception - not thrown + */ + inline bool tryAcquire(unsigned long timeout=0) { + + OSStatus status = + MPEnterCriticalRegion(_mtx, kDurationMillisecond * timeout); + + switch(status) { + case kMPTimeoutErr: + return false; + + case noErr: + return true; + + } + + assert(0); + throw Synchronization_Exception(); + + } + + /** + * Release an exclusive lock. No safety or state checks are performed. + * The caller should have already acquired the lock, and release it + * only once. + * + * @exception Synchronization_Exception - not thrown + */ + inline void release() { + + if(MPExitCriticalRegion(_mtx) != noErr) + throw Synchronization_Exception(); + + } + + +}; /* FastLock */ + + +}; + +#endif + + + diff --git a/dep/src/zthread/macos/Monitor.cxx b/dep/src/zthread/macos/Monitor.cxx new file mode 100644 index 00000000000..ab7806b13df --- /dev/null +++ b/dep/src/zthread/macos/Monitor.cxx @@ -0,0 +1,280 @@ +/* + * 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. + * + */ + +#include "Monitor.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +using namespace ZThread; + +Monitor::Monitor() : _owner(0), _waiting(false), _pending(false) { + + if(MPCreateSemaphore(1, 0, &_sema) != noErr) { + assert(0); + throw Initialization_Exception(); + } + +} + +Monitor::~Monitor() throw() { + + assert(!_waiting); + + OSStatus status = MPDeleteSemaphore(_sema); + if(status != noErr) + assert(false); + +} + +Monitor::STATE Monitor::wait(unsigned long timeout) { + + // Calcuate the time, taking into account Intertask Signaling Time + // http://developer.apple.com/techpubs/macosx/Carbon/oss/MultiPServices/Multiprocessing_Services/index.html?http://developer.apple.com/techpubs/macosx/Carbon/oss/MultiPServices/Multiprocessing_Services/Functions/Creating_and_ssage_Queues.html + + AbsoluteTime tTarget; + Duration waitDuration = + (timeout == 0) ? kDurationForever : (kDurationMillisecond * timeout); + + if(waitDuration != kDurationForever) + tTarget = AddDurationToAbsolute(waitDuration, UpTime()); + + // Update the owner on first use. The owner will not change, each + // thread waits only on a single Monitor and a Monitor is never + // shared + if(_owner == 0) + _owner = MPCurrentTaskID(); + + STATE state(INVALID); + + // Serialize access to the state of the Monitor + // and test the state to determine if a wait is needed. + _waitLock.acquire(); + + if(pending(ANYTHING)) { + + // Return without waiting when possible + state = next(); + + _waitLock.release(); + return state; + + } + // Unlock the external lock if a wait() is probably needed. + // Access to the state is still serial. + _lock.release(); + + // Wait for a transition in the state that is of interest, this + // allows waits to exclude certain flags (e.g. INTERRUPTED) + // for a single wait() w/o actually discarding those flags - + // they will remain set until a wait interested in those flags + // occurs. + + // Wait, ignoring signals + _waiting = true; + + _waitLock.release(); + + // Update the wait time + if(waitDuration != kDurationForever) + waitDuration = AbsoluteDeltaToDuration(tTarget, UpTime()); + + // Sleep until a signal arrives or a timeout occurs + OSStatus status = MPWaitOnSemaphore(_sema, waitDuration); + + // Reacquire serialized access to the state + _waitLock.acquire(); + + // Awaken only when the event is set or the timeout expired + assert(status == kMPTimeoutErr || status == noErr); + + if(status == kMPTimeoutErr) + push(TIMEDOUT); + + // Get the next available STATE + state = next(); + + _waiting = false; + + // Its possible that a timeout will wake the thread before a signal is + // delivered. Absorb that leftover so the next wait isn't aborted right away + if(status == kMPTimeoutErr && _pending) { + + status = MPWaitOnSemaphore(_sema, kDurationForever); + assert(status == noErr); + + } + + _pending = false; + + // Acquire the internal lock & release the external lock + _waitLock.release(); + + // Reaquire the external lock, keep from deadlocking threads calling + // notify(), interrupt(), etc. + _lock.acquire(); + + return state; + +} + + +bool Monitor::interrupt() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasInterruptable = !pending(INTERRUPTED); + bool hasWaiter = false; + + // Update the state & wake the waiter if there is one + if(wasInterruptable) { + + push(INTERRUPTED); + + wasInterruptable = false; + + if(_waiting && !_pending) { + + _pending = true; + hasWaiter = true; + + } else + wasInterruptable = !(_owner == MPCurrentTaskID()); + + } + + _waitLock.release(); + + if(hasWaiter && !masked(Monitor::INTERRUPTED)) + MPSignalSemaphore(_sema); + + return wasInterruptable; + +} + +bool Monitor::isInterrupted() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasInterrupted = pending(INTERRUPTED); + clear(INTERRUPTED); + + _waitLock.release(); + + return wasInterrupted; + +} + + +bool Monitor::notify() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasNotifyable = !pending(INTERRUPTED); + bool hasWaiter = false; + + // Set the flag if theres a waiter + if(wasNotifyable) { + + push(SIGNALED); + + if(_waiting && !_pending) { + + _pending = true; + hasWaiter = true; + + } + + } + + _waitLock.release(); + + if(hasWaiter) + MPSignalSemaphore(_sema); + + return wasNotifyable; + +} + + +bool Monitor::cancel() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasInterrupted = !pending(INTERRUPTED); + bool hasWaiter = false; + + push(CANCELED); + + // Update the state if theres a waiter + if(wasInterrupted) { + + push(INTERRUPTED); + + if(_waiting && !_pending) { + + _pending = true; + hasWaiter = true; + + } + + } + + _waitLock.release(); + + if(hasWaiter && !masked(Monitor::INTERRUPTED)) + MPSignalSemaphore(_sema); + + return wasInterrupted; + +} + +bool Monitor::isCanceled() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasCanceled = Status::examine(CANCELED); + + if(_owner == MPCurrentTaskID()) + clear(INTERRUPTED); + + _waitLock.release(); + + return wasCanceled; + +} + + + + + + + + + + diff --git a/dep/src/zthread/macos/Monitor.h b/dep/src/zthread/macos/Monitor.h new file mode 100644 index 00000000000..f4312d7b7ee --- /dev/null +++ b/dep/src/zthread/macos/Monitor.h @@ -0,0 +1,156 @@ +/* + * 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 __ZTMONITOR_H__ +#define __ZTMONITOR_H__ + +#include "../Status.h" +#include "../FastLock.h" + +namespace ZThread { + +/** + * @class Monitor + * @author Eric Crahen <http://www.code-foo.com/> + * @date <2003-07-29T11:24:58-0400> + * @version 2.2.1 + */ +class Monitor : public Status, private NonCopyable { + + //! Serialize access to external objects + FastLock _lock; + + //! Serialize access to internal state + FastLock _waitLock; + + //! Semaphore to control the owning thread + MPSemaphoreID _sema; + + //! Owning thread + MPTaskID _owner; + + //! Waiting flag, to avoid uneccessary signals + volatile bool _waiting; + + //! Waiting flag, to avoid too many signals + volatile bool _pending; + + //! State of the monitor + volatile int _state; + + public: + + //! Create a new monitor. + Monitor(); + + //! Destroy the monitor. + ~Monitor() throw(); + + //! Acquire the external lock for this monitor. + inline void acquire() { + _lock.acquire(); + } + + //! Try to acquire the external lock for this monitor. + inline bool tryAcquire() { + return _lock.tryAcquire(); + } + + //! Release the external lock for this monitor. + inline void release() { + _lock.release(); + } + + /** + * Wait for a state change and atomically unlock the external lock. + * Blocks for an indefinent amount of time. + * + * @return INTERRUPTED if the wait was ended by a interrupt() + * or SIGNALED if the wait was ended by a notify() + * + * @post the external lock is always acquired before this function returns + */ + inline STATE wait() { + return wait(0); + } + + /** + * Wait for a state change and atomically unlock the external lock. + * May blocks for an indefinent amount of time. + * + * @param timeout - maximum time to block (milliseconds) or 0 to + * block indefinently + * + * @return INTERRUPTED if the wait was ended by a interrupt() + * or TIMEDOUT if the maximum wait time expired. + * or SIGNALED if the wait was ended by a notify() + * + * @post the external lock is always acquired before this function returns + */ + STATE wait(unsigned long timeout); + + /** + * Interrupt this monitor. If there is a thread blocked on this monitor object + * it will be signaled and released. If there is no waiter, a flag is set and + * the next attempt to wait() will return INTERRUPTED w/o blocking. + * + * @return false if the thread was previously INTERRUPTED. + */ + bool interrupt(); + + /** + * Notify this monitor. If there is a thread blocked on this monitor object + * it will be signaled and released. If there is no waiter, a flag is set and + * the next attempt to wait() will return SIGNALED w/o blocking, if no other + * flag is set. + * + * @return false if the thread was previously INTERRUPTED. + */ + bool notify(); + + /** + * Check the state of this monitor, clearing the INTERRUPTED status if set. + * + * @return bool true if the monitor was INTERRUPTED. + * @post INTERRUPTED flag cleared if the calling thread owns the monitor. + */ + bool isInterrupted(); + + /** + * Mark the Status CANCELED, and INTERRUPT the montor. + * + * @see interrupt() + */ + bool cancel(); + + /** + * Test the CANCELED Status, clearing the INTERRUPTED status if set. + * + * @return bool + */ + bool isCanceled(); + +}; + +}; + +#endif diff --git a/dep/src/zthread/macos/TSS.h b/dep/src/zthread/macos/TSS.h new file mode 100644 index 00000000000..3f9805d0f7c --- /dev/null +++ b/dep/src/zthread/macos/TSS.h @@ -0,0 +1,120 @@ +/* + * 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 __ZTTSS_H__ +#define __ZTTSS_H__ + +#include "zthread/NonCopyable.h" +#include "zthread/Exceptions.h" + +#include <assert.h> +#include <CoreServices/CoreServices.h> +//#include <Multiprocessing.h> + +namespace ZThread { + + /** + * @class TSS + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-27T14:19:10-0400> + * @version 2.1.6 + * + * An abstraction for dealing with POSIX thread specific storage (tss). + * Provides get/set and creation/destruction. + */ + template <typename T> + class TSS : private NonCopyable { + + TaskStorageIndex _key; + + public: + + /** + * Create a new object for accessing tss. + */ + TSS() { + + // Apple TN1071 + static bool init = MPLibraryIsLoaded(); + + if(!init || MPAllocateTaskStorageIndex(&_key) != noErr) { + assert(0); + throw Initialization_Exception(); + } + + } + + /** + * Destroy the underlying supoprt for accessing tss with this + * object. + */ + ~TSS() { + + OSStatus status = MPDeallocateTaskStorageIndex(_key); + if(status != noErr) + assert(0); + + } + + /** + * Get the value stored in tss. + * + * @return T + * + * @exception InvalidOp_exception thrown when the tss is not properly initialized + */ + T get() const { + return reinterpret_cast<T>(MPGetTaskStorageValue(_key)); + } + + + /** + * Store a value in tss. + * + * @param value T + * @return T old value + * + * @exception InvalidOp_exception thrown when the tss is not properly initialized + */ + T set(T value) const { + + T oldValue = get(); + + OSStatus status = + MPSetTaskStorageValue(_key, reinterpret_cast<TaskStorageValue>(value)); + + if(status != noErr) { + assert(0); + throw Synchronization_Exception(); + } + + return oldValue; + + } + + }; + +} + +#endif + + diff --git a/dep/src/zthread/macos/ThreadOps.cxx b/dep/src/zthread/macos/ThreadOps.cxx new file mode 100644 index 00000000000..6a1a4106877 --- /dev/null +++ b/dep/src/zthread/macos/ThreadOps.cxx @@ -0,0 +1,103 @@ +/* + * 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. + * + */ + + +#include "ThreadOps.h" +#include "zthread/Exceptions.h" +#include "zthread/Runnable.h" + +namespace ZThread { + +const ThreadOps ThreadOps::INVALID(0); + +ThreadOps::ThreadOps() : _queue(0), _tid(0) { + + if(MPCreateQueue(&_queue) != noErr) + throw Initialization_Exception(); + +} + +ThreadOps::~ThreadOps() throw() { + + if(_queue != 0) { + + OSStatus status = MPDeleteQueue(_queue); + if(status != noErr) + assert(0); + + } + +} + +bool ThreadOps::join(ThreadOps* ops) { + + assert(ops); + assert(ops->_tid != 0); + + OSStatus status = MPWaitOnQueue(ops->_queue, NULL, NULL, NULL, kDurationForever); + + return status == noErr; + +} + +bool ThreadOps::yield() { + + MPYield(); + return true; + +} + +bool ThreadOps::setPriority(ThreadOps* impl, Priority p) { + return true; +} + +bool ThreadOps::getPriority(ThreadOps* impl, Priority& p) { + return true; +} + + +bool ThreadOps::spawn(Runnable* task) { + + OSStatus status = + MPCreateTask(&_dispatch, task, 0UL, _queue, NULL, NULL, 0UL, &_tid); + + return status == noErr; + +} + +OSStatus ThreadOps::_dispatch(void *arg) { + + Runnable* task = reinterpret_cast<Runnable*>(arg); + assert(task); + + // Run the task from the correct context + task->run(); + + // Exit the thread + MPExit(noErr); + return noErr; + +} + +} // namespace ZThread + + diff --git a/dep/src/zthread/macos/ThreadOps.h b/dep/src/zthread/macos/ThreadOps.h new file mode 100644 index 00000000000..c100fcfefe5 --- /dev/null +++ b/dep/src/zthread/macos/ThreadOps.h @@ -0,0 +1,167 @@ +/* + * 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 __ZTTHREADOPS_H__ +#define __ZTTHREADOPS_H__ + +#include "zthread/Priority.h" + +#include <assert.h> +#include <CoreServices/CoreServices.h> +//#include <Multiprocessing.h> +//#include <MultiprocessingInfo.h> + +namespace ZThread { + +class Runnable; + +/** + * @class ThreadOps + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:26:01-0400> + * @version 2.2.0 + * + * This class is an abstraction used to perform various operations on a + * native POSIX thread. + */ +class ThreadOps { + + //! Keep track of the pthreads handle for the native thread + MPQueueID _queue; + MPTaskID _tid; + + ThreadOps(MPTaskID tid) : _queue(0), _tid(tid) { } + + static OSStatus _dispatch(void*); + +public: + + const static ThreadOps INVALID; + + /** + * Create a new ThreadOps to manipulate a native thread. + */ + ThreadOps(); + + ThreadOps(const ThreadOps& ops) : _queue(0), _tid(ops._tid) {} + + ~ThreadOps() throw(); + + inline bool operator==(const ThreadOps& ops) const { + return ops._tid == _tid; + } + + const ThreadOps& operator=(const ThreadOps& ops) { + + assert(_queue == 0); + _tid = ops._tid; + + return *this; + + } + + static ThreadOps self() { + return ThreadOps(MPCurrentTaskID()); + } + + /** + * Activating an instance of ThreadOps will map it onto the currently + * executing thread. + */ + static void activate(ThreadOps* ops) { + + assert(ops); + assert(ops->_tid == 0); + + ops->_tid = MPCurrentTaskID(); + + } + + /** + * Test if this object represents the currently executing + * native thread. + * + * @return bool true if successful + */ + + static bool isCurrent(ThreadOps* ops) { + + assert(ops); + + return MPCurrentTaskID() == ops->_tid; + + } + + /** + * Join a native thread. + * + * @return bool true if successful + */ + static bool join(ThreadOps*); + + /** + * Force the current native thread to yield, letting the scheduler + * give the CPU time to another thread. + * + * @return bool true if successful, false if the operation can't + * be supported. + */ + static bool yield(); + + /** + * Set the priority for the native thread if supported by the + * system. + * + * @param PRIORITY requested priority + * @return bool false if unsuccessful + */ + static bool setPriority(ThreadOps*, Priority); + + /** + * Set the priority for the native thread if supported by the + * system. + * + * @param Thread::PRIORITY& current priority + * @return bool false if unsuccessful + */ + static bool getPriority(ThreadOps*, Priority&); + +protected: + + /** + * Spawn a native thread. + * + * @param ThreadImpl* parent thread + * @param ThreadImpl* child thread being started. + * @param Runnable* task being executed. + * + * @return bool true if successful + */ + bool spawn(Runnable*); + +}; + + +} + +#endif // __ZTTHREADOPS_H__ + diff --git a/dep/src/zthread/macos/UpTimeStrategy.h b/dep/src/zthread/macos/UpTimeStrategy.h new file mode 100644 index 00000000000..f2056e14ca2 --- /dev/null +++ b/dep/src/zthread/macos/UpTimeStrategy.h @@ -0,0 +1,86 @@ +/* + * 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 __ZTTIMESTRATEGY_H__ +#define __ZTTIMESTRATEGY_H__ + +#include <CoreServices/CoreServices.h> +//#include <DriverServices.h> + +namespace ZThread { + + +/** + * @class TimeStrategy + * + * Implement a strategy for time operatons based on UpTime + */ +class TimeStrategy { + + unsigned long _ms; + unsigned long _s; + + public: + + TimeStrategy() { + + // Get the absolute time in milliseconds relative to the program startup + static AbsoluteTime sysUpTime(UpTime()); + AbsoluteTime delta = AbsoluteDeltaToNanoseconds(UpTime(), sysUpTime); + + uint64_t now = *reinterpret_cast<uint64_t*>(&delta) / 1000000; + + _s = now / 1000; + _ms = now % 1000; + + } + + inline unsigned long seconds() const { + return _s; + } + + inline unsigned long milliseconds() const { + return _ms; + } + + unsigned long seconds(unsigned long s) { + + unsigned long z = seconds(); + _s = s; + return z; + + } + + unsigned long milliseconds(unsigned long ms) { + + unsigned long z = milliseconds(); + _ms = ms; + + return z; + + } + +}; + +}; + +#endif // __ZTTIMESTRATEGY_H__ diff --git a/dep/src/zthread/posix/ConditionRecursiveLock.h b/dep/src/zthread/posix/ConditionRecursiveLock.h new file mode 100644 index 00000000000..a46ed35548c --- /dev/null +++ b/dep/src/zthread/posix/ConditionRecursiveLock.h @@ -0,0 +1,146 @@ +/* + * 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 __ZTFASTRECURSIVELOCK_H__ +#define __ZTFASTRECURSIVELOCK_H__ + +#include "zthread/NonCopyable.h" +#include <pthread.h> +#include <errno.h> +#include <assert.h> + +namespace ZThread { + +/** + * @class FastRecursiveLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:28:37-0400> + * @version 2.2.0 + * + * This is an implementation of a FastRecursiveLock for any vannila + * POSIX system. It is based on a condition variable and a mutex; + * because of this it is important to not that its waiting properties + * are not the same as other mutex implementations that generally + * based on spin locks. Under high contention, this implementation may + * be preferable to a spin lock, although refactoring the design of + * code that puts a mutex under alot of preasure may be worth investigating. + */ +class FastRecursiveLock : private NonCopyable { + + //! Serialize state + pthread_mutex_t _mtx; + + //! Wait for lock + pthread_cond_t _cond; + + //! Owner + pthread_t _owner; + + //! Count + volatile unsigned int _count; + +public: + + inline FastRecursiveLock() : _owner(0), _count(0) { + + pthread_mutex_init(&_mtx, 0); + if(pthread_cond_init(&_cond, 0) != 0) { + assert(0); + } + + } + + inline ~FastRecursiveLock() { + + pthread_mutex_destroy(&_mtx); + if(pthread_cond_destroy(&_cond) != 0) { + assert(0); + } + + } + + inline void acquire() { + + pthread_t self = pthread_self(); + pthread_mutex_lock(&_mtx); + + // If the caller does not own the lock, wait until there is no owner + if(_owner != 0 && !pthread_equal(_owner, self)) { + + int status = 0; + do { // ignore signals + status = pthread_cond_wait(&_cond, &_mtx); + } while(status == EINTR && _owner == 0); + + } + + _owner = self; + _count++; + + pthread_mutex_unlock(&_mtx); + + } + + inline bool tryAcquire(unsigned long timeout=0) { + + pthread_t self = pthread_self(); + pthread_mutex_lock(&_mtx); + + // If the caller owns the lock, or there is no owner update the count + bool success = (_owner == 0 || pthread_equal(_owner, self)); + if(success) { + + _owner = self; + _count++; + + } + + pthread_mutex_unlock(&_mtx); + + return success; + + } + + inline void release() { + + assert(pthread_equal(_owner, pthread_self())); + + pthread_mutex_lock(&_mtx); + if(--_count == 0) { + + _owner = 0; + pthread_cond_signal(&_cond); + + } + + pthread_mutex_unlock(&_mtx); + + } + + +}; /* FastRecursiveLock */ + + +} // namespace ZThread + +#endif diff --git a/dep/src/zthread/posix/FastLock.h b/dep/src/zthread/posix/FastLock.h new file mode 100644 index 00000000000..87faf34d4ff --- /dev/null +++ b/dep/src/zthread/posix/FastLock.h @@ -0,0 +1,121 @@ +/* + * 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 __ZTFASTLOCK_H__ +#define __ZTFASTLOCK_H__ + +#include "zthread/Exceptions.h" +#include "zthread/NonCopyable.h" +#include <pthread.h> +#include <assert.h> + +namespace ZThread { + +/** + * @class FastLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:28:07-0400> + * @version 2.2.8 + * + * This is the smallest and fastest synchronization object in the library. + * It is an implementation of fast mutex, an all or nothing exclusive + * lock. It should be used only where you need speed and are willing + * to sacrifice all the state & safety checking provided by the framework + * for speed. + */ +class FastLock : private NonCopyable { + + pthread_mutex_t _mtx; + + public: + + /** + * Create a new FastLock. No safety or state checks are performed. + * + * @exception Initialization_Exception - not thrown + */ + inline FastLock() { + + if(pthread_mutex_init(&_mtx, 0) != 0) + throw Initialization_Exception(); + + } + + /** + * Destroy a FastLock. No safety or state checks are performed. + */ + inline ~FastLock() { + + if(pthread_mutex_destroy(&_mtx) != 0) { + assert(0); + } + + } + + /** + * Acquire an exclusive lock. No safety or state checks are performed. + * + * @exception Synchronization_Exception - not thrown + */ + inline void acquire() { + + if(pthread_mutex_lock(&_mtx) != 0) + throw Synchronization_Exception(); + + } + + /** + * Try to acquire an exclusive lock. No safety or state checks are performed. + * This function returns immediately regardless of the value of the timeout + * + * @param timeout Unused + * @return bool + * @exception Synchronization_Exception - not thrown + */ + inline bool tryAcquire(unsigned long /*timeout*/=0) { + + return (pthread_mutex_trylock(&_mtx) == 0); + + } + + /** + * Release an exclusive lock. No safety or state checks are performed. + * The caller should have already acquired the lock, and release it + * only once. + * + * @exception Synchronization_Exception - not thrown + */ + inline void release() { + + if(pthread_mutex_unlock(&_mtx) != 0) + throw Synchronization_Exception(); + + } + + +}; /* FastLock */ + + +}; + +#endif diff --git a/dep/src/zthread/posix/FtimeStrategy.h b/dep/src/zthread/posix/FtimeStrategy.h new file mode 100644 index 00000000000..5e703970c5c --- /dev/null +++ b/dep/src/zthread/posix/FtimeStrategy.h @@ -0,0 +1,84 @@ +/* + * 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 __ZTTIMESTRATEGY_H__ +#define __ZTTIMESTRATEGY_H__ + +#include <sys/timeb.h> + +#if defined(_MSC_VER) + +# include <time.h> + +# define timeb _timeb +# define ftime _ftime + +#endif + +namespace ZThread { + +/** + * @class TimeStrategy + * + * Implement a strategy for time operatons based on ftime + */ +class TimeStrategy { + + struct timeb _value; + +public: + + TimeStrategy() { + ftime(&_value); + } + + inline unsigned long seconds() const { + return (unsigned long)_value.time; + } + + inline unsigned long milliseconds() const { + return _value.millitm; + } + + unsigned long seconds(unsigned long s) { + + unsigned long z = seconds(); + _value.time = s; + + return z; + + } + + unsigned long milliseconds(unsigned long ms) { + + unsigned long z = milliseconds(); + _value.millitm = (unsigned short)ms; + + return z; + + } + +}; + +}; + +#endif // __ZTTIMESTRATEGY_H__ diff --git a/dep/src/zthread/posix/GetTimeOfDayStrategy.h b/dep/src/zthread/posix/GetTimeOfDayStrategy.h new file mode 100644 index 00000000000..8588807f4f7 --- /dev/null +++ b/dep/src/zthread/posix/GetTimeOfDayStrategy.h @@ -0,0 +1,75 @@ +/* + * 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 __ZTTIMESTRATEGY_H__ +#define __ZTTIMESTRATEGY_H__ + +#include <sys/time.h> + +namespace ZThread { + +/** + * @class TimeStrategy + * + * Implement a strategy for time operatons based on gettimeofday + */ +class TimeStrategy { + + struct timeval _value; + +public: + + TimeStrategy() { + gettimeofday(&_value, 0); + } + + inline unsigned long seconds() const { + return _value.tv_sec; + } + + inline unsigned long milliseconds() const { + return _value.tv_usec/1000; + } + + unsigned long seconds(unsigned long s) { + + unsigned long z = seconds(); + _value.tv_sec = s; + + return z; + + } + + unsigned long milliseconds(unsigned long ms) { + + unsigned long z = milliseconds(); + _value.tv_usec = ms*1000; + + return z; + + } + +}; + +}; + +#endif // __ZTTIMESTRATEGY_H__ diff --git a/dep/src/zthread/posix/Monitor.cxx b/dep/src/zthread/posix/Monitor.cxx new file mode 100644 index 00000000000..bb157dae0dc --- /dev/null +++ b/dep/src/zthread/posix/Monitor.cxx @@ -0,0 +1,257 @@ +/* + * 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. + * + */ + +#include "Monitor.h" +#include "../Debug.h" +#include "../TimeStrategy.h" + +#include <errno.h> +#include <assert.h> +#include <signal.h> + +namespace ZThread { + +Monitor::Monitor() : _owner(0), _waiting(false) { + + pthread_cond_init(&_waitCond, 0); + pthread_mutex_init(&_waitLock, 0); + +} + +Monitor::~Monitor() { + + assert(!_waiting); + + pthread_cond_destroy(&_waitCond); + pthread_mutex_destroy(&_waitLock); + +} + +Monitor::STATE Monitor::wait(unsigned long ms) { + + // Update the owner on first use. The owner will not change, each + // thread waits only on a single Monitor and a Monitor is never + // shared + if(_owner == 0) + _owner = pthread_self(); + + STATE state(INVALID); + + // Serialize access to the state of the Monitor + // and test the state to determine if a wait is needed. + + pthread_mutex_lock(&_waitLock); + + if(pending(ANYTHING)) { + + // Return without waiting when possible + state = next(); + + pthread_mutex_unlock(&_waitLock); + return state; + + } + + // Unlock the external lock if a wait() is probably needed. + // Access to the state is still serial. + _lock.release(); + + // Wait for a transition in the state that is of interest, this + // allows waits to exclude certain flags (e.g. INTERRUPTED) + // for a single wait() w/o actually discarding those flags - + // they will remain set until a wait interested in those flags + // occurs. + // if(!currentState(interest)) { + + // Wait, ignoring signals + _waiting = true; + int status = 0; + + if(ms == 0) { // Wait forever + + do { // ignore signals unless the state is interesting + status = pthread_cond_wait(&_waitCond, &_waitLock); + } while(status == EINTR && !pending(ANYTHING)); + + // Akwaken only when a state is pending + assert(status == 0); + + } else { + + // Find the target time + TimeStrategy t; + + ms += t.milliseconds(); + + unsigned long s = t.seconds() + (ms / 1000); + ms %= 1000; + + // Convert to a timespec + struct ::timespec timeout; + + timeout.tv_sec = s; + timeout.tv_nsec = ms*1000000; + + // Wait ignoring signals until the state is interesting + do { + + // When a timeout occurs, update the state to reflect that. + status = pthread_cond_timedwait(&_waitCond, &_waitLock, &timeout); + + } while(status == EINTR && !pending(ANYTHING)); + + // Akwaken only when a state is pending or when the timeout expired + assert(status == 0 || status == ETIMEDOUT); + + if(status == ETIMEDOUT) + push(TIMEDOUT); + + } + + // Get the next available STATE + state = next(); + _waiting = false; + + pthread_mutex_unlock(&_waitLock); + + // Reaquire the external lock, keep from deadlocking threads calling + // notify(), interrupt(), etc. + + _lock.acquire(); + + return state; + +} + + +bool Monitor::interrupt() { + + // Serialize access to the state + pthread_mutex_lock(&_waitLock); + + bool wasInterruptable = !pending(INTERRUPTED); + bool hadWaiter = _waiting; + + if(wasInterruptable) { + + // Update the state & wake the waiter if there is one + push(INTERRUPTED); + + wasInterruptable = false; + + if(hadWaiter && !masked(Monitor::INTERRUPTED)) + pthread_cond_signal(&_waitCond); + else + wasInterruptable = !pthread_equal(_owner, pthread_self()); + + } + + pthread_mutex_unlock(&_waitLock); + + // Only returns true when an interrupted thread is not currently blocked + return wasInterruptable; + +} + +bool Monitor::isInterrupted() { + + // Serialize access to the state + pthread_mutex_lock(&_waitLock); + + bool wasInterrupted = pending(INTERRUPTED); + + clear(INTERRUPTED); + + pthread_mutex_unlock(&_waitLock); + + return wasInterrupted; + +} + +bool Monitor::isCanceled() { + + // Serialize access to the state + pthread_mutex_lock(&_waitLock); + + bool wasCanceled = examine(CANCELED); + + if(pthread_equal(_owner, pthread_self())) + clear(INTERRUPTED); + + pthread_mutex_unlock(&_waitLock); + + return wasCanceled; + +} + +bool Monitor::cancel() { + + // Serialize access to the state + pthread_mutex_lock(&_waitLock); + + bool wasInterrupted = !pending(INTERRUPTED); + bool hadWaiter = _waiting; + + push(CANCELED); + + if(wasInterrupted) { + + // Update the state & wake the waiter if there is one + push(INTERRUPTED); + + if(hadWaiter && !masked(Monitor::INTERRUPTED)) + pthread_cond_signal(&_waitCond); + + } + + pthread_mutex_unlock(&_waitLock); + + return wasInterrupted; + +} + +bool Monitor::notify() { + + // Serialize access to the state + pthread_mutex_lock(&_waitLock); + + bool wasNotifyable = !pending(INTERRUPTED); + + if(wasNotifyable) { + + // Set the flag and wake the waiter if there + // is one + push(SIGNALED); + + if(_waiting) + pthread_cond_signal(&_waitCond); + + } + + pthread_mutex_unlock(&_waitLock); + + return wasNotifyable; + +} + +} // namespace ZThread + diff --git a/dep/src/zthread/posix/Monitor.h b/dep/src/zthread/posix/Monitor.h new file mode 100644 index 00000000000..945c879f421 --- /dev/null +++ b/dep/src/zthread/posix/Monitor.h @@ -0,0 +1,153 @@ +/* + * 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 __ZTMONITOR_H__ +#define __ZTMONITOR_H__ + +#include "../Status.h" +#include "../FastLock.h" + +namespace ZThread { + +/** + * @class Monitor + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-18T08:16:09-0400> + * @version 2.2.8 + */ +class Monitor : public Status, private NonCopyable { + private: + + //! Serialize access to external objects + FastLock _lock; + + //! Condition variable used to block a thread. + pthread_cond_t _waitCond; + + //! Serialize access to the internal state of the monitor + pthread_mutex_t _waitLock; + + //! Owning thread + pthread_t _owner; + + //! Waiting flag, to avoid uneccessary signals + volatile bool _waiting; + + public: + + typedef Status::STATE STATE; + + //! Create a new monitor. + Monitor(); + + //! Destroy the monitor. + ~Monitor(); + + //! Acquire the lock for this monitor. + inline void acquire() { + _lock.acquire(); + } + + //! Acquire the lock for this monitor. + inline bool tryAcquire() { + return _lock.tryAcquire(); + } + + //! Release the lock for this monitor + inline void release() { + _lock.release(); + } + + /** + * Wait for a state change and atomically unlock the external lock. + * Blocks for an indefinent amount of time. + * + * @return INTERRUPTED if the wait was ended by a interrupt() + * or SIGNALED if the wait was ended by a notify() + * + * @post the external lock is always acquired before this function returns + */ + inline STATE wait() { + return wait(0); + } + + /** + * Wait for a state change and atomically unlock the external lock. + * May blocks for an indefinent amount of time. + * + * @param timeout - maximum time to block (milliseconds) or 0 to + * block indefinently + * + * @return INTERRUPTED if the wait was ended by a interrupt() + * or TIMEDOUT if the maximum wait time expired. + * or SIGNALED if the wait was ended by a notify() + * + * @post the external lock is always acquired before this function returns + */ + STATE wait(unsigned long timeout); + + /** + * Interrupt this monitor. If there is a thread blocked on this monitor object + * it will be signaled and released. If there is no waiter, a flag is set and + * the next attempt to wait() will return INTERRUPTED w/o blocking. + * + * @return false if the thread was previously INTERRUPTED. + */ + bool interrupt(); + + /** + * Notify this monitor. If there is a thread blocked on this monitor object + * it will be signaled and released. If there is no waiter, a flag is set and + * the next attempt to wait() will return SIGNALED w/o blocking, if no other + * flag is set. + * + * @return false if the thread was previously INTERRUPTED. + */ + bool notify(); + + /** + * Check the state of this monitor, clearing the INTERRUPTED status if set. + * + * @return bool true if the monitor was INTERRUPTED. + * @post INTERRUPTED flag cleared if the calling thread owns the monitor. + */ + bool isInterrupted(); + + /** + * Mark the Status CANCELED, and INTERRUPT the montor. + * + * @see interrupt() + */ + bool cancel(); + + /** + * Test the CANCELED Status, clearing the INTERRUPTED status if set. + * + * @return bool + */ + bool isCanceled(); + +}; + +}; + +#endif diff --git a/dep/src/zthread/posix/PriorityOps.h b/dep/src/zthread/posix/PriorityOps.h new file mode 100644 index 00000000000..92da66a9cff --- /dev/null +++ b/dep/src/zthread/posix/PriorityOps.h @@ -0,0 +1,51 @@ +/* + * 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 __ZTPRIORITYOPS_H__ +#define __ZTPRIORITYOPS_H__ + +#include "zthread/Priority.h" +#include "../ThreadOps.h" + +namespace ZThread { + +/** + * @class PriorityOps + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:30:00-0400> + * @version 2.2.0 + * + * This class is an abstraction used to perform various operations on a + * native POSIX thread. + */ +class PriorityOps { + + +public: + + +}; + + +} // namespace ZThread + +#endif // __ZTPRIORITYOPS_H__ diff --git a/dep/src/zthread/posix/TSS.h b/dep/src/zthread/posix/TSS.h new file mode 100644 index 00000000000..931ff348b3d --- /dev/null +++ b/dep/src/zthread/posix/TSS.h @@ -0,0 +1,104 @@ +/* + * 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 __ZTTSS_H__ +#define __ZTTSS_H__ + +#include "zthread/NonCopyable.h" +#include <pthread.h> +#include <assert.h> + +namespace ZThread { + + /** + * @class TSS + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-27T14:18:37-0400> + * @version 2.3.0 + * + * An abstraction for dealing with POSIX thread specific storage (tss). + * Provides get/set and creation/destruction. + */ + template <typename T> + class TSS : private NonCopyable { + + pthread_key_t _key; + + public: + + /** + * Create a new object for accessing tss. + */ + TSS() { + + if(pthread_key_create(&_key, 0) != 0) { + assert(0); // Key creation failed + } + + } + + /** + * Destroy the underlying supoprt for accessing tss with this + * object. + */ + ~TSS() { + + pthread_key_delete(_key); + + } + + /** + * Get the value stored in tss. + * + * @return T + * + * @exception InvalidOp_exception thrown when the tss is not properly initialized + */ + T get() const { + return reinterpret_cast<T>(pthread_getspecific(_key)); + } + + + /** + * Store a value in tss. + * + * @param value T + * @return T old value + * + * @exception InvalidOp_exception thrown when the tss is not properly initialized + */ + T set(T value) const { + + T oldValue = get(); + pthread_setspecific(_key, value); + + return oldValue; + + } + + }; + +} + +#endif + + diff --git a/dep/src/zthread/posix/ThreadOps.cxx b/dep/src/zthread/posix/ThreadOps.cxx new file mode 100644 index 00000000000..e72ef78ada3 --- /dev/null +++ b/dep/src/zthread/posix/ThreadOps.cxx @@ -0,0 +1,147 @@ +/* + * 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. + * + */ + +#include "ThreadOps.h" +#include "zthread/Guard.h" +#include "zthread/Runnable.h" +#include <errno.h> + +#if defined(HAVE_SCHED_YIELD) +# include <sched.h> +#endif + +namespace ZThread { + +const ThreadOps ThreadOps::INVALID(0); + +bool ThreadOps::join(ThreadOps* ops) { + + assert(ops); + assert(ops->_tid != 0); + + int err = 0; + + do { + + err = pthread_join(ops->_tid, NULL); + + } while(err == EINTR); + + return err == 0; + +} + +bool ThreadOps::yield() { + + bool result = false; + +#if defined(HAVE_SCHED_YIELD) + result = sched_yield() == 0; +#endif + + return result; + +} + +bool ThreadOps::setPriority(ThreadOps* impl, Priority p) { + + assert(impl); + + bool result = true; + +#if !defined(ZTHREAD_DISABLE_PRIORITY) + + struct sched_param param; + + switch(p) { + case Low: + param.sched_priority = 0; + break; + case High: + param.sched_priority = 10; + break; + case Medium: + default: + param.sched_priority = 5; + } + + result = pthread_setschedparam(impl->_tid, SCHED_OTHER, ¶m) == 0; + +#endif + + return result; + +} + +bool ThreadOps::getPriority(ThreadOps* impl, Priority& p) { + + assert(impl); + + bool result = true; + +#if !defined(ZTHREAD_DISABLE_PRIORITY) + + struct sched_param param; + int policy = SCHED_OTHER; + + if(result = (pthread_getschedparam(impl->_tid, &policy, ¶m) == 0)) { + + // Convert to one of the PRIORITY values + if(param.sched_priority < 10) + p = Low; + else if(param.sched_priority == 10) + p = Medium; + else + p = High; + + } + +#endif + + return result; + +} + + +bool ThreadOps::spawn(Runnable* task) { + return pthread_create(&_tid, 0, _dispatch, task) == 0; +} + + + +extern "C" void *_dispatch(void *arg) { + + Runnable* task = reinterpret_cast<Runnable*>(arg); + assert(task); + + // Run the task from the correct context + task->run(); + + // Exit the thread + pthread_exit((void**)0); + return (void*)0; + +} + +} // namespace ZThread + + diff --git a/dep/src/zthread/posix/ThreadOps.h b/dep/src/zthread/posix/ThreadOps.h new file mode 100644 index 00000000000..be754c2d659 --- /dev/null +++ b/dep/src/zthread/posix/ThreadOps.h @@ -0,0 +1,154 @@ +/* + * 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 __ZTTHREADOPS_H__ +#define __ZTTHREADOPS_H__ + + +#include "zthread/Priority.h" +#include <pthread.h> +#include <assert.h> + +namespace ZThread { + +class Runnable; + +//! Dispatch function for native pthreads required extern C +//! linkage. +extern "C" void* _dispatch(void*); + +/** + * @class ThreadOps + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:30:25-0400> + * @version 2.2.8 + * + * This class is an abstraction used to perform various operations on a + * native POSIX thread. + */ +class ThreadOps { + + //! Keep track of the pthreads handle for the native thread + pthread_t _tid; + + ThreadOps(pthread_t tid) : _tid(tid) { } + +public: + + const static ThreadOps INVALID; + + /** + * Create a new ThreadOps to manipulate a native thread. + */ + ThreadOps() : _tid(0) { } + + + inline bool operator==(const ThreadOps& ops) const { + return pthread_equal(_tid, ops._tid); + } + + + static ThreadOps self() { + return ThreadOps(pthread_self()); + } + + /** + * Activating an instance of ThreadOps will map it onto the currently + * executing thread. + */ + static void activate(ThreadOps* ops) { + + assert(ops); + assert(ops->_tid == 0); + + ops->_tid = pthread_self(); + + } + + /** + * Test if this object represents the currently executing + * native thread. + * + * @return bool true if successful + */ + + static bool isCurrent(ThreadOps* ops) { + + assert(ops); + + return pthread_equal(pthread_self(), ops->_tid); + + } + + /** + * Join a native thread. + * + * @return bool true if successful + */ + static bool join(ThreadOps*); + + /** + * Force the current native thread to yield, letting the scheduler + * give the CPU time to another thread. + * + * @return bool true if successful, false if the operation can't + * be supported. + */ + static bool yield(); + + /** + * Set the priority for the native thread if supported by the + * system. + * + * @param PRIORITY requested priority + * @return bool false if unsuccessful + */ + static bool setPriority(ThreadOps*, Priority); + + /** + * Set the priority for the native thread if supported by the + * system. + * + * @param Thread::PRIORITY& current priority + * @return bool false if unsuccessful + */ + static bool getPriority(ThreadOps*, Priority&); + +protected: + + /** + * Spawn a native thread. + * + * @param ThreadImpl* parent thread + * @param ThreadImpl* child thread being started. + * @param Runnable* task being executed. + * + * @return bool true if successful + */ + bool spawn(Runnable*); + +}; + + +} + +#endif // __ZTTHREADOPS_H__ diff --git a/dep/src/zthread/solaris/FastRecursiveLock.h b/dep/src/zthread/solaris/FastRecursiveLock.h new file mode 100644 index 00000000000..956e1dbd3ad --- /dev/null +++ b/dep/src/zthread/solaris/FastRecursiveLock.h @@ -0,0 +1,122 @@ +/* + * 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 __ZTFASTRECURSIVELOCK_H__ +#define __ZTFASTRECURSIVELOCK_H__ + +#include "zthread/NonCopyable.h" +#include <pthread.h> + +namespace ZThread { + +/** + * @class FastRecursiveLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:31:23-0400> + * @version 2.2.0 + * + * This FastRecursiveLock implementation uses pthreads mutex attribute + * functions to create a recursive lock. This implementation is not + * specific to solaris and will work on any system that supports + * pthread_mutexattr_settype(). + */ +class FastRecursiveLock : private NonCopyable { + + pthread_mutex_t _mtx; + + /** + * @class Attribute + * + * Utility class to maintain the attribute as long as it is needed. + */ + class Attribute { + + pthread_mutexattr_t _attr; + + public: + + Attribute() { + + if(pthread_mutexattr_init(&_attr) != 0) { + assert(0); + } + + if(pthread_mutexattr_settype(&_attr, PTHREAD_MUTEX_RECURSIVE) != 0) { + assert(0); + } + + } + + ~Attribute() { + + if(pthread_mutexattr_destroy(&_attr) != 0) { + assert(0); + } + + } + + operator pthread_mutexattr_t*() { + return &_attr; + } + + }; + +public: + + inline FastRecursiveLock() { + + static Attribute attr; + pthread_mutex_init(&_mtx, (pthread_mutexattr_t*)attr); + + } + + inline ~FastRecursiveLock() { + + pthread_mutex_destroy(&_mtx); + + } + + inline void acquire() { + + pthread_mutex_lock(&_mtx); + + } + + inline void release() { + + pthread_mutex_unlock(&_mtx); + + } + + inline bool tryAcquire(unsigned long timeout=0) { + + return (pthread_mutex_trylock(&_mtx) == 0); + + } + +}; /* FastRecursiveLock */ + + +} // namespace ZThread + +#endif diff --git a/dep/src/zthread/vanilla/DualMutexRecursiveLock.h b/dep/src/zthread/vanilla/DualMutexRecursiveLock.h new file mode 100644 index 00000000000..ddce7a3cd27 --- /dev/null +++ b/dep/src/zthread/vanilla/DualMutexRecursiveLock.h @@ -0,0 +1,173 @@ +/* + * 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 __ZTFASTRECURSIVELOCK_H__ +#define __ZTFASTRECURSIVELOCK_H__ + +#include "../FastLock.h" +#include "../ThreadOps.h" +#include <assert.h> + +namespace ZThread { + +/** + * @class FastRecursiveLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:31:09-0400> + * @version 2.2.8 + * + * This is a vanilla FastRecursiveLock implementation for a + * system that doesn't provide recurisve locks. This implementation + * is based on using a pair of mutexes, because of this, it performs + * roughly the same as a spin lock would. + */ +class FastRecursiveLock : private NonCopyable { + + //! Lock for blocking + FastLock _blockingLock; + + //! Serialize state + FastLock _stateLock; + + //! Owner + ThreadOps _owner; + + //! Count + volatile unsigned int _count; + + public: + + inline FastRecursiveLock() : _owner(ThreadOps::INVALID), _count(0) { } + + inline ~FastRecursiveLock() { + + assert(_owner == ThreadOps::INVALID); + assert(_count == 0); + + } + + void acquire() { + + ThreadOps self(ThreadOps::self()); + + // Try to lock the blocking mutex first + bool wasLocked = _blockingLock.tryAcquire(); + if(!wasLocked) { + + // Otherwise, grab the lock for the state + _stateLock.acquire(); + + wasLocked = (_owner == self); + if(wasLocked) + _count++; + + _stateLock.release(); + + if(wasLocked) + return; + + // Try to be cooperative + ThreadOps::yield(); + _blockingLock.acquire(); + + } + + // Serialze access to the state + _stateLock.acquire(); + + // Take ownership + assert(_owner == ThreadOps::INVALID || _owner == self); + + _owner = self; + _count++; + + _stateLock.release(); + + } + + + bool tryAcquire(unsigned long timeout = 0) { + + ThreadOps self(ThreadOps::self()); + + // Try to lock the blocking mutex first + bool wasLocked = _blockingLock.tryAcquire(); + if(!wasLocked) { + + // Otherwise, grab the lock for the state + _stateLock.acquire(); + + wasLocked = (_owner == self); + if(wasLocked) + _count++; + + _stateLock.release(); + + return wasLocked; + + } + + // Serialze access to the state + _stateLock.acquire(); + + // Take ownership + assert(_owner == ThreadOps::INVALID || _owner == self); + + _owner = self; + _count++; + + _stateLock.release(); + + return true; + + } + + + void release() { + + // Assume that release is only used by the owning thread, as it + // should be. + assert(_count != 0); + assert(_owner == ThreadOps::self()); + + _stateLock.acquire(); + + // If the lock was owned and the count has reached 0, give up + // ownership and release the blocking lock + if(--_count == 0) { + + _owner = ThreadOps::INVALID; + _blockingLock.release(); + + } + + _stateLock.release(); + + } + + +}; /* FastRecursiveLock */ + +} // namespace ZThread + +#endif diff --git a/dep/src/zthread/vanilla/SimpleAtomicCount.cxx b/dep/src/zthread/vanilla/SimpleAtomicCount.cxx new file mode 100644 index 00000000000..fc63d141d6a --- /dev/null +++ b/dep/src/zthread/vanilla/SimpleAtomicCount.cxx @@ -0,0 +1,100 @@ +/* + * 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 __ZTATOMICCOUNTIMPL_H__ +#define __ZTATOMICCOUNTIMPL_H__ + +#include "zthread/Guard.h" +#include "../FastLock.h" + +#include <assert.h> + +namespace ZThread { + +typedef struct atomic_count_t { + + FastLock lock; + unsigned long count; + + atomic_count_t() : count(0) {} + +} ATOMIC_COUNT; + +AtomicCount::AtomicCount() { + + ATOMIC_COUNT* c = new ATOMIC_COUNT; + _value = reinterpret_cast<void*>(c); + +} + +AtomicCount::~AtomicCount() { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + assert(c->count == 0); + + delete c; + +} + +//! Postfix decrement and return the current value +size_t AtomicCount::operator--(int) { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + + Guard<FastLock> g(c->lock); + return c->count--; + +} + +//! Postfix increment and return the current value +size_t AtomicCount::operator++(int) { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + + Guard<FastLock> g(c->lock); + return c->count++; + +} + +//! Prefix decrement and return the current value +size_t AtomicCount::operator--() { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + + Guard<FastLock> g(c->lock); + return --c->count; + +} + +//! Prefix increment and return the current value +size_t AtomicCount::operator++() { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + + Guard<FastLock> g(c->lock); + return ++c->count; + +} + +}; + +#endif // __ZTATOMICCOUNTIMPL_H__ diff --git a/dep/src/zthread/vanilla/SimpleRecursiveLock.h b/dep/src/zthread/vanilla/SimpleRecursiveLock.h new file mode 100644 index 00000000000..f4f309218b6 --- /dev/null +++ b/dep/src/zthread/vanilla/SimpleRecursiveLock.h @@ -0,0 +1,130 @@ +/* + * 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 __ZTFASTRECURSIVELOCK_H__ +#define __ZTFASTRECURSIVELOCK_H__ + +#include "../FastLock.h" +#include "../ThreadOps.h" +#include <assert.h> + +namespace ZThread { + +/** + * @class FastRecursiveLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:30:59-0400> + * @version 2.2.8 + * + * This implementation of a FastRecursiveLock uses the which ever FastLock + * that is selected to create a recursive spin lock. + */ +class FastRecursiveLock : private NonCopyable { + + FastLock _lock; + + ThreadOps _owner; + + volatile unsigned int _count; + +public: + + inline FastRecursiveLock() : _owner(ThreadOps::INVALID), _count(0) {} + + inline ~FastRecursiveLock() { + + assert(_owner == ThreadOps::INVALID); + assert(_count == 0); + + } + + inline void acquire() { + + ThreadOps self(ThreadOps::self()); + bool wasLocked = false; + + do { + + _lock.acquire(); + + // If there is no owner, or the owner is the caller + // update the count + if(_owner == ThreadOps::INVALID || _owner == self) { + + _owner = self; + _count++; + + wasLocked = true; + + } + + _lock.release(); + + } while(!wasLocked); + + assert(_owner == ThreadOps::self()); + + } + + inline void release() { + + assert(_owner == ThreadOps::self()); + + _lock.acquire(); + + if(--_count == 0) + _owner = ThreadOps::INVALID; + + _lock.release(); + + } + + inline bool tryAcquire(unsigned long timeout=0) { + + ThreadOps self(ThreadOps::self()); + bool wasLocked = false; + + _lock.acquire(); + + if(_owner == ThreadOps::INVALID || _owner == self) { + + _owner = self; + _count++; + + wasLocked = true; + + } + + _lock.release(); + + assert(!wasLocked || _owner == ThreadOps::self()); + return wasLocked; + + } + +}; /* FastRecursiveLock */ + + +} // namespace ZThread + +#endif diff --git a/dep/src/zthread/win32/AtomicCount.cxx b/dep/src/zthread/win32/AtomicCount.cxx new file mode 100644 index 00000000000..84cbf8c3ddc --- /dev/null +++ b/dep/src/zthread/win32/AtomicCount.cxx @@ -0,0 +1,60 @@ +/* + * 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 __ZTATOMICCOUNTIMPL_H__ +#define __ZTATOMICCOUNTIMPL_H__ + +#include <windows.h> +#include <assert.h> + +namespace ZThread { + + +AtomicCount::AtomicCount() { + + _value = reinterpret_cast<void*>(new LONG(0)); + +} + +AtomicCount::~AtomicCount() { + + assert(*reinterpret_cast<LPLONG>(_value) == 0); + delete reinterpret_cast<LPLONG>(_value); + +} + +void AtomicCount::increment() { + + ::InterlockedIncrement(reinterpret_cast<LPLONG>(_value)); + +} + +bool AtomicCount::decrement() { + + LONG v = ::InterlockedDecrement(reinterpret_cast<LPLONG>(_value)); + return static_cast<unsigned long>(v) == 0; + +} + +}; + +#endif // __ZTATOMICCOUNTIMPL_H__ diff --git a/dep/src/zthread/win32/AtomicFastLock.h b/dep/src/zthread/win32/AtomicFastLock.h new file mode 100644 index 00000000000..a714c03789f --- /dev/null +++ b/dep/src/zthread/win32/AtomicFastLock.h @@ -0,0 +1,122 @@ +/* + * 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 __ZTFASTLOCK_H__ +#define __ZTFASTLOCK_H__ + +#include "zthread/NonCopyable.h" +#include <windows.h> +#include <assert.h> + +namespace ZThread { + + +/** + * @class FastLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:32:20-0400> + * @version 2.1.6 + * + * This is the smallest and fastest synchronization object in the library. + * It is an implementation of fast mutex, an all or nothing exclusive + * lock. It should be used only where you need speed and are willing + * to sacrifice all the state & safety checking provided by the framework + * for speed. + * + * The current Platform SDK defines: + * + * LONG InterlockedExchange(LPLONG, LONG) + * LONG InterlockedCompareExchange(LPLONG, LONG, LONG, LONG) + * + * If your compiler complains about LPLONG not being implicitly casted to + * a PVOID, then you should get the SDK update from microsoft or use the + * WIN9X implementation of this class. + * + * ---- + * Because Windows 95 and earlier can run on processors prior to the 486, they + * don't all support the CMPXCHG function, and so Windows 95 an earlier dont support + * InterlockedCompareExchange. For this, you should use the win9x implementation + * of this class + */ +class FastLock : private NonCopyable { + +#pragma pack(push, 8) + LONG volatile _lock; +#pragma pack(pop) + + public: + + /** + * Create a new FastLock + */ + inline FastLock() : _lock(0) { } + + + /** + * Destroy FastLock + */ + inline ~FastLock() { assert(_lock == 0); } + + /** + * Lock the fast Lock, no error check. + * + * @exception None + */ + inline void acquire() { + + while (::InterlockedCompareExchange(const_cast<LPLONG>(&_lock), 1, 0) != 0) + ::Sleep(0); + + } + + + /** + * Release the fast Lock, no error check. + * + * @exception None + */ + inline void release() { + + ::InterlockedExchange(const_cast<LPLONG>(&_lock), (LONG)0); + + } + + /** + * Try to acquire an exclusive lock. No safety or state checks are performed. + * This function returns immediately regardless of the value of the timeout + * + * @param timeout Unused + * @return bool + * @exception Synchronization_Exception - not thrown + */ + inline bool tryAcquire(unsigned long timeout=0) { + + return ::InterlockedCompareExchange(const_cast<LPLONG>(&_lock), 1, 0) == 0; + + } + +}; /* FastLock */ + + +}; +#endif diff --git a/dep/src/zthread/win32/AtomicFastRecursiveLock.h b/dep/src/zthread/win32/AtomicFastRecursiveLock.h new file mode 100644 index 00000000000..c6a61b03b5d --- /dev/null +++ b/dep/src/zthread/win32/AtomicFastRecursiveLock.h @@ -0,0 +1,158 @@ +/* + * 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 __ZTFASTRECURSIVELOCK_H__ +#define __ZTFASTRECURSIVELOCK_H__ + +#include "zthread/NonCopyable.h" +#include <windows.h> +#include <assert.h> + +namespace ZThread { + + +/** + * @class FastRecursiveLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:32:34-0400> + * @version 2.1.6 + * + * This is the smaller and faster implementation of a RecursiveLock. + * A single thread can acquire the mutex any number of times, but it + * must perform a release for each acquire(). Other threads are blocked + * until a thread has released all of its locks on the mutex. + * + * This particular implementation performs fewer safety checks. Like + * the FastLock implementation, any waiting caused by an acquire() request + * is not interruptable. This is so that the mutex can have the fastest + * response time for a time critical application while still having a good + * degree of reliability. + * + * TryEnterCriticalSection() does not work at all on some systems, so its + * not used. + * + * + * The current Platform SDK defines: + * + * LONG InterlockedExchange(LPLONG, LONG) + * LONG InterlockedCompareExchange(LPLONG, LONG, LONG, LONG) + * + * If your compiler complains about LPLONG not being implicitly casted to + * a PVOID, then you should get the SDK update from microsoft or use the + * WIN9X implementation of this class. + * + * ---- + * Because Windows 95 and earlier can run on processors prior to the 486, they + * don't all support the CMPXCHG function, and so Windows 95 an earlier dont support + * InterlockedCompareExchange. If you define ZT_WIN9X, you'll get a version of the + * FastLock that uses the XCHG instruction + */ +class FastRecursiveLock : private NonCopyable { + +// Add def for mingw32 or other non-ms compiler to align on 64-bit +// boundary +#pragma pack(push, 8) + LONG volatile _lock; +#pragma pack(pop) + LONG _count; + + public: + + /** + * Create a new FastRecursiveLock + */ + inline FastRecursiveLock() : _lock(0), _count(0) { } + + + /** + * Destroy FastLock + */ + inline ~FastRecursiveLock() { + assert(_lock == 0); + } + + /** + * Lock the fast Lock, no error check. + * + * @exception None + */ + inline void acquire() { + + DWORD id = ::GetCurrentThreadId(); + + // Take ownership if the lock is free or owned by the calling thread + do { + + DWORD owner = (DWORD)::InterlockedCompareExchange(const_cast<LPLONG>(&_lock), id, 0); + if(owner == 0 || owner == id) + break; + + ::Sleep(0); + + } while(1); + + _count++; + + } + + + /** + * Release the fast Lock, no error check. + * + * @exception None + */ + inline void release() { + + if(--_count == 0) + ::InterlockedExchange(const_cast<LPLONG>(&_lock), 0); + + } + + /** + * Try to acquire an exclusive lock. No safety or state checks are performed. + * This function returns immediately regardless of the value of the timeout + * + * @param timeout Unused + * @return bool + * @exception Synchronization_Exception - not thrown + */ + inline bool tryAcquire(unsigned long timeout=0) { + + DWORD id = ::GetCurrentThreadId(); + DWORD owner = (DWORD)::InterlockedCompareExchange(const_cast<LPLONG>(&_lock), id, 0); + + if(owner == 0 || owner == id) { + _count++; + return true; + } + + return false; + + } + + +}; /* FastRecursiveLock */ + + +}; +#endif diff --git a/dep/src/zthread/win32/FastLock.h b/dep/src/zthread/win32/FastLock.h new file mode 100644 index 00000000000..2e9fe829af6 --- /dev/null +++ b/dep/src/zthread/win32/FastLock.h @@ -0,0 +1,146 @@ +/* + * 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 __ZTFASTLOCK_H__ +#define __ZTFASTLOCK_H__ + +#include "zthread/Exceptions.h" +#include "zthread/NonCopyable.h" +#include "../ThreadOps.h" +#include <windows.h> +#include <assert.h> + +namespace ZThread { + + /** + * @class FastLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:32:44-0400> + * @version 2.2.11 + * + * This FastLock implementation is based on a Win32 Mutex + * object. This will perform better under high contention, + * but will not be as fast as the spin lock under reasonable + * circumstances. + */ + class FastLock : private NonCopyable { + + HANDLE _hMutex; +#ifndef NDEBUG + volatile bool _locked; +#endif + + public: + + /** + * Create a new FastLock + */ + FastLock() { + +#ifndef NDEBUG + _locked = false; +#endif + + _hMutex = ::CreateMutex(0, 0, 0); + assert(_hMutex != NULL); + if(_hMutex == NULL) + throw Initialization_Exception(); + + } + + + ~FastLock() { + ::CloseHandle(_hMutex); + } + + void acquire() { + + if(::WaitForSingleObject(_hMutex, INFINITE) != WAIT_OBJECT_0) { + assert(0); + throw Synchronization_Exception(); + } + +#ifndef NDEBUG + + // Simulate deadlock to provide consistent behavior. This + // will help avoid errors when porting. Avoiding situations + // where a FastMutex mistakenly behaves as a recursive lock. + + while(_locked) + ThreadOps::yield(); + + _locked = true; + +#endif + + } + + void release() { + +#ifndef NDEBUG + _locked = false; +#endif + + if(::ReleaseMutex(_hMutex) == 0) { + assert(0); + throw Synchronization_Exception(); + } + + } + + + bool tryAcquire(unsigned long timeout = 0) { + + switch(::WaitForSingleObject(_hMutex, timeout)) { + case WAIT_OBJECT_0: + +#ifndef NDEBUG + + // Simulate deadlock to provide consistent behavior. This + // will help avoid errors when porting. Avoiding situations + // where a FastMutex mistakenly behaves as a recursive lock. + + while(_locked) + ThreadOps::yield(); + + _locked = true; + +#endif + + return true; + case WAIT_TIMEOUT: + return false; + default: + break; + } + + assert(0); + throw Synchronization_Exception(); + + } + + }; + +} // namespace ZThread + +#endif diff --git a/dep/src/zthread/win32/FastRecursiveLock.h b/dep/src/zthread/win32/FastRecursiveLock.h new file mode 100644 index 00000000000..e1a6e7cd692 --- /dev/null +++ b/dep/src/zthread/win32/FastRecursiveLock.h @@ -0,0 +1,109 @@ +/* + * 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 __ZTFASTRECURSIVELOCK_H__ +#define __ZTFASTRECURSIVELOCK_H__ + +#include "zthread/Exceptions.h" +#include "zthread/NonCopyable.h" +#include <windows.h> +#include <assert.h> + +namespace ZThread { + + +/** + * @class FastRecursiveLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:32:56-0400> + * @version 2.2.11 + * + * This FastRecursiveLock implementation is based on a Win32 Mutex + * object. This will perform better under high contention, + * but will not be as fast as the spin lock under reasonable + * circumstances. + */ +class FastRecursiveLock : private NonCopyable { + + HANDLE _hMutex; + volatile unsigned int _count; + + public: + + /** + * Create a new FastRecursiveLock + */ + FastRecursiveLock() : _count(0) { + + _hMutex = ::CreateMutex(0, 0, 0); + assert(_hMutex != NULL); + if(_hMutex == NULL) + throw Initialization_Exception(); + + } + + + ~FastRecursiveLock() { + ::CloseHandle(_hMutex); + } + + + void acquire() { + + if(::WaitForSingleObject(_hMutex, INFINITE) != WAIT_OBJECT_0) { + assert(0); + throw Synchronization_Exception(); + } + + } + + void release() { + + if(::ReleaseMutex(_hMutex) == 0) { + assert(0); + throw Synchronization_Exception(); + } + + } + + bool tryAcquire(unsigned long) { + + switch(::WaitForSingleObject(_hMutex, 0)) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + default: + break; + } + + assert(0); + throw Synchronization_Exception(); + + } + +}; /* FastRecursiveLock */ + +} // namespace ZThread + +#endif diff --git a/dep/src/zthread/win32/Monitor.cxx b/dep/src/zthread/win32/Monitor.cxx new file mode 100644 index 00000000000..6e69487c054 --- /dev/null +++ b/dep/src/zthread/win32/Monitor.cxx @@ -0,0 +1,242 @@ +/* + * 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. + * + */ + +#include "Monitor.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +using namespace ZThread; + +Monitor::Monitor() : _owner(0), _waiting(false) { + + _handle = ::CreateEvent(0, TRUE, FALSE, 0); + if(_handle == NULL) { + assert(0); + } + +} + +Monitor::~Monitor() { + + assert(!_waiting); + + ::CloseHandle(_handle); + +} + +Monitor::STATE Monitor::wait(unsigned long ms) { + + // Update the owner on first use. The owner will not change, each + // thread waits only on a single Monitor and a Monitor is never + // shared + if(_owner == 0) + _owner = ::GetCurrentThreadId(); + + STATE state; //(INVALID); + + // Serialize access to the state of the Monitor + // and test the state to determine if a wait is needed. + _waitLock.acquire(); + + if(pending(ANYTHING)) { + + // Return without waiting when possible + state = next(); + + _waitLock.release(); + return state; + + } + // Unlock the external lock if a wait() is probably needed. + // Access to the state is still serial. + _lock.release(); + + // Wait for a transition in the state that is of interest, this + // allows waits to exclude certain flags (e.g. INTERRUPTED) + // for a single wait() w/o actually discarding those flags - + // they will remain set until a wait interested in those flags + // occurs. + // if(!currentState(interest)) { + + // Wait, ignoring signals + _waiting = true; + + // Block until the event is set. + _waitLock.release(); + + // The event is manual reset so this lack of atmoicity will not + // be an issue + + DWORD dwResult = + ::WaitForSingleObject(_handle, ((ms == 0) ? INFINITE : (DWORD)ms)); + + // Reacquire serialized access to the state + _waitLock.acquire(); + + // Awaken only when the event is set or the timeout expired + assert(dwResult == WAIT_OBJECT_0 || dwResult == WAIT_TIMEOUT); + + if(dwResult == WAIT_TIMEOUT) + push(TIMEDOUT); + + // Get the next available STATE + state = next(); + _waiting = false; + + ::ResetEvent(_handle); + + // Acquire the internal lock & release the external lock + _waitLock.release(); + + // Reaquire the external lock, keep from deadlocking threads calling + // notify(), interrupt(), etc. + _lock.acquire(); + + return state; + +} + + +bool Monitor::interrupt() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasInterruptable = !pending(INTERRUPTED); + bool hadWaiter = _waiting; + + if(wasInterruptable) { + + // Update the state & wake the waiter if there is one + push(INTERRUPTED); + + wasInterruptable = false; + + if(hadWaiter && !masked(Monitor::INTERRUPTED)) { + + // Blocked on a synchronization object + if(::SetEvent(_handle) == FALSE) { + assert(0); + } + + } else + wasInterruptable = !(_owner == ::GetCurrentThreadId()); + + } + + _waitLock.release(); + + // Only returns true when an interrupted thread is not currently blocked + return wasInterruptable; + +} + +bool Monitor::isInterrupted() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasInterrupted = pending(INTERRUPTED); + clear(INTERRUPTED); + + _waitLock.release(); + + return wasInterrupted; + +} + + +bool Monitor::notify() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasNotifyable = !pending(INTERRUPTED); + + if(wasNotifyable) { + + // Set the flag and wake the waiter if there + // is one + push(SIGNALED); + + // If there is a waiter then send the signal. + if(_waiting) + if(::SetEvent(_handle) == FALSE) { + assert(0); + } + + } + + _waitLock.release(); + + return wasNotifyable; + +} + + +bool Monitor::cancel() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasInterrupted = !pending(INTERRUPTED); + bool hadWaiter = _waiting; + + push(CANCELED); + + if(wasInterrupted) { + + // Update the state & wake the waiter if there is one + push(INTERRUPTED); + + // If there is a waiter then send the signal. + if(hadWaiter && !masked(Monitor::INTERRUPTED)) + if(::SetEvent(_handle) == FALSE) { + assert(0); + } + + } + + _waitLock.release(); + + return wasInterrupted; + +} + +bool Monitor::isCanceled() { + + // Serialize access to the state + _waitLock.acquire(); + + bool wasCanceled = examine(CANCELED); + + if(_owner == ::GetCurrentThreadId()) + clear(INTERRUPTED); + + _waitLock.release(); + + return wasCanceled; + +} + diff --git a/dep/src/zthread/win32/Monitor.h b/dep/src/zthread/win32/Monitor.h new file mode 100644 index 00000000000..7073343b7f8 --- /dev/null +++ b/dep/src/zthread/win32/Monitor.h @@ -0,0 +1,153 @@ +/* + * 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 __ZTMONITOR_H__ +#define __ZTMONITOR_H__ + +#include "../Status.h" +#include "../FastLock.h" + +namespace ZThread { + +/** + * @class Monitor + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:33:10-0400> + * @version 2.2.11 + */ +class Monitor : public Status, private NonCopyable { + + //! Serialize access to external objects + FastLock _lock; + + //! Event used to block thread + HANDLE _handle; + + //! Serialize access to the internal state of the monitor + FastLock _waitLock; + + //! Owning thread + DWORD _owner; + + //! Waiting flag, to avoid uneccessary signals + volatile bool _waiting; + + //! State of the monitor + volatile int _state; + + public: + + //! Create a new monitor. + Monitor(); + + //! Destroy the monitor. + ~Monitor(); + + //! Acquire the external lock for this monitor. + inline void acquire() { + _lock.acquire(); + } + + //! Try to acquire the external lock for this monitor. + inline bool tryAcquire() { + return _lock.tryAcquire(); + } + + //! Release the external lock for this monitor. + inline void release() { + _lock.release(); + } + + /** + * Wait for a state change and atomically unlock the external lock. + * Blocks for an indefinent amount of time. + * + * @return INTERRUPTED if the wait was ended by a interrupt() + * or SIGNALED if the wait was ended by a notify() + * + * @post the external lock is always acquired before this function returns + */ + inline STATE wait() { + return wait(0); + } + + /** + * Wait for a state change and atomically unlock the external lock. + * May blocks for an indefinent amount of time. + * + * @param timeout - maximum time to block (milliseconds) or 0 to + * block indefinently + * + * @return INTERRUPTED if the wait was ended by a interrupt() + * or TIMEDOUT if the maximum wait time expired. + * or SIGNALED if the wait was ended by a notify() + * + * @post the external lock is always acquired before this function returns + */ + STATE wait(unsigned long timeout); + + /** + * Interrupt this monitor. If there is a thread blocked on this monitor object + * it will be signaled and released. If there is no waiter, a flag is set and + * the next attempt to wait() will return INTERRUPTED w/o blocking. + * + * @return false if the thread was previously INTERRUPTED. + */ + bool interrupt(); + + /** + * Notify this monitor. If there is a thread blocked on this monitor object + * it will be signaled and released. If there is no waiter, a flag is set and + * the next attempt to wait() will return SIGNALED w/o blocking, if no other + * flag is set. + * + * @return false if the thread was previously INTERRUPTED. + */ + bool notify(); + + /** + * Check the state of this monitor, clearing the INTERRUPTED status if set. + * + * @return bool true if the monitor was INTERRUPTED. + * @post INTERRUPTED flag cleared if the calling thread owns the monitor. + */ + bool isInterrupted(); + + /** + * Mark the Status CANCELED, and INTERRUPT the montor. + * + * @see interrupt() + */ + bool cancel(); + + /** + * Test the CANCELED Status, clearing the INTERRUPTED status if set. + * + * @return bool + */ + bool isCanceled(); + +}; + +}; + +#endif diff --git a/dep/src/zthread/win32/PerformanceCounterStrategy.h b/dep/src/zthread/win32/PerformanceCounterStrategy.h new file mode 100644 index 00000000000..95b526830b3 --- /dev/null +++ b/dep/src/zthread/win32/PerformanceCounterStrategy.h @@ -0,0 +1,108 @@ +/* + * 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 __ZTTIMESTRATEGY_H__ +#define __ZTTIMESTRATEGY_H__ + +#include <assert.h> +#include <windows.h> + +namespace ZThread { + +/** + * @class PerformanceCounterStrategy + * + * Implement a strategy for time operatons based on + * Windows QueryPerformanceXXX() functions. + * This only (erroneously) considers the lower 32 bits. + */ +class TimeStrategy { + + unsigned long _secs; + unsigned long _millis; + +public: + + TimeStrategy() { + + // Keep track of the relative time the program started + static LARGE_INTEGER i; + static BOOL valid(::QueryPerformanceCounter(&i)); + + assert(valid == TRUE); + + LARGE_INTEGER j; + ::QueryPerformanceCounter(&j); + + j.LowPart -= i.LowPart; + j.LowPart /= frequency(); + + // Mask off the high bits + _millis = (unsigned long)j.LowPart / 1000; + _secs = (unsigned long)j.LowPart - _millis; + + } + + unsigned long seconds() const { + return _secs; + } + + unsigned long milliseconds() const { + return _millis; + } + + unsigned long seconds(unsigned long s) { + + unsigned long z = seconds(); + + _secs = s; + return z; + + } + + unsigned long milliseconds(unsigned long ms) { + + unsigned long z = milliseconds(); + + _millis = ms; + return z; + + } + +private: + + // Get the frequency + static DWORD frequency() { + + static LARGE_INTEGER i; + static BOOL valid(::QueryPerformanceFrequency(&i)); + + assert(valid == TRUE); + return i.LowPart; + + } + +}; + +}; + +#endif // __ZTTIMESTRATEGY_H__ diff --git a/dep/src/zthread/win32/TSS.h b/dep/src/zthread/win32/TSS.h new file mode 100644 index 00000000000..2400830f06a --- /dev/null +++ b/dep/src/zthread/win32/TSS.h @@ -0,0 +1,108 @@ +/* + * 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 __ZTTSS_H__ +#define __ZTTSS_H__ + +#include <windows.h> + +namespace ZThread { + + /** + * @class TSS + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-27T14:18:43-0400> + * @version 2.3.0 + * + * An abstraction for dealing with WIN32 thread specific storage (tss). + * Provides get/set and creation/destruction. + */ + template <typename T> + class TSS { + + DWORD _key; + bool _valid; + + public: + + /** + * Create a new object for accessing tss. The def + */ + TSS() { + + _key = ::TlsAlloc(); + _valid = (_key != 0xFFFFFFFF); + + } + + /** + * Destroy the underlying supoprt for accessing tss with this + * object. + */ + virtual ~TSS() { + + if(_valid) + ::TlsFree(_key); + + } + + /** + * Get the value stored in tss. + * + * @return T + * + * @exception InvalidOp_exception thrown when the tss is not properly initialized + */ + inline T get() const { + + if(!_valid) + throw InvalidOp_Exception(); + + return static_cast<T>(::TlsGetValue(_key)); + + } + + /** + * Store a value in tss. + * + * @param value T + * @return T old value + * + * @exception InvalidOp_exception thrown when the tss is not properly initialized + */ + inline T set(T value) const { + + T oldValue = get(); + ::TlsSetValue(_key, value); + + return oldValue; + + } + + + }; + +} + +#endif + + diff --git a/dep/src/zthread/win32/ThreadOps.cxx b/dep/src/zthread/win32/ThreadOps.cxx new file mode 100644 index 00000000000..6e8fb8d3b71 --- /dev/null +++ b/dep/src/zthread/win32/ThreadOps.cxx @@ -0,0 +1,197 @@ +/* + * 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. + * + */ + +#include "ThreadOps.h" +#include "zthread/Runnable.h" +#include <process.h> + +namespace ZThread { + +const ThreadOps ThreadOps::INVALID(0); + +/** + * Detect OS at runtime and attempt to locate the SwitchToThread + * function, which will assist in making the spin lock implementation + * which use ThreadOps::yield() a bit fairer. + */ +class YieldOps { + + typedef BOOL (*Yield)(void); + Yield _fnYield; + +public: + + YieldOps() : _fnYield(NULL) { + + OSVERSIONINFO v; + v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if(::GetVersionEx(&v) && (v.dwPlatformId == VER_PLATFORM_WIN32_NT)) { + + // Uses GetModuleHandle() so the reference count on the dll is + // not affected. There is a warning about race conditions involving + // this function being called as FreeLibrary() completes; however + // nearly all win32 applications load this particular and will keep it + // in memory until the process exits. + HINSTANCE hInst = ::GetModuleHandle("Kernel32.dll"); + if(hInst != NULL) + _fnYield = (Yield)::GetProcAddress(hInst, "SwitchToThread"); + + // REMIND: possibly need to use _T() macro for these strings + } + + } + + bool operator()() { + + // Attempt to yield using the best function available + if(!_fnYield || !_fnYield()) + ::Sleep(0); + + return true; + + } + +}; + +bool ThreadOps::join(ThreadOps* ops) { + + assert(ops); + assert(ops->_tid != 0); + assert(ops->_hThread != NULL); + + if(::WaitForSingleObjectEx(ops->_hThread, INFINITE, FALSE) != WAIT_OBJECT_0) + return false; + + ::CloseHandle(ops->_hThread); + ops->_hThread = NULL; + + return true; + +} + +bool ThreadOps::yield() { + + static YieldOps yielder; + + yielder(); + + return true; + +} + +bool ThreadOps::setPriority(ThreadOps* impl, Priority p) { + + assert(impl); + +#if !defined(ZTHREAD_DISABLE_PRIORITY) + + bool result; + + // Convert + int n; + switch(p) { + case Low: + n = THREAD_PRIORITY_BELOW_NORMAL; + break; + case High: + n = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case Medium: + default: + n = THREAD_PRIORITY_NORMAL; + } + + + result = (::SetThreadPriority(impl->_hThread, n) != THREAD_PRIORITY_ERROR_RETURN); + return result; + +#else + + return true; + +#endif + +} + +bool ThreadOps::getPriority(ThreadOps* impl, Priority& p) { + + assert(impl); + bool result = true; + +#if !defined(ZTHREAD_DISABLE_PRIORITY) + + // Convert to one of the PRIORITY values + switch(::GetThreadPriority(impl->_hThread)) { + case THREAD_PRIORITY_ERROR_RETURN: + result = false; + case THREAD_PRIORITY_BELOW_NORMAL: + p = Low; + break; + case THREAD_PRIORITY_ABOVE_NORMAL: + p = High; + break; + case THREAD_PRIORITY_NORMAL: + default: + p = Medium; + } + +#endif + + return result; + +} + + +bool ThreadOps::spawn(Runnable* task) { + +// Start the thread. +#if defined(HAVE_BEGINTHREADEX) + _hThread = (HANDLE)::_beginthreadex(0, 0, &_dispatch, task, 0, (unsigned int*)&_tid); +#else + _hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&_dispatch, task, 0, (DWORD*)&_tid); +#endif + + return _hThread != NULL; + +} + +unsigned int __stdcall ThreadOps::_dispatch(void *arg) { + + Runnable* task = reinterpret_cast<Runnable*>(arg); + assert(task); + + // Run the task from the correct context + task->run(); + + // Exit the thread +#if defined(HAVE_BEGINTHREADEX) + ::_endthreadex(0); +#else + ExitThread(0); +#endif + + return 0; + +} + +} diff --git a/dep/src/zthread/win32/ThreadOps.h b/dep/src/zthread/win32/ThreadOps.h new file mode 100644 index 00000000000..4a3eeac2ed9 --- /dev/null +++ b/dep/src/zthread/win32/ThreadOps.h @@ -0,0 +1,152 @@ +/* + * 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 __ZTTHREADOPS_H__ +#define __ZTTHREADOPS_H__ + +#include "zthread/Priority.h" +#include <windows.h> +#include <assert.h> + +namespace ZThread { + +class Runnable; + +/** + * @class ThreadOps + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:33:59-0400> + * @version 2.2.8 + * + * This class is an abstraction used to perform various operations on a + * native WIN32 thread. + */ +class ThreadOps { + + //! Dispatch function for native thread + static unsigned int __stdcall _dispatch(void*); + + //! TID while the thread is executing. + HANDLE _hThread; + DWORD _tid; + + ThreadOps(DWORD tid) : _tid(tid) { } + + public: + + const static ThreadOps INVALID; + + /** + * Create a new ThreadOps to represent a native thread. + */ + ThreadOps() : _tid(0), _hThread(NULL) {} + + + inline bool operator==(const ThreadOps& ops) const { + return _tid == ops._tid; + } + + + static ThreadOps self() { + return ThreadOps(::GetCurrentThreadId()); + } + + /** + * Update the native tid for this thread so it matches the current + * thread. + */ + static void activate(ThreadOps* ops) { + + assert(ops); + assert(ops->_tid == 0); + + ops->_tid = ::GetCurrentThreadId(); + + } + + /** + * Test if this object representative of the currently executing + * native thread. + * + * @return bool true if successful + */ + static bool isCurrent(ThreadOps* ops) { + + assert(ops); + + return ops->_tid == ::GetCurrentThreadId(); + + } + + /** + * Join a native thread. + * + * @return bool true if successful + */ + static bool join(ThreadOps*); + + /** + * Force the current native thread to yield, letting the scheduler + * give the CPU time to another thread. + * + * @return bool true if successful + */ + static bool yield(); + + /** + * Set the priority for the native thread if supported by the + * system. + * + * @param PRIORITY requested priority + * @return bool false if unsuccessful + */ + static bool setPriority(ThreadOps*, Priority); + + /** + * Set the priority for the native thread if supported by the + * system. + * + * @param Thread::PRIORITY& current priority + * @return bool false if unsuccessful + */ + static bool getPriority(ThreadOps*, Priority&); + +protected: + + /** + * Spawn a native thread. + * + * @param ThreadImpl* parent thread + * @param ThreadImpl* child thread being started. + * @param Runnable* task being executed. + * + * @return bool true if successful + */ + bool spawn(Runnable*); + + +}; + + +} + +#endif diff --git a/dep/src/zthread/win9x/AtomicCount.cxx b/dep/src/zthread/win9x/AtomicCount.cxx new file mode 100644 index 00000000000..2bf07dcd2e7 --- /dev/null +++ b/dep/src/zthread/win9x/AtomicCount.cxx @@ -0,0 +1,115 @@ +/* + * 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 __ZTATOMICCOUNTIMPL_H__ +#define __ZTATOMICCOUNTIMPL_H__ + +#include <windows.h> +#include <assert.h> + +namespace ZThread { + +typedef struct atomic_count_t { + + CRITICAL_SECTION cs; + size_t count; + + atomic_count_t() : count(0) {} + +} ATOMIC_COUNT; + +AtomicCount::AtomicCount() { + + ATOMIC_COUNT* c = new ATOMIC_COUNT; + _value = reinterpret_cast<void*>(c); + ::InitializeCriticalSection(&c->cs); + +} + +AtomicCount::~AtomicCount() { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + assert(c->count == 0); + ::DeleteCriticalSection(&c->cs); + delete c; + +} + +//! Postfix decrement and return the current value +size_t AtomicCount::operator--(int) { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + size_t value; + + ::EnterCriticalSection(&c->cs); + value = c->count--; + ::LeaveCriticalSection(&c->cs); + + return value; + +} + +//! Postfix increment and return the current value +size_t AtomicCount::operator++(int) { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + size_t value; + + ::EnterCriticalSection(&c->cs); + value = c->count++; + ::LeaveCriticalSection(&c->cs); + + return value; + +} + +//! Prefix decrement and return the current value +size_t AtomicCount::operator--() { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + size_t value; + + ::EnterCriticalSection(&c->cs); + value = --c->count; + ::LeaveCriticalSection(&c->cs); + + return value; + +} + +//! Prefix increment and return the current value +size_t AtomicCount::operator++() { + + ATOMIC_COUNT* c = reinterpret_cast<ATOMIC_COUNT*>(_value); + size_t value; + + ::EnterCriticalSection(&c->cs); + value = ++c->count; + ::LeaveCriticalSection(&c->cs); + + return value; + +} + +}; + +#endif // __ZTATOMICCOUNTIMPL_H__ diff --git a/dep/src/zthread/win9x/AtomicFastLock.h b/dep/src/zthread/win9x/AtomicFastLock.h new file mode 100644 index 00000000000..5b50a9c7337 --- /dev/null +++ b/dep/src/zthread/win9x/AtomicFastLock.h @@ -0,0 +1,130 @@ +/* + * 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 __ZTFASTLOCK_H__ +#define __ZTFASTLOCK_H__ + +#include "zthread/NonCopyable.h" +#include <windows.h> +#include <assert.h> + +namespace ZThread { + + +/** + * @class FastLock + * + * @author Eric Crahen <http://www.code-foo.com> + * @date <2003-07-16T23:31:51-0400> + * @version 2.2.0 + * + * This uses a custom spin lock based on the older swap & compare approach + * using the XCHG instruction. You should only use this is you *absolutely* need + * to use an older system, like Windows 95. If you can, use the Win32 version. + * + * Because Windows 95 and earlier can run on processors prior to the 486, they + * don't all support the CMPXCHG function, and so Windows 95 an earlier dont support + * InterlockedCompareExchange. + * + * This is asm inlined for microsoft visual c, it needs to be changed in order to + * compile with gcc, or another win32 compiler - but more likely than not you'll + * be using the Win32 version on those compilers and this won't be a problem. + */ +class FastLock : private NonCopyable { + +// Add def for mingw32 or other non-ms compiler to align on 32-bit boundary +#pragma pack(push, 4) + unsigned char volatile _lock; +#pragma pack(pop) + + public: + + /** + * Create a new FastLock + */ + inline FastLock() : _lock(0) { } + + + inline ~FastLock() { + assert(_lock == 0); + } + + inline void acquire() { + + DWORD dw = (DWORD)&_lock; + + _asm { // swap & compare + spin_lock: + + mov al, 1 + mov esi, dw + xchg [esi], al + and al,al + jz spin_locked + } + + ::Sleep(0); + + _asm { + jmp spin_lock + spin_locked: + } + + } + + inline void release() { + + DWORD dw = (DWORD)&_lock; + + _asm { + mov al, 0 + mov esi, dw + xchg [esi], al + } + + + } + + inline bool tryAcquire(unsigned long timeout=0) { + + volatile DWORD dw = (DWORD)&_lock; + volatile DWORD result; + + _asm { + + mov al, 1 + mov esi, dw + xchg [esi], al + and al,al + mov esi, result + xchg [esi], al + } + + return result == 0; + + } + +}; /* Fast Lock */ + +} // namespace ZThread + +#endif |