Wolfgang Thallergcc16 merge934c3885 months agoHistory
432 lines · 10.4 KB · D
RawDownload
/** * The mutex module provides a primitive for maintaining mutually exclusive * access. * * Copyright: Copyright Sean Kelly 2005 - 2009. * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors:   Sean Kelly * Source:    $(DRUNTIMESRC core/sync/_mutex.d) */ /*          Copyright Sean Kelly 2005 - 2009. * Distributed under the Boost Software License, Version 1.0. *    (See accompanying file LICENSE or copy at *          http://www.boost.org/LICENSE_1_0.txt) */module core.sync.mutex;  public import core.sync.exception; version (Windows){    import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection,        EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection,        TryEnterCriticalSection+/;}else version (Posix){    import core.sys.posix.pthread : pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock,        PTHREAD_MUTEX_RECURSIVE, pthread_mutex_trylock, pthread_mutex_unlock, pthread_mutexattr_destroy,        pthread_mutexattr_init, pthread_mutexattr_settype;    import core.sys.posix.sys.types : pthread_mutex_t, pthread_mutexattr_t;}else{    static assert(false, "Platform not supported");} ////////////////////////////////////////////////////////////////////////////////// Mutex//// void lock();// void unlock();// bool tryLock();////////////////////////////////////////////////////////////////////////////////  /** * This class represents a general purpose, recursive mutex. * * Implemented using `pthread_mutex` on Posix and `CRITICAL_SECTION` * on Windows. */class Mutex :    Object.Monitor{    ////////////////////////////////////////////////////////////////////////////    // Initialization    ////////////////////////////////////////////////////////////////////////////      /**     * Initializes a mutex object.     *     */    this() @trusted nothrow @nogc    {        this(true);    }     /// ditto    this() shared @trusted nothrow @nogc    {        this(true);    }     // Undocumented, useful only in Mutex.this().    private this(this Q)(bool _unused_) @trusted nothrow @nogc        if (is(Q == Mutex) || is(Q == shared Mutex))    {        version (Windows)        {            InitializeCriticalSection(cast(CRITICAL_SECTION*) &m_hndl);        }        else version (Posix)        {            import core.internal.abort : abort;            pthread_mutexattr_t attr = void;             !pthread_mutexattr_init(&attr) ||                abort("Error: pthread_mutexattr_init failed.");             scope (exit) !pthread_mutexattr_destroy(&attr) ||                abort("Error: pthread_mutexattr_destroy failed.");             !pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) ||                abort("Error: pthread_mutexattr_settype failed.");             !pthread_mutex_init(cast(pthread_mutex_t*) &m_hndl, &attr) ||                abort("Error: pthread_mutex_init failed.");        }         auto self = cast(Mutex) this;        self.m_proxy.link = self;        this.__monitor = cast(void*) &m_proxy;    }      /**     * Initializes a mutex object and sets it as the monitor for `obj`.     *     * In:     *  `obj` must not already have a monitor.     */    this(Object obj) @trusted nothrow @nogc    {        this(obj, true);    }     /// ditto    this(Object obj) shared @trusted nothrow @nogc    {        this(obj, true);    }     // Undocumented, useful only in Mutex.this(Object).    private this(this Q)(Object obj, bool _unused_) @trusted nothrow @nogc        if (is(Q == Mutex) || is(Q == shared Mutex))    in    {        assert(obj !is null,            "The provided object must not be null.");        assert(obj.__monitor is null,            "The provided object has a monitor already set!");    }    do    {        this();        obj.__monitor = cast(void*) &m_proxy;    }      ~this() @trusted nothrow @nogc    {        version (Windows)        {            DeleteCriticalSection(&m_hndl);        }        else version (Posix)        {            import core.internal.abort : abort;            !pthread_mutex_destroy(&m_hndl) ||                abort("Error: pthread_mutex_destroy failed.");        }        this.__monitor = 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.     *     * Note:     *    `Mutex.lock` does not throw, but a class derived from Mutex can throw.     *    Use `lock_nothrow` in `nothrow @nogc` code.     */    @trusted void lock()    {        lock_nothrow();    }     /// ditto    @trusted void lock() shared    {        lock_nothrow();    }     /// ditto    final void lock_nothrow(this Q)() nothrow @trusted @nogc        if (is(Q == Mutex) || is(Q == shared Mutex))    {        auto self = cast(Mutex) this;        version (Windows)        {            EnterCriticalSection(&self.m_hndl);        }        else version (Posix)        {            if (pthread_mutex_lock(&self.m_hndl) == 0)                return;             SyncError syncErr = cast(SyncError) __traits(initSymbol, SyncError).ptr;            syncErr.msg = "Unable to lock mutex.";            throw syncErr;        }    }     /**     * Decrements the internal lock count by one.  If this brings the count to     * zero, the lock is released.     *     * Note:     *    `Mutex.unlock` does not throw, but a class derived from Mutex can throw.     *    Use `unlock_nothrow` in `nothrow @nogc` code.     */    @trusted void unlock()    {        unlock_nothrow();    }     /// ditto    @trusted void unlock() shared    {        unlock_nothrow();    }     /// ditto    final void unlock_nothrow(this Q)() nothrow @trusted @nogc        if (is(Q == Mutex) || is(Q == shared Mutex))    {        auto self = cast(Mutex) this;        version (Windows)        {            LeaveCriticalSection(&self.m_hndl);        }        else version (Posix)        {            if (pthread_mutex_unlock(&self.m_hndl) == 0)                return;             SyncError syncErr = cast(SyncError) __traits(initSymbol, SyncError).ptr;            syncErr.msg = "Unable to unlock mutex.";            throw syncErr;        }    }     /**     * 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.     *     * Note:     *    `Mutex.tryLock` does not throw, but a class derived from Mutex can throw.     *    Use `tryLock_nothrow` in `nothrow @nogc` code.     */    bool tryLock() @trusted    {        return tryLock_nothrow();    }     /// ditto    bool tryLock() shared @trusted    {        return tryLock_nothrow();    }     /// ditto    final bool tryLock_nothrow(this Q)() nothrow @trusted @nogc        if (is(Q == Mutex) || is(Q == shared Mutex))    {        auto self = cast(Mutex) this;        version (Windows)        {            return TryEnterCriticalSection(&self.m_hndl) != 0;        }        else version (Posix)        {            return pthread_mutex_trylock(&self.m_hndl) == 0;        }    }  private:    version (Windows)    {        CRITICAL_SECTION    m_hndl;    }    else version (Posix)    {        pthread_mutex_t     m_hndl;    }     struct MonitorProxy    {        Object.Monitor link;    }     MonitorProxy            m_proxy;  package:    version (Posix)    {        pthread_mutex_t* handleAddr() @nogc        {            return &m_hndl;        }    }} ////* @safe nothrow -> see druntime PR 1726 */// Test regular usage.unittest{    import core.thread : Thread;     class Resource    {        Mutex mtx;        int cargo;         this() shared @safe nothrow        {            mtx = new shared Mutex();            cargo = 42;        }         void useResource() shared @trusted nothrow @nogc        {            mtx.lock_nothrow();            (cast() cargo) += 1;            mtx.unlock_nothrow();        }    }     shared Resource res = new shared Resource();     auto otherThread = new Thread(    {        foreach (i; 0 .. 10000)            res.useResource();    }).start();     foreach (i; 0 .. 10000)        res.useResource();     otherThread.join();     assert (res.cargo == 20042);} // Test @nogc usage.@system @nogc nothrow unittest{    import core.lifetime : emplace;    import core.stdc.stdlib : free, malloc;     auto mtx = cast(shared Mutex) malloc(__traits(classInstanceSize, Mutex));    emplace(mtx);     mtx.lock_nothrow();     { // test recursive locking        mtx.tryLock_nothrow();        mtx.unlock_nothrow();    }     mtx.unlock_nothrow();     // In general destorying classes like this is not    // safe, but since we know that the only base class    // of Mutex is Object and it doesn't have a dtor    // we can simply call the non-virtual __dtor() here.     // Ok to cast away shared because destruction    // should happen only from a single thread.    (cast(Mutex) mtx).__dtor();     // Verify that the underlying implementation has been destroyed by checking    // that locking is not possible. This assumes that the underlying    // implementation is well behaved and makes the object non-lockable upon    // destruction. The Bionic, DragonFly, Musl, and Solaris C runtimes don't    // appear to do so, so skip this test.    version (CRuntime_Bionic) {} else    version (CRuntime_Musl) {} else    version (DragonFlyBSD) {} else    version (Solaris) {} else    assert(!mtx.tryLock_nothrow());     free(cast(void*) mtx);} // Test single-thread (non-shared) use.unittest{    Mutex m = new Mutex();     m.lock();     m.tryLock();    m.unlock();     m.unlock();} unittest{    import core.thread;     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);}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t