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
_d_setSameMutex(shared Object ownee,shared Object owner)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
_d_monitordelete(Object h,bool det)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
_d_monitordelete_nogc(Object h)57 extern (C) void _d_monitordelete_nogc(Object h) @nogc
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
_d_monitorenter(Object h)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
_d_monitorexit(Object h)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
rt_attachDisposeEvent(Object h,DEvent e)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
rt_detachDisposeEvent(Object h,DEvent e)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
_d_monitor_staticctor()151 extern (C) void _d_monitor_staticctor()
152 {
153 version (Posix)
154 {
155 pthread_mutexattr_init(&gattr);
156 pthread_mutexattr_settype(&gattr, PTHREAD_MUTEX_RECURSIVE);
157 }
158 initMutex(&gmtx);
159 }
160
_d_monitor_staticdtor()161 extern (C) void _d_monitor_staticdtor()
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
version(Windows)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 }
version(Posix)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
shared(Monitor *)233 @property ref shared(Monitor*) monitor(return scope Object h) pure nothrow @nogc
234 {
235 return *cast(shared Monitor**)&h.__monitor;
236 }
237
shared(Monitor)238 private shared(Monitor)* getMonitor(Object h) pure @nogc
239 {
240 return atomicLoad!(MemoryOrder.acq)(h.monitor);
241 }
242
setMonitor(Object h,shared (Monitor)* m)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
shared(Monitor)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
deleteMonitor(Monitor * m)285 void deleteMonitor(Monitor* m) @nogc
286 {
287 destroyMutex(&m.mtx);
288 free(m);
289 }
290
disposeEvent(Monitor * m,Object h)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 }
313