- Implemented a few information classes in NtQuerySystemInformation.
[wine] / dlls / ntdll / critsection.c
1 /*
2  * Win32 critical sections
3  *
4  * Copyright 1998 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include "winerror.h"
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winreg.h"
32 #include "winternl.h"
33 #include "wine/debug.h"
34 #include "ntdll_misc.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
37 WINE_DECLARE_DEBUG_CHANNEL(relay);
38
39 inline static LONG interlocked_inc( PLONG dest )
40 {
41     return interlocked_xchg_add( dest, 1 ) + 1;
42 }
43
44 inline static LONG interlocked_dec( PLONG dest )
45 {
46     return interlocked_xchg_add( dest, -1 ) - 1;
47 }
48
49 /***********************************************************************
50  *           get_semaphore
51  */
52 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
53 {
54     HANDLE ret = crit->LockSemaphore;
55     if (!ret)
56     {
57         HANDLE sem;
58         if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
59         if (!(ret = (HANDLE)interlocked_cmpxchg_ptr( (PVOID *)&crit->LockSemaphore,
60                                                      (PVOID)sem, 0 )))
61             ret = sem;
62         else
63             NtClose(sem);  /* somebody beat us to it */
64     }
65     return ret;
66 }
67
68 /***********************************************************************
69  *           RtlInitializeCriticalSection   (NTDLL.@)
70  *
71  * Initialise a new RTL_CRITICAL_SECTION.
72  *
73  * PARAMS
74  *  crit [O] Critical section to initialise
75  *
76  * RETURN
77  *  STATUS_SUCCESS.
78  */
79 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
80 {
81     if (!ntdll_get_process_heap()) crit->DebugInfo = NULL;
82     else
83     {
84         crit->DebugInfo = RtlAllocateHeap(ntdll_get_process_heap(), 0, sizeof(CRITICAL_SECTION_DEBUG));
85         if (crit->DebugInfo)
86         {
87             crit->DebugInfo->Type = 0;
88             crit->DebugInfo->CreatorBackTraceIndex = 0;
89             crit->DebugInfo->CriticalSection = crit;
90             crit->DebugInfo->ProcessLocksList.Blink = &(crit->DebugInfo->ProcessLocksList);
91             crit->DebugInfo->ProcessLocksList.Flink = &(crit->DebugInfo->ProcessLocksList);
92             crit->DebugInfo->EntryCount = 0;
93             crit->DebugInfo->ContentionCount = 0;
94             crit->DebugInfo->Spare[0] = 0;
95             crit->DebugInfo->Spare[1] = 0;
96         }
97     }
98     crit->LockCount      = -1;
99     crit->RecursionCount = 0;
100     crit->OwningThread   = 0;
101     crit->LockSemaphore  = 0;
102     return STATUS_SUCCESS;
103 }
104
105 /***********************************************************************
106  *           RtlInitializeCriticalSectionAndSpinCount   (NTDLL.@)
107  *
108  * Initialise a new RTL_CRITICAL_SECTION with a given spin count.
109  *
110  * PARAMS
111  *   crit      [O] Critical section to initialise
112  *   spincount [I] Spin count for crit
113  * 
114  * RETURNS
115  *  STATUS_SUCCESS.
116  *
117  * NOTES
118  * The InitializeCriticalSectionAndSpinCount() (KERNEL32) function is
119  * available on NT4SP3 or later, and Win98 or later.
120  * I am assuming that this is the correct definition given the MSDN
121  * docs for the kernel32 functions.
122  */
123 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, DWORD spincount )
124 {
125     if(spincount) TRACE("critsection=%p: spincount=%ld not supported\n", crit, spincount);
126     crit->SpinCount = spincount;
127     return RtlInitializeCriticalSection( crit );
128 }
129
130
131 /***********************************************************************
132  *           RtlDeleteCriticalSection   (NTDLL.@)
133  *
134  * Free the resources used by an RTL_CRITICAL_SECTION.
135  *
136  * PARAMS
137  *  crit [I/O] Critical section to free
138  *
139  * RETURNS
140  *  STATUS_SUCCESS.
141  */
142 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
143 {
144     crit->LockCount      = -1;
145     crit->RecursionCount = 0;
146     crit->OwningThread   = 0;
147     if (crit->LockSemaphore) NtClose( crit->LockSemaphore );
148     crit->LockSemaphore  = 0;
149     if (crit->DebugInfo)
150     {
151         /* only free the ones we made in here */
152         if (!crit->DebugInfo->Spare[1])
153         {
154             
155             RtlFreeHeap( ntdll_get_process_heap(), 0, crit->DebugInfo );
156             crit->DebugInfo = NULL;
157         }
158     }
159     return STATUS_SUCCESS;
160 }
161
162
163 /***********************************************************************
164  *           RtlpWaitForCriticalSection   (NTDLL.@)
165  *
166  * Wait for an RTL_CRITICAL_SECTION to become free.
167  * 
168  * PARAMS
169  *  crit [I/O] Critical section to wait for
170  *
171  * RETURNS
172  *  STATUS_SUCCESS.
173  */
174 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
175 {
176     for (;;)
177     {
178         EXCEPTION_RECORD rec;
179         HANDLE sem = get_semaphore( crit );
180         LARGE_INTEGER time;
181         DWORD status;
182
183         time.QuadPart = -5000 * 10000;  /* 5 seconds */
184         status = NtWaitForSingleObject( sem, FALSE, &time );
185         if ( status == WAIT_TIMEOUT )
186         {
187             const char *name = NULL;
188             if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[1];
189             if (!name) name = "?";
190             ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (60 sec)\n",
191                  crit, debugstr_a(name), GetCurrentThreadId(), (DWORD)crit->OwningThread );
192             time.QuadPart = -60000 * 10000;
193             status = NtWaitForSingleObject( sem, FALSE, &time );
194             if ( status == WAIT_TIMEOUT && TRACE_ON(relay) )
195             {
196                 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (5 min)\n",
197                      crit, debugstr_a(name), GetCurrentThreadId(), (DWORD) crit->OwningThread );
198                 time.QuadPart = -300000 * (ULONGLONG)10000;
199                 status = NtWaitForSingleObject( sem, FALSE, &time );
200             }
201         }
202         if (status == STATUS_WAIT_0) return STATUS_SUCCESS;
203
204         /* Throw exception only for Wine internal locks */
205         if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[1])) continue;
206
207         rec.ExceptionCode    = STATUS_POSSIBLE_DEADLOCK;
208         rec.ExceptionFlags   = 0;
209         rec.ExceptionRecord  = NULL;
210         rec.ExceptionAddress = RtlRaiseException;  /* sic */
211         rec.NumberParameters = 1;
212         rec.ExceptionInformation[0] = (DWORD)crit;
213         RtlRaiseException( &rec );
214     }
215 }
216
217
218 /***********************************************************************
219  *           RtlpUnWaitCriticalSection   (NTDLL.@)
220  */
221 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
222 {
223     HANDLE sem = get_semaphore( crit );
224     NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
225     if (res) RtlRaiseStatus( res );
226     return res;
227 }
228
229
230 /***********************************************************************
231  *           RtlEnterCriticalSection   (NTDLL.@)
232  *
233  * Enter an RTL_CRITICAL_SECTION.
234  *
235  * PARAMS
236  *  crit [I/O] Critical section to enter
237  *
238  * RETURNS
239  *  STATUS_SUCCESS. The critical section is held by the caller.
240  *  
241  * NOTES
242  *  The caller will wait until the critical section is availale.
243  */
244 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
245 {
246     if (interlocked_inc( &crit->LockCount ))
247     {
248         if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
249         {
250             crit->RecursionCount++;
251             return STATUS_SUCCESS;
252         }
253
254         /* Now wait for it */
255         RtlpWaitForCriticalSection( crit );
256     }
257     crit->OwningThread   = (HANDLE)GetCurrentThreadId();
258     crit->RecursionCount = 1;
259     return STATUS_SUCCESS;
260 }
261
262
263 /***********************************************************************
264  *           RtlTryEnterCriticalSection   (NTDLL.@)
265  *
266  * Enter an RTL_CRITICAL_SECTION without waiting.
267  *
268  * PARAMS
269  *  crit [I/O] Critical section to enter
270  *
271  * RETURNS
272  *  Success: TRUE. The critical section is held by the caller.
273  *  Failure: FALSE. The critical section is currently held by another thread.
274  */
275 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
276 {
277     BOOL ret = FALSE;
278     if (interlocked_cmpxchg( &crit->LockCount, 0L, -1 ) == -1)
279     {
280         crit->OwningThread   = (HANDLE)GetCurrentThreadId();
281         crit->RecursionCount = 1;
282         ret = TRUE;
283     }
284     else if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
285     {
286         interlocked_inc( &crit->LockCount );
287         crit->RecursionCount++;
288         ret = TRUE;
289     }
290     return ret;
291 }
292
293
294 /***********************************************************************
295  *           RtlLeaveCriticalSection   (NTDLL.@)
296  *
297  * Leave an RTL_CRITICAL_SECTION.
298  *
299  * PARAMS
300  *  crit [I/O] Critical section to enter
301  *
302  * RETURNS
303  *  STATUS_SUCCESS.
304  */
305 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
306 {
307     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
308     else
309     {
310         crit->OwningThread = 0;
311         if (interlocked_dec( &crit->LockCount ) >= 0)
312         {
313             /* someone is waiting */
314             RtlpUnWaitCriticalSection( crit );
315         }
316     }
317     return STATUS_SUCCESS;
318 }