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