mirror of
https://review.haiku-os.org/buildtools
synced 2025-02-14 09:47:56 +01:00
Updated dependencies: * GMP 6.2.1 * ISL 0.24 * MPL 1.2.1 * MPFR 4.1.0 The dependencies were pulled in by running the ./contrib/download_prerequisites script and then manually removing the symbolic links and archives, and renaming the directories (i.e mv isl-0.24 to isl)
426 lines
9.9 KiB
D
426 lines
9.9 KiB
D
/**
|
|
* 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;
|
|
}
|
|
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.");
|
|
}
|
|
|
|
m_proxy.link = this;
|
|
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))
|
|
{
|
|
version (Windows)
|
|
{
|
|
EnterCriticalSection(&m_hndl);
|
|
}
|
|
else version (Posix)
|
|
{
|
|
if (pthread_mutex_lock(&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))
|
|
{
|
|
version (Windows)
|
|
{
|
|
LeaveCriticalSection(&m_hndl);
|
|
}
|
|
else version (Posix)
|
|
{
|
|
if (pthread_mutex_unlock(&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))
|
|
{
|
|
version (Windows)
|
|
{
|
|
return TryEnterCriticalSection(&m_hndl) != 0;
|
|
}
|
|
else version (Posix)
|
|
{
|
|
return pthread_mutex_trylock(&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 @safe 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.stdc.stdlib : malloc, free;
|
|
import core.lifetime : emplace;
|
|
|
|
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);
|
|
}
|