1 module rt.monitor_;
2 
3 version(LDC)
4 	pragma(LDC_no_moduleinfo);
5 
6 version(LWDR_Sync):
7 
8 import rtoslink;
9 import lwdr.tracking;
10 
11 import core.atomic;
12 
13 /++
14 First field of any Object monitor must be a reference to Object.Monitor, even for custom implementations.
15 Failure to do so will result in memory corruption.
16 ++/
17 
18 package struct Monitor
19 {
20 	Object.Monitor userMonitor;
21 	size_t referenceCount;
22 	void* mutex;
23 }
24 
25 /++
26 Called on entry to a `synchronized` statement. If `o` does not already have a monitor, it will be created.
27 The monitor will then be locked.
28 
29 `o` must not be null.
30 ++/
31 extern(C) void _d_monitorenter(Object o)
32 in(o !is null, "Cannot sync null object")
33 {
34 	auto m = cast(Monitor*)ensureMonitor(o);
35 	if(m.userMonitor is null)
36 		rtosbackend_mutexLock(m.mutex);
37 	else
38 		m.userMonitor.lock;
39 }
40 
41 /++
42 Called on exit from a `synchronized` statement. It will will unlock the monitor.
43 ++/
44 extern(C) void _d_monitorexit(Object o)
45 {
46 	// don't apply null checking, as thread will have already crashed.
47 	auto m = cast(Monitor*)getMonitor(o);
48 	if(m.userMonitor is null)
49 		rtosbackend_mutexUnlock(m.mutex);
50 	else 
51 		m.userMonitor.unlock;
52 }
53 
54 /++
55 INTERNAL USE!
56 Called during finalisation to remove monitor.
57 ++/
58 void _lwdr_monitorDelete(Object o) @nogc nothrow
59 {
60 	auto m = getMonitor(o);
61 	if(m is null) 
62 		return;
63 
64 	scope(exit) setMonitor(o, null);
65 
66 	// user monitor should outlive object (ie, core.sync.Mutex) or be destroyed in dtor.
67 	if(m.userMonitor !is null)
68 		return;
69 
70 	if(atomicOp!"-="(m.referenceCount, cast(size_t)1) == 0)
71 	{
72 		// referenceCount == 0 means unshared -> no sync needed
73 		deleteMonitor(cast(Monitor*)m);
74 	}
75 }
76 
77 private
78 {
79 	__gshared void* globalMutex;
80 
81 	@property ref shared(Monitor*) monitor(return Object o) pure nothrow @nogc
82 	{ return *cast(shared Monitor**)&o.__monitor; }
83 
84 	shared(Monitor)* getMonitor(Object o) pure @nogc nothrow
85 	{ return atomicLoad!(MemoryOrder.acq)(o.monitor); }
86 
87 	void setMonitor(Object o, shared(Monitor)* m) pure @nogc nothrow
88 	{ atomicStore!(MemoryOrder.rel)(o.monitor, m); }
89 
90 	/// Gets existing monitor, or assigns one.
91 	shared(Monitor)* ensureMonitor(Object o)
92 	{
93 		if(auto m = getMonitor(o))
94 			return m;
95 
96 		auto monitor = cast(Monitor*)lwdrInternal_alloc(Monitor.sizeof);
97 		assert(monitor !is null);
98 		*monitor = Monitor();
99 		
100 		monitor.mutex = rtosbackend_mutexInit();
101 
102 		bool success;
103 		rtosbackend_mutexLock(globalMutex);
104 		if(getMonitor(o) is null)
105 		{
106 			monitor.referenceCount = 1;
107 			setMonitor(o, cast(shared)monitor);
108 			success = true;
109 		}
110 		rtosbackend_mutexUnlock(globalMutex);
111 
112 		if(success)
113 			return cast(shared(Monitor)*)monitor;
114 		else
115 		{
116 			deleteMonitor(monitor);
117 			return getMonitor(o);
118 		}
119 	}
120 
121 	/// Deletes mutex and monitor
122 	void deleteMonitor(Monitor* m) @nogc nothrow
123 	{
124 		rtosbackend_mutexDestroy(m.mutex);
125 		lwdrInternal_free(m);
126 	}
127 }
128 
129 void __lwdr_monitor_init() nothrow @nogc
130 {
131 	globalMutex = rtosbackend_globalMutexInit;
132 	assert(globalMutex !is null);
133 }
134 
135 void __lwdr_monitor_deinit() nothrow @nogc
136 {
137 	rtosbackend_globalMutexDestroy(globalMutex);
138 	globalMutex = null;
139 }