Implement A->W call for GetNamedSecurityInfo.
[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 "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include "winerror.h"
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winreg.h"
32 #include "winternl.h"
33 #include "wine/debug.h"
34 #include "ntdll_misc.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
37 WINE_DECLARE_DEBUG_CHANNEL(relay);
38
39 inline static LONG interlocked_inc( PLONG dest )
40 {
41     return interlocked_xchg_add( dest, 1 ) + 1;
42 }
43
44 inline static LONG interlocked_dec( PLONG dest )
45 {
46     return interlocked_xchg_add( dest, -1 ) - 1;
47 }
48
49 inline static void small_pause(void)
50 {
51 #ifdef __i386__
52     __asm__ __volatile__( "rep;nop" : : : "memory" );
53 #else
54     __asm__ __volatile__( "" : : : "memory" );
55 #endif
56 }
57
58 /***********************************************************************
59  *           get_semaphore
60  */
61 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
62 {
63     HANDLE ret = crit->LockSemaphore;
64     if (!ret)
65     {
66         HANDLE sem;
67         if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
68         if (!(ret = (HANDLE)interlocked_cmpxchg_ptr( (PVOID *)&crit->LockSemaphore,
69                                                      (PVOID)sem, 0 )))
70             ret = sem;
71         else
72             NtClose(sem);  /* somebody beat us to it */
73     }
74     return ret;
75 }
76
77 /***********************************************************************
78  *           RtlInitializeCriticalSection   (NTDLL.@)
79  *
80  * Initialise a new RTL_CRITICAL_SECTION.
81  *
82  * PARAMS
83  *  crit [O] Critical section to initialise
84  *
85  * RETURN
86  *  STATUS_SUCCESS.
87  */
88 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
89 {
90     return RtlInitializeCriticalSectionAndSpinCount( crit, 0 );
91 }
92
93 /***********************************************************************
94  *           RtlInitializeCriticalSectionAndSpinCount   (NTDLL.@)
95  *
96  * Initialise a new RTL_CRITICAL_SECTION with a given spin count.
97  *
98  * PARAMS
99  *   crit      [O] Critical section to initialise
100  *   spincount [I] Spin count for crit
101  * 
102  * RETURNS
103  *  STATUS_SUCCESS.
104  *
105  * NOTES
106  * The InitializeCriticalSectionAndSpinCount() (KERNEL32) function is
107  * available on NT4SP3 or later, and Win98 or later.
108  */
109 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
110 {
111     if (!GetProcessHeap()) crit->DebugInfo = NULL;
112     else
113     {
114         crit->DebugInfo = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(CRITICAL_SECTION_DEBUG));
115         if (crit->DebugInfo)
116         {
117             crit->DebugInfo->Type = 0;
118             crit->DebugInfo->CreatorBackTraceIndex = 0;
119             crit->DebugInfo->CriticalSection = crit;
120             crit->DebugInfo->ProcessLocksList.Blink = &(crit->DebugInfo->ProcessLocksList);
121             crit->DebugInfo->ProcessLocksList.Flink = &(crit->DebugInfo->ProcessLocksList);
122             crit->DebugInfo->EntryCount = 0;
123             crit->DebugInfo->ContentionCount = 0;
124             crit->DebugInfo->Spare[0] = 0;
125             crit->DebugInfo->Spare[1] = 0;
126         }
127     }
128     crit->LockCount      = -1;
129     crit->RecursionCount = 0;
130     crit->OwningThread   = 0;
131     crit->LockSemaphore  = 0;
132     crit->SpinCount      = spincount;
133     return STATUS_SUCCESS;
134 }
135
136 /***********************************************************************
137  *           RtlSetCriticalSectionSpinCount   (NTDLL.@)
138  *
139  * Sets the spin count of a critical section.
140  *
141  * PARAMS
142  *   crit      [O] Critical section
143  *   spincount [I] Spin count for crit
144  *
145  * RETURNS
146  *  The previous spin count.
147  *
148  * NOTES
149  *  If the system is not SMP, spincount is ignored and set to 0.
150  */
151 ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
152 {
153     ULONG oldspincount = crit->SpinCount;
154     if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
155     crit->SpinCount = spincount;
156     return oldspincount;
157 }
158
159 /***********************************************************************
160  *           RtlDeleteCriticalSection   (NTDLL.@)
161  *
162  * Free the resources used by an RTL_CRITICAL_SECTION.
163  *
164  * PARAMS
165  *  crit [I/O] Critical section to free
166  *
167  * RETURNS
168  *  STATUS_SUCCESS.
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     if (crit->DebugInfo)
178     {
179         /* only free the ones we made in here */
180         if (!crit->DebugInfo->Spare[1])
181         {
182             RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
183             crit->DebugInfo = NULL;
184         }
185     }
186     return STATUS_SUCCESS;
187 }
188
189
190 /***********************************************************************
191  *           RtlpWaitForCriticalSection   (NTDLL.@)
192  *
193  * Wait for an RTL_CRITICAL_SECTION to become free.
194  * 
195  * PARAMS
196  *  crit [I/O] Critical section to wait for
197  *
198  * RETURNS
199  *  STATUS_SUCCESS.
200  */
201 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
202 {
203     for (;;)
204     {
205         EXCEPTION_RECORD rec;
206         HANDLE sem = get_semaphore( crit );
207         LARGE_INTEGER time;
208         DWORD status;
209
210         time.QuadPart = -5000 * 10000;  /* 5 seconds */
211         status = NtWaitForSingleObject( sem, FALSE, &time );
212         if ( status == WAIT_TIMEOUT )
213         {
214             const char *name = NULL;
215             if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[1];
216             if (!name) name = "?";
217             ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (60 sec)\n",
218                  crit, debugstr_a(name), GetCurrentThreadId(), (DWORD)crit->OwningThread );
219             time.QuadPart = -60000 * 10000;
220             status = NtWaitForSingleObject( sem, FALSE, &time );
221             if ( status == WAIT_TIMEOUT && TRACE_ON(relay) )
222             {
223                 ERR( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (5 min)\n",
224                      crit, debugstr_a(name), GetCurrentThreadId(), (DWORD) crit->OwningThread );
225                 time.QuadPart = -300000 * (ULONGLONG)10000;
226                 status = NtWaitForSingleObject( sem, FALSE, &time );
227             }
228         }
229         if (status == STATUS_WAIT_0) return STATUS_SUCCESS;
230
231         /* Throw exception only for Wine internal locks */
232         if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[1])) continue;
233
234         rec.ExceptionCode    = STATUS_POSSIBLE_DEADLOCK;
235         rec.ExceptionFlags   = 0;
236         rec.ExceptionRecord  = NULL;
237         rec.ExceptionAddress = RtlRaiseException;  /* sic */
238         rec.NumberParameters = 1;
239         rec.ExceptionInformation[0] = (DWORD)crit;
240         RtlRaiseException( &rec );
241     }
242 }
243
244
245 /***********************************************************************
246  *           RtlpUnWaitCriticalSection   (NTDLL.@)
247  */
248 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
249 {
250     HANDLE sem = get_semaphore( crit );
251     NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
252     if (res) RtlRaiseStatus( res );
253     return res;
254 }
255
256
257 /***********************************************************************
258  *           RtlEnterCriticalSection   (NTDLL.@)
259  *
260  * Enter an RTL_CRITICAL_SECTION.
261  *
262  * PARAMS
263  *  crit [I/O] Critical section to enter
264  *
265  * RETURNS
266  *  STATUS_SUCCESS. The critical section is held by the caller.
267  *  
268  * NOTES
269  *  The caller will wait until the critical section is availale.
270  */
271 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
272 {
273     if (crit->SpinCount)
274     {
275         ULONG count;
276
277         if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
278         for (count = crit->SpinCount; count > 0; count--)
279         {
280             if (crit->LockCount > 0) break;  /* more than one waiter, don't bother spinning */
281             if (crit->LockCount == -1)       /* try again */
282             {
283                 if (interlocked_cmpxchg( &crit->LockCount, 0, -1 ) == -1) goto done;
284             }
285             small_pause();
286         }
287     }
288
289     if (interlocked_inc( &crit->LockCount ))
290     {
291         if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
292         {
293             crit->RecursionCount++;
294             return STATUS_SUCCESS;
295         }
296
297         /* Now wait for it */
298         RtlpWaitForCriticalSection( crit );
299     }
300 done:
301     crit->OwningThread   = (HANDLE)GetCurrentThreadId();
302     crit->RecursionCount = 1;
303     return STATUS_SUCCESS;
304 }
305
306
307 /***********************************************************************
308  *           RtlTryEnterCriticalSection   (NTDLL.@)
309  *
310  * Enter an RTL_CRITICAL_SECTION without waiting.
311  *
312  * PARAMS
313  *  crit [I/O] Critical section to enter
314  *
315  * RETURNS
316  *  Success: TRUE. The critical section is held by the caller.
317  *  Failure: FALSE. The critical section is currently held by another thread.
318  */
319 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
320 {
321     BOOL ret = FALSE;
322     if (interlocked_cmpxchg( &crit->LockCount, 0L, -1 ) == -1)
323     {
324         crit->OwningThread   = (HANDLE)GetCurrentThreadId();
325         crit->RecursionCount = 1;
326         ret = TRUE;
327     }
328     else if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
329     {
330         interlocked_inc( &crit->LockCount );
331         crit->RecursionCount++;
332         ret = TRUE;
333     }
334     return ret;
335 }
336
337
338 /***********************************************************************
339  *           RtlLeaveCriticalSection   (NTDLL.@)
340  *
341  * Leave an RTL_CRITICAL_SECTION.
342  *
343  * PARAMS
344  *  crit [I/O] Critical section to enter
345  *
346  * RETURNS
347  *  STATUS_SUCCESS.
348  */
349 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
350 {
351     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
352     else
353     {
354         crit->OwningThread = 0;
355         if (interlocked_dec( &crit->LockCount ) >= 0)
356         {
357             /* someone is waiting */
358             RtlpUnWaitCriticalSection( crit );
359         }
360     }
361     return STATUS_SUCCESS;
362 }