Cleaned up a bit, removed todos for OpenThread, avoid TerminateThread
[wine] / dlls / ntdll / critsection.c
1 /*
2  * Win32 critical sections
3  *
4  * Copyright 1998 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include "winerror.h"
26 #include "ntddk.h"
27 #include "wine/debug.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
30 WINE_DECLARE_DEBUG_CHANNEL(relay);
31
32 /* Define the atomic exchange/inc/dec functions.
33  * These are available in kernel32.dll already,
34  * but we don't want to import kernel32 from ntdll.
35  */
36
37 #ifdef __i386__
38
39 # ifdef __GNUC__
40
41 inline static PVOID interlocked_cmpxchg( PVOID *dest, PVOID xchg, PVOID compare )
42 {
43     PVOID ret;
44     __asm__ __volatile__( "lock; cmpxchgl %2,(%1)"
45                           : "=a" (ret) : "r" (dest), "r" (xchg), "0" (compare) : "memory" );
46     return ret;
47 }
48 inline static LONG interlocked_inc( PLONG dest )
49 {
50     LONG ret;
51     __asm__ __volatile__( "lock; xaddl %0,(%1)"
52                           : "=r" (ret) : "r" (dest), "0" (1) : "memory" );
53     return ret + 1;
54 }
55 inline static LONG interlocked_dec( PLONG dest )
56 {
57     LONG ret;
58     __asm__ __volatile__( "lock; xaddl %0,(%1)"
59                           : "=r" (ret) : "r" (dest), "0" (-1) : "memory" );
60     return ret - 1;
61 }
62
63 # else  /* __GNUC__ */
64
65 PVOID WINAPI interlocked_cmpxchg( PVOID *dest, PVOID xchg, PVOID compare );
66 __ASM_GLOBAL_FUNC(interlocked_cmpxchg,
67                   "movl 12(%esp),%eax\n\t"
68                   "movl 8(%esp),%ecx\n\t"
69                   "movl 4(%esp),%edx\n\t"
70                   "lock; cmpxchgl %ecx,(%edx)\n\t"
71                   "ret $12");
72 LONG WINAPI interlocked_inc( PLONG dest );
73 __ASM_GLOBAL_FUNC(interlocked_inc,
74                   "movl 4(%esp),%edx\n\t"
75                   "movl $1,%eax\n\t"
76                   "lock; xaddl %eax,(%edx)\n\t"
77                   "incl %eax\n\t"
78                   "ret $4");
79 LONG WINAPI interlocked_dec( PLONG dest );
80 __ASM_GLOBAL_FUNC(interlocked_dec,
81                   "movl 4(%esp),%edx\n\t"
82                   "movl $-1,%eax\n\t"
83                   "lock; xaddl %eax,(%edx)\n\t"
84                   "decl %eax\n\t"
85                   "ret $4");
86 # endif  /* __GNUC__ */
87
88 #elif defined(__sparc__) && defined(__sun__)
89 /*
90  * As the earlier Sparc processors lack necessary atomic instructions,
91  * I'm simply falling back to the library-provided _lwp_mutex routines
92  * to ensure mutual exclusion in a way appropriate for the current 
93  * architecture.  
94  *
95  * FIXME:  If we have the compare-and-swap instruction (Sparc v9 and above)
96  *         we could use this to speed up the Interlocked operations ...
97  */
98 #include <synch.h>
99 static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX;
100
101 static PVOID interlocked_cmpxchg( PVOID *dest, PVOID xchg, PVOID compare )
102 {
103     _lwp_mutex_lock( &interlocked_mutex );
104     if ( *dest == compare )
105         *dest = xchg;
106     else
107         compare = *dest;
108     _lwp_mutex_unlock( &interlocked_mutex );
109     return compare;
110 }
111
112 static LONG interlocked_inc( PLONG dest )
113 {
114     LONG retv;
115     _lwp_mutex_lock( &interlocked_mutex );
116     retv = ++*dest;
117     _lwp_mutex_unlock( &interlocked_mutex );
118     return retv;
119 }
120
121 static LONG interlocked_dec( PLONG dest )
122 {
123     LONG retv;
124     _lwp_mutex_lock( &interlocked_mutex );
125     retv = --*dest;
126     _lwp_mutex_unlock( &interlocked_mutex );
127     return retv;
128 }
129 #else
130 # error You must implement the interlocked* functions for your CPU
131 #endif
132
133
134 /***********************************************************************
135  *           get_semaphore
136  */
137 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
138 {
139     HANDLE ret = crit->LockSemaphore;
140     if (!ret)
141     {
142         HANDLE sem;
143         if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
144         if (!(ret = (HANDLE)interlocked_cmpxchg( (PVOID *)&crit->LockSemaphore,
145                                                  (PVOID)sem, 0 )))
146             ret = sem;
147         else
148             NtClose(sem);  /* somebody beat us to it */
149     }
150     return ret;
151 }
152
153 /***********************************************************************
154  *           RtlInitializeCriticalSection   (NTDLL.@)
155  */
156 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
157 {
158     crit->DebugInfo      = NULL;
159     crit->LockCount      = -1;
160     crit->RecursionCount = 0;
161     crit->OwningThread   = 0;
162     crit->LockSemaphore  = 0;
163     return STATUS_SUCCESS;
164 }
165
166 /***********************************************************************
167  *           RtlInitializeCriticalSectionAndSpinCount   (NTDLL.@)
168  * The InitializeCriticalSectionAndSpinCount (KERNEL32) function is
169  * available on NT4SP3 or later, and Win98 or later.
170  * I am assuming that this is the correct definition given the MSDN
171  * docs for the kernel32 functions.
172  */
173 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, DWORD spincount )
174 {
175     if(spincount) TRACE("critsection=%p: spincount=%ld not supported\n", crit, spincount);
176     crit->SpinCount = spincount;
177     return RtlInitializeCriticalSection( crit );
178 }
179
180
181 /***********************************************************************
182  *           RtlDeleteCriticalSection   (NTDLL.@)
183  */
184 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
185 {
186     crit->LockCount      = -1;
187     crit->RecursionCount = 0;
188     crit->OwningThread   = 0;
189     if (crit->LockSemaphore) NtClose( crit->LockSemaphore );
190     crit->LockSemaphore  = 0;
191     return STATUS_SUCCESS;
192 }
193
194
195 /***********************************************************************
196  *           RtlpWaitForCriticalSection   (NTDLL.@)
197  */
198 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
199 {
200     for (;;)
201     {
202         EXCEPTION_RECORD rec;
203         HANDLE sem = get_semaphore( crit );
204
205         DWORD res = WaitForSingleObject( sem, 5000L );
206         if ( res == WAIT_TIMEOUT )
207         {
208             const char *name = (char *)crit->DebugInfo;
209             if (!name || IsBadStringPtrA(name,80)) name = "?";
210             ERR( "section %p %s wait timed out, retrying (60 sec) fs=%04x\n",
211                  crit, debugstr_a(name), __get_fs() );
212             res = WaitForSingleObject( sem, 60000L );
213             if ( res == WAIT_TIMEOUT && TRACE_ON(relay) )
214             {
215                 ERR( "section %p %s wait timed out, retrying (5 min) fs=%04x\n",
216                      crit, debugstr_a(name), __get_fs() );
217                 res = WaitForSingleObject( sem, 300000L );
218             }
219         }
220         if (res == STATUS_WAIT_0) return STATUS_SUCCESS;
221
222         rec.ExceptionCode    = EXCEPTION_CRITICAL_SECTION_WAIT;
223         rec.ExceptionFlags   = 0;
224         rec.ExceptionRecord  = NULL;
225         rec.ExceptionAddress = RtlRaiseException;  /* sic */
226         rec.NumberParameters = 1;
227         rec.ExceptionInformation[0] = (DWORD)crit;
228         RtlRaiseException( &rec );
229     }
230 }
231
232
233 /***********************************************************************
234  *           RtlpUnWaitCriticalSection   (NTDLL.@)
235  */
236 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
237 {
238     HANDLE sem = get_semaphore( crit );
239     NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
240     if (res) RtlRaiseStatus( res );
241     return res;
242 }
243
244
245 /***********************************************************************
246  *           RtlEnterCriticalSection   (NTDLL.@)
247  */
248 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
249 {
250     if (interlocked_inc( &crit->LockCount ))
251     {
252         if (crit->OwningThread == GetCurrentThreadId())
253         {
254             crit->RecursionCount++;
255             return STATUS_SUCCESS;
256         }
257
258         /* Now wait for it */
259         RtlpWaitForCriticalSection( crit );
260     }
261     crit->OwningThread   = GetCurrentThreadId();
262     crit->RecursionCount = 1;
263     return STATUS_SUCCESS;
264 }
265
266
267 /***********************************************************************
268  *           RtlTryEnterCriticalSection   (NTDLL.@)
269  */
270 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
271 {
272     BOOL ret = FALSE;
273     if (interlocked_cmpxchg( (PVOID *)&crit->LockCount, (PVOID)0L, (PVOID)-1L ) == (PVOID)-1L)
274     {
275         crit->OwningThread   = GetCurrentThreadId();
276         crit->RecursionCount = 1;
277         ret = TRUE;
278     }
279     else if (crit->OwningThread == GetCurrentThreadId())
280     {
281         interlocked_inc( &crit->LockCount );
282         crit->RecursionCount++;
283         ret = TRUE;
284     }
285     return ret;
286 }
287
288
289 /***********************************************************************
290  *           RtlLeaveCriticalSection   (NTDLL.@)
291  */
292 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
293 {
294     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
295     else
296     {
297         crit->OwningThread = 0;
298         if (interlocked_dec( &crit->LockCount ) >= 0)
299         {
300             /* someone is waiting */
301             RtlpUnWaitCriticalSection( crit );
302         }
303     }
304     return STATUS_SUCCESS;
305 }