xref: /reactos/ntoskrnl/ke/mutex.c (revision c2c66aff)
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     Mutant->Header.Type = MutantObject;
53     Mutant->Header.Size = sizeof(KMUTANT) / sizeof(ULONG);
54     Mutant->Header.SignalState = InitialOwner ? 0 : 1;
55     InitializeListHead(&(Mutant->Header.WaitListHead));
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     Mutex->Header.Type = MutantObject;
72     Mutex->Header.Size = sizeof(KMUTEX) / sizeof(ULONG);
73     Mutex->Header.SignalState = 1;
74     InitializeListHead(&(Mutex->Header.WaitListHead));
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 */
199