save old text color during a call of DrawCaptionTempW
[wine/kumbayo.git] / dlls / ntdll / critsection.c
blob064ce8209ad980081038a38fb7fbd3ac3ac146b0
1 /*
2 * Win32 critical sections
4 * Copyright 1998 Alexandre Julliard
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.
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.
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
21 #include "config.h"
22 #include "wine/port.h"
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"
37 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
38 WINE_DECLARE_DEBUG_CHANNEL(relay);
39 WINE_DECLARE_DEBUG_CHANNEL(list_static_cs);
41 /* protects RtlCriticalSectionList, never deleted */
42 static RTL_CRITICAL_SECTION RtlCriticalSectionLock;
44 /* list of all RTL_CRITICAL_SECTION_DEBUG */
45 static LIST_ENTRY RtlCriticalSectionList = { &RtlCriticalSectionList, &RtlCriticalSectionList };
47 void criticalsection_init( void )
49 assert(IsListEmpty(&RtlCriticalSectionList));
50 RtlInitializeCriticalSection( &RtlCriticalSectionLock );
53 static inline LONG interlocked_inc( PLONG dest )
55 return interlocked_xchg_add( (int *)dest, 1 ) + 1;
58 static inline LONG interlocked_dec( PLONG dest )
60 return interlocked_xchg_add( (int *)dest, -1 ) - 1;
63 static inline void small_pause(void)
65 #ifdef __i386__
66 __asm__ __volatile__( "rep;nop" : : : "memory" );
67 #else
68 __asm__ __volatile__( "" : : : "memory" );
69 #endif
72 #if defined(linux) && defined(__i386__)
74 static inline int futex_wait( int *addr, int val, struct timespec *timeout )
76 int res;
77 __asm__ __volatile__( "xchgl %2,%%ebx\n\t"
78 "int $0x80\n\t"
79 "xchgl %2,%%ebx"
80 : "=a" (res)
81 : "0" (240) /* SYS_futex */, "D" (addr),
82 "c" (0) /* FUTEX_WAIT */, "d" (val), "S" (timeout) );
83 return res;
86 static inline int futex_wake( int *addr, int val )
88 int res;
89 __asm__ __volatile__( "xchgl %2,%%ebx\n\t"
90 "int $0x80\n\t"
91 "xchgl %2,%%ebx"
92 : "=a" (res)
93 : "0" (240) /* SYS_futex */, "D" (addr),
94 "c" (1) /* FUTEX_WAKE */, "d" (val) );
95 return res;
98 static inline int use_futexes(void)
100 static int supported = -1;
102 if (supported == -1) supported = (futex_wait( &supported, 10, NULL ) != -ENOSYS);
103 return supported;
106 static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
108 int val;
109 struct timespec timespec;
111 if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
113 timespec.tv_sec = timeout;
114 timespec.tv_nsec = 0;
115 while ((val = interlocked_cmpxchg( (int *)&crit->LockSemaphore, 0, 1 )) != 1)
117 /* note: this may wait longer than specified in case of signals or */
118 /* multiple wake-ups, but that shouldn't be a problem */
119 if (futex_wait( (int *)&crit->LockSemaphore, val, &timespec ) == -ETIMEDOUT)
120 return STATUS_TIMEOUT;
122 return STATUS_WAIT_0;
125 static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
127 if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
129 *(int *)&crit->LockSemaphore = 1;
130 futex_wake( (int *)&crit->LockSemaphore, 1 );
131 return STATUS_SUCCESS;
134 static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
136 if (!use_futexes()) NtClose( crit->LockSemaphore );
139 #elif defined(__APPLE__)
141 #include <mach/mach.h>
142 #include <mach/task.h>
143 #include <mach/semaphore.h>
145 static inline semaphore_t get_mach_semaphore( RTL_CRITICAL_SECTION *crit )
147 semaphore_t ret = *(int *)&crit->LockSemaphore;
148 if (!ret)
150 semaphore_t sem;
151 if (semaphore_create( mach_task_self(), &sem, SYNC_POLICY_FIFO, 0 )) return 0;
152 if (!(ret = interlocked_cmpxchg( (int *)&crit->LockSemaphore, sem, 0 )))
153 ret = sem;
154 else
155 semaphore_destroy( mach_task_self(), sem ); /* somebody beat us to it */
157 return ret;
160 static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
162 mach_timespec_t timespec;
163 semaphore_t sem = get_mach_semaphore( crit );
165 timespec.tv_sec = timeout;
166 timespec.tv_nsec = 0;
167 for (;;)
169 switch( semaphore_timedwait( sem, timespec ))
171 case KERN_SUCCESS:
172 return STATUS_WAIT_0;
173 case KERN_ABORTED:
174 continue; /* got a signal, restart */
175 case KERN_OPERATION_TIMED_OUT:
176 return STATUS_TIMEOUT;
177 default:
178 return STATUS_INVALID_HANDLE;
183 static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
185 semaphore_t sem = get_mach_semaphore( crit );
186 semaphore_signal( sem );
187 return STATUS_SUCCESS;
190 static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
192 semaphore_destroy( mach_task_self(), *(int *)&crit->LockSemaphore );
195 #else /* __APPLE__ */
197 static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
199 return STATUS_NOT_IMPLEMENTED;
202 static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
204 return STATUS_NOT_IMPLEMENTED;
207 static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
209 NtClose( crit->LockSemaphore );
212 #endif
214 /***********************************************************************
215 * get_semaphore
217 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
219 HANDLE ret = crit->LockSemaphore;
220 if (!ret)
222 HANDLE sem;
223 if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
224 if (!(ret = (HANDLE)interlocked_cmpxchg_ptr( (PVOID *)&crit->LockSemaphore,
225 sem, 0 )))
226 ret = sem;
227 else
228 NtClose(sem); /* somebody beat us to it */
230 return ret;
233 /***********************************************************************
234 * wait_semaphore
236 static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
238 NTSTATUS ret;
240 /* debug info is cleared by MakeCriticalSectionGlobal */
241 if (!crit->DebugInfo || ((ret = fast_wait( crit, timeout )) == STATUS_NOT_IMPLEMENTED))
243 HANDLE sem = get_semaphore( crit );
244 LARGE_INTEGER time;
246 time.QuadPart = timeout * (LONGLONG)-10000000;
247 ret = NTDLL_wait_for_multiple_objects( 1, &sem, 0, &time, 0 );
249 return ret;
252 /***********************************************************************
253 * RtlInitializeCriticalSection (NTDLL.@)
255 * Initialises a new critical section.
257 * PARAMS
258 * crit [O] Critical section to initialise
260 * RETURNS
261 * STATUS_SUCCESS.
263 * SEE
264 * RtlInitializeCriticalSectionAndSpinCount(), RtlDeleteCriticalSection(),
265 * RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
266 * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
268 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
270 return RtlInitializeCriticalSectionAndSpinCount( crit, 0 );
273 /***********************************************************************
274 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
276 * Initialises a new critical section with a given spin count.
278 * PARAMS
279 * crit [O] Critical section to initialise
280 * spincount [I] Spin count for crit
282 * RETURNS
283 * STATUS_SUCCESS.
285 * NOTES
286 * Available on NT4 SP3 or later.
288 * SEE
289 * RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
290 * RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
291 * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
293 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
295 crit->DebugInfo = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG));
296 if (crit->DebugInfo)
298 /* do not enter critical section we are trying to initialize */
299 BOOL needlock = !IsListEmpty( &RtlCriticalSectionList );
300 crit->DebugInfo->Type = 0;
301 crit->DebugInfo->CreatorBackTraceIndex = 0;
302 crit->DebugInfo->CriticalSection = crit;
303 crit->DebugInfo->EntryCount = 0;
304 crit->DebugInfo->ContentionCount = 0;
305 if (needlock)
306 RtlEnterCriticalSection( &RtlCriticalSectionLock );
307 InsertTailList( &RtlCriticalSectionList, &crit->DebugInfo->ProcessLocksList );
308 if (needlock)
309 RtlLeaveCriticalSection( &RtlCriticalSectionLock );
310 memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) );
312 crit->LockCount = -1;
313 crit->RecursionCount = 0;
314 crit->OwningThread = 0;
315 crit->LockSemaphore = 0;
316 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
317 crit->SpinCount = spincount & ~0x80000000;
318 return STATUS_SUCCESS;
321 /***********************************************************************
322 * RtlSetCriticalSectionSpinCount (NTDLL.@)
324 * Sets the spin count of a critical section.
326 * PARAMS
327 * crit [I/O] Critical section
328 * spincount [I] Spin count for crit
330 * RETURNS
331 * The previous spin count.
333 * NOTES
334 * If the system is not SMP, spincount is ignored and set to 0.
336 * SEE
337 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
338 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
339 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
341 ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
343 ULONG oldspincount = crit->SpinCount;
344 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
345 crit->SpinCount = spincount;
346 return oldspincount;
349 /***********************************************************************
350 * RtlDeleteCriticalSection (NTDLL.@)
352 * Frees the resources used by a critical section.
354 * PARAMS
355 * crit [I/O] Critical section to free
357 * RETURNS
358 * STATUS_SUCCESS.
360 * SEE
361 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
362 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
363 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
365 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
367 if (crit->DebugInfo)
369 RtlEnterCriticalSection( &RtlCriticalSectionLock );
370 RemoveEntryList(&crit->DebugInfo->ProcessLocksList);
371 RtlLeaveCriticalSection( &RtlCriticalSectionLock );
372 /* only free the ones we made in here */
373 if (!crit->DebugInfo->Spare[0])
374 RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
375 close_semaphore( crit );
377 else NtClose( crit->LockSemaphore );
378 memset( crit, 0, sizeof(RTL_CRITICAL_SECTION) );
379 return STATUS_SUCCESS;
383 /***********************************************************************
384 * RtlpWaitForCriticalSection (NTDLL.@)
386 * Waits for a busy critical section to become free.
388 * PARAMS
389 * crit [I/O] Critical section to wait for
391 * RETURNS
392 * STATUS_SUCCESS.
394 * NOTES
395 * Use RtlEnterCriticalSection() instead of this function as it is often much
396 * faster.
398 * SEE
399 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
400 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
401 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
403 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
405 for (;;)
407 EXCEPTION_RECORD rec;
408 NTSTATUS status = wait_semaphore( crit, 5 );
410 if ( status == STATUS_TIMEOUT )
412 const char *name = NULL;
413 if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[0];
414 if (!name) name = "?";
415 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n",
416 crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
417 status = wait_semaphore( crit, 60 );
418 if ( status == STATUS_TIMEOUT && TRACE_ON(relay) )
420 ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n",
421 crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
422 status = wait_semaphore( crit, 300 );
425 if (status == STATUS_WAIT_0) break;
427 /* Throw exception only for Wine internal locks */
428 if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[0])) continue;
430 rec.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
431 rec.ExceptionFlags = 0;
432 rec.ExceptionRecord = NULL;
433 rec.ExceptionAddress = RtlRaiseException; /* sic */
434 rec.NumberParameters = 1;
435 rec.ExceptionInformation[0] = (ULONG_PTR)crit;
436 RtlRaiseException( &rec );
438 if (crit->DebugInfo) crit->DebugInfo->ContentionCount++;
439 return STATUS_SUCCESS;
443 /***********************************************************************
444 * RtlpUnWaitCriticalSection (NTDLL.@)
446 * Notifies other threads waiting on the busy critical section that it has
447 * become free.
449 * PARAMS
450 * crit [I/O] Critical section
452 * RETURNS
453 * Success: STATUS_SUCCESS.
454 * Failure: Any error returned by NtReleaseSemaphore()
456 * NOTES
457 * Use RtlLeaveCriticalSection() instead of this function as it is often much
458 * faster.
460 * SEE
461 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
462 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
463 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
465 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
467 NTSTATUS ret;
469 /* debug info is cleared by MakeCriticalSectionGlobal */
470 if (!crit->DebugInfo || ((ret = fast_wake( crit )) == STATUS_NOT_IMPLEMENTED))
472 HANDLE sem = get_semaphore( crit );
473 ret = NtReleaseSemaphore( sem, 1, NULL );
475 if (ret) RtlRaiseStatus( ret );
476 return ret;
480 /***********************************************************************
481 * RtlEnterCriticalSection (NTDLL.@)
483 * Enters a critical section, waiting for it to become available if necessary.
485 * PARAMS
486 * crit [I/O] Critical section to enter
488 * RETURNS
489 * STATUS_SUCCESS. The critical section is held by the caller.
491 * SEE
492 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
493 * RtlDeleteCriticalSection(), RtlSetCriticalSectionSpinCount(),
494 * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
496 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
498 if(TRACE_ON(list_static_cs) && !IsListEmpty(&RtlCriticalSectionList))
500 BOOL in_list = FALSE;
501 LIST_ENTRY* le = RtlCriticalSectionList.Flink;
502 while(le != &RtlCriticalSectionList)
504 if(le == &crit->DebugInfo->ProcessLocksList)
506 in_list = TRUE;
507 break;
509 le = le->Flink;
511 if(!in_list)
513 TRACE_(list_static_cs)("inserting uninitialized cs %p into list\n", &crit->DebugInfo->ProcessLocksList);
514 InsertTailList( &RtlCriticalSectionList, &crit->DebugInfo->ProcessLocksList );
518 if (crit->SpinCount)
520 ULONG count;
522 if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
523 for (count = crit->SpinCount; count > 0; count--)
525 if (crit->LockCount > 0) break; /* more than one waiter, don't bother spinning */
526 if (crit->LockCount == -1) /* try again */
528 if (interlocked_cmpxchg( (int *)&crit->LockCount, 0, -1 ) == -1) goto done;
530 small_pause();
534 if (interlocked_inc( &crit->LockCount ))
536 if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
538 crit->RecursionCount++;
539 return STATUS_SUCCESS;
542 /* Now wait for it */
543 RtlpWaitForCriticalSection( crit );
545 done:
546 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
547 crit->RecursionCount = 1;
548 return STATUS_SUCCESS;
552 /***********************************************************************
553 * RtlTryEnterCriticalSection (NTDLL.@)
555 * Tries to enter a critical section without waiting.
557 * PARAMS
558 * crit [I/O] Critical section to enter
560 * RETURNS
561 * Success: TRUE. The critical section is held by the caller.
562 * Failure: FALSE. The critical section is currently held by another thread.
564 * SEE
565 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
566 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
567 * RtlLeaveCriticalSection(), RtlSetCriticalSectionSpinCount()
569 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
571 BOOL ret = FALSE;
572 if (interlocked_cmpxchg( (int *)&crit->LockCount, 0, -1 ) == -1)
574 crit->OwningThread = ULongToHandle(GetCurrentThreadId());
575 crit->RecursionCount = 1;
576 ret = TRUE;
578 else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
580 interlocked_inc( &crit->LockCount );
581 crit->RecursionCount++;
582 ret = TRUE;
584 return ret;
588 /***********************************************************************
589 * RtlLeaveCriticalSection (NTDLL.@)
591 * Leaves a critical section.
593 * PARAMS
594 * crit [I/O] Critical section to leave.
596 * RETURNS
597 * STATUS_SUCCESS.
599 * SEE
600 * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
601 * RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
602 * RtlSetCriticalSectionSpinCount(), RtlTryEnterCriticalSection()
604 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
606 if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
607 else
609 crit->OwningThread = 0;
610 if (interlocked_dec( &crit->LockCount ) >= 0)
612 /* someone is waiting */
613 RtlpUnWaitCriticalSection( crit );
616 return STATUS_SUCCESS;