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 }