ntdll: Fix typo in HAVE_STRUCT_MTGET_MT_GSTAT ifdef.
[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 <errno.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <time.h>
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32 #include "windef.h"
33 #include "winternl.h"
34 #include "wine/debug.h"
35 #include "ntdll_misc.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
38 WINE_DECLARE_DEBUG_CHANNEL(relay);
39
40 inline static LONG interlocked_inc( PLONG dest )
41 {
42     return interlocked_xchg_add( (int *)dest, 1 ) + 1;
43 }
44
45 inline static LONG interlocked_dec( PLONG dest )
46 {
47     return interlocked_xchg_add( (int *)dest, -1 ) - 1;
48 }
49
50 inline static void small_pause(void)
51 {
52 #ifdef __i386__
53     __asm__ __volatile__( "rep;nop" : : : "memory" );
54 #else
55     __asm__ __volatile__( "" : : : "memory" );
56 #endif
57 }
58
59 #if defined(linux) && defined(__i386__)
60
61 static inline int futex_wait( int *addr, int val, struct timespec *timeout )
62 {
63     int res;
64     __asm__ __volatile__( "xchgl %2,%%ebx\n\t"
65                           "int $0x80\n\t"
66                           "xchgl %2,%%ebx"
67                           : "=a" (res)
68                           : "0" (240) /* SYS_futex */, "D" (addr),
69                             "c" (0) /* FUTEX_WAIT */, "d" (val), "S" (timeout) );
70     return res;
71 }
72
73 static inline int futex_wake( int *addr, int val )
74 {
75     int res;
76     __asm__ __volatile__( "xchgl %2,%%ebx\n\t"
77                           "int $0x80\n\t"
78                           "xchgl %2,%%ebx"
79                           : "=a" (res)
80                           : "0" (240) /* SYS_futex */, "D" (addr),
81                             "c" (1)  /* FUTEX_WAKE */, "d" (val) );
82     return res;
83 }
84
85 static inline int use_futexes(void)
86 {
87     static int supported = -1;
88
89     if (supported == -1) supported = (futex_wait( &supported, 10, NULL ) != -ENOSYS);
90     return supported;
91 }
92
93 #else  /* linux */
94
95 static inline int futex_wait( int *addr, int val, struct timespec *timeout ) { return -ENOSYS; }
96 static inline int futex_wake( int *addr, int val ) { return -ENOSYS; }
97 static inline int use_futexes(void) { return 0; }
98
99 #endif
100
101 /***********************************************************************
102  *           get_semaphore
103  */
104 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
105 {
106     HANDLE ret = crit->LockSemaphore;
107     if (!ret)
108     {
109         HANDLE sem;
110         if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
111         if (!(ret = (HANDLE)interlocked_cmpxchg_ptr( (PVOID *)&crit->LockSemaphore,
112                                                      (PVOID)sem, 0 )))
113             ret = sem;
114         else
115             NtClose(sem);  /* somebody beat us to it */
116     }
117     return ret;
118 }
119
120 /***********************************************************************
121  *           wait_semaphore
122  */
123 static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
124 {
125     if (use_futexes() && crit->DebugInfo)  /* debug info is cleared by MakeCriticalSectionGlobal */
126     {
127         int val;
128         struct timespec timespec;
129
130         timespec.tv_sec  = timeout;
131         timespec.tv_nsec = 0;
132         while ((val = interlocked_cmpxchg( (int *)&crit->LockSemaphore, 0, 1 )) != 1)
133         {
134             /* note: this may wait longer than specified in case of signals or */
135             /*       multiple wake-ups, but that shouldn't be a problem */
136             if (futex_wait( (int *)&crit->LockSemaphore, val, &timespec ) == -ETIMEDOUT)
137                 return STATUS_TIMEOUT;
138         }
139         return STATUS_WAIT_0;
140     }
141     else
142     {
143         HANDLE sem = get_semaphore( crit );
144         LARGE_INTEGER time;
145
146         time.QuadPart = timeout * (LONGLONG)-10000000;
147         return NtWaitForSingleObject( sem, FALSE, &time );
148     }
149 }
150
151 /***********************************************************************
152  *           RtlInitializeCriticalSection   (NTDLL.@)
153  *
154  * Initialises a new critical section.
155  *
156  * PARAMS
157  *  crit [O] Critical section to initialise
158  *
159  * RETURNS
160  *  STATUS_SUCCESS.
161  *
162  * SEE
163  *  RtlInitializeCriticalSectionAndSpinCount(), RtlDeleteCriticalSection(),
164  *  RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
165  *  RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
166  */
167 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
168 {
169     return RtlInitializeCriticalSectionAndSpinCount( crit, 0 );
170 }
171
172 /***********************************************************************
173  *           RtlInitializeCriticalSectionAndSpinCount   (NTDLL.@)
174  *
175  * Initialises a new critical section with a given spin count.
176  *
177  * PARAMS
178  *   crit      [O] Critical section to initialise
179  *   spincount [I] Spin count for crit
180  * 
181  * RETURNS
182  *  STATUS_SUCCESS.
183  *
184  * NOTES
185  *  Available on NT4 SP3 or later.
186  *
187  * SEE
188  *  RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
189  *  RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
190  *  RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
191  */
192 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
193 {
194     crit->DebugInfo = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG));
195     if (crit->DebugInfo)
196     {
197         crit->DebugInfo->Type = 0;
198         crit->DebugInfo->CreatorBackTraceIndex = 0;
199         crit->DebugInfo->CriticalSection = crit;
200         crit->DebugInfo->ProcessLocksList.Blink = &(crit->DebugInfo->ProcessLocksList);
201         crit->DebugInfo->ProcessLocksList.Flink = &(crit->DebugInfo->ProcessLocksList);
202         crit->DebugInfo->EntryCount = 0;
203         crit->DebugInfo->ContentionCount = 0;
204         memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) );
205     }
206     crit->LockCount      = -1;
207     crit->RecursionCount = 0;
208     crit->OwningThread   = 0;
209     crit->LockSemaphore  = 0;
210     if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
211     crit->SpinCount = spincount & ~0x80000000;
212     return STATUS_SUCCESS;
213 }
214
215 /***********************************************************************
216  *           RtlSetCriticalSectionSpinCount   (NTDLL.@)
217  *
218  * Sets the spin count of a critical section.
219  *
220  * PARAMS
221  *   crit      [I/O] Critical section
222  *   spincount [I] Spin count for crit
223  *
224  * RETURNS
225  *  The previous spin count.
226  *
227  * NOTES
228  *  If the system is not SMP, spincount is ignored and set to 0.
229  *
230  * SEE
231  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
232  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
233  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
234  */
235 ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
236 {
237     ULONG oldspincount = crit->SpinCount;
238     if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
239     crit->SpinCount = spincount;
240     return oldspincount;
241 }
242
243 /***********************************************************************
244  *           RtlDeleteCriticalSection   (NTDLL.@)
245  *
246  * Frees the resources used by a critical section.
247  *
248  * PARAMS
249  *  crit [I/O] Critical section to free
250  *
251  * RETURNS
252  *  STATUS_SUCCESS.
253  *
254  * SEE
255  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
256  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
257  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
258  */
259 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
260 {
261     crit->LockCount      = -1;
262     crit->RecursionCount = 0;
263     crit->OwningThread   = 0;
264     if (crit->LockSemaphore) NtClose( crit->LockSemaphore );
265     crit->LockSemaphore  = 0;
266     if (crit->DebugInfo)
267     {
268         /* only free the ones we made in here */
269         if (!crit->DebugInfo->Spare[0])
270         {
271             RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
272             crit->DebugInfo = NULL;
273         }
274     }
275     return STATUS_SUCCESS;
276 }
277
278
279 /***********************************************************************
280  *           RtlpWaitForCriticalSection   (NTDLL.@)
281  *
282  * Waits for a busy critical section to become free.
283  * 
284  * PARAMS
285  *  crit [I/O] Critical section to wait for
286  *
287  * RETURNS
288  *  STATUS_SUCCESS.
289  *
290  * NOTES
291  *  Use RtlEnterCriticalSection() instead of this function as it is often much
292  *  faster.
293  *
294  * SEE
295  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
296  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
297  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
298  */
299 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
300 {
301     for (;;)
302     {
303         EXCEPTION_RECORD rec;
304         NTSTATUS status = wait_semaphore( crit, 5 );
305
306         if ( status == STATUS_TIMEOUT )
307         {
308             const char *name = NULL;
309             if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[0];
310             if (!name) name = "?";
311             ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (60 sec)\n",
312                  crit, debugstr_a(name), GetCurrentThreadId(), (DWORD)crit->OwningThread );
313             status = wait_semaphore( crit, 60 );
314             if ( status == STATUS_TIMEOUT && TRACE_ON(relay) )
315             {
316                 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (5 min)\n",
317                      crit, debugstr_a(name), GetCurrentThreadId(), (DWORD) crit->OwningThread );
318                 status = wait_semaphore( crit, 300 );
319             }
320         }
321         if (status == STATUS_WAIT_0) break;
322
323         /* Throw exception only for Wine internal locks */
324         if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[0])) continue;
325
326         rec.ExceptionCode    = STATUS_POSSIBLE_DEADLOCK;
327         rec.ExceptionFlags   = 0;
328         rec.ExceptionRecord  = NULL;
329         rec.ExceptionAddress = RtlRaiseException;  /* sic */
330         rec.NumberParameters = 1;
331         rec.ExceptionInformation[0] = (ULONG_PTR)crit;
332         RtlRaiseException( &rec );
333     }
334     if (crit->DebugInfo) crit->DebugInfo->ContentionCount++;
335     return STATUS_SUCCESS;
336 }
337
338
339 /***********************************************************************
340  *           RtlpUnWaitCriticalSection   (NTDLL.@)
341  *
342  * Notifies other threads waiting on the busy critical section that it has
343  * become free.
344  * 
345  * PARAMS
346  *  crit [I/O] Critical section
347  *
348  * RETURNS
349  *  Success: STATUS_SUCCESS.
350  *  Failure: Any error returned by NtReleaseSemaphore()
351  *
352  * NOTES
353  *  Use RtlLeaveCriticalSection() instead of this function as it is often much
354  *  faster.
355  *
356  * SEE
357  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
358  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
359  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
360  */
361 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
362 {
363     if (use_futexes() && crit->DebugInfo)  /* debug info is cleared by MakeCriticalSectionGlobal */
364     {
365         *(int *)&crit->LockSemaphore = 1;
366         futex_wake( (int *)&crit->LockSemaphore, 1 );
367         return STATUS_SUCCESS;
368     }
369     else
370     {
371         HANDLE sem = get_semaphore( crit );
372         NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
373         if (res) RtlRaiseStatus( res );
374         return res;
375     }
376 }
377
378
379 /***********************************************************************
380  *           RtlEnterCriticalSection   (NTDLL.@)
381  *
382  * Enters a critical section, waiting for it to become available if necessary.
383  *
384  * PARAMS
385  *  crit [I/O] Critical section to enter
386  *
387  * RETURNS
388  *  STATUS_SUCCESS. The critical section is held by the caller.
389  *  
390  * SEE
391  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
392  *  RtlDeleteCriticalSection(), RtlSetCriticalSectionSpinCount(),
393  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
394  */
395 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
396 {
397     if (crit->SpinCount)
398     {
399         ULONG count;
400
401         if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
402         for (count = crit->SpinCount; count > 0; count--)
403         {
404             if (crit->LockCount > 0) break;  /* more than one waiter, don't bother spinning */
405             if (crit->LockCount == -1)       /* try again */
406             {
407                 if (interlocked_cmpxchg( (int *)&crit->LockCount, 0, -1 ) == -1) goto done;
408             }
409             small_pause();
410         }
411     }
412
413     if (interlocked_inc( &crit->LockCount ))
414     {
415         if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
416         {
417             crit->RecursionCount++;
418             return STATUS_SUCCESS;
419         }
420
421         /* Now wait for it */
422         RtlpWaitForCriticalSection( crit );
423     }
424 done:
425     crit->OwningThread   = (HANDLE)GetCurrentThreadId();
426     crit->RecursionCount = 1;
427     return STATUS_SUCCESS;
428 }
429
430
431 /***********************************************************************
432  *           RtlTryEnterCriticalSection   (NTDLL.@)
433  *
434  * Tries to enter a critical section without waiting.
435  *
436  * PARAMS
437  *  crit [I/O] Critical section to enter
438  *
439  * RETURNS
440  *  Success: TRUE. The critical section is held by the caller.
441  *  Failure: FALSE. The critical section is currently held by another thread.
442  *
443  * SEE
444  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
445  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
446  *  RtlLeaveCriticalSection(), RtlSetCriticalSectionSpinCount()
447  */
448 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
449 {
450     BOOL ret = FALSE;
451     if (interlocked_cmpxchg( (int *)&crit->LockCount, 0, -1 ) == -1)
452     {
453         crit->OwningThread   = (HANDLE)GetCurrentThreadId();
454         crit->RecursionCount = 1;
455         ret = TRUE;
456     }
457     else if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
458     {
459         interlocked_inc( &crit->LockCount );
460         crit->RecursionCount++;
461         ret = TRUE;
462     }
463     return ret;
464 }
465
466
467 /***********************************************************************
468  *           RtlLeaveCriticalSection   (NTDLL.@)
469  *
470  * Leaves a critical section.
471  *
472  * PARAMS
473  *  crit [I/O] Critical section to leave.
474  *
475  * RETURNS
476  *  STATUS_SUCCESS.
477  *
478  * SEE
479  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
480  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
481  *  RtlSetCriticalSectionSpinCount(), RtlTryEnterCriticalSection()
482  */
483 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
484 {
485     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
486     else
487     {
488         crit->OwningThread = 0;
489         if (interlocked_dec( &crit->LockCount ) >= 0)
490         {
491             /* someone is waiting */
492             RtlpUnWaitCriticalSection( crit );
493         }
494     }
495     return STATUS_SUCCESS;
496 }