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