xref: /reactos/sdk/lib/rtl/resource.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS system libraries
4  * FILE:              lib/rtl/resource.c
5  * PURPOSE:           Resource (multiple-reader-single-writer lock) functions
6  * PROGRAMMER:        Partially takem from Wine:
7  *                    Copyright 1996-1998 Marcus Meissner
8  *                              1999 Alex Korobka
9  */
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include <rtl.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 /* FUNCTIONS ***************************************************************/
19 
20 /*
21  * @implemented
22  */
23 VOID
24 NTAPI
RtlInitializeResource(PRTL_RESOURCE Resource)25 RtlInitializeResource(PRTL_RESOURCE Resource)
26 {
27     NTSTATUS Status;
28 
29     Status = RtlInitializeCriticalSection(&Resource->Lock);
30     if (!NT_SUCCESS(Status))
31     {
32         RtlRaiseStatus(Status);
33     }
34 
35     Status = NtCreateSemaphore(&Resource->SharedSemaphore,
36                                SEMAPHORE_ALL_ACCESS,
37                                NULL,
38                                0,
39                                65535);
40     if (!NT_SUCCESS(Status))
41     {
42         RtlRaiseStatus(Status);
43     }
44     Resource->SharedWaiters = 0;
45 
46     Status = NtCreateSemaphore(&Resource->ExclusiveSemaphore,
47                                SEMAPHORE_ALL_ACCESS,
48                                NULL,
49                                0,
50                                65535);
51     if (!NT_SUCCESS(Status))
52     {
53         RtlRaiseStatus(Status);
54     }
55 
56     Resource->ExclusiveWaiters = 0;
57     Resource->NumberActive = 0;
58     Resource->OwningThread = NULL;
59     Resource->TimeoutBoost = 0; /* no info on this one, default value is 0 */
60 }
61 
62 
63 /*
64  * @implemented
65  */
66 VOID
67 NTAPI
RtlDeleteResource(PRTL_RESOURCE Resource)68 RtlDeleteResource(PRTL_RESOURCE Resource)
69 {
70     RtlDeleteCriticalSection(&Resource->Lock);
71     NtClose(Resource->ExclusiveSemaphore);
72     NtClose(Resource->SharedSemaphore);
73     Resource->OwningThread = NULL;
74     Resource->ExclusiveWaiters = 0;
75     Resource->SharedWaiters = 0;
76     Resource->NumberActive = 0;
77 }
78 
79 
80 /*
81  * @implemented
82  */
83 BOOLEAN
84 NTAPI
RtlAcquireResourceExclusive(PRTL_RESOURCE Resource,BOOLEAN Wait)85 RtlAcquireResourceExclusive(
86     PRTL_RESOURCE Resource,
87     BOOLEAN Wait)
88 {
89     NTSTATUS Status;
90     BOOLEAN retVal = FALSE;
91 
92 start:
93     RtlEnterCriticalSection(&Resource->Lock);
94     if (Resource->NumberActive == 0) /* lock is free */
95     {
96         Resource->NumberActive = -1;
97         retVal = TRUE;
98     }
99     else if (Resource->NumberActive < 0) /* exclusive lock in progress */
100     {
101         if (Resource->OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
102         {
103             retVal = TRUE;
104             Resource->NumberActive--;
105             goto done;
106         }
107 wait:
108         if (Wait)
109         {
110             Resource->ExclusiveWaiters++;
111 
112             RtlLeaveCriticalSection(&Resource->Lock);
113             Status = NtWaitForSingleObject(Resource->ExclusiveSemaphore,
114                                            FALSE,
115                                            NULL);
116             if (!NT_SUCCESS(Status))
117                 goto done;
118             goto start; /* restart the acquisition to avoid deadlocks */
119         }
120     }
121     else  /* one or more shared locks are in progress */
122     {
123         if (Wait)
124             goto wait;
125     }
126     if (retVal)
127         Resource->OwningThread = NtCurrentTeb()->ClientId.UniqueThread;
128 done:
129     RtlLeaveCriticalSection(&Resource->Lock);
130     return retVal;
131 }
132 
133 
134 /*
135  * @implemented
136  */
137 BOOLEAN
138 NTAPI
RtlAcquireResourceShared(PRTL_RESOURCE Resource,BOOLEAN Wait)139 RtlAcquireResourceShared(
140     PRTL_RESOURCE Resource,
141     BOOLEAN Wait)
142 {
143     NTSTATUS Status = STATUS_UNSUCCESSFUL;
144     BOOLEAN retVal = FALSE;
145 
146 start:
147     RtlEnterCriticalSection(&Resource->Lock);
148     if (Resource->NumberActive < 0)
149     {
150         if (Resource->OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
151         {
152             Resource->NumberActive--;
153             retVal = TRUE;
154             goto done;
155         }
156 
157         if (Wait)
158         {
159             Resource->SharedWaiters++;
160             RtlLeaveCriticalSection(&Resource->Lock);
161             Status = NtWaitForSingleObject(Resource->SharedSemaphore,
162                                            FALSE,
163                                            NULL);
164             if (!NT_SUCCESS(Status))
165                 goto done;
166             goto start;
167         }
168     }
169     else
170     {
171         if (Status != STATUS_WAIT_0) /* otherwise RtlReleaseResource() has already done it */
172             Resource->NumberActive++;
173         retVal = TRUE;
174     }
175 done:
176     RtlLeaveCriticalSection(&Resource->Lock);
177     return retVal;
178 }
179 
180 
181 /*
182  * @implemented
183  */
184 VOID
185 NTAPI
RtlConvertExclusiveToShared(PRTL_RESOURCE Resource)186 RtlConvertExclusiveToShared(PRTL_RESOURCE Resource)
187 {
188     RtlEnterCriticalSection(&Resource->Lock);
189 
190     if (Resource->NumberActive == -1)
191     {
192         Resource->OwningThread = NULL;
193 
194         if (Resource->SharedWaiters > 0)
195         {
196             ULONG n;
197             /* prevent new writers from joining until
198              * all queued readers have done their thing */
199             n = Resource->SharedWaiters;
200             Resource->NumberActive = Resource->SharedWaiters + 1;
201             Resource->SharedWaiters = 0;
202             NtReleaseSemaphore(Resource->SharedSemaphore,
203                                n,
204                                NULL);
205         }
206         else
207         {
208             Resource->NumberActive = 1;
209         }
210     }
211 
212     RtlLeaveCriticalSection(&Resource->Lock);
213 }
214 
215 
216 /*
217  * @implemented
218  */
219 VOID
220 NTAPI
RtlConvertSharedToExclusive(PRTL_RESOURCE Resource)221 RtlConvertSharedToExclusive(PRTL_RESOURCE Resource)
222 {
223     NTSTATUS Status;
224 
225     RtlEnterCriticalSection(&Resource->Lock);
226 
227     if (Resource->NumberActive == 1)
228     {
229         Resource->OwningThread = NtCurrentTeb()->ClientId.UniqueThread;
230         Resource->NumberActive = -1;
231     }
232     else
233     {
234         Resource->ExclusiveWaiters++;
235 
236         RtlLeaveCriticalSection(&Resource->Lock);
237         Status = NtWaitForSingleObject(Resource->ExclusiveSemaphore,
238                                        FALSE,
239                                        NULL);
240         if (!NT_SUCCESS(Status))
241             return;
242 
243         RtlEnterCriticalSection(&Resource->Lock);
244         Resource->OwningThread = NtCurrentTeb()->ClientId.UniqueThread;
245         Resource->NumberActive = -1;
246     }
247     RtlLeaveCriticalSection(&Resource->Lock);
248 }
249 
250 
251 /*
252  * @implemented
253  */
254 VOID
255 NTAPI
RtlReleaseResource(PRTL_RESOURCE Resource)256 RtlReleaseResource(PRTL_RESOURCE Resource)
257 {
258     RtlEnterCriticalSection(&Resource->Lock);
259 
260     if (Resource->NumberActive > 0) /* have one or more readers */
261     {
262         Resource->NumberActive--;
263         if (Resource->NumberActive == 0)
264         {
265             if (Resource->ExclusiveWaiters > 0)
266             {
267 wake_exclusive:
268                 Resource->ExclusiveWaiters--;
269                 NtReleaseSemaphore(Resource->ExclusiveSemaphore,
270                                    1,
271                                    NULL);
272             }
273         }
274     }
275     else if (Resource->NumberActive < 0) /* have a writer, possibly recursive */
276     {
277         Resource->NumberActive++;
278         if (Resource->NumberActive == 0)
279         {
280             Resource->OwningThread = 0;
281             if (Resource->ExclusiveWaiters > 0)
282             {
283                 goto wake_exclusive;
284             }
285             else
286             {
287                 if (Resource->SharedWaiters > 0)
288                 {
289                     ULONG n;
290                     /* prevent new writers from joining until
291                      * all queued readers have done their thing */
292                     n = Resource->SharedWaiters;
293                     Resource->NumberActive = Resource->SharedWaiters;
294                     Resource->SharedWaiters = 0;
295                     NtReleaseSemaphore(Resource->SharedSemaphore,
296                                        n,
297                                        NULL);
298                 }
299             }
300         }
301     }
302     RtlLeaveCriticalSection(&Resource->Lock);
303 }
304 
305 
306 /*
307  * @implemented
308  */
309 VOID
310 NTAPI
RtlDumpResource(PRTL_RESOURCE Resource)311 RtlDumpResource(PRTL_RESOURCE Resource)
312 {
313     DbgPrint("RtlDumpResource(%p):\n\tactive count = %d\n\twaiting readers = %u\n\twaiting writers = %u\n",
314              Resource,
315              Resource->NumberActive,
316              Resource->SharedWaiters,
317              Resource->ExclusiveWaiters);
318 
319     if (Resource->NumberActive != 0)
320     {
321         DbgPrint("\towner thread = %p\n",
322                  Resource->OwningThread);
323     }
324 }
325 
326 /* EOF */
327