/** * Contains the implementation for object monitors. * * Copyright: Copyright Digital Mars 2000 - 2015. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright, Sean Kelly, Martin Nowak * Source: $(DRUNTIMESRC rt/_monitor_.d) */module rt.monitor_; import core.atomic;import core.stdc.stdlib : calloc, free, realloc;import core.stdc.string : memmove; version (Windows){ import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection, EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection+/;}else version (Posix){ import core.sys.posix.pthread : pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock, PTHREAD_MUTEX_RECURSIVE, 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(0, "Unsupported platform");} // NOTE: The dtor callback feature is only supported for monitors that are not// supplied by the user. The assumption is that any object with a user-// supplied monitor may have special storage or lifetime requirements and// that as a result, storing references to local objects within Monitor// may not be safe or desirable. Thus, devt is only valid if impl is// null. extern (C) void _d_setSameMutex(shared Object ownee, shared Object owner) @trusted nothrowin{ assert(ownee.__monitor is null);}do{ auto m = ensureMonitor(cast(Object) cast(void*) owner); if (m.impl is null) { atomicOp!"+="(m.refs, size_t(1)); } // Assume the monitor is garbage collected and simply copy the reference. ownee.__monitor = owner.__monitor;} extern (C) void _d_monitordelete(Object h, bool det){ auto m = getMonitor(h); if (m is null) return; if (m.impl) { // let the GC collect the monitor setMonitor(h, null); } else if (!atomicOp!"-="(m.refs, size_t(1))) { // refcount == 0 means unshared => no synchronization required disposeEvent(cast(Monitor*) m, h); deleteMonitor(cast(Monitor*) m); setMonitor(h, null); }} // does not call dispose events, for internal use onlyextern (C) void _d_monitordelete_nogc(Object h) @nogc nothrow{ auto m = getMonitor(h); if (m is null) return; if (m.impl) { // let the GC collect the monitor setMonitor(h, null); } else if (!atomicOp!"-="(m.refs, size_t(1))) { // refcount == 0 means unshared => no synchronization required deleteMonitor(cast(Monitor*) m); setMonitor(h, null); }} extern (C) void _d_monitorenter(Object h)in{ assert(h !is null, "Synchronized object must not be null.");}do{ auto m = cast(Monitor*) ensureMonitor(h); auto i = m.impl; if (i is null) lockMutex(&m.mtx); else i.lock();} extern (C) void _d_monitorexit(Object h){ auto m = cast(Monitor*) getMonitor(h); auto i = m.impl; if (i is null) unlockMutex(&m.mtx); else i.unlock();} extern (C) void rt_attachDisposeEvent(Object h, DEvent e){ synchronized (h) { auto m = cast(Monitor*) getMonitor(h); assert(m.impl is null); foreach (ref v; m.devt) { if (v is null || v == e) { v = e; return; } } auto len = m.devt.length + 4; // grow by 4 elements auto pos = m.devt.length; // insert position auto p = realloc(m.devt.ptr, DEvent.sizeof * len); import core.exception : onOutOfMemoryError; if (!p) onOutOfMemoryError(); m.devt = (cast(DEvent*) p)[0 .. len]; m.devt[pos + 1 .. len] = null; m.devt[pos] = e; }} extern (C) void rt_detachDisposeEvent(Object h, DEvent e){ synchronized (h) { auto m = cast(Monitor*) getMonitor(h); assert(m.impl is null); foreach (p, v; m.devt) { if (v == e) { memmove(&m.devt[p], &m.devt[p + 1], (m.devt.length - p - 1) * DEvent.sizeof); m.devt[$ - 1] = null; return; } } }} nothrow: extern (C) void _d_monitor_staticctor() @nogc nothrow{ version (Posix) { pthread_mutexattr_init(&gattr); pthread_mutexattr_settype(&gattr, PTHREAD_MUTEX_RECURSIVE); } initMutex(&gmtx);} extern (C) void _d_monitor_staticdtor() @nogc nothrow{ destroyMutex(&gmtx); version (Posix) pthread_mutexattr_destroy(&gattr);} package: // This is what the monitor reference in Object points toalias IMonitor = Object.Monitor;alias DEvent = void delegate(Object); version (Windows){ alias Mutex = CRITICAL_SECTION; alias initMutex = InitializeCriticalSection; alias destroyMutex = DeleteCriticalSection; alias lockMutex = EnterCriticalSection; alias unlockMutex = LeaveCriticalSection;}else version (Posix){@nogc: alias Mutex = pthread_mutex_t; __gshared pthread_mutexattr_t gattr; void initMutex(pthread_mutex_t* mtx) { pthread_mutex_init(mtx, &gattr) && assert(0); } void destroyMutex(pthread_mutex_t* mtx) { pthread_mutex_destroy(mtx) && assert(0); } void lockMutex(pthread_mutex_t* mtx) { pthread_mutex_lock(mtx) && assert(0); } void unlockMutex(pthread_mutex_t* mtx) { pthread_mutex_unlock(mtx) && assert(0); }} struct Monitor{ IMonitor impl; // for user-level monitors DEvent[] devt; // for internal monitors size_t refs; // reference count Mutex mtx;} private: __gshared Mutex gmtx; shared(Monitor*)* monitorPtr(return scope Object h) pure nothrow @nogc @trusted{ return cast(shared(Monitor*)*) &h.__monitor;} shared(Monitor)* getMonitor(Object h) pure @nogc{ return atomicLoad!(MemoryOrder.acq)(*monitorPtr(h));} void setMonitor(Object h, shared(Monitor)* m) pure @nogc{ atomicStore!(MemoryOrder.rel)(*monitorPtr(h), m);} shared(Monitor)* ensureMonitor(Object h){ if (auto m = getMonitor(h)) return m; auto m = cast(Monitor*) calloc(Monitor.sizeof, 1); assert(m); initMutex(&m.mtx); bool success; lockMutex(&gmtx); if (getMonitor(h) is null) { m.refs = 1; setMonitor(h, cast(shared) m); success = true; } unlockMutex(&gmtx); if (success) { // Set the finalize bit so that the monitor gets collected (Bugzilla 14573) import core.memory : GC; if (!(typeid(h).m_flags & TypeInfo_Class.ClassFlags.hasDtor)) GC.setAttr(cast(void*) h, GC.BlkAttr.FINALIZE); return cast(shared(Monitor)*) m; } else // another thread succeeded instead { deleteMonitor(m); return getMonitor(h); }} void deleteMonitor(Monitor* m) @nogc{ destroyMutex(&m.mtx); free(m);} void disposeEvent(Monitor* m, Object h){ foreach (v; m.devt) { if (v) v(h); } if (m.devt.ptr) free(m.devt.ptr);} // Bugzilla 14573unittest{ import core.memory : GC; auto obj = new Object; assert(!(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE)); ensureMonitor(obj); assert(getMonitor(obj) !is null); assert(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE);}
/**
* Contains the implementation for object monitors.
*
* Copyright: Copyright Digital Mars 2000 - 2015.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright, Sean Kelly, Martin Nowak
* Source: $(DRUNTIMESRC rt/_monitor_.d)
*/
module rt.monitor_;
import core.atomic;
import core.stdc.stdlib : calloc, free, realloc;
import core.stdc.string : memmove;
version (Windows)
{
import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection,
EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection+/;
}
else version (Posix)
{
import core.sys.posix.pthread : pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock,
PTHREAD_MUTEX_RECURSIVE, 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(0, "Unsupported platform");
}
// NOTE: The dtor callback feature is only supported for monitors that are not
// supplied by the user. The assumption is that any object with a user-
// supplied monitor may have special storage or lifetime requirements and
// that as a result, storing references to local objects within Monitor
// may not be safe or desirable. Thus, devt is only valid if impl is
// null.
extern (C) void _d_setSameMutex(shared Object ownee, shared Object owner) @trusted nothrow
in
{
assert(ownee.__monitor is null);
}
do
{
auto m = ensureMonitor(cast(Object) cast(void*) owner);
if (m.impl is null)
{
atomicOp!"+="(m.refs, size_t(1));
}
// Assume the monitor is garbage collected and simply copy the reference.
ownee.__monitor = owner.__monitor;
}
extern (C) void _d_monitordelete(Object h, bool det)
{
auto m = getMonitor(h);
if (m is null)
return;
if (m.impl)
{
// let the GC collect the monitor
setMonitor(h, null);
}
else if (!atomicOp!"-="(m.refs, size_t(1)))
{
// refcount == 0 means unshared => no synchronization required
disposeEvent(cast(Monitor*) m, h);
deleteMonitor(cast(Monitor*) m);
setMonitor(h, null);
}
}
// does not call dispose events, for internal use only
extern (C) void _d_monitordelete_nogc(Object h) @nogc nothrow
{
auto m = getMonitor(h);
if (m is null)
return;
if (m.impl)
{
// let the GC collect the monitor
setMonitor(h, null);
}
else if (!atomicOp!"-="(m.refs, size_t(1)))
{
// refcount == 0 means unshared => no synchronization required
deleteMonitor(cast(Monitor*) m);
setMonitor(h, null);
}
}
extern (C) void _d_monitorenter(Object h)
in
{
assert(h !is null, "Synchronized object must not be null.");
}
do
{
auto m = cast(Monitor*) ensureMonitor(h);
auto i = m.impl;
if (i is null)
lockMutex(&m.mtx);
else
i.lock();
}
extern (C) void _d_monitorexit(Object h)
{
auto m = cast(Monitor*) getMonitor(h);
auto i = m.impl;
if (i is null)
unlockMutex(&m.mtx);
else
i.unlock();
}
extern (C) void rt_attachDisposeEvent(Object h, DEvent e)
{
synchronized (h)
{
auto m = cast(Monitor*) getMonitor(h);
assert(m.impl is null);
foreach (ref v; m.devt)
{
if (v is null || v == e)
{
v = e;
return;
}
}
auto len = m.devt.length + 4; // grow by 4 elements
auto pos = m.devt.length; // insert position
auto p = realloc(m.devt.ptr, DEvent.sizeof * len);
import core.exception : onOutOfMemoryError;
if (!p)
onOutOfMemoryError();
m.devt = (cast(DEvent*) p)[0 .. len];
m.devt[pos + 1 .. len] = null;
m.devt[pos] = e;
}
}
extern (C) void rt_detachDisposeEvent(Object h, DEvent e)
{
synchronized (h)
{
auto m = cast(Monitor*) getMonitor(h);
assert(m.impl is null);
foreach (p, v; m.devt)
{
if (v == e)
{
memmove(&m.devt[p], &m.devt[p + 1], (m.devt.length - p - 1) * DEvent.sizeof);
m.devt[$ - 1] = null;
return;
}
}
}
}
nothrow:
extern (C) void _d_monitor_staticctor() @nogc nothrow
{
version (Posix)
{
pthread_mutexattr_init(&gattr);
pthread_mutexattr_settype(&gattr, PTHREAD_MUTEX_RECURSIVE);
}
initMutex(&gmtx);
}
extern (C) void _d_monitor_staticdtor() @nogc nothrow
{
destroyMutex(&gmtx);
version (Posix)
pthread_mutexattr_destroy(&gattr);
}
package:
// This is what the monitor reference in Object points to
alias IMonitor = Object.Monitor;
alias DEvent = void delegate(Object);
version (Windows)
{
alias Mutex = CRITICAL_SECTION;
alias initMutex = InitializeCriticalSection;
alias destroyMutex = DeleteCriticalSection;
alias lockMutex = EnterCriticalSection;
alias unlockMutex = LeaveCriticalSection;
}
else version (Posix)
{
@nogc:
alias Mutex = pthread_mutex_t;
__gshared pthread_mutexattr_t gattr;
void initMutex(pthread_mutex_t* mtx)
{
pthread_mutex_init(mtx, &gattr) && assert(0);
}
void destroyMutex(pthread_mutex_t* mtx)
{
pthread_mutex_destroy(mtx) && assert(0);
}
void lockMutex(pthread_mutex_t* mtx)
{
pthread_mutex_lock(mtx) && assert(0);
}
void unlockMutex(pthread_mutex_t* mtx)
{
pthread_mutex_unlock(mtx) && assert(0);
}
}
struct Monitor
{
IMonitor impl; // for user-level monitors
DEvent[] devt; // for internal monitors
size_t refs; // reference count
Mutex mtx;
}
private:
__gshared Mutex gmtx;
shared(Monitor*)* monitorPtr(return scope Object h) pure nothrow @nogc @trusted
{
return cast(shared(Monitor*)*) &h.__monitor;
}
shared(Monitor)* getMonitor(Object h) pure @nogc
{
return atomicLoad!(MemoryOrder.acq)(*monitorPtr(h));
}
void setMonitor(Object h, shared(Monitor)* m) pure @nogc
{
atomicStore!(MemoryOrder.rel)(*monitorPtr(h), m);
}
shared(Monitor)* ensureMonitor(Object h)
{
if (auto m = getMonitor(h))
return m;
auto m = cast(Monitor*) calloc(Monitor.sizeof, 1);
assert(m);
initMutex(&m.mtx);
bool success;
lockMutex(&gmtx);
if (getMonitor(h) is null)
{
m.refs = 1;
setMonitor(h, cast(shared) m);
success = true;
}
unlockMutex(&gmtx);
if (success)
{
// Set the finalize bit so that the monitor gets collected (Bugzilla 14573)
import core.memory : GC;
if (!(typeid(h).m_flags & TypeInfo_Class.ClassFlags.hasDtor))
GC.setAttr(cast(void*) h, GC.BlkAttr.FINALIZE);
return cast(shared(Monitor)*) m;
}
else // another thread succeeded instead
{
deleteMonitor(m);
return getMonitor(h);
}
}
void deleteMonitor(Monitor* m) @nogc
{
destroyMutex(&m.mtx);
free(m);
}
void disposeEvent(Monitor* m, Object h)
{
foreach (v; m.devt)
{
if (v)
v(h);
}
if (m.devt.ptr)
free(m.devt.ptr);
}
// Bugzilla 14573
unittest
{
import core.memory : GC;
auto obj = new Object;
assert(!(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE));
ensureMonitor(obj);
assert(getMonitor(obj) !is null);
assert(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE);
}