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