OpenPrinter should fail if pPrinterName is "".
[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)interlocked_cmpxchg( (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->DebugInfo      = NULL;
145     crit->LockCount      = -1;
146     crit->RecursionCount = 0;
147     crit->OwningThread   = 0;
148     crit->LockSemaphore  = 0;
149     return STATUS_SUCCESS;
150 }
151
152 /***********************************************************************
153  *           RtlInitializeCriticalSectionAndSpinCount   (NTDLL.@)
154  * The InitializeCriticalSectionAndSpinCount (KERNEL32) function is
155  * available on NT4SP3 or later, and Win98 or later.
156  * I am assuming that this is the correct definition given the MSDN
157  * docs for the kernel32 functions.
158  */
159 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, DWORD spincount )
160 {
161     if(spincount) TRACE("critsection=%p: spincount=%ld not supported\n", crit, spincount);
162     crit->SpinCount = spincount;
163     return RtlInitializeCriticalSection( crit );
164 }
165
166
167 /***********************************************************************
168  *           RtlDeleteCriticalSection   (NTDLL.@)
169  */
170 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
171 {
172     crit->LockCount      = -1;
173     crit->RecursionCount = 0;
174     crit->OwningThread   = 0;
175     if (crit->LockSemaphore) NtClose( crit->LockSemaphore );
176     crit->LockSemaphore  = 0;
177     return STATUS_SUCCESS;
178 }
179
180
181 /***********************************************************************
182  *           RtlpWaitForCriticalSection   (NTDLL.@)
183  */
184 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
185 {
186     for (;;)
187     {
188         EXCEPTION_RECORD rec;
189         HANDLE sem = get_semaphore( crit );
190
191         DWORD res = WaitForSingleObject( sem, 5000L );
192         if ( res == WAIT_TIMEOUT )
193         {
194             const char *name = (char *)crit->DebugInfo;
195             if (!name || IsBadStringPtrA(name,80)) name = "?";
196             ERR( "section %p %s wait timed out, retrying (60 sec) fs=%04x\n",
197                  crit, debugstr_a(name), __get_fs() );
198             res = WaitForSingleObject( sem, 60000L );
199             if ( res == WAIT_TIMEOUT && TRACE_ON(relay) )
200             {
201                 ERR( "section %p %s wait timed out, retrying (5 min) fs=%04x\n",
202                      crit, debugstr_a(name), __get_fs() );
203                 res = WaitForSingleObject( sem, 300000L );
204             }
205         }
206         if (res == STATUS_WAIT_0) return STATUS_SUCCESS;
207
208         rec.ExceptionCode    = EXCEPTION_CRITICAL_SECTION_WAIT;
209         rec.ExceptionFlags   = 0;
210         rec.ExceptionRecord  = NULL;
211         rec.ExceptionAddress = RtlRaiseException;  /* sic */
212         rec.NumberParameters = 1;
213         rec.ExceptionInformation[0] = (DWORD)crit;
214         RtlRaiseException( &rec );
215     }
216 }
217
218
219 /***********************************************************************
220  *           RtlpUnWaitCriticalSection   (NTDLL.@)
221  */
222 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
223 {
224     HANDLE sem = get_semaphore( crit );
225     NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
226     if (res) RtlRaiseStatus( res );
227     return res;
228 }
229
230
231 /***********************************************************************
232  *           RtlEnterCriticalSection   (NTDLL.@)
233  */
234 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
235 {
236     if (interlocked_inc( &crit->LockCount ))
237     {
238         if (crit->OwningThread == GetCurrentThreadId())
239         {
240             crit->RecursionCount++;
241             return STATUS_SUCCESS;
242         }
243
244         /* Now wait for it */
245         RtlpWaitForCriticalSection( crit );
246     }
247     crit->OwningThread   = GetCurrentThreadId();
248     crit->RecursionCount = 1;
249     return STATUS_SUCCESS;
250 }
251
252
253 /***********************************************************************
254  *           RtlTryEnterCriticalSection   (NTDLL.@)
255  */
256 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
257 {
258     BOOL ret = FALSE;
259     if (interlocked_cmpxchg( (PVOID *)&crit->LockCount, (PVOID)0L, (PVOID)-1L ) == (PVOID)-1L)
260     {
261         crit->OwningThread   = GetCurrentThreadId();
262         crit->RecursionCount = 1;
263         ret = TRUE;
264     }
265     else if (crit->OwningThread == GetCurrentThreadId())
266     {
267         interlocked_inc( &crit->LockCount );
268         crit->RecursionCount++;
269         ret = TRUE;
270     }
271     return ret;
272 }
273
274
275 /***********************************************************************
276  *           RtlLeaveCriticalSection   (NTDLL.@)
277  */
278 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
279 {
280     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
281     else
282     {
283         crit->OwningThread = 0;
284         if (interlocked_dec( &crit->LockCount ) >= 0)
285         {
286             /* someone is waiting */
287             RtlpUnWaitCriticalSection( crit );
288         }
289     }
290     return STATUS_SUCCESS;
291 }