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