Further code simplifications and interface (consistency) improvements.
[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 /***********************************************************************
23  *           InitializeCriticalSection   (KERNEL32.472) (NTDLL.406)
24  */
25 void WINAPI InitializeCriticalSection( CRITICAL_SECTION *crit )
26 {
27     crit->LockCount      = -1;
28     crit->RecursionCount = 0;
29     crit->OwningThread   = 0;
30     crit->LockSemaphore  = CreateSemaphoreA( NULL, 0, 1, NULL );
31     crit->Reserved       = GetCurrentProcessId();
32 }
33
34
35 /***********************************************************************
36  *           DeleteCriticalSection   (KERNEL32.185) (NTDLL.327)
37  */
38 void WINAPI DeleteCriticalSection( CRITICAL_SECTION *crit )
39 {
40     if (crit->LockSemaphore)
41     {
42         if (crit->RecursionCount)  /* Should not happen */
43             ERR("Deleting owned critical section (%p)\n", crit );
44
45         crit->LockCount      = -1;
46         crit->RecursionCount = 0;
47         crit->OwningThread   = 0;
48         CloseHandle( crit->LockSemaphore );
49         crit->LockSemaphore  = 0;
50         crit->Reserved       = (DWORD)-1;
51     }
52 }
53
54
55 /***********************************************************************
56  *           EnterCriticalSection   (KERNEL32.195) (NTDLL.344)
57  */
58 void WINAPI EnterCriticalSection( CRITICAL_SECTION *crit )
59 {
60     DWORD res;
61
62     if (!crit->LockSemaphore)
63     {
64         FIXME("entering uninitialized section(%p)?\n",crit);
65         InitializeCriticalSection(crit);
66     }
67     if ( crit->Reserved && crit->Reserved != GetCurrentProcessId() )
68     {
69         FIXME("Crst %p belongs to process %ld, current is %ld!\n", 
70               crit, crit->Reserved, GetCurrentProcessId() );
71         return;
72     }
73     if (InterlockedIncrement( &crit->LockCount ))
74     {
75         if (crit->OwningThread == GetCurrentThreadId())
76         {
77             crit->RecursionCount++;
78             return;
79         }
80
81         /* Now wait for it */
82         for (;;)
83         {
84             EXCEPTION_RECORD rec;
85
86             res = WaitForSingleObject( crit->LockSemaphore, 5000L );
87             if ( res == WAIT_TIMEOUT )
88             {
89                 ERR("Critical section %p wait timed out, retrying (60 sec)\n", crit );
90                 res = WaitForSingleObject( crit->LockSemaphore, 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( crit->LockSemaphore, 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         ReleaseSemaphore( crit->LockSemaphore, 1, NULL );
153     }
154 }
155
156
157 /***********************************************************************
158  *           MakeCriticalSectionGlobal   (KERNEL32.515)
159  */
160 void WINAPI MakeCriticalSectionGlobal( CRITICAL_SECTION *crit )
161 {
162     crit->LockSemaphore = ConvertToGlobalHandle( crit->LockSemaphore );
163     crit->Reserved      = 0L;
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     else if ( crit->Reserved && crit->Reserved != GetCurrentProcessId() )
176     {
177         FIXME("(%p) called for %08lx first, %08lx now: making global\n", 
178               crit, crit->Reserved, GetCurrentProcessId() );
179
180         MakeCriticalSectionGlobal( crit );
181     }
182 }
183
184
185 /***********************************************************************
186  *           UninitializeCriticalSection   (KERNEL32.703)
187  */
188 void WINAPI UninitializeCriticalSection( CRITICAL_SECTION *crit )
189 {
190     if ( crit->LockSemaphore )
191     {
192         if ( crit->Reserved )  /* not global */
193             DeleteCriticalSection( crit );
194         else
195             FIXME("(%p) for %08lx: Crst is global, don't know whether to delete\n", 
196                   crit, GetCurrentProcessId() );
197     }
198 }
199
200 #ifdef __i386__
201
202 /* PVOID WINAPI InterlockedCompareExchange( PVOID *dest, PVOID xchg, PVOID compare ); */
203 __ASM_GLOBAL_FUNC(InterlockedCompareExchange,
204                   "movl 12(%esp),%eax\n\t"
205                   "movl 8(%esp),%ecx\n\t"
206                   "movl 4(%esp),%edx\n\t"
207                   "lock; cmpxchgl %ecx,(%edx)\n\t"
208                   "ret $12");
209
210 /* LONG WINAPI InterlockedExchange( PLONG dest, LONG val ); */
211 __ASM_GLOBAL_FUNC(InterlockedExchange,
212                   "movl 8(%esp),%eax\n\t"
213                   "movl 4(%esp),%edx\n\t"
214                   "lock; xchgl %eax,(%edx)\n\t"
215                   "ret $8");
216
217 /* LONG WINAPI InterlockedExchangeAdd( PLONG dest, LONG incr ); */
218 __ASM_GLOBAL_FUNC(InterlockedExchangeAdd,
219                   "movl 8(%esp),%eax\n\t"
220                   "movl 4(%esp),%edx\n\t"
221                   "lock; xaddl %eax,(%edx)\n\t"
222                   "ret $8");
223
224 /* LONG WINAPI InterlockedIncrement( PLONG dest ); */
225 __ASM_GLOBAL_FUNC(InterlockedIncrement,
226                   "movl 4(%esp),%edx\n\t"
227                   "movl $1,%eax\n\t"
228                   "lock; xaddl %eax,(%edx)\n\t"
229                   "incl %eax\n\t"
230                   "ret $4");
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 #else /* __i386__ */
241 #error You must implement the Interlocked* functions for your CPU
242 #endif /* __i386__ */