:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[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 VOID STDCALL
32 RtlInitializeResource(PRTL_RESOURCE Resource)
33 {
34    NTSTATUS Status;
35    
36    Status = RtlInitializeCriticalSection(&Resource->Lock);
37    if (!NT_SUCCESS(Status))
38      {
39         RtlRaiseStatus(Status);
40      }
41    
42    Status = NtCreateSemaphore(&Resource->SharedSemaphore,
43                               SEMAPHORE_ALL_ACCESS,
44                               NULL,
45                               0,
46                               65535);
47    if (!NT_SUCCESS(Status))
48      {
49         RtlRaiseStatus(Status);
50      }
51    Resource->SharedWaiters = 0;
52    
53    Status = NtCreateSemaphore(&Resource->ExclusiveSemaphore,
54                               SEMAPHORE_ALL_ACCESS,
55                               NULL,
56                               0,
57                               65535);
58    if (!NT_SUCCESS(Status))
59      {
60         RtlRaiseStatus(Status);
61      }
62    Resource->ExclusiveWaiters = 0;
63    
64    Resource->NumberActive = 0;
65    Resource->OwningThread = NULL;
66    Resource->TimeoutBoost = 0; /* no info on this one, default value is 0 */
67 }
68
69
70 VOID STDCALL
71 RtlDeleteResource(PRTL_RESOURCE Resource)
72 {
73    RtlDeleteCriticalSection(&Resource->Lock);
74    NtClose(Resource->ExclusiveSemaphore);
75    NtClose(Resource->SharedSemaphore);
76    Resource->OwningThread = NULL;
77    Resource->ExclusiveWaiters = 0;
78    Resource->SharedWaiters = 0;
79    Resource->NumberActive = 0;
80 }
81
82
83 BOOLEAN STDCALL
84 RtlAcquireResourceExclusive(PRTL_RESOURCE Resource,
85                             BOOLEAN Wait)
86 {
87    NTSTATUS Status;
88    BOOLEAN retVal = FALSE;
89
90 start:
91     RtlEnterCriticalSection(&Resource->Lock);
92     if (Resource->NumberActive == 0) /* lock is free */
93       {
94         Resource->NumberActive = -1;
95         retVal = TRUE;
96       }
97     else if (Resource->NumberActive < 0) /* exclusive lock in progress */
98       {
99         if (Resource->OwningThread == NtCurrentTeb()->Cid.UniqueThread)
100           {
101              retVal = TRUE;
102              Resource->NumberActive--;
103              goto done;
104           }
105 wait:
106         if (Wait == TRUE)
107           {
108              Resource->ExclusiveWaiters++;
109
110              RtlLeaveCriticalSection(&Resource->Lock);
111              Status = NtWaitForSingleObject(Resource->ExclusiveSemaphore,
112                                             FALSE,
113                                             NULL);
114              if (!NT_SUCCESS(Status))
115                goto done;
116              goto start; /* restart the acquisition to avoid deadlocks */
117           }
118      }
119    else  /* one or more shared locks are in progress */
120      {
121         if (Wait == TRUE)
122           goto wait;
123      }
124    if (retVal == TRUE)
125      Resource->OwningThread = NtCurrentTeb()->Cid.UniqueThread;
126 done:
127     RtlLeaveCriticalSection(&Resource->Lock);
128     return retVal;
129 }
130
131
132 BOOLEAN STDCALL
133 RtlAcquireResourceShared(PRTL_RESOURCE Resource,
134                          BOOLEAN Wait)
135 {
136    NTSTATUS Status = STATUS_UNSUCCESSFUL;
137    BOOLEAN retVal = FALSE;
138
139 start:
140    RtlEnterCriticalSection(&Resource->Lock);
141    if (Resource->NumberActive < 0)
142      {
143         if (Resource->OwningThread == NtCurrentTeb()->Cid.UniqueThread)
144           {
145              Resource->NumberActive--;
146              retVal = TRUE;
147              goto done;
148           }
149         
150         if (Wait == TRUE)
151           {
152              Resource->SharedWaiters++;
153              RtlLeaveCriticalSection(&Resource->Lock);
154              Status = NtWaitForSingleObject(Resource->SharedSemaphore,
155                                             FALSE,
156                                             NULL);
157              if (!NT_SUCCESS(Status))
158                goto done;
159              goto start;
160           }
161      }
162    else
163      {
164         if (Status != STATUS_WAIT_0) /* otherwise RtlReleaseResource() has already done it */
165           Resource->NumberActive++;
166         retVal = TRUE;
167      }
168 done:
169    RtlLeaveCriticalSection(&Resource->Lock);
170    return retVal;
171 }
172
173
174 VOID STDCALL
175 RtlConvertExclusiveToShared(PRTL_RESOURCE Resource)
176 {
177    RtlEnterCriticalSection(&Resource->Lock);
178    
179    if (Resource->NumberActive == -1)
180      {
181         Resource->OwningThread = NULL;
182    
183         if (Resource->SharedWaiters > 0)
184           {
185              ULONG n;
186              /* prevent new writers from joining until
187               * all queued readers have done their thing */
188              n = Resource->SharedWaiters;
189              Resource->NumberActive = Resource->SharedWaiters + 1;
190              Resource->SharedWaiters = 0;
191              NtReleaseSemaphore(Resource->SharedSemaphore,
192                                 n,
193                                 NULL);
194           }
195         else
196           {
197              Resource->NumberActive = 1;
198           }
199      }
200    
201    RtlLeaveCriticalSection(&Resource->Lock);
202 }
203
204
205 VOID STDCALL
206 RtlConvertSharedToExclusive(PRTL_RESOURCE Resource)
207 {
208    NTSTATUS Status;
209    
210    RtlEnterCriticalSection(&Resource->Lock);
211    
212    if (Resource->NumberActive == 1)
213      {
214         Resource->OwningThread = NtCurrentTeb()->Cid.UniqueThread;
215         Resource->NumberActive = -1;
216      }
217    else
218      {
219         Resource->ExclusiveWaiters++;
220    
221         RtlLeaveCriticalSection(&Resource->Lock);
222         Status = NtWaitForSingleObject(Resource->ExclusiveSemaphore,
223                                        FALSE,
224                                        NULL);
225         if (!NT_SUCCESS(Status))
226            return;
227    
228         RtlEnterCriticalSection(&Resource->Lock);
229         Resource->OwningThread = NtCurrentTeb()->Cid.UniqueThread;
230         Resource->NumberActive = -1;
231      }
232    RtlLeaveCriticalSection(&Resource->Lock);
233 }
234
235
236 VOID STDCALL
237 RtlReleaseResource(PRTL_RESOURCE Resource)
238 {
239    RtlEnterCriticalSection(&Resource->Lock);
240
241    if (Resource->NumberActive > 0) /* have one or more readers */
242      {
243         Resource->NumberActive--;
244         if (Resource->NumberActive == 0)
245           {
246              if (Resource->ExclusiveWaiters > 0)
247                {
248 wake_exclusive:
249                   Resource->ExclusiveWaiters--;
250                   NtReleaseSemaphore(Resource->ExclusiveSemaphore,
251                                      1,
252                                      NULL);
253                }
254           }
255      }
256    else if (Resource->NumberActive < 0) /* have a writer, possibly recursive */
257      {
258         Resource->NumberActive++;
259         if (Resource->NumberActive == 0)
260           {
261              Resource->OwningThread = 0;
262              if (Resource->ExclusiveWaiters > 0)
263                {
264                   goto wake_exclusive;
265                }
266              else
267                {
268                   if (Resource->SharedWaiters > 0)
269                     {
270                        ULONG n;
271                        /* prevent new writers from joining until
272                         * all queued readers have done their thing */
273                        n = Resource->SharedWaiters;
274                        Resource->NumberActive = Resource->SharedWaiters;
275                        Resource->SharedWaiters = 0;
276                        NtReleaseSemaphore(Resource->SharedSemaphore,
277                                           n,
278                                           NULL);
279                     }
280                }
281           }
282      }
283    RtlLeaveCriticalSection(&Resource->Lock);
284 }
285
286
287 VOID STDCALL
288 RtlDumpResource(PRTL_RESOURCE Resource)
289 {
290    DbgPrint("RtlDumpResource(%p):\n\tactive count = %i\n\twaiting readers = %i\n\twaiting writers = %i\n",
291             Resource,
292             Resource->NumberActive,
293             Resource->SharedWaiters,
294             Resource->ExclusiveWaiters);
295    if (Resource->NumberActive != 0)
296      {
297         DbgPrint("\towner thread = %08x\n",
298                  Resource->OwningThread);
299      }
300 }
301
302 /* EOF */