Added regedit unit test, a couple minor changes to regedit.
[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 "ntddk.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 || IsBadStringPtrA(name,80)) 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         rec.ExceptionCode    = EXCEPTION_CRITICAL_SECTION_WAIT;
134         rec.ExceptionFlags   = 0;
135         rec.ExceptionRecord  = NULL;
136         rec.ExceptionAddress = RtlRaiseException;  /* sic */
137         rec.NumberParameters = 1;
138         rec.ExceptionInformation[0] = (DWORD)crit;
139         RtlRaiseException( &rec );
140     }
141 }
142
143
144 /***********************************************************************
145  *           RtlpUnWaitCriticalSection   (NTDLL.@)
146  */
147 NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
148 {
149     HANDLE sem = get_semaphore( crit );
150     NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL );
151     if (res) RtlRaiseStatus( res );
152     return res;
153 }
154
155
156 /***********************************************************************
157  *           RtlEnterCriticalSection   (NTDLL.@)
158  */
159 NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
160 {
161     if (interlocked_inc( &crit->LockCount ))
162     {
163         if (crit->OwningThread == GetCurrentThreadId())
164         {
165             crit->RecursionCount++;
166             return STATUS_SUCCESS;
167         }
168
169         /* Now wait for it */
170         RtlpWaitForCriticalSection( crit );
171     }
172     crit->OwningThread   = GetCurrentThreadId();
173     crit->RecursionCount = 1;
174     return STATUS_SUCCESS;
175 }
176
177
178 /***********************************************************************
179  *           RtlTryEnterCriticalSection   (NTDLL.@)
180  */
181 BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
182 {
183     BOOL ret = FALSE;
184     if (interlocked_cmpxchg( &crit->LockCount, 0L, -1 ) == -1)
185     {
186         crit->OwningThread   = GetCurrentThreadId();
187         crit->RecursionCount = 1;
188         ret = TRUE;
189     }
190     else if (crit->OwningThread == GetCurrentThreadId())
191     {
192         interlocked_inc( &crit->LockCount );
193         crit->RecursionCount++;
194         ret = TRUE;
195     }
196     return ret;
197 }
198
199
200 /***********************************************************************
201  *           RtlLeaveCriticalSection   (NTDLL.@)
202  */
203 NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
204 {
205     if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
206     else
207     {
208         crit->OwningThread = 0;
209         if (interlocked_dec( &crit->LockCount ) >= 0)
210         {
211             /* someone is waiting */
212             RtlpUnWaitCriticalSection( crit );
213         }
214     }
215     return STATUS_SUCCESS;
216 }