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