fixed some warnings with gcc4 (mostly assignment differs in signedness warnings)
[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 KiKernelApcDeliveryCheck();
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 KeAcquireGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
136 {
137 /* Disable Special APCs */
138 KeEnterGuardedRegion();
139
140 /* Do the Unsafe Acquire */
141 KeAcquireGuardedMutexUnsafe(GuardedMutex);
142 }
143
144 VOID
145 FASTCALL
146 KeAcquireGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
147 {
148 /* Remove the lock */
149 if (!InterlockedClearBit(&GuardedMutex->Count, 0))
150 {
151 /* The Guarded Mutex was already locked, enter contented case */
152 KiAcquireGuardedMutexContented(GuardedMutex);
153 }
154
155 /* Set the Owner */
156 GuardedMutex->Owner = KeGetCurrentThread();
157 }
158
159 VOID
160 FASTCALL
161 KeReleaseGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
162 {
163 LONG OldValue;
164
165 /* Destroy the Owner */
166 GuardedMutex->Owner = NULL;
167
168 /* Add the Lock Bit */
169 OldValue = InterlockedExchangeAdd(&GuardedMutex->Count, 1);
170
171 /* Check if it was already locked, but not woken */
172 if (OldValue && !(OldValue & GM_LOCK_WAITER_WOKEN))
173 {
174 /* Update the Oldvalue to what it should be now */
175 OldValue |= GM_LOCK_BIT;
176
177 /* Remove the Woken bit */
178 if (InterlockedCompareExchange(&GuardedMutex->Count,
179 OldValue &~ GM_LOCK_WAITER_WOKEN,
180 OldValue) == OldValue)
181 {
182 /* Signal the Gate */
183 KeSignalGateBoostPriority(&GuardedMutex->Gate);
184 }
185 }
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 */