secur32: Eliminate broken clean-up "cheat".
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 #ifdef HAVE_SYS_SYSCALL_H
30 #include <sys/syscall.h>
31 #endif
32 #include <time.h>
33 #include "ntstatus.h"
34 #define WIN32_NO_STATUS
35 #include "windef.h"
36 #include "winternl.h"
37 #include "wine/debug.h"
38 #include "ntdll_misc.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
41 WINE_DECLARE_DEBUG_CHANNEL(relay);
42
43 static inline LONG interlocked_inc( PLONG dest )
44 {
45     return interlocked_xchg_add( dest, 1 ) + 1;
46 }
47
48 static inline LONG interlocked_dec( PLONG dest )
49 {
50     return interlocked_xchg_add( dest, -1 ) - 1;
51 }
52
53 static inline void small_pause(void)
54 {
55 #ifdef __i386__
56     __asm__ __volatile__( "rep;nop" : : : "memory" );
57 #else
58     __asm__ __volatile__( "" : : : "memory" );
59 #endif
60 }
61
62 #ifdef __linux__
63
64 static int wait_op = 128; /*FUTEX_WAIT|FUTEX_PRIVATE_FLAG*/
65 static int wake_op = 129; /*FUTEX_WAKE|FUTEX_PRIVATE_FLAG*/
66
67 static inline int futex_wait( int *addr, int val, struct timespec *timeout )
68 {
69     return syscall( SYS_futex, addr, wait_op, val, timeout, 0, 0 );
70 }
71
72 static inline int futex_wake( int *addr, int val )
73 {
74     return syscall( SYS_futex, addr, wake_op, val, NULL, 0, 0 );
75 }
76
77 static inline int use_futexes(void)
78 {
79     static int supported = -1;
80
81     if (supported == -1)
82     {
83         futex_wait( &supported, 10, NULL );
84         if (errno == ENOSYS)
85         {
86             wait_op = 0; /*FUTEX_WAIT*/
87             wake_op = 1; /*FUTEX_WAKE*/
88             futex_wait( &supported, 10, NULL );
89         }
90         supported = (errno != ENOSYS);
91     }
92     return supported;
93 }
94
95 static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
96 {
97     int val;
98     struct timespec timespec;
99
100     if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
101
102     timespec.tv_sec  = timeout;
103     timespec.tv_nsec = 0;
104     while ((val = interlocked_cmpxchg( (int *)&crit->LockSemaphore, 0, 1 )) != 1)
105     {
106         /* note: this may wait longer than specified in case of signals or */
107         /*       multiple wake-ups, but that shouldn't be a problem */
108         if (futex_wait( (int *)&crit->LockSemaphore, val, &timespec ) == -1 && errno == ETIMEDOUT)
109             return STATUS_TIMEOUT;
110     }
111     return STATUS_WAIT_0;
112 }
113
114 static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
115 {
116     if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
117
118     *(int *)&crit->LockSemaphore = 1;
119     futex_wake( (int *)&crit->LockSemaphore, 1 );
120     return STATUS_SUCCESS;
121 }
122
123 static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
124 {
125     if (!use_futexes()) NtClose( crit->LockSemaphore );
126 }
127
128 #elif defined(__APPLE__)
129
130 #include <mach/mach.h>
131 #include <mach/task.h>
132 #include <mach/semaphore.h>
133
134 static inline semaphore_t get_mach_semaphore( RTL_CRITICAL_SECTION *crit )
135 {
136     semaphore_t ret = *(int *)&crit->LockSemaphore;
137     if (!ret)
138     {
139         semaphore_t sem;
140         if (semaphore_create( mach_task_self(), &sem, SYNC_POLICY_FIFO, 0 )) return 0;
141         if (!(ret = interlocked_cmpxchg( (int *)&crit->LockSemaphore, sem, 0 )))
142             ret = sem;
143         else
144             semaphore_destroy( mach_task_self(), sem );  /* somebody beat us to it */
145     }
146     return ret;
147 }
148
149 static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
150 {
151     mach_timespec_t timespec;
152     semaphore_t sem = get_mach_semaphore( crit );
153
154     timespec.tv_sec = timeout;
155     timespec.tv_nsec = 0;
156     for (;;)
157     {
158         switch( semaphore_timedwait( sem, timespec ))
159         {
160         case KERN_SUCCESS:
161             return STATUS_WAIT_0;
162         case KERN_ABORTED:
163             continue;  /* got a signal, restart */
164         case KERN_OPERATION_TIMED_OUT:
165             return STATUS_TIMEOUT;
166         default:
167             return STATUS_INVALID_HANDLE;
168         }
169     }
170 }
171
172 static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
173 {
174     semaphore_t sem = get_mach_semaphore( crit );
175     semaphore_signal( sem );
176     return STATUS_SUCCESS;
177 }
178
179 static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
180 {
181     semaphore_destroy( mach_task_self(), *(int *)&crit->LockSemaphore );
182 }
183
184 #else  /* __APPLE__ */
185
186 static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
187 {
188     return STATUS_NOT_IMPLEMENTED;
189 }
190
191 static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
192 {
193     return STATUS_NOT_IMPLEMENTED;
194 }
195
196 static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
197 {
198     NtClose( crit->LockSemaphore );
199 }
200
201 #endif
202
203 /***********************************************************************
204  *           get_semaphore
205  */
206 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
207 {
208     HANDLE ret = crit->LockSemaphore;
209     if (!ret)
210     {
211         HANDLE sem;
212         if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
213         if (!(ret = interlocked_cmpxchg_ptr( &crit->LockSemaphore, sem, 0 )))
214             ret = sem;
215         else
216             NtClose(sem);  /* somebody beat us to it */
217     }
218     return ret;
219 }
220
221 /***********************************************************************
222  *           wait_semaphore
223  */
224 static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
225 {
226     NTSTATUS ret;
227
228     /* debug info is cleared by MakeCriticalSectionGlobal */
229     if (!crit->DebugInfo || ((ret = fast_wait( crit, timeout )) == STATUS_NOT_IMPLEMENTED))
230     {
231         HANDLE sem = get_semaphore( crit );
232         LARGE_INTEGER time;
233
234         time.QuadPart = timeout * (LONGLONG)-10000000;
235         ret = NTDLL_wait_for_multiple_objects( 1, &sem, 0, &time, 0 );
236     }
237     return ret;
238 }
239
240 /***********************************************************************
241  *           RtlInitializeCriticalSection   (NTDLL.@)
242  *
243  * Initialises a new critical section.
244  *
245  * PARAMS
246  *  crit [O] Critical section to initialise
247  *
248  * RETURNS
249  *  STATUS_SUCCESS.
250  *
251  * SEE
252  *  RtlInitializeCriticalSectionEx(),
253  *  RtlInitializeCriticalSectionAndSpinCount(), RtlDeleteCriticalSection(),
254  *  RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
255  *  RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
256  */
257 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
258 {
259     return RtlInitializeCriticalSectionEx( crit, 0, 0 );
260 }
261
262 /***********************************************************************
263  *           RtlInitializeCriticalSectionAndSpinCount   (NTDLL.@)
264  *
265  * Initialises a new critical section with a given spin count.
266  *
267  * PARAMS
268  *   crit      [O] Critical section to initialise
269  *   spincount [I] Spin count for crit
270  * 
271  * RETURNS
272  *  STATUS_SUCCESS.
273  *
274  * NOTES
275  *  Available on NT4 SP3 or later.
276  *
277  * SEE
278  *  RtlInitializeCriticalSectionEx(),
279  *  RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
280  *  RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
281  *  RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
282  */
283 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
284 {
285     return RtlInitializeCriticalSectionEx( crit, spincount, 0 );
286 }
287
288 /***********************************************************************
289  *           RtlInitializeCriticalSectionEx   (NTDLL.@)
290  *
291  * Initialises a new critical section with a given spin count and flags.
292  *
293  * PARAMS
294  *   crit      [O] Critical section to initialise.
295  *   spincount [I] Number of times to spin upon contention.
296  *   flags     [I] RTL_CRITICAL_SECTION_FLAG_ flags from winnt.h.
297  *
298  * RETURNS
299  *  STATUS_SUCCESS.
300  *
301  * NOTES
302  *  Available on Vista or later.
303  *
304  * SEE
305  *  RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
306  *  RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
307  *  RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
308  */
309 NTSTATUS WINAPI RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION *crit, ULONG spincount, ULONG flags )
310 {
311     if (flags & (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT))
312         FIXME("(%p,%u,0x%08x) semi-stub\n", crit, spincount, flags);
313
314     /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
315      * memory from a static pool to hold the debug info. Then heap.c could pass
316      * this flag rather than initialising the process heap CS by hand. If this
317      * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
318      * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
319      */
320     if (flags & RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO)
321         crit->DebugInfo = NULL;
322     else
323         crit->DebugInfo = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG));
324
325     if (crit->DebugInfo)
326     {
327         crit->DebugInfo->Type = 0;
328         crit->DebugInfo->CreatorBackTraceIndex = 0;
329         crit->DebugInfo->CriticalSection = crit;
330         crit->DebugInfo->ProcessLocksList.Blink = &(crit->DebugInfo->ProcessLocksList);
331         crit->DebugInfo->ProcessLocksList.Flink = &(crit->DebugInfo->ProcessLocksList);
332         crit->DebugInfo->EntryCount = 0;
333         crit->DebugInfo->ContentionCount = 0;
334         memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) );
335     }
336     crit->LockCount      = -1;
337     crit->RecursionCount = 0;
338     crit->OwningThread   = 0;
339     crit->LockSemaphore  = 0;
340     if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
341     crit->SpinCount = spincount & ~0x80000000;
342     return STATUS_SUCCESS;
343 }
344
345 /***********************************************************************
346  *           RtlSetCriticalSectionSpinCount   (NTDLL.@)
347  *
348  * Sets the spin count of a critical section.
349  *
350  * PARAMS
351  *   crit      [I/O] Critical section
352  *   spincount [I] Spin count for crit
353  *
354  * RETURNS
355  *  The previous spin count.
356  *
357  * NOTES
358  *  If the system is not SMP, spincount is ignored and set to 0.
359  *
360  * SEE
361  *  RtlInitializeCriticalSectionEx(),
362  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
363  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
364  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
365  */
366 ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
367 {
368     ULONG oldspincount = crit->SpinCount;
369     if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
370     crit->SpinCount = spincount;
371     return oldspincount;
372 }
373
374 /***********************************************************************
375  *           RtlDeleteCriticalSection   (NTDLL.@)
376  *
377  * Frees the resources used by a critical section.
378  *
379  * PARAMS
380  *  crit [I/O] Critical section to free
381  *
382  * RETURNS
383  *  STATUS_SUCCESS.
384  *
385  * SEE
386  *  RtlInitializeCriticalSectionEx(),
387  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
388  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
389  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
390  */
391 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
392 {
393     crit->LockCount      = -1;
394     crit->RecursionCount = 0;
395     crit->OwningThread   = 0;
396     if (crit->DebugInfo)
397     {
398         /* only free the ones we made in here */
399         if (!crit->DebugInfo->Spare[0])
400         {
401             RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
402             crit->DebugInfo = NULL;
403         }
404         close_semaphore( crit );
405     }
406     else NtClose( crit->LockSemaphore );
407     crit->LockSemaphore = 0;
408     return STATUS_SUCCESS;
409 }
410
411
412 /***********************************************************************
413  *           RtlpWaitForCriticalSection   (NTDLL.@)
414  *
415  * Waits for a busy critical section to become free.
416  * 
417  * PARAMS
418  *  crit [I/O] Critical section to wait for
419  *
420  * RETURNS
421  *  STATUS_SUCCESS.
422  *
423  * NOTES
424  *  Use RtlEnterCriticalSection() instead of this function as it is often much
425  *  faster.
426  *
427  * SEE
428  *  RtlInitializeCriticalSectionEx(),
429  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
430  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
431  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
432  */
433 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
434 {
435     for (;;)
436     {
437         EXCEPTION_RECORD rec;
438         NTSTATUS status = wait_semaphore( crit, 5 );
439
440         if ( status == STATUS_TIMEOUT )
441         {
442             const char *name = NULL;
443             if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[0];
444             if (!name) name = "?";
445             ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n",
446                  crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
447             status = wait_semaphore( crit, 60 );
448             if ( status == STATUS_TIMEOUT && TRACE_ON(relay) )
449             {
450                 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n",
451                      crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
452                 status = wait_semaphore( crit, 300 );
453             }
454         }
455         if (status == STATUS_WAIT_0) break;
456
457         /* Throw exception only for Wine internal locks */
458         if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[0])) continue;
459
460         rec.ExceptionCode    = STATUS_POSSIBLE_DEADLOCK;
461         rec.ExceptionFlags   = 0;
462         rec.ExceptionRecord  = NULL;
463         rec.ExceptionAddress = RtlRaiseException;  /* sic */
464         rec.NumberParameters = 1;
465         rec.ExceptionInformation[0] = (ULONG_PTR)crit;
466         RtlRaiseException( &rec );
467     }
468     if (crit->DebugInfo) crit->DebugInfo->ContentionCount++;
469     return STATUS_SUCCESS;
470 }
471
472
473 /***********************************************************************
474  *           RtlpUnWaitCriticalSection   (NTDLL.@)
475  *
476  * Notifies other threads waiting on the busy critical section that it has
477  * become free.
478  * 
479  * PARAMS
480  *  crit [I/O] Critical section
481  *
482  * RETURNS
483  *  Success: STATUS_SUCCESS.
484  *  Failure: Any error returned by NtReleaseSemaphore()
485  *
486  * NOTES
487  *  Use RtlLeaveCriticalSection() instead of this function as it is often much
488  *  faster.
489  *
490  * SEE
491  *  RtlInitializeCriticalSectionEx(),
492  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
493  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
494  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
495  */
496 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
497 {
498     NTSTATUS ret;
499
500     /* debug info is cleared by MakeCriticalSectionGlobal */
501     if (!crit->DebugInfo || ((ret = fast_wake( crit )) == STATUS_NOT_IMPLEMENTED))
502     {
503         HANDLE sem = get_semaphore( crit );
504         ret = NtReleaseSemaphore( sem, 1, NULL );
505     }
506     if (ret) RtlRaiseStatus( ret );
507     return ret;
508 }
509
510
511 /***********************************************************************
512  *           RtlEnterCriticalSection   (NTDLL.@)
513  *
514  * Enters a critical section, waiting for it to become available if necessary.
515  *
516  * PARAMS
517  *  crit [I/O] Critical section to enter
518  *
519  * RETURNS
520  *  STATUS_SUCCESS. The critical section is held by the caller.
521  *  
522  * SEE
523  *  RtlInitializeCriticalSectionEx(),
524  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
525  *  RtlDeleteCriticalSection(), RtlSetCriticalSectionSpinCount(),
526  *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
527  */
528 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
529 {
530     if (crit->SpinCount)
531     {
532         ULONG count;
533
534         if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
535         for (count = crit->SpinCount; count > 0; count--)
536         {
537             if (crit->LockCount > 0) break;  /* more than one waiter, don't bother spinning */
538             if (crit->LockCount == -1)       /* try again */
539             {
540                 if (interlocked_cmpxchg( &crit->LockCount, 0, -1 ) == -1) goto done;
541             }
542             small_pause();
543         }
544     }
545
546     if (interlocked_inc( &crit->LockCount ))
547     {
548         if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
549         {
550             crit->RecursionCount++;
551             return STATUS_SUCCESS;
552         }
553
554         /* Now wait for it */
555         RtlpWaitForCriticalSection( crit );
556     }
557 done:
558     crit->OwningThread   = ULongToHandle(GetCurrentThreadId());
559     crit->RecursionCount = 1;
560     return STATUS_SUCCESS;
561 }
562
563
564 /***********************************************************************
565  *           RtlTryEnterCriticalSection   (NTDLL.@)
566  *
567  * Tries to enter a critical section without waiting.
568  *
569  * PARAMS
570  *  crit [I/O] Critical section to enter
571  *
572  * RETURNS
573  *  Success: TRUE. The critical section is held by the caller.
574  *  Failure: FALSE. The critical section is currently held by another thread.
575  *
576  * SEE
577  *  RtlInitializeCriticalSectionEx(),
578  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
579  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
580  *  RtlLeaveCriticalSection(), RtlSetCriticalSectionSpinCount()
581  */
582 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
583 {
584     BOOL ret = FALSE;
585     if (interlocked_cmpxchg( &crit->LockCount, 0, -1 ) == -1)
586     {
587         crit->OwningThread   = ULongToHandle(GetCurrentThreadId());
588         crit->RecursionCount = 1;
589         ret = TRUE;
590     }
591     else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
592     {
593         interlocked_inc( &crit->LockCount );
594         crit->RecursionCount++;
595         ret = TRUE;
596     }
597     return ret;
598 }
599
600
601 /***********************************************************************
602  *           RtlLeaveCriticalSection   (NTDLL.@)
603  *
604  * Leaves a critical section.
605  *
606  * PARAMS
607  *  crit [I/O] Critical section to leave.
608  *
609  * RETURNS
610  *  STATUS_SUCCESS.
611  *
612  * SEE
613  *  RtlInitializeCriticalSectionEx(),
614  *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
615  *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
616  *  RtlSetCriticalSectionSpinCount(), RtlTryEnterCriticalSection()
617  */
618 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
619 {
620     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
621     else
622     {
623         crit->OwningThread = 0;
624         if (interlocked_dec( &crit->LockCount ) >= 0)
625         {
626             /* someone is waiting */
627             RtlpUnWaitCriticalSection( crit );
628         }
629     }
630     return STATUS_SUCCESS;
631 }