Added _vsnwprintf (needed by some native XP dlls).
[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 (!GetProcessHeap()) crit->DebugInfo = NULL;
82     else
83     {
84         crit->DebugInfo = RtlAllocateHeap(GetProcessHeap(), 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             RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
155             crit->DebugInfo = NULL;
156         }
157     }
158     return STATUS_SUCCESS;
159 }
160
161
162 /***********************************************************************
163  *           RtlpWaitForCriticalSection   (NTDLL.@)
164  *
165  * Wait for an RTL_CRITICAL_SECTION to become free.
166  * 
167  * PARAMS
168  *  crit [I/O] Critical section to wait for
169  *
170  * RETURNS
171  *  STATUS_SUCCESS.
172  */
173 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
174 {
175     for (;;)
176     {
177         EXCEPTION_RECORD rec;
178         HANDLE sem = get_semaphore( crit );
179         LARGE_INTEGER time;
180         DWORD status;
181
182         time.QuadPart = -5000 * 10000;  /* 5 seconds */
183         status = NtWaitForSingleObject( sem, FALSE, &time );
184         if ( status == WAIT_TIMEOUT )
185         {
186             const char *name = NULL;
187             if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[1];
188             if (!name) name = "?";
189             ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (60 sec)\n",
190                  crit, debugstr_a(name), GetCurrentThreadId(), (DWORD)crit->OwningThread );
191             time.QuadPart = -60000 * 10000;
192             status = NtWaitForSingleObject( sem, FALSE, &time );
193             if ( status == WAIT_TIMEOUT && TRACE_ON(relay) )
194             {
195                 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (5 min)\n",
196                      crit, debugstr_a(name), GetCurrentThreadId(), (DWORD) crit->OwningThread );
197                 time.QuadPart = -300000 * (ULONGLONG)10000;
198                 status = NtWaitForSingleObject( sem, FALSE, &time );
199             }
200         }
201         if (status == STATUS_WAIT_0) return STATUS_SUCCESS;
202
203         /* Throw exception only for Wine internal locks */
204         if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[1])) continue;
205
206         rec.ExceptionCode    = STATUS_POSSIBLE_DEADLOCK;
207         rec.ExceptionFlags   = 0;
208         rec.ExceptionRecord  = NULL;
209         rec.ExceptionAddress = RtlRaiseException;  /* sic */
210         rec.NumberParameters = 1;
211         rec.ExceptionInformation[0] = (DWORD)crit;
212         RtlRaiseException( &rec );
213     }
214 }
215
216
217 /***********************************************************************
218  *           RtlpUnWaitCriticalSection   (NTDLL.@)
219  */
220 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
221 {
222     HANDLE sem = get_semaphore( crit );
223     NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
224     if (res) RtlRaiseStatus( res );
225     return res;
226 }
227
228
229 /***********************************************************************
230  *           RtlEnterCriticalSection   (NTDLL.@)
231  *
232  * Enter an RTL_CRITICAL_SECTION.
233  *
234  * PARAMS
235  *  crit [I/O] Critical section to enter
236  *
237  * RETURNS
238  *  STATUS_SUCCESS. The critical section is held by the caller.
239  *  
240  * NOTES
241  *  The caller will wait until the critical section is availale.
242  */
243 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
244 {
245     if (interlocked_inc( &crit->LockCount ))
246     {
247         if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
248         {
249             crit->RecursionCount++;
250             return STATUS_SUCCESS;
251         }
252
253         /* Now wait for it */
254         RtlpWaitForCriticalSection( crit );
255     }
256     crit->OwningThread   = (HANDLE)GetCurrentThreadId();
257     crit->RecursionCount = 1;
258     return STATUS_SUCCESS;
259 }
260
261
262 /***********************************************************************
263  *           RtlTryEnterCriticalSection   (NTDLL.@)
264  *
265  * Enter an RTL_CRITICAL_SECTION without waiting.
266  *
267  * PARAMS
268  *  crit [I/O] Critical section to enter
269  *
270  * RETURNS
271  *  Success: TRUE. The critical section is held by the caller.
272  *  Failure: FALSE. The critical section is currently held by another thread.
273  */
274 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
275 {
276     BOOL ret = FALSE;
277     if (interlocked_cmpxchg( &crit->LockCount, 0L, -1 ) == -1)
278     {
279         crit->OwningThread   = (HANDLE)GetCurrentThreadId();
280         crit->RecursionCount = 1;
281         ret = TRUE;
282     }
283     else if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
284     {
285         interlocked_inc( &crit->LockCount );
286         crit->RecursionCount++;
287         ret = TRUE;
288     }
289     return ret;
290 }
291
292
293 /***********************************************************************
294  *           RtlLeaveCriticalSection   (NTDLL.@)
295  *
296  * Leave an RTL_CRITICAL_SECTION.
297  *
298  * PARAMS
299  *  crit [I/O] Critical section to enter
300  *
301  * RETURNS
302  *  STATUS_SUCCESS.
303  */
304 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
305 {
306     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
307     else
308     {
309         crit->OwningThread = 0;
310         if (interlocked_dec( &crit->LockCount ) >= 0)
311         {
312             /* someone is waiting */
313             RtlpUnWaitCriticalSection( crit );
314         }
315     }
316     return STATUS_SUCCESS;
317 }