Fix another error under Win9x (hopefully).
[wine] / dlls / kernel / pthread.c
1 /*
2  * pthread emulation for re-entrant libcs
3  *
4  * Copyright 1999 Ove Kåven
5  * Copyright 2003 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #define _GNU_SOURCE /* we may need to override some GNU extensions */
26
27 #include <assert.h>
28 #include <errno.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <setjmp.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include <string.h>
36 #include <sys/types.h>
37 #if HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_SYS_MMAN_H
41 #include <sys/mman.h>
42 #endif
43
44 #include "windef.h"
45 #include "winbase.h"
46 #include "thread.h"
47 #include "winternl.h"
48 #include "wine/pthread.h"
49
50 #define P_OUTPUT(stuff) write(2,stuff,strlen(stuff))
51
52 static const struct wine_pthread_functions functions;
53
54 DECL_GLOBAL_CONSTRUCTOR(pthread_init) { wine_pthread_init_process( &functions ); }
55
56 static inline int init_done(void) { return GetProcessHeap() != 0; }
57
58 /* NOTE: This is a truly extremely incredibly ugly hack!
59  * But it does seem to work... */
60
61 /* assume that pthread_mutex_t has room for at least one pointer,
62  * and hope that the users of pthread_mutex_t considers it opaque
63  * (never checks what's in it)
64  * also: assume that static initializer sets pointer to NULL
65  */
66 typedef struct
67 {
68 #ifdef __GLIBC__
69   int reserved;
70 #endif
71   CRITICAL_SECTION *critsect;
72 } *wine_mutex;
73
74 /* see wine_mutex above for comments */
75 typedef struct {
76   RTL_RWLOCK *lock;
77 } *wine_rwlock;
78
79 struct pthread_thread_init
80 {
81     void* (*start_routine)(void*);
82     void* arg;
83 };
84
85 static DWORD CALLBACK pthread_thread_start(LPVOID data)
86 {
87   struct pthread_thread_init init = *(struct pthread_thread_init*)data;
88   HeapFree(GetProcessHeap(),0,data);
89   return (DWORD)init.start_routine(init.arg);
90 }
91
92 static int wine_pthread_create(pthread_t* thread, const pthread_attr_t* attr, void*
93                                (*start_routine)(void *), void* arg)
94 {
95   HANDLE hThread;
96   struct pthread_thread_init* idata = HeapAlloc(GetProcessHeap(), 0, sizeof(struct pthread_thread_init));
97
98   idata->start_routine = start_routine;
99   idata->arg = arg;
100   hThread = CreateThread( NULL, 0, pthread_thread_start, idata, 0, (LPDWORD)thread);
101
102   if(hThread)
103     CloseHandle(hThread);
104   else
105   {
106     HeapFree(GetProcessHeap(),0,idata); /* free idata struct on failure */
107     return EAGAIN;
108   }
109
110   return 0;
111 }
112
113 static int wine_pthread_cancel(pthread_t thread)
114 {
115   HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)thread);
116
117   if(!TerminateThread(hThread, 0))
118   {
119     CloseHandle(hThread);
120     return EINVAL;      /* return error */
121   }
122
123   CloseHandle(hThread);
124
125   return 0;             /* return success */
126 }
127
128 static int wine_pthread_join(pthread_t thread, void **value_ptr)
129 {
130   HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)thread);
131
132   WaitForSingleObject(hThread, INFINITE);
133   if(!GetExitCodeThread(hThread, (LPDWORD)value_ptr))
134   {
135     CloseHandle(hThread);
136     return EINVAL; /* FIXME: make this more correctly match */
137   }                /* windows errors */
138
139   CloseHandle(hThread);
140   return 0;
141 }
142
143 /*FIXME: not sure what to do with this one... */
144 static int wine_pthread_detach(pthread_t thread)
145 {
146   P_OUTPUT("FIXME:pthread_detach\n");
147   return 0;
148 }
149
150 /***** MUTEXES *****/
151
152 static int wine_pthread_mutex_init(pthread_mutex_t *mutex,
153                                    const pthread_mutexattr_t *mutexattr)
154 {
155   /* glibc has a tendency to initialize mutexes very often, even
156      in situations where they are not really used later on.
157
158      As for us, initializing a mutex is very expensive, we postpone
159      the real initialization until the time the mutex is first used. */
160
161   ((wine_mutex)mutex)->critsect = NULL;
162   return 0;
163 }
164
165 static void mutex_real_init( pthread_mutex_t *mutex )
166 {
167   CRITICAL_SECTION *critsect = HeapAlloc(GetProcessHeap(), 0, sizeof(CRITICAL_SECTION));
168   RtlInitializeCriticalSection(critsect);
169
170   if (InterlockedCompareExchangePointer((void**)&(((wine_mutex)mutex)->critsect),critsect,NULL) != NULL) {
171     /* too late, some other thread already did it */
172     RtlDeleteCriticalSection(critsect);
173     HeapFree(GetProcessHeap(), 0, critsect);
174   }
175 }
176
177 static int wine_pthread_mutex_lock(pthread_mutex_t *mutex)
178 {
179   if (!init_done()) return 0;
180   if (!((wine_mutex)mutex)->critsect)
181     mutex_real_init( mutex );
182
183   RtlEnterCriticalSection(((wine_mutex)mutex)->critsect);
184   return 0;
185 }
186
187 static int wine_pthread_mutex_trylock(pthread_mutex_t *mutex)
188 {
189   if (!init_done()) return 0;
190   if (!((wine_mutex)mutex)->critsect)
191     mutex_real_init( mutex );
192
193   if (!RtlTryEnterCriticalSection(((wine_mutex)mutex)->critsect)) {
194     errno = EBUSY;
195     return -1;
196   }
197   return 0;
198 }
199
200 static int wine_pthread_mutex_unlock(pthread_mutex_t *mutex)
201 {
202   if (!((wine_mutex)mutex)->critsect) return 0;
203   RtlLeaveCriticalSection(((wine_mutex)mutex)->critsect);
204   return 0;
205 }
206
207 static int wine_pthread_mutex_destroy(pthread_mutex_t *mutex)
208 {
209   if (!((wine_mutex)mutex)->critsect) return 0;
210   if (((wine_mutex)mutex)->critsect->RecursionCount) {
211 #if 0 /* there seems to be a bug in libc6 that makes this a bad idea */
212     return EBUSY;
213 #else
214     while (((wine_mutex)mutex)->critsect->RecursionCount)
215       RtlLeaveCriticalSection(((wine_mutex)mutex)->critsect);
216 #endif
217   }
218   RtlDeleteCriticalSection(((wine_mutex)mutex)->critsect);
219   HeapFree(GetProcessHeap(), 0, ((wine_mutex)mutex)->critsect);
220   ((wine_mutex)mutex)->critsect = NULL;
221   return 0;
222 }
223
224 /***** READ-WRITE LOCKS *****/
225
226 static void rwlock_real_init(pthread_rwlock_t *rwlock)
227 {
228   RTL_RWLOCK *lock = HeapAlloc(GetProcessHeap(), 0, sizeof(RTL_RWLOCK));
229   RtlInitializeResource(lock);
230
231   if (InterlockedCompareExchangePointer((void**)&(((wine_rwlock)rwlock)->lock),lock,NULL) != NULL) {
232     /* too late, some other thread already did it */
233     RtlDeleteResource(lock);
234     HeapFree(GetProcessHeap(), 0, lock);
235   }
236 }
237
238 static int wine_pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *rwlock_attr)
239 {
240   ((wine_rwlock)rwlock)->lock = NULL;
241   return 0;
242 }
243
244 static int wine_pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
245 {
246   if (!((wine_rwlock)rwlock)->lock) return 0;
247   RtlDeleteResource(((wine_rwlock)rwlock)->lock);
248   HeapFree(GetProcessHeap(), 0, ((wine_rwlock)rwlock)->lock);
249   return 0;
250 }
251
252 static int wine_pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
253 {
254   if (!init_done()) return 0;
255   if (!((wine_rwlock)rwlock)->lock)
256     rwlock_real_init( rwlock );
257
258   while(TRUE)
259     if (RtlAcquireResourceShared(((wine_rwlock)rwlock)->lock, TRUE))
260       return 0;
261 }
262
263 static int wine_pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
264 {
265   if (!init_done()) return 0;
266   if (!((wine_rwlock)rwlock)->lock)
267     rwlock_real_init( rwlock );
268
269   if (!RtlAcquireResourceShared(((wine_rwlock)rwlock)->lock, FALSE)) {
270     errno = EBUSY;
271     return -1;
272   }
273   return 0;
274 }
275
276 static int wine_pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
277 {
278   if (!init_done()) return 0;
279   if (!((wine_rwlock)rwlock)->lock)
280     rwlock_real_init( rwlock );
281
282   while(TRUE)
283     if (RtlAcquireResourceExclusive(((wine_rwlock)rwlock)->lock, TRUE))
284       return 0;
285 }
286
287 static int wine_pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
288 {
289   if (!init_done()) return 0;
290   if (!((wine_rwlock)rwlock)->lock)
291     rwlock_real_init( rwlock );
292
293   if (!RtlAcquireResourceExclusive(((wine_rwlock)rwlock)->lock, FALSE)) {
294     errno = EBUSY;
295     return -1;
296   }
297   return 0;
298 }
299
300 static int wine_pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
301 {
302   if (!((wine_rwlock)rwlock)->lock) return 0;
303   RtlReleaseResource( ((wine_rwlock)rwlock)->lock );
304   return 0;
305 }
306
307 /***** MISC *****/
308
309 static pthread_t wine_pthread_self(void)
310 {
311   return (pthread_t)GetCurrentThreadId();
312 }
313
314 static int wine_pthread_equal(pthread_t thread1, pthread_t thread2)
315 {
316   return (DWORD)thread1 == (DWORD)thread2;
317 }
318
319 static void wine_pthread_exit(void *retval, char *currentframe)
320 {
321     ExitThread((DWORD)retval);
322 }
323
324 static void *wine_get_thread_data(void)
325 {
326     return NtCurrentTeb()->pthread_data;
327 }
328
329 static void wine_set_thread_data( void *data )
330 {
331     NtCurrentTeb()->pthread_data = data;
332 }
333
334 static const struct wine_pthread_functions functions =
335 {
336     wine_get_thread_data,           /* ptr_get_thread_data */
337     wine_set_thread_data,           /* ptr_set_thread_data */
338     wine_pthread_self,              /* ptr_pthread_self */
339     wine_pthread_equal,             /* ptr_pthread_equal */
340     wine_pthread_create,            /* ptr_pthread_create */
341     wine_pthread_cancel,            /* ptr_pthread_cancel */
342     wine_pthread_join,              /* ptr_pthread_join */
343     wine_pthread_detach,            /* ptr_pthread_detach */
344     wine_pthread_exit,              /* ptr_pthread_exit */
345     wine_pthread_mutex_init,        /* ptr_pthread_mutex_init */
346     wine_pthread_mutex_lock,        /* ptr_pthread_mutex_lock */
347     wine_pthread_mutex_trylock,     /* ptr_pthread_mutex_trylock */
348     wine_pthread_mutex_unlock,      /* ptr_pthread_mutex_unlock */
349     wine_pthread_mutex_destroy,     /* ptr_pthread_mutex_destroy */
350     wine_pthread_rwlock_init,       /* ptr_pthread_rwlock_init */
351     wine_pthread_rwlock_destroy,    /* ptr_pthread_rwlock_destroy */
352     wine_pthread_rwlock_rdlock,     /* ptr_pthread_rwlock_rdlock */
353     wine_pthread_rwlock_tryrdlock,  /* ptr_pthread_rwlock_tryrdlock */
354     wine_pthread_rwlock_wrlock,     /* ptr_pthread_rwlock_wrlock */
355     wine_pthread_rwlock_trywrlock,  /* ptr_pthread_rwlock_trywrlock */
356     wine_pthread_rwlock_unlock      /* ptr_pthread_rwlock_unlock */
357 };