- Use _SEH2_YIELD when returning from an exception instead of returning outside the...
[reactos.git] / reactos / ntoskrnl / ke / mutex.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/mutex.c
5 * PURPOSE: Implements the Mutant Dispatcher Object
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* FUNCTIONS *****************************************************************/
16
17 /*
18 * @implemented
19 */
20 VOID
21 NTAPI
22 KeInitializeMutant(IN PKMUTANT Mutant,
23 IN BOOLEAN InitialOwner)
24 {
25 PKTHREAD CurrentThread;
26 KIRQL OldIrql;
27
28 /* Check if we have an initial owner */
29 if (InitialOwner)
30 {
31 /* We also need to associate a thread */
32 CurrentThread = KeGetCurrentThread();
33 Mutant->OwnerThread = CurrentThread;
34
35 /* We're about to touch the Thread, so lock the Dispatcher */
36 OldIrql = KiAcquireDispatcherLock();
37
38 /* And insert it into its list */
39 InsertTailList(&CurrentThread->MutantListHead,
40 &Mutant->MutantListEntry);
41
42 /* Release Dispatcher Lock */
43 KiReleaseDispatcherLock(OldIrql);
44 }
45 else
46 {
47 /* In this case, we don't have an owner yet */
48 Mutant->OwnerThread = NULL;
49 }
50
51 /* Now we set up the Dispatcher Header */
52 KeInitializeDispatcherHeader(&Mutant->Header,
53 MutantObject,
54 sizeof(KMUTANT) / sizeof(ULONG),
55 InitialOwner ? FALSE : TRUE);
56
57 /* Initialize the default data */
58 Mutant->Abandoned = FALSE;
59 Mutant->ApcDisable = 0;
60 }
61
62 /*
63 * @implemented
64 */
65 VOID
66 NTAPI
67 KeInitializeMutex(IN PKMUTEX Mutex,
68 IN ULONG Level)
69 {
70 /* Set up the Dispatcher Header */
71 KeInitializeDispatcherHeader(&Mutex->Header,
72 MutantObject,
73 sizeof(KMUTEX) / sizeof(ULONG),
74 TRUE);
75
76 /* Initialize the default data */
77 Mutex->OwnerThread = NULL;
78 Mutex->Abandoned = FALSE;
79 Mutex->ApcDisable = 1;
80 }
81
82 /*
83 * @implemented
84 */
85 LONG
86 NTAPI
87 KeReadStateMutant(IN PKMUTANT Mutant)
88 {
89 /* Return the Signal State */
90 return Mutant->Header.SignalState;
91 }
92
93 /*
94 * @implemented
95 */
96 LONG
97 NTAPI
98 KeReleaseMutant(IN PKMUTANT Mutant,
99 IN KPRIORITY Increment,
100 IN BOOLEAN Abandon,
101 IN BOOLEAN Wait)
102 {
103 KIRQL OldIrql;
104 LONG PreviousState;
105 PKTHREAD CurrentThread = KeGetCurrentThread();
106 BOOLEAN EnableApc = FALSE;
107 ASSERT_MUTANT(Mutant);
108 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
109
110 /* Lock the Dispatcher Database */
111 OldIrql = KiAcquireDispatcherLock();
112
113 /* Save the Previous State */
114 PreviousState = Mutant->Header.SignalState;
115
116 /* Check if it is to be abandonned */
117 if (Abandon == FALSE)
118 {
119 /* Make sure that the Owner Thread is the current Thread */
120 if (Mutant->OwnerThread != CurrentThread)
121 {
122 /* Release the lock */
123 KiReleaseDispatcherLock(OldIrql);
124
125 /* Raise an exception */
126 ExRaiseStatus(Mutant->Abandoned ? STATUS_ABANDONED :
127 STATUS_MUTANT_NOT_OWNED);
128 }
129
130 /* If the thread owns it, then increase the signal state */
131 Mutant->Header.SignalState++;
132 }
133 else
134 {
135 /* It's going to be abandonned */
136 Mutant->Header.SignalState = 1;
137 Mutant->Abandoned = TRUE;
138 }
139
140 /* Check if the signal state is only single */
141 if (Mutant->Header.SignalState == 1)
142 {
143 /* Check if it's below 0 now */
144 if (PreviousState <= 0)
145 {
146 /* Remove the mutant from the list */
147 RemoveEntryList(&Mutant->MutantListEntry);
148
149 /* Save if we need to re-enable APCs */
150 EnableApc = Mutant->ApcDisable;
151 }
152
153 /* Remove the Owning Thread and wake it */
154 Mutant->OwnerThread = NULL;
155
156 /* Check if the Wait List isn't empty */
157 if (!IsListEmpty(&Mutant->Header.WaitListHead))
158 {
159 /* Wake the Mutant */
160 KiWaitTest(&Mutant->Header, Increment);
161 }
162 }
163
164 /* Check if the caller wants to wait after this release */
165 if (Wait == FALSE)
166 {
167 /* Release the Lock */
168 KiReleaseDispatcherLock(OldIrql);
169 }
170 else
171 {
172 /* Set a wait */
173 CurrentThread->WaitNext = TRUE;
174 CurrentThread->WaitIrql = OldIrql;
175 }
176
177 /* Check if we need to re-enable APCs */
178 if (EnableApc) KeLeaveCriticalRegion();
179
180 /* Return the previous state */
181 return PreviousState;
182 }
183
184 /*
185 * @implemented
186 */
187 LONG
188 NTAPI
189 KeReleaseMutex(IN PKMUTEX Mutex,
190 IN BOOLEAN Wait)
191 {
192 ASSERT_MUTANT(Mutex);
193
194 /* There's no difference at this level between the two */
195 return KeReleaseMutant(Mutex, 1, FALSE, Wait);
196 }
197
198 /* EOF */