- Implemented InterlockedBitTestAndReset, InterlockedBitTestAndSet, InterlockedExchan...
[reactos.git] / reactos / ntoskrnl / ke / gmutex.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ke/gmutex.c
5 * PURPOSE: Implements Guarded Mutex
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) and
8 * Filip Navara (xnavara@volny.cz)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #include <internal/debug.h>
15
16 UCHAR
17 FASTCALL
18 InterlockedClearBit(PLONG Destination,
19 LONG Bit);
20
21 typedef enum _KGUARDED_MUTEX_BITS
22 {
23 GM_LOCK_BIT = 1,
24 GM_LOCK_WAITER_WOKEN = 2,
25 GM_LOCK_WAITER_INC = 4
26 } KGUARDED_MUTEX_BITS;
27
28 /* FUNCTIONS *****************************************************************/
29
30 /**
31 * @name KeEnterGuardedRegion
32 *
33 * Enters a guarded region. This causes all (incl. special kernel) APCs
34 * to be disabled.
35 */
36 VOID
37 STDCALL
38 KeEnterGuardedRegion(VOID)
39 {
40 /* Disable Special APCs */
41 KeGetCurrentThread()->SpecialApcDisable--;
42 }
43
44 /**
45 * @name KeLeaveGuardedRegion
46 *
47 * Leaves a guarded region and delivers pending APCs if possible.
48 */
49 VOID
50 STDCALL
51 KeLeaveGuardedRegion(VOID)
52 {
53 PKTHREAD Thread = KeGetCurrentThread();
54
55 /* Boost the enable count and check if Special APCs are enabled */
56 if (++Thread->SpecialApcDisable == 0)
57 {
58 /* Check if there are Kernel APCs on the list */
59 if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
60 {
61 /* Check for APC Delivery */
62 KiCheckForKernelApcDelivery();
63 }
64 }
65 }
66
67 VOID
68 FASTCALL
69 KeInitializeGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
70 {
71 /* Setup the Initial Data */
72 GuardedMutex->Count = GM_LOCK_BIT;
73 GuardedMutex->Owner = NULL;
74 GuardedMutex->Contention = 0;
75
76 /* Initialize the Wait Gate */
77 KeInitializeGate(&GuardedMutex->Gate);
78 }
79
80 VOID
81 FASTCALL
82 KiAcquireGuardedMutexContented(PKGUARDED_MUTEX GuardedMutex)
83 {
84 ULONG BitsToRemove;
85 ULONG BitsToAdd;
86 LONG OldValue;
87
88 /* Increase the contention count */
89 InterlockedIncrement((PLONG)&GuardedMutex->Contention);
90
91 /* Start by unlocking the Guarded Mutex */
92 BitsToRemove = GM_LOCK_BIT;
93 BitsToAdd = GM_LOCK_WAITER_INC;
94
95 while (1)
96 {
97 /* Get the Count Bits */
98 OldValue = (volatile LONG)GuardedMutex->Count;
99
100 /* Check if the Guarded Mutex is locked */
101 if (OldValue & GM_LOCK_BIT)
102 {
103 /* Unlock it by removing the Lock Bit */
104 if (InterlockedCompareExchange(&GuardedMutex->Count,
105 OldValue &~ BitsToRemove,
106 OldValue) == OldValue)
107 {
108 /* The Guarded Mutex is now unlocked */
109 break;
110 }
111 }
112 else
113 {
114 /* The Guarded Mutex isn't locked, so simply set the bits */
115 if (InterlockedCompareExchange(&GuardedMutex->Count,
116 OldValue | BitsToAdd,
117 OldValue) != OldValue)
118 {
119 /* The Guarded Mutex value changed behind our back, start over */
120 continue;
121 }
122
123 /* Now we have to wait for it */
124 KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode);
125
126 /* Ok, the wait is done, so set the new bits */
127 BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN;
128 BitsToAdd = GM_LOCK_WAITER_WOKEN;
129 }
130 }
131 }
132
133 VOID
134 FASTCALL
135 KeAcquireGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
136 {
137 /* Remove the lock */
138 if (!InterlockedClearBit(&GuardedMutex->Count, 0))
139 {
140 /* The Guarded Mutex was already locked, enter contented case */
141 KiAcquireGuardedMutexContented(GuardedMutex);
142 }
143
144 /* Set the Owner */
145 GuardedMutex->Owner = KeGetCurrentThread();
146 }
147
148 VOID
149 FASTCALL
150 KeReleaseGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
151 {
152 LONG OldValue;
153
154 /* Destroy the Owner */
155 GuardedMutex->Owner = NULL;
156
157 /* Add the Lock Bit */
158 OldValue = InterlockedExchangeAdd(&GuardedMutex->Count, 1);
159
160 /* Check if it was already locked, but not woken */
161 if (OldValue && !(OldValue & GM_LOCK_WAITER_WOKEN))
162 {
163 /* Update the Oldvalue to what it should be now */
164 OldValue |= GM_LOCK_BIT;
165
166 /* Remove the Woken bit */
167 if (InterlockedCompareExchange(&GuardedMutex->Count,
168 OldValue &~ GM_LOCK_WAITER_WOKEN,
169 OldValue) == OldValue)
170 {
171 /* Signal the Gate */
172 KeSignalGateBoostPriority(&GuardedMutex->Gate);
173 }
174 }
175 }
176
177 VOID
178 FASTCALL
179 KeAcquireGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
180 {
181 /* Disable Special APCs */
182 KeEnterGuardedRegion();
183
184 /* Do the Unsafe Acquire */
185 KeAcquireGuardedMutexUnsafe(GuardedMutex);
186 }
187
188 VOID
189 FASTCALL
190 KeReleaseGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
191 {
192 /* Do the actual release */
193 KeReleaseGuardedMutexUnsafe(GuardedMutex);
194
195 /* Re-enable APCs */
196 KeLeaveGuardedRegion();
197 }
198
199 BOOL
200 FASTCALL
201 KeTryToAcquireGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
202 {
203 /* Block APCs */
204 KeEnterGuardedRegion();
205
206 /* Remove the lock */
207 if (InterlockedClearBit(&GuardedMutex->Count, 0))
208 {
209 /* Re-enable APCs */
210 KeLeaveGuardedRegion();
211
212 /* Return failure */
213 return FALSE;
214 }
215
216 /* Set the Owner */
217 GuardedMutex->Owner = KeGetCurrentThread();
218 return TRUE;
219 }
220
221 /* EOF */