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