/** * 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);}
/**
* 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);
}