| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 | /** * The mutex module provides a primitive for maintaining mutually exclusive * access. * * Copyright: Copyright (C) 2005-2006 Sean Kelly. All rights reserved. * License: BSD style: $(LICENSE) * Author: Sean Kelly */ module tango.core.sync.Mutex; public import tango.core.Exception : SyncException; version( Win32 ) { private import tango.sys.win32.UserGdi; } else version( Posix ) { private import tango.stdc.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. * * Returns: * true if the lock was acquired and false if not. * * Throws: * SyncException on error. */ bool tryLock() { version( Win32 ) { return TryEnterCriticalSection( &m_hndl ) != 0; } else version( Posix ) { return pthread_mutex_trylock( &m_hndl ) == 0; } } version( Posix ) { shared static this() { int rc = pthread_mutexattr_init( &sm_attr ); assert( !rc ); rc = pthread_mutexattr_settype( &sm_attr, PTHREAD_MUTEX_RECURSIVE ); assert( !rc ); } shared static ~this() { int rc = pthread_mutexattr_destroy( &sm_attr ); assert( !rc ); } } private: version( Win32 ) { CRITICAL_SECTION m_hndl; } else version( Posix ) { static __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 //////////////////////////////////////////////////////////////////////////////// debug( UnitTest ) { private import tango.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 ); } } |