Fixed a few problems with DCX_* flags handling.
[wine] / dlls / ntdll / critsection.c
1 /*
2  * Win32 critical sections
3  *
4  * Copyright 1998 Alexandre Julliard
5  */
6
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"
14
15 DEFAULT_DEBUG_CHANNEL(ntdll);
16 DECLARE_DEBUG_CHANNEL(relay);
17
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.
21  */
22
23 #ifdef __i386__
24
25 # ifdef __GNUC__
26
27 inline static PVOID interlocked_cmpxchg( PVOID *dest, PVOID xchg, PVOID compare )
28 {
29     PVOID ret;
30     __asm__ __volatile__( "lock; cmpxchgl %2,(%1)"
31                           : "=a" (ret) : "r" (dest), "r" (xchg), "0" (compare) : "memory" );
32     return ret;
33 }
34 inline static LONG interlocked_inc( PLONG dest )
35 {
36     LONG ret;
37     __asm__ __volatile__( "lock; xaddl %0,(%1)"
38                           : "=r" (ret) : "r" (dest), "0" (1) : "memory" );
39     return ret + 1;
40 }
41 inline static LONG interlocked_dec( PLONG dest )
42 {
43     LONG ret;
44     __asm__ __volatile__( "lock; xaddl %0,(%1)"
45                           : "=r" (ret) : "r" (dest), "0" (-1) : "memory" );
46     return ret - 1;
47 }
48
49 # else  /* __GNUC__ */
50
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__ */
73
74 #elif defined(__sparc__) && defined(__sun__)
75 /*
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.  
80  *
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 ...
83  */
84 #include <synch.h>
85 static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX;
86
87 static PVOID interlocked_cmpxchg( PVOID *dest, PVOID xchg, PVOID compare )
88 {
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;
96 }
97
98 static LONG interlocked_inc( PLONG dest )
99 {
100     LONG retv;
101     _lwp_mutex_lock( &interlocked_mutex );
102     retv = ++*dest;
103     _lwp_mutex_unlock( &interlocked_mutex );
104     return retv;
105 }
106
107 static LONG interlocked_dec( PLONG dest )
108 {
109     LONG retv;
110     _lwp_mutex_lock( &interlocked_mutex );
111     retv = --*dest;
112     _lwp_mutex_unlock( &interlocked_mutex );
113     return retv;
114 }
115 #else
116 # error You must implement the interlocked* functions for your CPU
117 #endif
118
119
120 /***********************************************************************
121  *           get_semaphore
122  */
123 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
124 {
125     HANDLE ret = crit->LockSemaphore;
126     if (!ret)
127     {
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 */
135     }
136     return ret;
137 }
138
139 /***********************************************************************
140  *           RtlInitializeCriticalSection   (NTDLL.@)
141  */
142 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
143 {
144     crit->LockCount      = -1;
145     crit->RecursionCount = 0;
146     crit->OwningThread   = 0;
147     crit->LockSemaphore  = 0;
148     return STATUS_SUCCESS;
149 }
150
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.
157  */
158 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, DWORD spincount )
159 {
160     if(spincount) TRACE("critsection=%p: spincount=%ld not supported\n", crit, spincount);
161     crit->SpinCount = spincount;
162     return RtlInitializeCriticalSection( crit );
163 }
164
165
166 /***********************************************************************
167  *           RtlDeleteCriticalSection   (NTDLL.@)
168  */
169 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
170 {
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;
177 }
178
179
180 /***********************************************************************
181  *           RtlpWaitForCriticalSection   (NTDLL.@)
182  */
183 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
184 {
185     for (;;)
186     {
187         EXCEPTION_RECORD rec;
188         HANDLE sem = get_semaphore( crit );
189
190         DWORD res = WaitForSingleObject( sem, 5000L );
191         if ( res == WAIT_TIMEOUT )
192         {
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) )
196             {
197                 ERR("Critical section %p wait timed out, retrying (5 min) fs=%04x\n", crit, __get_fs() );
198                 res = WaitForSingleObject( sem, 300000L );
199             }
200         }
201         if (res == STATUS_WAIT_0) return STATUS_SUCCESS;
202
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 );
210     }
211 }
212
213
214 /***********************************************************************
215  *           RtlpUnWaitCriticalSection   (NTDLL.@)
216  */
217 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
218 {
219     HANDLE sem = get_semaphore( crit );
220     NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
221     if (res) RtlRaiseStatus( res );
222     return res;
223 }
224
225
226 /***********************************************************************
227  *           RtlEnterCriticalSection   (NTDLL.@)
228  */
229 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
230 {
231     if (interlocked_inc( &crit->LockCount ))
232     {
233         if (crit->OwningThread == GetCurrentThreadId())
234         {
235             crit->RecursionCount++;
236             return STATUS_SUCCESS;
237         }
238
239         /* Now wait for it */
240         RtlpWaitForCriticalSection( crit );
241     }
242     crit->OwningThread   = GetCurrentThreadId();
243     crit->RecursionCount = 1;
244     return STATUS_SUCCESS;
245 }
246
247
248 /***********************************************************************
249  *           RtlTryEnterCriticalSection   (NTDLL.@)
250  */
251 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
252 {
253     BOOL ret = FALSE;
254     if (interlocked_cmpxchg( (PVOID *)&crit->LockCount, (PVOID)0L, (PVOID)-1L ) == (PVOID)-1L)
255     {
256         crit->OwningThread   = GetCurrentThreadId();
257         crit->RecursionCount = 1;
258         ret = TRUE;
259     }
260     else if (crit->OwningThread == GetCurrentThreadId())
261     {
262         interlocked_inc( &crit->LockCount );
263         crit->RecursionCount++;
264         ret = TRUE;
265     }
266     return ret;
267 }
268
269
270 /***********************************************************************
271  *           RtlLeaveCriticalSection   (NTDLL.@)
272  */
273 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
274 {
275     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
276     else
277     {
278         crit->OwningThread = 0;
279         if (interlocked_dec( &crit->LockCount ) >= 0)
280         {
281             /* someone is waiting */
282             RtlpUnWaitCriticalSection( crit );
283         }
284     }
285     return STATUS_SUCCESS;
286 }