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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "wine/port.h"
27 #include <sys/types.h>
33 #include "wine/debug.h"
34 #include "ntdll_misc.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
37 WINE_DECLARE_DEBUG_CHANNEL(relay);
39 inline static LONG interlocked_inc( PLONG dest )
41 return interlocked_xchg_add( dest, 1 ) + 1;
44 inline static LONG interlocked_dec( PLONG dest )
46 return interlocked_xchg_add( dest, -1 ) - 1;
49 inline static void small_pause(void)
52 __asm__ __volatile__( "rep;nop" : : : "memory" );
54 __asm__ __volatile__( "" : : : "memory" );
58 /***********************************************************************
61 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
63 HANDLE ret = crit->LockSemaphore;
67 if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
68 if (!(ret = (HANDLE)interlocked_cmpxchg_ptr( (PVOID *)&crit->LockSemaphore,
72 NtClose(sem); /* somebody beat us to it */
77 /***********************************************************************
78 * RtlInitializeCriticalSection (NTDLL.@)
80 * Initialise a new RTL_CRITICAL_SECTION.
83 * crit [O] Critical section to initialise
88 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
90 return RtlInitializeCriticalSectionAndSpinCount( crit, 0 );
93 /***********************************************************************
94 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
96 * Initialise a new RTL_CRITICAL_SECTION with a given spin count.
99 * crit [O] Critical section to initialise
100 * spincount [I] Spin count for crit
106 * The InitializeCriticalSectionAndSpinCount() (KERNEL32) function is
107 * available on NT4SP3 or later, and Win98 or later.
109 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
111 if (!GetProcessHeap()) crit->DebugInfo = NULL;
114 crit->DebugInfo = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(CRITICAL_SECTION_DEBUG));
117 crit->DebugInfo->Type = 0;
118 crit->DebugInfo->CreatorBackTraceIndex = 0;
119 crit->DebugInfo->CriticalSection = crit;
120 crit->DebugInfo->ProcessLocksList.Blink = &(crit->DebugInfo->ProcessLocksList);
121 crit->DebugInfo->ProcessLocksList.Flink = &(crit->DebugInfo->ProcessLocksList);
122 crit->DebugInfo->EntryCount = 0;
123 crit->DebugInfo->ContentionCount = 0;
124 crit->DebugInfo->Spare[0] = 0;
125 crit->DebugInfo->Spare[1] = 0;
128 crit->LockCount = -1;
129 crit->RecursionCount = 0;
130 crit->OwningThread = 0;
131 crit->LockSemaphore = 0;
132 crit->SpinCount = spincount;
133 return STATUS_SUCCESS;
136 /***********************************************************************
137 * RtlSetCriticalSectionSpinCount (NTDLL.@)
139 * Sets the spin count of a critical section.
142 * crit [O] Critical section
143 * spincount [I] Spin count for crit
146 * The previous spin count.
149 * If the system is not SMP, spincount is ignored and set to 0.
151 ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
153 ULONG oldspincount = crit->SpinCount;
154 if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
155 crit->SpinCount = spincount;
159 /***********************************************************************
160 * RtlDeleteCriticalSection (NTDLL.@)
162 * Free the resources used by an RTL_CRITICAL_SECTION.
165 * crit [I/O] Critical section to free
170 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
172 crit->LockCount = -1;
173 crit->RecursionCount = 0;
174 crit->OwningThread = 0;
175 if (crit->LockSemaphore) NtClose( crit->LockSemaphore );
176 crit->LockSemaphore = 0;
179 /* only free the ones we made in here */
180 if (!crit->DebugInfo->Spare[1])
182 RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
183 crit->DebugInfo = NULL;
186 return STATUS_SUCCESS;
190 /***********************************************************************
191 * RtlpWaitForCriticalSection (NTDLL.@)
193 * Wait for an RTL_CRITICAL_SECTION to become free.
196 * crit [I/O] Critical section to wait for
201 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
205 EXCEPTION_RECORD rec;
206 HANDLE sem = get_semaphore( crit );
210 time.QuadPart = -5000 * 10000; /* 5 seconds */
211 status = NtWaitForSingleObject( sem, FALSE, &time );
212 if ( status == WAIT_TIMEOUT )
214 const char *name = NULL;
215 if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[1];
216 if (!name) name = "?";
217 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (60 sec)\n",
218 crit, debugstr_a(name), GetCurrentThreadId(), (DWORD)crit->OwningThread );
219 time.QuadPart = -60000 * 10000;
220 status = NtWaitForSingleObject( sem, FALSE, &time );
221 if ( status == WAIT_TIMEOUT && TRACE_ON(relay) )
223 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (5 min)\n",
224 crit, debugstr_a(name), GetCurrentThreadId(), (DWORD) crit->OwningThread );
225 time.QuadPart = -300000 * (ULONGLONG)10000;
226 status = NtWaitForSingleObject( sem, FALSE, &time );
229 if (status == STATUS_WAIT_0) return STATUS_SUCCESS;
231 /* Throw exception only for Wine internal locks */
232 if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[1])) continue;
234 rec.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
235 rec.ExceptionFlags = 0;
236 rec.ExceptionRecord = NULL;
237 rec.ExceptionAddress = RtlRaiseException; /* sic */
238 rec.NumberParameters = 1;
239 rec.ExceptionInformation[0] = (DWORD)crit;
240 RtlRaiseException( &rec );
245 /***********************************************************************
246 * RtlpUnWaitCriticalSection (NTDLL.@)
248 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
250 HANDLE sem = get_semaphore( crit );
251 NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
252 if (res) RtlRaiseStatus( res );
257 /***********************************************************************
258 * RtlEnterCriticalSection (NTDLL.@)
260 * Enter an RTL_CRITICAL_SECTION.
263 * crit [I/O] Critical section to enter
266 * STATUS_SUCCESS. The critical section is held by the caller.
269 * The caller will wait until the critical section is availale.
271 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
277 if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
278 for (count = crit->SpinCount; count > 0; count--)
280 if (crit->LockCount > 0) break; /* more than one waiter, don't bother spinning */
281 if (crit->LockCount == -1) /* try again */
283 if (interlocked_cmpxchg( &crit->LockCount, 0, -1 ) == -1) goto done;
289 if (interlocked_inc( &crit->LockCount ))
291 if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
293 crit->RecursionCount++;
294 return STATUS_SUCCESS;
297 /* Now wait for it */
298 RtlpWaitForCriticalSection( crit );
301 crit->OwningThread = (HANDLE)GetCurrentThreadId();
302 crit->RecursionCount = 1;
303 return STATUS_SUCCESS;
307 /***********************************************************************
308 * RtlTryEnterCriticalSection (NTDLL.@)
310 * Enter an RTL_CRITICAL_SECTION without waiting.
313 * crit [I/O] Critical section to enter
316 * Success: TRUE. The critical section is held by the caller.
317 * Failure: FALSE. The critical section is currently held by another thread.
319 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
322 if (interlocked_cmpxchg( &crit->LockCount, 0L, -1 ) == -1)
324 crit->OwningThread = (HANDLE)GetCurrentThreadId();
325 crit->RecursionCount = 1;
328 else if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
330 interlocked_inc( &crit->LockCount );
331 crit->RecursionCount++;
338 /***********************************************************************
339 * RtlLeaveCriticalSection (NTDLL.@)
341 * Leave an RTL_CRITICAL_SECTION.
344 * crit [I/O] Critical section to enter
349 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
351 if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
354 crit->OwningThread = 0;
355 if (interlocked_dec( &crit->LockCount ) >= 0)
357 /* someone is waiting */
358 RtlpUnWaitCriticalSection( crit );
361 return STATUS_SUCCESS;