Added support for backslash escaping of special characters.
[wine.git] / dlls / ntdll / critsection.c
blobe9f49aa5a4d07cbbf77215f0596ea284fd288e7f
1 /*
2 * Win32 critical sections
4 * Copyright 1998 Alexandre Julliard
5 */
7 #include <assert.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include "winerror.h"
12 #include "ntddk.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.
23 #ifdef __i386__
25 # ifdef __GNUC__
27 inline static PVOID interlocked_cmpxchg( PVOID *dest, PVOID xchg, PVOID compare )
29 PVOID ret;
30 __asm__ __volatile__( "lock; cmpxchgl %2,(%1)"
31 : "=a" (ret) : "r" (dest), "r" (xchg), "0" (compare) : "memory" );
32 return ret;
34 inline static LONG interlocked_inc( PLONG dest )
36 LONG ret;
37 __asm__ __volatile__( "lock; xaddl %0,(%1)"
38 : "=r" (ret) : "r" (dest), "0" (1) : "memory" );
39 return ret + 1;
41 inline static LONG interlocked_dec( PLONG dest )
43 LONG ret;
44 __asm__ __volatile__( "lock; xaddl %0,(%1)"
45 : "=r" (ret) : "r" (dest), "0" (-1) : "memory" );
46 return ret - 1;
49 # else /* __GNUC__ */
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"
57 "ret $12");
58 LONG WINAPI interlocked_inc( PLONG dest );
59 __ASM_GLOBAL_FUNC(interlocked_inc,
60 "movl 4(%esp),%edx\n\t"
61 "movl $1,%eax\n\t"
62 "lock; xaddl %eax,(%edx)\n\t"
63 "incl %eax\n\t"
64 "ret $4");
65 LONG WINAPI interlocked_dec( PLONG dest );
66 __ASM_GLOBAL_FUNC(interlocked_dec,
67 "movl 4(%esp),%edx\n\t"
68 "movl $-1,%eax\n\t"
69 "lock; xaddl %eax,(%edx)\n\t"
70 "decl %eax\n\t"
71 "ret $4");
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
79 * architecture.
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 ...
84 #include <synch.h>
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 )
91 *dest = xchg;
92 else
93 compare = *dest;
94 _lwp_mutex_unlock( &interlocked_mutex );
95 return compare;
98 static LONG interlocked_inc( PLONG dest )
100 LONG retv;
101 _lwp_mutex_lock( &interlocked_mutex );
102 retv = ++*dest;
103 _lwp_mutex_unlock( &interlocked_mutex );
104 return retv;
107 static LONG interlocked_dec( PLONG dest )
109 LONG retv;
110 _lwp_mutex_lock( &interlocked_mutex );
111 retv = --*dest;
112 _lwp_mutex_unlock( &interlocked_mutex );
113 return retv;
115 #else
116 # error You must implement the interlocked* functions for your CPU
117 #endif
120 /***********************************************************************
121 * get_semaphore
123 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
125 HANDLE ret = crit->LockSemaphore;
126 if (!ret)
128 HANDLE sem;
129 if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
130 if (!(ret = (HANDLE)InterlockedCompareExchange( (PVOID *)&crit->LockSemaphore,
131 (PVOID)sem, 0 )))
132 ret = sem;
133 else
134 NtClose(sem); /* somebody beat us to it */
136 return ret;
139 /***********************************************************************
140 * RtlInitializeCriticalSection (NTDLL.@)
142 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
144 crit->LockCount = -1;
145 crit->RecursionCount = 0;
146 crit->OwningThread = 0;
147 crit->LockSemaphore = 0;
148 return STATUS_SUCCESS;
151 /***********************************************************************
152 * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@)
153 * The InitializeCriticalSectionAndSpinCount (KERNEL32) function is
154 * available on NT4SP3 or later, and Win98 or later.
155 * I am assuming that this is the correct definition given the MSDN
156 * docs for the kernel32 functions.
158 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, DWORD spincount )
160 if(spincount) TRACE("critsection=%p: spincount=%ld not supported\n", crit, spincount);
161 crit->SpinCount = spincount;
162 return RtlInitializeCriticalSection( crit );
166 /***********************************************************************
167 * RtlDeleteCriticalSection (NTDLL.@)
169 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
171 crit->LockCount = -1;
172 crit->RecursionCount = 0;
173 crit->OwningThread = 0;
174 if (crit->LockSemaphore) NtClose( crit->LockSemaphore );
175 crit->LockSemaphore = 0;
176 return STATUS_SUCCESS;
180 /***********************************************************************
181 * RtlpWaitForCriticalSection (NTDLL.@)
183 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
185 for (;;)
187 EXCEPTION_RECORD rec;
188 HANDLE sem = get_semaphore( crit );
190 DWORD res = WaitForSingleObject( sem, 5000L );
191 if ( res == WAIT_TIMEOUT )
193 ERR("Critical section %p wait timed out, retrying (60 sec) fs=%04x\n", crit, __get_fs() );
194 res = WaitForSingleObject( sem, 60000L );
195 if ( res == WAIT_TIMEOUT && TRACE_ON(relay) )
197 ERR("Critical section %p wait timed out, retrying (5 min) fs=%04x\n", crit, __get_fs() );
198 res = WaitForSingleObject( sem, 300000L );
201 if (res == STATUS_WAIT_0) return STATUS_SUCCESS;
203 rec.ExceptionCode = EXCEPTION_CRITICAL_SECTION_WAIT;
204 rec.ExceptionFlags = 0;
205 rec.ExceptionRecord = NULL;
206 rec.ExceptionAddress = RtlRaiseException; /* sic */
207 rec.NumberParameters = 1;
208 rec.ExceptionInformation[0] = (DWORD)crit;
209 RtlRaiseException( &rec );
214 /***********************************************************************
215 * RtlpUnWaitCriticalSection (NTDLL.@)
217 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
219 HANDLE sem = get_semaphore( crit );
220 NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
221 if (res) RtlRaiseStatus( res );
222 return res;
226 /***********************************************************************
227 * RtlEnterCriticalSection (NTDLL.@)
229 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
231 if (interlocked_inc( &crit->LockCount ))
233 if (crit->OwningThread == GetCurrentThreadId())
235 crit->RecursionCount++;
236 return STATUS_SUCCESS;
239 /* Now wait for it */
240 RtlpWaitForCriticalSection( crit );
242 crit->OwningThread = GetCurrentThreadId();
243 crit->RecursionCount = 1;
244 return STATUS_SUCCESS;
248 /***********************************************************************
249 * RtlTryEnterCriticalSection (NTDLL.@)
251 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
253 BOOL ret = FALSE;
254 if (interlocked_cmpxchg( (PVOID *)&crit->LockCount, (PVOID)0L, (PVOID)-1L ) == (PVOID)-1L)
256 crit->OwningThread = GetCurrentThreadId();
257 crit->RecursionCount = 1;
258 ret = TRUE;
260 else if (crit->OwningThread == GetCurrentThreadId())
262 interlocked_inc( &crit->LockCount );
263 crit->RecursionCount++;
264 ret = TRUE;
266 return ret;
270 /***********************************************************************
271 * RtlLeaveCriticalSection (NTDLL.@)
273 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
275 if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
276 else
278 crit->OwningThread = 0;
279 if (interlocked_dec( &crit->LockCount ) >= 0)
281 /* someone is waiting */
282 RtlpUnWaitCriticalSection( crit );
285 return STATUS_SUCCESS;