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