Added WINESERVER environment variable to allow overriding the normal
[wine] / scheduler / 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 "winbase.h"
13 #include "ntddk.h"
14 #include "heap.h"
15 #include "debugtools.h"
16 #include "thread.h"
17
18 DEFAULT_DEBUG_CHANNEL(win32);
19 DECLARE_DEBUG_CHANNEL(relay);
20
21 /***********************************************************************
22  *           get_semaphore
23  */
24 static inline HANDLE get_semaphore( CRITICAL_SECTION *crit )
25 {
26     HANDLE ret = crit->LockSemaphore;
27     if (!ret)
28     {
29         HANDLE sem = CreateSemaphoreA( NULL, 0, 1, NULL );
30         if (!(ret = (HANDLE)InterlockedCompareExchange( (PVOID *)&crit->LockSemaphore,
31                                                         (PVOID)sem, 0 )))
32             ret = sem;
33         else
34             CloseHandle(sem);  /* somebody beat us to it */
35     }
36     return ret;
37 }
38
39 /***********************************************************************
40  *           InitializeCriticalSection   (KERNEL32.472) (NTDLL.406)
41  */
42 void WINAPI InitializeCriticalSection( CRITICAL_SECTION *crit )
43 {
44     crit->LockCount      = -1;
45     crit->RecursionCount = 0;
46     crit->OwningThread   = 0;
47     crit->LockSemaphore  = 0;
48 }
49
50
51 /***********************************************************************
52  *           DeleteCriticalSection   (KERNEL32.185) (NTDLL.327)
53  */
54 void WINAPI DeleteCriticalSection( CRITICAL_SECTION *crit )
55 {
56     if (crit->RecursionCount && crit->OwningThread != GetCurrentThreadId())
57         ERR("Deleting owned critical section (%p)\n", crit );
58
59     crit->LockCount      = -1;
60     crit->RecursionCount = 0;
61     crit->OwningThread   = 0;
62     if (crit->LockSemaphore) CloseHandle( crit->LockSemaphore );
63     crit->LockSemaphore  = 0;
64 }
65
66
67 /***********************************************************************
68  *           RtlpWaitForCriticalSection   (NTDLL.@)
69  */
70 void WINAPI RtlpWaitForCriticalSection( CRITICAL_SECTION *crit )
71 {
72     for (;;)
73     {
74         EXCEPTION_RECORD rec;
75         HANDLE sem = get_semaphore( crit );
76
77         DWORD res = WaitForSingleObject( sem, 5000L );
78         if ( res == WAIT_TIMEOUT )
79         {
80             ERR("Critical section %p wait timed out, retrying (60 sec)\n", crit );
81             res = WaitForSingleObject( sem, 60000L );
82             if ( res == WAIT_TIMEOUT && TRACE_ON(relay) )
83             {
84                 ERR("Critical section %p wait timed out, retrying (5 min)\n", crit );
85                 res = WaitForSingleObject( sem, 300000L );
86             }
87         }
88         if (res == STATUS_WAIT_0) break;
89
90         rec.ExceptionCode    = EXCEPTION_CRITICAL_SECTION_WAIT;
91         rec.ExceptionFlags   = 0;
92         rec.ExceptionRecord  = NULL;
93         rec.ExceptionAddress = RtlRaiseException;  /* sic */
94         rec.NumberParameters = 1;
95         rec.ExceptionInformation[0] = (DWORD)crit;
96         RtlRaiseException( &rec );
97     }
98 }
99
100
101 /***********************************************************************
102  *           RtlpUnWaitCriticalSection   (NTDLL.@)
103  */
104 void WINAPI RtlpUnWaitCriticalSection( CRITICAL_SECTION *crit )
105 {
106     HANDLE sem = get_semaphore( crit );
107     ReleaseSemaphore( sem, 1, NULL );
108 }
109
110
111 /***********************************************************************
112  *           EnterCriticalSection   (KERNEL32.195) (NTDLL.344)
113  */
114 void WINAPI EnterCriticalSection( CRITICAL_SECTION *crit )
115 {
116     if (InterlockedIncrement( &crit->LockCount ))
117     {
118         if (crit->OwningThread == GetCurrentThreadId())
119         {
120             crit->RecursionCount++;
121             return;
122         }
123
124         /* Now wait for it */
125         RtlpWaitForCriticalSection( crit );
126     }
127     crit->OwningThread   = GetCurrentThreadId();
128     crit->RecursionCount = 1;
129 }
130
131
132 /***********************************************************************
133  *           TryEnterCriticalSection   (KERNEL32.898) (NTDLL.969)
134  */
135 BOOL WINAPI TryEnterCriticalSection( CRITICAL_SECTION *crit )
136 {
137     BOOL ret = FALSE;
138     if (InterlockedCompareExchange( (PVOID *)&crit->LockCount,
139                                     (PVOID)0L, (PVOID)-1L ) == (PVOID)-1L)
140     {
141         crit->OwningThread   = GetCurrentThreadId();
142         crit->RecursionCount = 1;
143         ret = TRUE;
144     }
145     else if (crit->OwningThread == GetCurrentThreadId())
146     {
147         InterlockedIncrement( &crit->LockCount );
148         crit->RecursionCount++;
149         ret = TRUE;
150     }
151     return ret;
152 }
153
154
155 /***********************************************************************
156  *           LeaveCriticalSection   (KERNEL32.494) (NTDLL.426)
157  */
158 void WINAPI LeaveCriticalSection( CRITICAL_SECTION *crit )
159 {
160     if (crit->OwningThread != GetCurrentThreadId()) return;
161        
162     if (--crit->RecursionCount)
163     {
164         InterlockedDecrement( &crit->LockCount );
165         return;
166     }
167     crit->OwningThread = 0;
168     if (InterlockedDecrement( &crit->LockCount ) >= 0)
169     {
170         /* Someone is waiting */
171         RtlpUnWaitCriticalSection( crit );
172     }
173 }
174
175
176 /***********************************************************************
177  *           MakeCriticalSectionGlobal   (KERNEL32.515)
178  */
179 void WINAPI MakeCriticalSectionGlobal( CRITICAL_SECTION *crit )
180 {
181     crit->LockSemaphore = ConvertToGlobalHandle( get_semaphore( crit ) );
182 }
183
184
185 /***********************************************************************
186  *           ReinitializeCriticalSection   (KERNEL32.581)
187  */
188 void WINAPI ReinitializeCriticalSection( CRITICAL_SECTION *crit )
189 {
190     if ( !crit->LockSemaphore )
191         InitializeCriticalSection( crit );
192 }
193
194
195 /***********************************************************************
196  *           UninitializeCriticalSection   (KERNEL32.703)
197  */
198 void WINAPI UninitializeCriticalSection( CRITICAL_SECTION *crit )
199 {
200     DeleteCriticalSection( crit );
201 }
202
203 #ifdef __i386__
204
205 /***********************************************************************
206  *              InterlockCompareExchange (KERNEL32.@)
207  */
208 PVOID WINAPI InterlockedCompareExchange( PVOID *dest, PVOID xchg, PVOID compare );
209 __ASM_GLOBAL_FUNC(InterlockedCompareExchange,
210                   "movl 12(%esp),%eax\n\t"
211                   "movl 8(%esp),%ecx\n\t"
212                   "movl 4(%esp),%edx\n\t"
213                   "lock; cmpxchgl %ecx,(%edx)\n\t"
214                   "ret $12");
215
216 /***********************************************************************
217  *              InterlockedExchange (KERNEL32.@)
218  */
219 LONG WINAPI InterlockedExchange( PLONG dest, LONG val );
220 __ASM_GLOBAL_FUNC(InterlockedExchange,
221                   "movl 8(%esp),%eax\n\t"
222                   "movl 4(%esp),%edx\n\t"
223                   "lock; xchgl %eax,(%edx)\n\t"
224                   "ret $8");
225
226 /***********************************************************************
227  *              InterlockedExchangeAdd (KERNEL32.@)
228  */
229 LONG WINAPI InterlockedExchangeAdd( PLONG dest, LONG incr );
230 __ASM_GLOBAL_FUNC(InterlockedExchangeAdd,
231                   "movl 8(%esp),%eax\n\t"
232                   "movl 4(%esp),%edx\n\t"
233                   "lock; xaddl %eax,(%edx)\n\t"
234                   "ret $8");
235
236 /***********************************************************************
237  *              InterlockedIncrement (KERNEL32.@)
238  */
239 LONG WINAPI InterlockedIncrement( PLONG dest );
240 __ASM_GLOBAL_FUNC(InterlockedIncrement,
241                   "movl 4(%esp),%edx\n\t"
242                   "movl $1,%eax\n\t"
243                   "lock; xaddl %eax,(%edx)\n\t"
244                   "incl %eax\n\t"
245                   "ret $4");
246
247 /***********************************************************************
248  *              InterlockDecrement (KERNEL32.@)
249  */
250 LONG WINAPI InterlockedDecrement( PLONG dest );
251 __ASM_GLOBAL_FUNC(InterlockedDecrement,
252                   "movl 4(%esp),%edx\n\t"
253                   "movl $-1,%eax\n\t"
254                   "lock; xaddl %eax,(%edx)\n\t"
255                   "decl %eax\n\t"
256                   "ret $4");
257
258 #elif defined(__sparc__) && defined(__sun__)
259
260 /*
261  * As the earlier Sparc processors lack necessary atomic instructions,
262  * I'm simply falling back to the library-provided _lwp_mutex routines
263  * to ensure mutual exclusion in a way appropriate for the current 
264  * architecture.  
265  *
266  * FIXME:  If we have the compare-and-swap instruction (Sparc v9 and above)
267  *         we could use this to speed up the Interlocked operations ...
268  */
269
270 #include <synch.h>
271 static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX;
272
273 /***********************************************************************
274  *              InterlockedCompareExchange (KERNEL32.@)
275  */
276 PVOID WINAPI InterlockedCompareExchange( PVOID *dest, PVOID xchg, PVOID compare )
277 {
278     _lwp_mutex_lock( &interlocked_mutex );
279
280     if ( *dest == compare )
281         *dest = xchg;
282     else
283         compare = *dest;
284     
285     _lwp_mutex_unlock( &interlocked_mutex );
286     return compare;
287 }
288
289 /***********************************************************************
290  *              InterlockedExchange (KERNEL32.@)
291  */
292 LONG WINAPI InterlockedExchange( PLONG dest, LONG val )
293 {
294     LONG retv;
295     _lwp_mutex_lock( &interlocked_mutex );
296
297     retv = *dest;
298     *dest = val;
299
300     _lwp_mutex_unlock( &interlocked_mutex );
301     return retv;
302 }
303
304 /***********************************************************************
305  *              InterlockedExchangeAdd (KERNEL32.@)
306  */
307 LONG WINAPI InterlockedExchangeAdd( PLONG dest, LONG incr )
308 {
309     LONG retv;
310     _lwp_mutex_lock( &interlocked_mutex );
311
312     retv = *dest;
313     *dest += incr;
314
315     _lwp_mutex_unlock( &interlocked_mutex );
316     return retv;
317 }
318
319 /***********************************************************************
320  *              InterlockedIncrement (KERNEL32.@)
321  */
322 LONG WINAPI InterlockedIncrement( PLONG dest )
323 {
324     LONG retv;
325     _lwp_mutex_lock( &interlocked_mutex );
326
327     retv = ++*dest;
328
329     _lwp_mutex_unlock( &interlocked_mutex );
330     return retv;
331 }
332
333 /***********************************************************************
334  *              InterlockedDecrement (KERNEL32.@)
335  */
336 LONG WINAPI InterlockedDecrement( PLONG dest )
337 {
338     LONG retv;
339     _lwp_mutex_lock( &interlocked_mutex );
340
341     retv = --*dest;
342
343     _lwp_mutex_unlock( &interlocked_mutex );
344     return retv;
345 }
346
347 #else
348 #error You must implement the Interlocked* functions for your CPU
349 #endif