2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/mutex.c
5 * PURPOSE: Implements Mutexes and Mutants (that silly davec...)
8 * Alex Ionescu (alex@relsoft.net) - Reorganized/commented some of the code.
9 * Simplified some functions, fixed some return values and
10 * corrected some minor bugs, added debug output.
11 * David Welch (welch@mcmail.com)
14 /* INCLUDES *****************************************************************/
18 #include <internal/debug.h>
20 /* FUNCTIONS *****************************************************************/
27 KeInitializeMutant(IN PKMUTANT Mutant
,
28 IN BOOLEAN InitialOwner
)
30 PKTHREAD CurrentThread
;
32 DPRINT("KeInitializeMutant: %x\n", Mutant
);
34 /* Check if we have an initial owner */
35 if (InitialOwner
== TRUE
)
37 /* We also need to associate a thread */
38 CurrentThread
= KeGetCurrentThread();
39 Mutant
->OwnerThread
= CurrentThread
;
41 /* We're about to touch the Thread, so lock the Dispatcher */
42 OldIrql
= KeAcquireDispatcherDatabaseLock();
44 /* And insert it into its list */
45 InsertTailList(&CurrentThread
->MutantListHead
,
46 &Mutant
->MutantListEntry
);
48 /* Release Dispatcher Lock */
49 KeReleaseDispatcherDatabaseLock(OldIrql
);
50 DPRINT("Mutant with Initial Owner\n");
54 /* In this case, we don't have an owner yet */
55 Mutant
->OwnerThread
= NULL
;
58 /* Now we set up the Dispatcher Header */
59 KeInitializeDispatcherHeader(&Mutant
->Header
,
61 sizeof(KMUTANT
) / sizeof(ULONG
),
62 InitialOwner
? FALSE
: TRUE
);
64 /* Initialize the default data */
65 Mutant
->Abandoned
= FALSE
;
66 Mutant
->ApcDisable
= 0;
74 KeInitializeMutex(IN PKMUTEX Mutex
,
77 DPRINT("KeInitializeMutex: %x\n", Mutex
);
79 /* Set up the Dispatcher Header */
80 KeInitializeDispatcherHeader(&Mutex
->Header
,
82 sizeof(KMUTEX
) / sizeof(ULONG
),
85 /* Initialize the default data */
86 Mutex
->OwnerThread
= NULL
;
87 Mutex
->Abandoned
= FALSE
;
88 Mutex
->ApcDisable
= 1;
96 KeReadStateMutant(IN PKMUTANT Mutant
)
98 /* Return the Signal State */
99 return(Mutant
->Header
.SignalState
);
107 KeReleaseMutant(IN PKMUTANT Mutant
,
108 IN KPRIORITY Increment
,
114 PKTHREAD CurrentThread
= KeGetCurrentThread();
115 DPRINT("KeReleaseMutant: %x\n", Mutant
);
117 /* Lock the Dispatcher Database */
118 OldIrql
= KeAcquireDispatcherDatabaseLock();
120 /* Save the Previous State */
121 PreviousState
= Mutant
->Header
.SignalState
;
123 /* Check if it is to be abandonned */
124 if (Abandon
== FALSE
)
126 /* Make sure that the Owner Thread is the current Thread */
127 if (Mutant
->OwnerThread
!= CurrentThread
)
129 DPRINT1("Trying to touch a Mutant that the caller doesn't own!\n");
131 /* Release the lock */
132 KeReleaseDispatcherDatabaseLock(OldIrql
);
134 /* Raise an exception */
135 ExRaiseStatus(Mutant
->Abandoned
? STATUS_ABANDONED
:
136 STATUS_MUTANT_NOT_OWNED
);
139 /* If the thread owns it, then increase the signal state */
140 Mutant
->Header
.SignalState
++;
144 /* It's going to be abandonned */
145 DPRINT("Abandonning the Mutant\n");
146 Mutant
->Header
.SignalState
= 1;
147 Mutant
->Abandoned
= TRUE
;
150 /* Check if the signal state is only single */
151 if (Mutant
->Header
.SignalState
== 1)
153 /* Check if it's below 0 now */
154 if (PreviousState
<= 0)
156 /* Remove the mutant from the list */
157 DPRINT("Removing Mutant %p\n", Mutant
);
158 RemoveEntryList(&Mutant
->MutantListEntry
);
161 DPRINT("Re-enabling APCs\n");
162 CurrentThread
->KernelApcDisable
+= Mutant
->ApcDisable
;
164 /* Check if the thread has APCs enabled */
165 if (!CurrentThread
->KernelApcDisable
)
167 /* Check if any are pending */
168 if (!IsListEmpty(&CurrentThread
->ApcState
.ApcListHead
[KernelMode
]))
170 /* Set Kernel APC Pending */
171 CurrentThread
->ApcState
.KernelApcPending
= TRUE
;
173 /* Request the Interrupt */
174 DPRINT("Requesting APC Interupt\n");
175 HalRequestSoftwareInterrupt(APC_LEVEL
);
180 /* Remove the Owning Thread and wake it */
181 Mutant
->OwnerThread
= NULL
;
183 /* Check if the Wait List isn't empty */
184 DPRINT("Checking whether to wake the Mutant\n");
185 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
187 /* Wake the Mutant */
188 DPRINT("Waking the Mutant\n");
189 KiWaitTest(&Mutant
->Header
, Increment
);
193 /* If the Wait is true, then return with a Wait and don't unlock the Dispatcher Database */
196 /* Release the Lock */
197 KeReleaseDispatcherDatabaseLock(OldIrql
);
202 CurrentThread
->WaitNext
= TRUE
;
203 CurrentThread
->WaitIrql
= OldIrql
;
206 /* Return the previous state */
207 return PreviousState
;
215 KeReleaseMutex(IN PKMUTEX Mutex
,
218 /* There's no difference at this level between the two */
219 return KeReleaseMutant(Mutex
, 1, FALSE
, Wait
);