Home | History | Annotate | Line # | Download | only in sync
      1 /**
      2  * The mutex module provides a primitive for maintaining mutually exclusive
      3  * access.
      4  *
      5  * Copyright: Copyright Sean Kelly 2005 - 2009.
      6  * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
      7  * Authors:   Sean Kelly
      8  * Source:    $(DRUNTIMESRC core/sync/_mutex.d)
      9  */
     10 
     11 /*          Copyright Sean Kelly 2005 - 2009.
     12  * Distributed under the Boost Software License, Version 1.0.
     13  *    (See accompanying file LICENSE or copy at
     14  *          http://www.boost.org/LICENSE_1_0.txt)
     15  */
     16 module core.sync.mutex;
     17 
     18 
     19 public import core.sync.exception;
     20 
     21 version (Windows)
     22 {
     23     import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection,
     24         EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection,
     25         TryEnterCriticalSection+/;
     26 }
     27 else version (Posix)
     28 {
     29     import core.sys.posix.pthread;
     30 }
     31 else
     32 {
     33     static assert(false, "Platform not supported");
     34 }
     35 
     36 ////////////////////////////////////////////////////////////////////////////////
     37 // Mutex
     38 //
     39 // void lock();
     40 // void unlock();
     41 // bool tryLock();
     42 ////////////////////////////////////////////////////////////////////////////////
     43 
     44 
     45 /**
     46  * This class represents a general purpose, recursive mutex.
     47  *
     48  * Implemented using `pthread_mutex` on Posix and `CRITICAL_SECTION`
     49  * on Windows.
     50  */
     51 class Mutex :
     52     Object.Monitor
     53 {
     54     ////////////////////////////////////////////////////////////////////////////
     55     // Initialization
     56     ////////////////////////////////////////////////////////////////////////////
     57 
     58 
     59     /**
     60      * Initializes a mutex object.
     61      *
     62      */
     63     this() @trusted nothrow @nogc
     64     {
     65         this(true);
     66     }
     67 
     68     /// ditto
     69     this() shared @trusted nothrow @nogc
     70     {
     71         this(true);
     72     }
     73 
     74     // Undocumented, useful only in Mutex.this().
     75     private this(this Q)(bool _unused_) @trusted nothrow @nogc
     76         if (is(Q == Mutex) || is(Q == shared Mutex))
     77     {
     78         version (Windows)
     79         {
     80             InitializeCriticalSection(cast(CRITICAL_SECTION*) &m_hndl);
     81         }
     82         else version (Posix)
     83         {
     84             import core.internal.abort : abort;
     85             pthread_mutexattr_t attr = void;
     86 
     87             !pthread_mutexattr_init(&attr) ||
     88                 abort("Error: pthread_mutexattr_init failed.");
     89 
     90             scope (exit) !pthread_mutexattr_destroy(&attr) ||
     91                 abort("Error: pthread_mutexattr_destroy failed.");
     92 
     93             !pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) ||
     94                 abort("Error: pthread_mutexattr_settype failed.");
     95 
     96             !pthread_mutex_init(cast(pthread_mutex_t*) &m_hndl, &attr) ||
     97                 abort("Error: pthread_mutex_init failed.");
     98         }
     99 
    100         m_proxy.link = this;
    101         this.__monitor = cast(void*) &m_proxy;
    102     }
    103 
    104 
    105     /**
    106      * Initializes a mutex object and sets it as the monitor for `obj`.
    107      *
    108      * In:
    109      *  `obj` must not already have a monitor.
    110      */
    111     this(Object obj) @trusted nothrow @nogc
    112     {
    113         this(obj, true);
    114     }
    115 
    116     /// ditto
    117     this(Object obj) shared @trusted nothrow @nogc
    118     {
    119         this(obj, true);
    120     }
    121 
    122     // Undocumented, useful only in Mutex.this(Object).
    123     private this(this Q)(Object obj, bool _unused_) @trusted nothrow @nogc
    124         if (is(Q == Mutex) || is(Q == shared Mutex))
    125     in
    126     {
    127         assert(obj !is null,
    128             "The provided object must not be null.");
    129         assert(obj.__monitor is null,
    130             "The provided object has a monitor already set!");
    131     }
    132     do
    133     {
    134         this();
    135         obj.__monitor = cast(void*) &m_proxy;
    136     }
    137 
    138 
    139     ~this() @trusted nothrow @nogc
    140     {
    141         version (Windows)
    142         {
    143             DeleteCriticalSection(&m_hndl);
    144         }
    145         else version (Posix)
    146         {
    147             import core.internal.abort : abort;
    148             !pthread_mutex_destroy(&m_hndl) ||
    149                 abort("Error: pthread_mutex_destroy failed.");
    150         }
    151         this.__monitor = null;
    152     }
    153 
    154 
    155     ////////////////////////////////////////////////////////////////////////////
    156     // General Actions
    157     ////////////////////////////////////////////////////////////////////////////
    158 
    159 
    160     /**
    161      * If this lock is not already held by the caller, the lock is acquired,
    162      * then the internal counter is incremented by one.
    163      *
    164      * Note:
    165      *    `Mutex.lock` does not throw, but a class derived from Mutex can throw.
    166      *    Use `lock_nothrow` in `nothrow @nogc` code.
    167      */
    168     @trusted void lock()
    169     {
    170         lock_nothrow();
    171     }
    172 
    173     /// ditto
    174     @trusted void lock() shared
    175     {
    176         lock_nothrow();
    177     }
    178 
    179     /// ditto
    180     final void lock_nothrow(this Q)() nothrow @trusted @nogc
    181         if (is(Q == Mutex) || is(Q == shared Mutex))
    182     {
    183         version (Windows)
    184         {
    185             EnterCriticalSection(&m_hndl);
    186         }
    187         else version (Posix)
    188         {
    189             if (pthread_mutex_lock(&m_hndl) == 0)
    190                 return;
    191 
    192             SyncError syncErr = cast(SyncError) __traits(initSymbol, SyncError).ptr;
    193             syncErr.msg = "Unable to lock mutex.";
    194             throw syncErr;
    195         }
    196     }
    197 
    198     /**
    199      * Decrements the internal lock count by one.  If this brings the count to
    200      * zero, the lock is released.
    201      *
    202      * Note:
    203      *    `Mutex.unlock` does not throw, but a class derived from Mutex can throw.
    204      *    Use `unlock_nothrow` in `nothrow @nogc` code.
    205      */
    206     @trusted void unlock()
    207     {
    208         unlock_nothrow();
    209     }
    210 
    211     /// ditto
    212     @trusted void unlock() shared
    213     {
    214         unlock_nothrow();
    215     }
    216 
    217     /// ditto
    218     final void unlock_nothrow(this Q)() nothrow @trusted @nogc
    219         if (is(Q == Mutex) || is(Q == shared Mutex))
    220     {
    221         version (Windows)
    222         {
    223             LeaveCriticalSection(&m_hndl);
    224         }
    225         else version (Posix)
    226         {
    227             if (pthread_mutex_unlock(&m_hndl) == 0)
    228                 return;
    229 
    230             SyncError syncErr = cast(SyncError) __traits(initSymbol, SyncError).ptr;
    231             syncErr.msg = "Unable to unlock mutex.";
    232             throw syncErr;
    233         }
    234     }
    235 
    236     /**
    237      * If the lock is held by another caller, the method returns.  Otherwise,
    238      * the lock is acquired if it is not already held, and then the internal
    239      * counter is incremented by one.
    240      *
    241      * Returns:
    242      *  true if the lock was acquired and false if not.
    243      *
    244      * Note:
    245      *    `Mutex.tryLock` does not throw, but a class derived from Mutex can throw.
    246      *    Use `tryLock_nothrow` in `nothrow @nogc` code.
    247      */
    248     bool tryLock() @trusted
    249     {
    250         return tryLock_nothrow();
    251     }
    252 
    253     /// ditto
    254     bool tryLock() shared @trusted
    255     {
    256         return tryLock_nothrow();
    257     }
    258 
    259     /// ditto
    260     final bool tryLock_nothrow(this Q)() nothrow @trusted @nogc
    261         if (is(Q == Mutex) || is(Q == shared Mutex))
    262     {
    263         version (Windows)
    264         {
    265             return TryEnterCriticalSection(&m_hndl) != 0;
    266         }
    267         else version (Posix)
    268         {
    269             return pthread_mutex_trylock(&m_hndl) == 0;
    270         }
    271     }
    272 
    273 
    274 private:
    275     version (Windows)
    276     {
    277         CRITICAL_SECTION    m_hndl;
    278     }
    279     else version (Posix)
    280     {
    281         pthread_mutex_t     m_hndl;
    282     }
    283 
    284     struct MonitorProxy
    285     {
    286         Object.Monitor link;
    287     }
    288 
    289     MonitorProxy            m_proxy;
    290 
    291 
    292 package:
    293     version (Posix)
    294     {
    295         pthread_mutex_t* handleAddr()
    296         {
    297             return &m_hndl;
    298         }
    299     }
    300 }
    301 
    302 ///
    303 /* @safe nothrow -> see druntime PR 1726 */
    304 // Test regular usage.
    305 unittest
    306 {
    307     import core.thread : Thread;
    308 
    309     class Resource
    310     {
    311         Mutex mtx;
    312         int cargo;
    313 
    314         this() shared @safe nothrow
    315         {
    316             mtx = new shared Mutex();
    317             cargo = 42;
    318         }
    319 
    320         void useResource() shared @safe nothrow @nogc
    321         {
    322             mtx.lock_nothrow();
    323             (cast() cargo) += 1;
    324             mtx.unlock_nothrow();
    325         }
    326     }
    327 
    328     shared Resource res = new shared Resource();
    329 
    330     auto otherThread = new Thread(
    331     {
    332         foreach (i; 0 .. 10000)
    333             res.useResource();
    334     }).start();
    335 
    336     foreach (i; 0 .. 10000)
    337         res.useResource();
    338 
    339     otherThread.join();
    340 
    341     assert (res.cargo == 20042);
    342 }
    343 
    344 // Test @nogc usage.
    345 @system @nogc nothrow unittest
    346 {
    347     import core.stdc.stdlib : malloc, free;
    348     import core.lifetime : emplace;
    349 
    350     auto mtx = cast(shared Mutex) malloc(__traits(classInstanceSize, Mutex));
    351     emplace(mtx);
    352 
    353     mtx.lock_nothrow();
    354 
    355     { // test recursive locking
    356         mtx.tryLock_nothrow();
    357         mtx.unlock_nothrow();
    358     }
    359 
    360     mtx.unlock_nothrow();
    361 
    362     // In general destorying classes like this is not
    363     // safe, but since we know that the only base class
    364     // of Mutex is Object and it doesn't have a dtor
    365     // we can simply call the non-virtual __dtor() here.
    366 
    367     // Ok to cast away shared because destruction
    368     // should happen only from a single thread.
    369     (cast(Mutex) mtx).__dtor();
    370 
    371     // Verify that the underlying implementation has been destroyed by checking
    372     // that locking is not possible. This assumes that the underlying
    373     // implementation is well behaved and makes the object non-lockable upon
    374     // destruction. The Bionic, DragonFly, Musl, and Solaris C runtimes don't
    375     // appear to do so, so skip this test.
    376     version (CRuntime_Bionic) {} else
    377     version (CRuntime_Musl) {} else
    378     version (DragonFlyBSD) {} else
    379     version (Solaris) {} else
    380     assert(!mtx.tryLock_nothrow());
    381 
    382     free(cast(void*) mtx);
    383 }
    384 
    385 // Test single-thread (non-shared) use.
    386 unittest
    387 {
    388     Mutex m = new Mutex();
    389 
    390     m.lock();
    391 
    392     m.tryLock();
    393     m.unlock();
    394 
    395     m.unlock();
    396 }
    397 
    398 unittest
    399 {
    400     import core.thread;
    401 
    402     auto mutex      = new Mutex;
    403     int  numThreads = 10;
    404     int  numTries   = 1000;
    405     int  lockCount  = 0;
    406 
    407     void testFn()
    408     {
    409         for (int i = 0; i < numTries; ++i)
    410         {
    411             synchronized (mutex)
    412             {
    413                 ++lockCount;
    414             }
    415         }
    416     }
    417 
    418     auto group = new ThreadGroup;
    419 
    420     for (int i = 0; i < numThreads; ++i)
    421         group.create(&testFn);
    422 
    423     group.joinAll();
    424     assert(lockCount == numThreads * numTries);
    425 }
    426