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 */
9module rt.monitor_;
10
11import 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
20extern (C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow
21in
22{
23    assert(ownee.__monitor is null);
24}
25do
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
36extern (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
57extern (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
76extern (C) void _d_monitorenter(Object h)
77in
78{
79    assert(h !is null, "Synchronized object must not be null.");
80}
81do
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
91extern (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
101extern (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
130extern (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
149nothrow:
150
151extern (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
161extern (C) void _d_monitor_staticdtor()
162{
163    destroyMutex(&gmtx);
164    version (Posix)
165        pthread_mutexattr_destroy(&gattr);
166}
167
168package:
169
170// This is what the monitor reference in Object points to
171alias IMonitor = Object.Monitor;
172alias DEvent = void delegate(Object);
173
174version (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}
190else 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}
218else
219{
220    static assert(0, "Unsupported platform");
221}
222
223struct 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
231private:
232
233@property ref shared(Monitor*) monitor(return scope Object h) pure nothrow @nogc
234{
235    return *cast(shared Monitor**)&h.__monitor;
236}
237
238private shared(Monitor)* getMonitor(Object h) pure @nogc
239{
240    return atomicLoad!(MemoryOrder.acq)(h.monitor);
241}
242
243void setMonitor(Object h, shared(Monitor)* m) pure @nogc
244{
245    atomicStore!(MemoryOrder.rel)(h.monitor, m);
246}
247
248__gshared Mutex gmtx;
249
250shared(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
285void deleteMonitor(Monitor* m) @nogc
286{
287    destroyMutex(&m.mtx);
288    free(m);
289}
290
291void 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
303unittest
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