Moved implementation of GetPrinterDriverDirectory from ascii to unicode.
[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 <errno.h>
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include "winerror.h"
29 #include "winternl.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
33 WINE_DECLARE_DEBUG_CHANNEL(relay);
34
35 inline static LONG interlocked_inc( PLONG dest )
36 {
37     return interlocked_xchg_add( dest, 1 ) + 1;
38 }
39
40 inline static LONG interlocked_dec( PLONG dest )
41 {
42     return interlocked_xchg_add( dest, -1 ) - 1;
43 }
44
45 /***********************************************************************
46  *           get_semaphore
47  */
48 static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
49 {
50     HANDLE ret = crit->LockSemaphore;
51     if (!ret)
52     {
53         HANDLE sem;
54         if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
55         if (!(ret = (HANDLE)interlocked_cmpxchg_ptr( (PVOID *)&crit->LockSemaphore,
56                                                      (PVOID)sem, 0 )))
57             ret = sem;
58         else
59             NtClose(sem);  /* somebody beat us to it */
60     }
61     return ret;
62 }
63
64 /***********************************************************************
65  *           RtlInitializeCriticalSection   (NTDLL.@)
66  */
67 NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
68 {
69     crit->DebugInfo      = NULL;
70     crit->LockCount      = -1;
71     crit->RecursionCount = 0;
72     crit->OwningThread   = 0;
73     crit->LockSemaphore  = 0;
74     return STATUS_SUCCESS;
75 }
76
77 /***********************************************************************
78  *           RtlInitializeCriticalSectionAndSpinCount   (NTDLL.@)
79  * The InitializeCriticalSectionAndSpinCount (KERNEL32) function is
80  * available on NT4SP3 or later, and Win98 or later.
81  * I am assuming that this is the correct definition given the MSDN
82  * docs for the kernel32 functions.
83  */
84 NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, DWORD spincount )
85 {
86     if(spincount) TRACE("critsection=%p: spincount=%ld not supported\n", crit, spincount);
87     crit->SpinCount = spincount;
88     return RtlInitializeCriticalSection( crit );
89 }
90
91
92 /***********************************************************************
93  *           RtlDeleteCriticalSection   (NTDLL.@)
94  */
95 NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
96 {
97     crit->LockCount      = -1;
98     crit->RecursionCount = 0;
99     crit->OwningThread   = 0;
100     if (crit->LockSemaphore) NtClose( crit->LockSemaphore );
101     crit->LockSemaphore  = 0;
102     return STATUS_SUCCESS;
103 }
104
105
106 /***********************************************************************
107  *           RtlpWaitForCriticalSection   (NTDLL.@)
108  */
109 NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
110 {
111     for (;;)
112     {
113         EXCEPTION_RECORD rec;
114         HANDLE sem = get_semaphore( crit );
115
116         DWORD res = WaitForSingleObject( sem, 5000L );
117         if ( res == WAIT_TIMEOUT )
118         {
119             const char *name = (char *)crit->DebugInfo;
120             if (!name) name = "?";
121             ERR( "section %p %s wait timed out, retrying (60 sec) tid=%08lx\n",
122                  crit, debugstr_a(name), GetCurrentThreadId() );
123             res = WaitForSingleObject( sem, 60000L );
124             if ( res == WAIT_TIMEOUT && TRACE_ON(relay) )
125             {
126                 ERR( "section %p %s wait timed out, retrying (5 min) tid=%08lx\n",
127                      crit, debugstr_a(name), GetCurrentThreadId() );
128                 res = WaitForSingleObject( sem, 300000L );
129             }
130         }
131         if (res == STATUS_WAIT_0) return STATUS_SUCCESS;
132
133         /* Throw exception only for Wine internal locks */
134         if (!crit->DebugInfo) continue;
135
136         rec.ExceptionCode    = EXCEPTION_CRITICAL_SECTION_WAIT;
137         rec.ExceptionFlags   = 0;
138         rec.ExceptionRecord  = NULL;
139         rec.ExceptionAddress = RtlRaiseException;  /* sic */
140         rec.NumberParameters = 1;
141         rec.ExceptionInformation[0] = (DWORD)crit;
142         RtlRaiseException( &rec );
143     }
144 }
145
146
147 /***********************************************************************
148  *           RtlpUnWaitCriticalSection   (NTDLL.@)
149  */
150 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
151 {
152     HANDLE sem = get_semaphore( crit );
153     NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
154     if (res) RtlRaiseStatus( res );
155     return res;
156 }
157
158
159 /***********************************************************************
160  *           RtlEnterCriticalSection   (NTDLL.@)
161  */
162 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
163 {
164     if (interlocked_inc( &crit->LockCount ))
165     {
166         if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
167         {
168             crit->RecursionCount++;
169             return STATUS_SUCCESS;
170         }
171
172         /* Now wait for it */
173         RtlpWaitForCriticalSection( crit );
174     }
175     crit->OwningThread   = (HANDLE)GetCurrentThreadId();
176     crit->RecursionCount = 1;
177     return STATUS_SUCCESS;
178 }
179
180
181 /***********************************************************************
182  *           RtlTryEnterCriticalSection   (NTDLL.@)
183  */
184 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
185 {
186     BOOL ret = FALSE;
187     if (interlocked_cmpxchg( &crit->LockCount, 0L, -1 ) == -1)
188     {
189         crit->OwningThread   = (HANDLE)GetCurrentThreadId();
190         crit->RecursionCount = 1;
191         ret = TRUE;
192     }
193     else if (crit->OwningThread == (HANDLE)GetCurrentThreadId())
194     {
195         interlocked_inc( &crit->LockCount );
196         crit->RecursionCount++;
197         ret = TRUE;
198     }
199     return ret;
200 }
201
202
203 /***********************************************************************
204  *           RtlLeaveCriticalSection   (NTDLL.@)
205  */
206 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
207 {
208     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
209     else
210     {
211         crit->OwningThread = 0;
212         if (interlocked_dec( &crit->LockCount ) >= 0)
213         {
214             /* someone is waiting */
215             RtlpUnWaitCriticalSection( crit );
216         }
217     }
218     return STATUS_SUCCESS;
219 }