2 * Win32 critical sections
4 * Copyright 1998 Alexandre Julliard
10 #include <sys/types.h>
13 #include "debugtools.h"
15 DEFAULT_DEBUG_CHANNEL(ntdll);
16 DECLARE_DEBUG_CHANNEL(relay);
18 /* Define the atomic exchange/inc/dec functions.
19 * These are available in kernel32.dll already,
20 * but we don't want to import kernel32 from ntdll.
27 inline static PVOID interlocked_cmpxchg( PVOID *dest, PVOID xchg, PVOID compare )
30 __asm__ __volatile__( "lock; cmpxchgl %2,(%1)"
31 : "=a" (ret) : "r" (dest), "r" (xchg), "0" (compare) : "memory" );
34 inline static LONG interlocked_inc( PLONG dest )
37 __asm__ __volatile__( "lock; xaddl %0,(%1)"
38 : "=r" (ret) : "r" (dest), "0" (1) : "memory" );
41 inline static LONG interlocked_dec( PLONG dest )
44 __asm__ __volatile__( "lock; xaddl %0,(%1)"
45 : "=r" (ret) : "r" (dest), "0" (-1) : "memory" );
51 PVOID WINAPI interlocked_cmpxchg( PVOID *dest, PVOID xchg, PVOID compare );
52 __ASM_GLOBAL_FUNC(interlocked_cmpxchg,
53 "movl 12(%esp),%eax\n\t"
54 "movl 8(%esp),%ecx\n\t"
55 "movl 4(%esp),%edx\n\t"
56 "lock; cmpxchgl %ecx,(%edx)\n\t"
58 LONG WINAPI interlocked_inc( PLONG dest );
59 __ASM_GLOBAL_FUNC(interlocked_inc,
60 "movl 4(%esp),%edx\n\t"
62 "lock; xaddl %eax,(%edx)\n\t"
65 LONG WINAPI interlocked_dec( PLONG dest );
66 __ASM_GLOBAL_FUNC(interlocked_dec,
67 "movl 4(%esp),%edx\n\t"
69 "lock; xaddl %eax,(%edx)\n\t"
72 # endif /* __GNUC__ */
74 #elif defined(__sparc__) && defined(__sun__)
76 * As the earlier Sparc processors lack necessary atomic instructions,
77 * I'm simply falling back to the library-provided _lwp_mutex routines
78 * to ensure mutual exclusion in a way appropriate for the current
81 * FIXME: If we have the compare-and-swap instruction (Sparc v9 and above)
82 * we could use this to speed up the Interlocked operations ...
85 static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX;
87 static PVOID interlocked_cmpxchg( PVOID *dest, PVOID xchg, PVOID compare )
89 _lwp_mutex_lock( &interlocked_mutex );
90 if ( *dest == compare )
94 _lwp_mutex_unlock( &interlocked_mutex );
98 static LONG interlocked_inc( PLONG dest )
101 _lwp_mutex_lock( &interlocked_mutex );
103 _lwp_mutex_unlock( &interlocked_mutex );
107 static LONG interlocked_dec( PLONG dest )
110 _lwp_mutex_lock( &interlocked_mutex );
112 _lwp_mutex_unlock( &interlocked_mutex );
116 # error You must implement the interlocked* functions for your CPU
120 /***********************************************************************
123 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
125 HANDLE ret = crit->LockSemaphore;
129 if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
130 if (!(ret = (HANDLE)interlocked_cmpxchg( (PVOID *)&crit->LockSemaphore,
134 NtClose(sem); /* somebody beat us to it */
139 /***********************************************************************
140 * RtlInitializeCriticalSection (NTDLL.@)
142 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
144 crit->DebugInfo = NULL;
145 crit->LockCount = -1;
146 crit->RecursionCount = 0;
147 crit->OwningThread = 0;
148 crit->LockSemaphore = 0;
149 return STATUS_SUCCESS;
152 /***********************************************************************
153 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
154 * The InitializeCriticalSectionAndSpinCount (KERNEL32) function is
155 * available on NT4SP3 or later, and Win98 or later.
156 * I am assuming that this is the correct definition given the MSDN
157 * docs for the kernel32 functions.
159 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, DWORD spincount )
161 if(spincount) TRACE("critsection=%p: spincount=%ld not supported\n", crit, spincount);
162 crit->SpinCount = spincount;
163 return RtlInitializeCriticalSection( crit );
167 /***********************************************************************
168 * RtlDeleteCriticalSection (NTDLL.@)
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;
177 return STATUS_SUCCESS;
181 /***********************************************************************
182 * RtlpWaitForCriticalSection (NTDLL.@)
184 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
188 EXCEPTION_RECORD rec;
189 HANDLE sem = get_semaphore( crit );
191 DWORD res = WaitForSingleObject( sem, 5000L );
192 if ( res == WAIT_TIMEOUT )
194 const char *name = (char *)crit->DebugInfo;
195 if (!name || IsBadStringPtrA(name,80)) name = "?";
196 ERR( "section %p %s wait timed out, retrying (60 sec) fs=%04x\n",
197 crit, debugstr_a(name), __get_fs() );
198 res = WaitForSingleObject( sem, 60000L );
199 if ( res == WAIT_TIMEOUT && TRACE_ON(relay) )
201 ERR( "section %p %s wait timed out, retrying (5 min) fs=%04x\n",
202 crit, debugstr_a(name), __get_fs() );
203 res = WaitForSingleObject( sem, 300000L );
206 if (res == STATUS_WAIT_0) return STATUS_SUCCESS;
208 rec.ExceptionCode = EXCEPTION_CRITICAL_SECTION_WAIT;
209 rec.ExceptionFlags = 0;
210 rec.ExceptionRecord = NULL;
211 rec.ExceptionAddress = RtlRaiseException; /* sic */
212 rec.NumberParameters = 1;
213 rec.ExceptionInformation[0] = (DWORD)crit;
214 RtlRaiseException( &rec );
219 /***********************************************************************
220 * RtlpUnWaitCriticalSection (NTDLL.@)
222 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
224 HANDLE sem = get_semaphore( crit );
225 NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
226 if (res) RtlRaiseStatus( res );
231 /***********************************************************************
232 * RtlEnterCriticalSection (NTDLL.@)
234 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
236 if (interlocked_inc( &crit->LockCount ))
238 if (crit->OwningThread == GetCurrentThreadId())
240 crit->RecursionCount++;
241 return STATUS_SUCCESS;
244 /* Now wait for it */
245 RtlpWaitForCriticalSection( crit );
247 crit->OwningThread = GetCurrentThreadId();
248 crit->RecursionCount = 1;
249 return STATUS_SUCCESS;
253 /***********************************************************************
254 * RtlTryEnterCriticalSection (NTDLL.@)
256 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
259 if (interlocked_cmpxchg( (PVOID *)&crit->LockCount, (PVOID)0L, (PVOID)-1L ) == (PVOID)-1L)
261 crit->OwningThread = GetCurrentThreadId();
262 crit->RecursionCount = 1;
265 else if (crit->OwningThread == GetCurrentThreadId())
267 interlocked_inc( &crit->LockCount );
268 crit->RecursionCount++;
275 /***********************************************************************
276 * RtlLeaveCriticalSection (NTDLL.@)
278 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
280 if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
283 crit->OwningThread = 0;
284 if (interlocked_dec( &crit->LockCount ) >= 0)
286 /* someone is waiting */
287 RtlpUnWaitCriticalSection( crit );
290 return STATUS_SUCCESS;