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 */
12module rt.monitor_;
13
14import 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
23extern (C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow
24in
25{
26    assert(ownee.__monitor is null);
27}
28body
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
43extern (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
63extern (C) void _d_monitorenter(Object h)
64in
65{
66    assert(h !is null, "Synchronized object must not be null.");
67}
68body
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
78extern (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
88extern (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
117extern (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
136nothrow:
137
138extern (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
148extern (C) void _d_monitor_staticdtor()
149{
150    destroyMutex(&gmtx);
151    version (Posix)
152        pthread_mutexattr_destroy(&gattr);
153}
154
155package:
156
157// This is what the monitor reference in Object points to
158alias IMonitor = Object.Monitor;
159alias DEvent = void delegate(Object);
160
161version (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
170version (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}
190else 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}
206else 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}
234else
235{
236    static assert(0, "Unsupported platform");
237}
238
239struct 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
247private:
248
249@property ref shared(Monitor*) monitor(Object h) pure nothrow @nogc
250{
251    return *cast(shared Monitor**)&h.__monitor;
252}
253
254private shared(Monitor)* getMonitor(Object h) pure @nogc
255{
256    return atomicLoad!(MemoryOrder.acq)(h.monitor);
257}
258
259void setMonitor(Object h, shared(Monitor)* m) pure @nogc
260{
261    atomicStore!(MemoryOrder.rel)(h.monitor, m);
262}
263
264__gshared Mutex gmtx;
265
266shared(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
301void deleteMonitor(Monitor* m) @nogc
302{
303    destroyMutex(&m.mtx);
304    free(m);
305}
306
307void 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
319unittest
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