Mercurial > projects > ldc
diff druntime/src/common/core/sync/mutex.d @ 1458:e0b2d67cfe7c
Added druntime (this should be removed once it works).
author | Robert Clipsham <robert@octarineparrot.com> |
---|---|
date | Tue, 02 Jun 2009 17:43:06 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/druntime/src/common/core/sync/mutex.d Tue Jun 02 17:43:06 2009 +0100 @@ -0,0 +1,268 @@ +/** + * The mutex module provides a primitive for maintaining mutually exclusive + * access. + * + * Copyright: Copyright Sean Kelly 2005 - 2009. + * License: <a href="http://www.boost.org/LICENSE_1_0.txt>Boost License 1.0</a>. + * Authors: Sean Kelly + * + * Copyright Sean Kelly 2005 - 2009. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +module core.sync.mutex; + + +public import core.sync.exception; + +version( Win32 ) +{ + private import core.sys.windows.windows; +} +else version( Posix ) +{ + private import core.sys.posix.pthread; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Mutex +// +// void lock(); +// void unlock(); +// bool tryLock(); +//////////////////////////////////////////////////////////////////////////////// + + +/** + * This class represents a general purpose, recursive mutex. + */ +class Mutex : + Object.Monitor +{ + //////////////////////////////////////////////////////////////////////////// + // Initialization + //////////////////////////////////////////////////////////////////////////// + + + /** + * Initializes a mutex object. + * + * Throws: + * SyncException on error. + */ + this() + { + version( Win32 ) + { + InitializeCriticalSection( &m_hndl ); + } + else version( Posix ) + { + int rc = pthread_mutex_init( &m_hndl, &sm_attr ); + if( rc ) + throw new SyncException( "Unable to initialize mutex" ); + } + m_proxy.link = this; + // NOTE: With DMD this can be "this.__monitor = &m_proxy". + (cast(void**) this)[1] = &m_proxy; + } + + + /** + * Initializes a mutex object and sets it as the monitor for o. + * + * In: + * o must not already have a monitor. + */ + this( Object o ) + in + { + // NOTE: With DMD this can be "o.__monitor is null". + assert( (cast(void**) o)[1] is null ); + } + body + { + this(); + // NOTE: With DMD this can be "o.__monitor = &m_proxy". + (cast(void**) o)[1] = &m_proxy; + } + + + ~this() + { + version( Win32 ) + { + DeleteCriticalSection( &m_hndl ); + } + else version( Posix ) + { + int rc = pthread_mutex_destroy( &m_hndl ); + assert( !rc, "Unable to destroy mutex" ); + } + (cast(void**) this)[1] = null; + } + + + //////////////////////////////////////////////////////////////////////////// + // General Actions + //////////////////////////////////////////////////////////////////////////// + + + /** + * If this lock is not already held by the caller, the lock is acquired, + * then the internal counter is incremented by one. + * + * Throws: + * SyncException on error. + */ + void lock() + { + version( Win32 ) + { + EnterCriticalSection( &m_hndl ); + } + else version( Posix ) + { + int rc = pthread_mutex_lock( &m_hndl ); + if( rc ) + throw new SyncException( "Unable to lock mutex" ); + } + } + + + /** + * Decrements the internal lock count by one. If this brings the count to + * zero, the lock is released. + * + * Throws: + * SyncException on error. + */ + void unlock() + { + version( Win32 ) + { + LeaveCriticalSection( &m_hndl ); + } + else version( Posix ) + { + int rc = pthread_mutex_unlock( &m_hndl ); + if( rc ) + throw new SyncException( "Unable to unlock mutex" ); + } + } + + + /** + * If the lock is held by another caller, the method returns. Otherwise, + * the lock is acquired if it is not already held, and then the internal + * counter is incremented by one. + * + * Throws: + * SyncException on error. + * + * Returns: + * true if the lock was acquired and false if not. + */ + bool tryLock() + { + version( Win32 ) + { + return TryEnterCriticalSection( &m_hndl ) != 0; + } + else version( Posix ) + { + return pthread_mutex_trylock( &m_hndl ) == 0; + } + } + + + version( Posix ) + { + static this() + { + int rc = pthread_mutexattr_init( &sm_attr ); + assert( !rc ); + + rc = pthread_mutexattr_settype( &sm_attr, PTHREAD_MUTEX_RECURSIVE ); + assert( !rc ); + } + + + static ~this() + { + int rc = pthread_mutexattr_destroy( &sm_attr ); + assert( !rc ); + } + } + + +private: + version( Win32 ) + { + CRITICAL_SECTION m_hndl; + } + else version( Posix ) + { + __gshared pthread_mutexattr_t sm_attr; + + pthread_mutex_t m_hndl; + } + + struct MonitorProxy + { + Object.Monitor link; + } + + MonitorProxy m_proxy; + + +package: + version( Posix ) + { + pthread_mutex_t* handleAddr() + { + return &m_hndl; + } + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// Unit Tests +//////////////////////////////////////////////////////////////////////////////// + + +version( unittest ) +{ + private import core.thread; + + + unittest + { + auto mutex = new Mutex; + int numThreads = 10; + int numTries = 1000; + int lockCount = 0; + + void testFn() + { + for( int i = 0; i < numTries; ++i ) + { + synchronized( mutex ) + { + ++lockCount; + } + } + } + + auto group = new ThreadGroup; + + for( int i = 0; i < numThreads; ++i ) + group.create( &testFn ); + + group.joinAll(); + assert( lockCount == numThreads * numTries ); + } +}