1 /**
2 * Contains the implementation for object monitors.
3 *
4 * Copyright: Copyright Digital Mars 2000 - 2015.
5 * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 * Authors: Walter Bright, Sean Kelly, Martin Nowak
7 */
8
9 /* NOTE: This file has been patched from the original DMD distribution to
10 * work with the GDC compiler.
11 */
12 module rt.monitor_;
13
14 import core.atomic, core.stdc.stdlib, core.stdc.string;
15
16 // NOTE: The dtor callback feature is only supported for monitors that are not
17 // supplied by the user. The assumption is that any object with a user-
18 // supplied monitor may have special storage or lifetime requirements and
19 // that as a result, storing references to local objects within Monitor
20 // may not be safe or desirable. Thus, devt is only valid if impl is
21 // null.
22
_d_setSameMutex(shared Object ownee,shared Object owner)23 extern (C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow
24 in
25 {
26 assert(ownee.__monitor is null);
27 }
28 body
29 {
30 auto m = ensureMonitor(cast(Object) owner);
31 if (m.impl is null)
32 {
33 atomicOp!("+=")(m.refs, cast(size_t) 1);
34 }
35 // Assume the monitor is garbage collected and simply copy the reference.
36 ownee.__monitor = owner.__monitor;
37 }
38
_d_monitordelete(Object h,bool det)39 extern (C) void _d_monitordelete(Object h, bool det)
40 {
41 auto m = getMonitor(h);
42 if (m is null)
43 return;
44
45 if (m.impl)
46 {
47 // let the GC collect the monitor
48 setMonitor(h, null);
49 }
50 else if (!atomicOp!("-=")(m.refs, cast(size_t) 1))
51 {
52 // refcount == 0 means unshared => no synchronization required
53 disposeEvent(cast(Monitor*) m, h);
54 deleteMonitor(cast(Monitor*) m);
55 setMonitor(h, null);
56 }
57 }
58
59 // does not call dispose events, for internal use only
_d_monitordelete_nogc(Object h)60 extern (C) void _d_monitordelete_nogc(Object h) @nogc
61 {
62 auto m = getMonitor(h);
63 if (m is null)
64 return;
65
66 if (m.impl)
67 {
68 // let the GC collect the monitor
69 setMonitor(h, null);
70 }
71 else if (!atomicOp!("-=")(m.refs, cast(size_t) 1))
72 {
73 // refcount == 0 means unshared => no synchronization required
74 deleteMonitor(cast(Monitor*) m);
75 setMonitor(h, null);
76 }
77 }
78
_d_monitorenter(Object h)79 extern (C) void _d_monitorenter(Object h)
80 in
81 {
82 assert(h !is null, "Synchronized object must not be null.");
83 }
84 body
85 {
86 auto m = cast(Monitor*) ensureMonitor(h);
87 auto i = m.impl;
88 if (i is null)
89 lockMutex(&m.mtx);
90 else
91 i.lock();
92 }
93
_d_monitorexit(Object h)94 extern (C) void _d_monitorexit(Object h)
95 {
96 auto m = cast(Monitor*) getMonitor(h);
97 auto i = m.impl;
98 if (i is null)
99 unlockMutex(&m.mtx);
100 else
101 i.unlock();
102 }
103
rt_attachDisposeEvent(Object h,DEvent e)104 extern (C) void rt_attachDisposeEvent(Object h, DEvent e)
105 {
106 synchronized (h)
107 {
108 auto m = cast(Monitor*) getMonitor(h);
109 assert(m.impl is null);
110
111 foreach (ref v; m.devt)
112 {
113 if (v is null || v == e)
114 {
115 v = e;
116 return;
117 }
118 }
119
120 auto len = m.devt.length + 4; // grow by 4 elements
121 auto pos = m.devt.length; // insert position
122 auto p = realloc(m.devt.ptr, DEvent.sizeof * len);
123 import core.exception : onOutOfMemoryError;
124
125 if (!p)
126 onOutOfMemoryError();
127 m.devt = (cast(DEvent*) p)[0 .. len];
128 m.devt[pos + 1 .. len] = null;
129 m.devt[pos] = e;
130 }
131 }
132
rt_detachDisposeEvent(Object h,DEvent e)133 extern (C) void rt_detachDisposeEvent(Object h, DEvent e)
134 {
135 synchronized (h)
136 {
137 auto m = cast(Monitor*) getMonitor(h);
138 assert(m.impl is null);
139
140 foreach (p, v; m.devt)
141 {
142 if (v == e)
143 {
144 memmove(&m.devt[p], &m.devt[p + 1], (m.devt.length - p - 1) * DEvent.sizeof);
145 m.devt[$ - 1] = null;
146 return;
147 }
148 }
149 }
150 }
151
152 nothrow:
153
_d_monitor_staticctor()154 extern (C) void _d_monitor_staticctor()
155 {
156 version (Posix)
157 {
158 pthread_mutexattr_init(&gattr);
159 pthread_mutexattr_settype(&gattr, PTHREAD_MUTEX_RECURSIVE);
160 }
161 initMutex(&gmtx);
162 }
163
_d_monitor_staticdtor()164 extern (C) void _d_monitor_staticdtor()
165 {
166 destroyMutex(&gmtx);
167 version (Posix)
168 pthread_mutexattr_destroy(&gattr);
169 }
170
171 package:
172
173 // This is what the monitor reference in Object points to
174 alias IMonitor = Object.Monitor;
175 alias DEvent = void delegate(Object);
176
version(GNU)177 version (GNU)
178 {
179 import gcc.config;
180 static if (GNU_Thread_Model == ThreadModel.Single)
181 version = SingleThreaded;
182 // Ignore ThreadModel, we don't want posix threads on windows and
183 // will always use native threading instead.
184 }
185
version(SingleThreaded)186 version (SingleThreaded)
187 {
188 alias Mutex = int;
189
190 void initMutex(Mutex* mtx)
191 {
192 }
193
194 void destroyMutex(Mutex* mtx)
195 {
196 }
197
198 void lockMutex(Mutex* mtx)
199 {
200 }
201
202 void unlockMutex(Mutex* mtx)
203 {
204 }
205 }
version(Windows)206 else version (Windows)
207 {
208 version (CRuntime_DigitalMars)
209 {
210 pragma(lib, "snn.lib");
211 }
212 import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection,
213 EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection+/;
214
215 alias Mutex = CRITICAL_SECTION;
216
217 alias initMutex = InitializeCriticalSection;
218 alias destroyMutex = DeleteCriticalSection;
219 alias lockMutex = EnterCriticalSection;
220 alias unlockMutex = LeaveCriticalSection;
221 }
version(Posix)222 else version (Posix)
223 {
224 import core.sys.posix.pthread;
225
226 @nogc:
227 alias Mutex = pthread_mutex_t;
228 __gshared pthread_mutexattr_t gattr;
229
230 void initMutex(pthread_mutex_t* mtx)
231 {
232 pthread_mutex_init(mtx, &gattr) && assert(0);
233 }
234
235 void destroyMutex(pthread_mutex_t* mtx)
236 {
237 pthread_mutex_destroy(mtx) && assert(0);
238 }
239
240 void lockMutex(pthread_mutex_t* mtx)
241 {
242 pthread_mutex_lock(mtx) && assert(0);
243 }
244
245 void unlockMutex(pthread_mutex_t* mtx)
246 {
247 pthread_mutex_unlock(mtx) && assert(0);
248 }
249 }
250 else
251 {
252 static assert(0, "Unsupported platform");
253 }
254
255 struct Monitor
256 {
257 IMonitor impl; // for user-level monitors
258 DEvent[] devt; // for internal monitors
259 size_t refs; // reference count
260 Mutex mtx;
261 }
262
263 private:
264
shared(Monitor *)265 @property ref shared(Monitor*) monitor(Object h) pure nothrow @nogc
266 {
267 return *cast(shared Monitor**)&h.__monitor;
268 }
269
shared(Monitor)270 private shared(Monitor)* getMonitor(Object h) pure @nogc
271 {
272 return atomicLoad!(MemoryOrder.acq)(h.monitor);
273 }
274
setMonitor(Object h,shared (Monitor)* m)275 void setMonitor(Object h, shared(Monitor)* m) pure @nogc
276 {
277 atomicStore!(MemoryOrder.rel)(h.monitor, m);
278 }
279
280 __gshared Mutex gmtx;
281
shared(Monitor)282 shared(Monitor)* ensureMonitor(Object h)
283 {
284 if (auto m = getMonitor(h))
285 return m;
286
287 auto m = cast(Monitor*) calloc(Monitor.sizeof, 1);
288 assert(m);
289 initMutex(&m.mtx);
290
291 bool success;
292 lockMutex(&gmtx);
293 if (getMonitor(h) is null)
294 {
295 m.refs = 1;
296 setMonitor(h, cast(shared) m);
297 success = true;
298 }
299 unlockMutex(&gmtx);
300
301 if (success)
302 {
303 // Set the finalize bit so that the monitor gets collected (Bugzilla 14573)
304 import core.memory : GC;
305
306 if (!(typeid(h).m_flags & TypeInfo_Class.ClassFlags.hasDtor))
307 GC.setAttr(cast(void*) h, GC.BlkAttr.FINALIZE);
308 return cast(shared(Monitor)*) m;
309 }
310 else // another thread succeeded instead
311 {
312 deleteMonitor(m);
313 return getMonitor(h);
314 }
315 }
316
deleteMonitor(Monitor * m)317 void deleteMonitor(Monitor* m) @nogc
318 {
319 destroyMutex(&m.mtx);
320 free(m);
321 }
322
disposeEvent(Monitor * m,Object h)323 void disposeEvent(Monitor* m, Object h)
324 {
325 foreach (v; m.devt)
326 {
327 if (v)
328 v(h);
329 }
330 if (m.devt.ptr)
331 free(m.devt.ptr);
332 }
333
334 // Bugzilla 14573
335 unittest
336 {
337 import core.memory : GC;
338
339 auto obj = new Object;
340 assert(!(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE));
341 ensureMonitor(obj);
342 assert(getMonitor(obj) !is null);
343 assert(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE);
344 }
345