d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
[official-gcc.git] / libphobos / libdruntime / rt / monitor_.d
blob763f439282253d6f12034b9c7da21845ff85e705
1 /**
2 * Contains the implementation for object monitors.
4 * Copyright: Copyright Digital Mars 2000 - 2015.
5 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 * Authors: Walter Bright, Sean Kelly, Martin Nowak
7 * Source: $(DRUNTIMESRC rt/_monitor_.d)
8 */
10 /* NOTE: This file has been patched from the original DMD distribution to
11 * work with the GDC compiler.
13 module rt.monitor_;
15 import core.atomic, core.stdc.stdlib, core.stdc.string;
17 // NOTE: The dtor callback feature is only supported for monitors that are not
18 // supplied by the user. The assumption is that any object with a user-
19 // supplied monitor may have special storage or lifetime requirements and
20 // that as a result, storing references to local objects within Monitor
21 // may not be safe or desirable. Thus, devt is only valid if impl is
22 // null.
24 extern (C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow
27 assert(ownee.__monitor is null);
31 auto m = ensureMonitor(cast(Object) owner);
32 if (m.impl is null)
34 atomicOp!("+=")(m.refs, cast(size_t) 1);
36 // Assume the monitor is garbage collected and simply copy the reference.
37 ownee.__monitor = owner.__monitor;
40 extern (C) void _d_monitordelete(Object h, bool det)
42 auto m = getMonitor(h);
43 if (m is null)
44 return;
46 if (m.impl)
48 // let the GC collect the monitor
49 setMonitor(h, null);
51 else if (!atomicOp!("-=")(m.refs, cast(size_t) 1))
53 // refcount == 0 means unshared => no synchronization required
54 disposeEvent(cast(Monitor*) m, h);
55 deleteMonitor(cast(Monitor*) m);
56 setMonitor(h, null);
60 // does not call dispose events, for internal use only
61 extern (C) void _d_monitordelete_nogc(Object h) @nogc
63 auto m = getMonitor(h);
64 if (m is null)
65 return;
67 if (m.impl)
69 // let the GC collect the monitor
70 setMonitor(h, null);
72 else if (!atomicOp!("-=")(m.refs, cast(size_t) 1))
74 // refcount == 0 means unshared => no synchronization required
75 deleteMonitor(cast(Monitor*) m);
76 setMonitor(h, null);
80 extern (C) void _d_monitorenter(Object h)
83 assert(h !is null, "Synchronized object must not be null.");
87 auto m = cast(Monitor*) ensureMonitor(h);
88 auto i = m.impl;
89 if (i is null)
90 lockMutex(&m.mtx);
91 else
92 i.lock();
95 extern (C) void _d_monitorexit(Object h)
97 auto m = cast(Monitor*) getMonitor(h);
98 auto i = m.impl;
99 if (i is null)
100 unlockMutex(&m.mtx);
101 else
102 i.unlock();
105 extern (C) void rt_attachDisposeEvent(Object h, DEvent e)
107 synchronized (h)
109 auto m = cast(Monitor*) getMonitor(h);
110 assert(m.impl is null);
112 foreach (ref v; m.devt)
114 if (v is null || v == e)
116 v = e;
117 return;
121 auto len = m.devt.length + 4; // grow by 4 elements
122 auto pos = m.devt.length; // insert position
123 auto p = realloc(m.devt.ptr, DEvent.sizeof * len);
124 import core.exception : onOutOfMemoryError;
126 if (!p)
127 onOutOfMemoryError();
128 m.devt = (cast(DEvent*) p)[0 .. len];
129 m.devt[pos + 1 .. len] = null;
130 m.devt[pos] = e;
134 extern (C) void rt_detachDisposeEvent(Object h, DEvent e)
136 synchronized (h)
138 auto m = cast(Monitor*) getMonitor(h);
139 assert(m.impl is null);
141 foreach (p, v; m.devt)
143 if (v == e)
145 memmove(&m.devt[p], &m.devt[p + 1], (m.devt.length - p - 1) * DEvent.sizeof);
146 m.devt[$ - 1] = null;
147 return;
153 nothrow:
155 extern (C) void _d_monitor_staticctor()
157 version (Posix)
159 pthread_mutexattr_init(&gattr);
160 pthread_mutexattr_settype(&gattr, PTHREAD_MUTEX_RECURSIVE);
162 initMutex(&gmtx);
165 extern (C) void _d_monitor_staticdtor()
167 destroyMutex(&gmtx);
168 version (Posix)
169 pthread_mutexattr_destroy(&gattr);
172 package:
174 // This is what the monitor reference in Object points to
175 alias IMonitor = Object.Monitor;
176 alias DEvent = void delegate(Object);
178 version (GNU)
180 import gcc.config;
181 static if (GNU_Thread_Model == ThreadModel.Single)
182 version = SingleThreaded;
183 // Ignore ThreadModel, we don't want posix threads on windows and
184 // will always use native threading instead.
187 version (SingleThreaded)
189 @nogc:
190 alias Mutex = int;
192 void initMutex(Mutex* mtx)
196 void destroyMutex(Mutex* mtx)
200 void lockMutex(Mutex* mtx)
204 void unlockMutex(Mutex* mtx)
208 else version (Windows)
210 version (CRuntime_DigitalMars)
212 pragma(lib, "snn.lib");
214 import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection,
215 EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection+/;
217 alias Mutex = CRITICAL_SECTION;
219 alias initMutex = InitializeCriticalSection;
220 alias destroyMutex = DeleteCriticalSection;
221 alias lockMutex = EnterCriticalSection;
222 alias unlockMutex = LeaveCriticalSection;
224 else version (Posix)
226 import core.sys.posix.pthread;
228 @nogc:
229 alias Mutex = pthread_mutex_t;
230 __gshared pthread_mutexattr_t gattr;
232 void initMutex(pthread_mutex_t* mtx)
234 pthread_mutex_init(mtx, &gattr) && assert(0);
237 void destroyMutex(pthread_mutex_t* mtx)
239 pthread_mutex_destroy(mtx) && assert(0);
242 void lockMutex(pthread_mutex_t* mtx)
244 pthread_mutex_lock(mtx) && assert(0);
247 void unlockMutex(pthread_mutex_t* mtx)
249 pthread_mutex_unlock(mtx) && assert(0);
252 else
254 static assert(0, "Unsupported platform");
257 struct Monitor
259 IMonitor impl; // for user-level monitors
260 DEvent[] devt; // for internal monitors
261 size_t refs; // reference count
262 Mutex mtx;
265 private:
267 @property ref shared(Monitor*) monitor(return scope Object h) pure nothrow @nogc
269 return *cast(shared Monitor**)&h.__monitor;
272 private shared(Monitor)* getMonitor(Object h) pure @nogc
274 return atomicLoad!(MemoryOrder.acq)(h.monitor);
277 void setMonitor(Object h, shared(Monitor)* m) pure @nogc
279 atomicStore!(MemoryOrder.rel)(h.monitor, m);
282 __gshared Mutex gmtx;
284 shared(Monitor)* ensureMonitor(Object h)
286 if (auto m = getMonitor(h))
287 return m;
289 auto m = cast(Monitor*) calloc(Monitor.sizeof, 1);
290 assert(m);
291 initMutex(&m.mtx);
293 bool success;
294 lockMutex(&gmtx);
295 if (getMonitor(h) is null)
297 m.refs = 1;
298 setMonitor(h, cast(shared) m);
299 success = true;
301 unlockMutex(&gmtx);
303 if (success)
305 // Set the finalize bit so that the monitor gets collected (Bugzilla 14573)
306 import core.memory : GC;
308 if (!(typeid(h).m_flags & TypeInfo_Class.ClassFlags.hasDtor))
309 GC.setAttr(cast(void*) h, GC.BlkAttr.FINALIZE);
310 return cast(shared(Monitor)*) m;
312 else // another thread succeeded instead
314 deleteMonitor(m);
315 return getMonitor(h);
319 void deleteMonitor(Monitor* m) @nogc
321 destroyMutex(&m.mtx);
322 free(m);
325 void disposeEvent(Monitor* m, Object h)
327 foreach (v; m.devt)
329 if (v)
330 v(h);
332 if (m.devt.ptr)
333 free(m.devt.ptr);
336 // Bugzilla 14573
337 unittest
339 import core.memory : GC;
341 auto obj = new Object;
342 assert(!(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE));
343 ensureMonitor(obj);
344 assert(getMonitor(obj) !is null);
345 assert(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE);