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