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 auto i = m.impl;
32 if (i is null)
33 {
34 atomicOp!("+=")(m.refs, cast(size_t) 1);
35 ownee.__monitor = owner.__monitor;
36 return;
37 }
38 // If m.impl is set (ie. if this is a user-created monitor), assume
39 // the monitor is garbage collected and simply copy the reference.
40 ownee.__monitor = owner.__monitor;
41 }
42
_d_monitordelete(Object h,bool det)43 extern (C) void _d_monitordelete(Object h, bool det)
44 {
45 auto m = getMonitor(h);
46 if (m is null)
47 return;
48
49 if (m.impl)
50 {
51 // let the GC collect the monitor
52 setMonitor(h, null);
53 }
54 else if (!atomicOp!("-=")(m.refs, cast(size_t) 1))
55 {
56 // refcount == 0 means unshared => no synchronization required
57 disposeEvent(cast(Monitor*) m, h);
58 deleteMonitor(cast(Monitor*) m);
59 setMonitor(h, null);
60 }
61 }
62
_d_monitorenter(Object h)63 extern (C) void _d_monitorenter(Object h)
64 in
65 {
66 assert(h !is null, "Synchronized object must not be null.");
67 }
68 body
69 {
70 auto m = cast(Monitor*) ensureMonitor(h);
71 auto i = m.impl;
72 if (i is null)
73 lockMutex(&m.mtx);
74 else
75 i.lock();
76 }
77
_d_monitorexit(Object h)78 extern (C) void _d_monitorexit(Object h)
79 {
80 auto m = cast(Monitor*) getMonitor(h);
81 auto i = m.impl;
82 if (i is null)
83 unlockMutex(&m.mtx);
84 else
85 i.unlock();
86 }
87
rt_attachDisposeEvent(Object h,DEvent e)88 extern (C) void rt_attachDisposeEvent(Object h, DEvent e)
89 {
90 synchronized (h)
91 {
92 auto m = cast(Monitor*) getMonitor(h);
93 assert(m.impl is null);
94
95 foreach (ref v; m.devt)
96 {
97 if (v is null || v == e)
98 {
99 v = e;
100 return;
101 }
102 }
103
104 auto len = m.devt.length + 4; // grow by 4 elements
105 auto pos = m.devt.length; // insert position
106 auto p = realloc(m.devt.ptr, DEvent.sizeof * len);
107 import core.exception : onOutOfMemoryError;
108
109 if (!p)
110 onOutOfMemoryError();
111 m.devt = (cast(DEvent*) p)[0 .. len];
112 m.devt[pos + 1 .. len] = null;
113 m.devt[pos] = e;
114 }
115 }
116
rt_detachDisposeEvent(Object h,DEvent e)117 extern (C) void rt_detachDisposeEvent(Object h, DEvent e)
118 {
119 synchronized (h)
120 {
121 auto m = cast(Monitor*) getMonitor(h);
122 assert(m.impl is null);
123
124 foreach (p, v; m.devt)
125 {
126 if (v == e)
127 {
128 memmove(&m.devt[p], &m.devt[p + 1], (m.devt.length - p - 1) * DEvent.sizeof);
129 m.devt[$ - 1] = null;
130 return;
131 }
132 }
133 }
134 }
135
136 nothrow:
137
_d_monitor_staticctor()138 extern (C) void _d_monitor_staticctor()
139 {
140 version (Posix)
141 {
142 pthread_mutexattr_init(&gattr);
143 pthread_mutexattr_settype(&gattr, PTHREAD_MUTEX_RECURSIVE);
144 }
145 initMutex(&gmtx);
146 }
147
_d_monitor_staticdtor()148 extern (C) void _d_monitor_staticdtor()
149 {
150 destroyMutex(&gmtx);
151 version (Posix)
152 pthread_mutexattr_destroy(&gattr);
153 }
154
155 package:
156
157 // This is what the monitor reference in Object points to
158 alias IMonitor = Object.Monitor;
159 alias DEvent = void delegate(Object);
160
version(GNU)161 version (GNU)
162 {
163 import gcc.config;
164 static if (GNU_Thread_Model == ThreadModel.Single)
165 version = SingleThreaded;
166 // Ignore ThreadModel, we don't want posix threads on windows and
167 // will always use native threading instead.
168 }
169
version(SingleThreaded)170 version (SingleThreaded)
171 {
172 alias Mutex = int;
173
174 void initMutex(Mutex* mtx)
175 {
176 }
177
178 void destroyMutex(Mutex* mtx)
179 {
180 }
181
182 void lockMutex(Mutex* mtx)
183 {
184 }
185
186 void unlockMutex(Mutex* mtx)
187 {
188 }
189 }
version(Windows)190 else version (Windows)
191 {
192 version (CRuntime_DigitalMars)
193 {
194 pragma(lib, "snn.lib");
195 }
196 import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection,
197 EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection+/;
198
199 alias Mutex = CRITICAL_SECTION;
200
201 alias initMutex = InitializeCriticalSection;
202 alias destroyMutex = DeleteCriticalSection;
203 alias lockMutex = EnterCriticalSection;
204 alias unlockMutex = LeaveCriticalSection;
205 }
version(Posix)206 else version (Posix)
207 {
208 import core.sys.posix.pthread;
209
210 @nogc:
211 alias Mutex = pthread_mutex_t;
212 __gshared pthread_mutexattr_t gattr;
213
214 void initMutex(pthread_mutex_t* mtx)
215 {
216 pthread_mutex_init(mtx, &gattr) && assert(0);
217 }
218
219 void destroyMutex(pthread_mutex_t* mtx)
220 {
221 pthread_mutex_destroy(mtx) && assert(0);
222 }
223
224 void lockMutex(pthread_mutex_t* mtx)
225 {
226 pthread_mutex_lock(mtx) && assert(0);
227 }
228
229 void unlockMutex(pthread_mutex_t* mtx)
230 {
231 pthread_mutex_unlock(mtx) && assert(0);
232 }
233 }
234 else
235 {
236 static assert(0, "Unsupported platform");
237 }
238
239 struct Monitor
240 {
241 IMonitor impl; // for user-level monitors
242 DEvent[] devt; // for internal monitors
243 size_t refs; // reference count
244 Mutex mtx;
245 }
246
247 private:
248
shared(Monitor *)249 @property ref shared(Monitor*) monitor(Object h) pure nothrow @nogc
250 {
251 return *cast(shared Monitor**)&h.__monitor;
252 }
253
shared(Monitor)254 private shared(Monitor)* getMonitor(Object h) pure @nogc
255 {
256 return atomicLoad!(MemoryOrder.acq)(h.monitor);
257 }
258
setMonitor(Object h,shared (Monitor)* m)259 void setMonitor(Object h, shared(Monitor)* m) pure @nogc
260 {
261 atomicStore!(MemoryOrder.rel)(h.monitor, m);
262 }
263
264 __gshared Mutex gmtx;
265
shared(Monitor)266 shared(Monitor)* ensureMonitor(Object h)
267 {
268 if (auto m = getMonitor(h))
269 return m;
270
271 auto m = cast(Monitor*) calloc(Monitor.sizeof, 1);
272 assert(m);
273 initMutex(&m.mtx);
274
275 bool success;
276 lockMutex(&gmtx);
277 if (getMonitor(h) is null)
278 {
279 m.refs = 1;
280 setMonitor(h, cast(shared) m);
281 success = true;
282 }
283 unlockMutex(&gmtx);
284
285 if (success)
286 {
287 // Set the finalize bit so that the monitor gets collected (Bugzilla 14573)
288 import core.memory : GC;
289
290 if (!(typeid(h).m_flags & TypeInfo_Class.ClassFlags.hasDtor))
291 GC.setAttr(cast(void*) h, GC.BlkAttr.FINALIZE);
292 return cast(shared(Monitor)*) m;
293 }
294 else // another thread succeeded instead
295 {
296 deleteMonitor(m);
297 return getMonitor(h);
298 }
299 }
300
deleteMonitor(Monitor * m)301 void deleteMonitor(Monitor* m) @nogc
302 {
303 destroyMutex(&m.mtx);
304 free(m);
305 }
306
disposeEvent(Monitor * m,Object h)307 void disposeEvent(Monitor* m, Object h)
308 {
309 foreach (v; m.devt)
310 {
311 if (v)
312 v(h);
313 }
314 if (m.devt.ptr)
315 free(m.devt.ptr);
316 }
317
318 // Bugzilla 14573
319 unittest
320 {
321 import core.memory : GC;
322
323 auto obj = new Object;
324 assert(!(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE));
325 ensureMonitor(obj);
326 assert(getMonitor(obj) !is null);
327 assert(GC.getAttr(cast(void*) obj) & GC.BlkAttr.FINALIZE);
328 }
329