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