OSDN Git Service

10cfef9d9eb073eaffff9febff9411cf07a6e20d
[pf3gnuchains/gcc-fork.git] / boehm-gc / win32_threads.c
1 #if defined(GC_WIN32_THREADS) 
2
3 #include "private/gc_priv.h"
4 #include <windows.h>
5
6 #ifdef CYGWIN32
7 # include <errno.h>
8
9  /* Cygwin-specific forward decls */
10 # undef pthread_create 
11 # undef pthread_sigmask 
12 # undef pthread_join 
13 # undef dlopen 
14
15 # define DEBUG_CYGWIN_THREADS 0
16
17   GC_bool GC_thr_initialized = FALSE;
18   void * GC_start_routine(void * arg);
19   void GC_thread_exit_proc(void *arg);
20
21 #endif
22
23 #ifndef MAX_THREADS
24 # define MAX_THREADS 64
25 #endif
26
27 struct thread_entry {
28   LONG in_use;
29   DWORD id;
30   HANDLE handle;
31   void *stack;          /* The cold end of the stack.   */
32                         /* 0 ==> entry not valid.       */
33                         /* !in_use ==> stack == 0       */
34   CONTEXT context;
35   GC_bool suspended;
36
37 # ifdef CYGWIN32
38     void *status; /* hold exit value until join in case it's a pointer */
39     pthread_t pthread_id;
40 # endif
41
42 };
43
44 volatile GC_bool GC_please_stop = FALSE;
45
46 volatile struct thread_entry thread_table[MAX_THREADS];
47
48 void GC_push_thread_structures GC_PROTO((void))
49 {
50     /* Unlike the other threads implementations, the thread table here  */
51     /* contains no pointers to the collectable heap.  Thus we have      */
52     /* no private structures we need to preserve.                       */
53 # ifdef CYGWIN32
54   { int i; /* pthreads may keep a pointer in the thread exit value */
55     for (i = 0; i < MAX_THREADS; i++)
56       if (thread_table[i].in_use) GC_push_all((ptr_t)&(thread_table[i].status),(ptr_t)(&(thread_table[i].status)+1));
57   }
58 # endif
59 }
60
61 void GC_stop_world()
62 {
63   DWORD thread_id = GetCurrentThreadId();
64   int i;
65
66 #ifdef CYGWIN32
67   if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()");
68 #endif
69
70   GC_please_stop = TRUE;
71   for (i = 0; i < MAX_THREADS; i++)
72     if (thread_table[i].stack != 0
73         && thread_table[i].id != thread_id) {
74 #     ifdef MSWINCE
75         /* SuspendThread will fail if thread is running kernel code */
76         while (SuspendThread(thread_table[i].handle) == (DWORD)-1)
77           Sleep(10);
78 #     else
79         /* Apparently the Windows 95 GetOpenFileName call creates       */
80         /* a thread that does not properly get cleaned up, and          */
81         /* SuspendThread on its descriptor may provoke a crash.         */
82         /* This reduces the probability of that event, though it still  */
83         /* appears there's a race here.                                 */
84         DWORD exitCode; 
85         if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
86             exitCode != STILL_ACTIVE) {
87           thread_table[i].stack = 0; /* prevent stack from being pushed */
88 #         ifndef CYGWIN32
89             /* this breaks pthread_join on Cygwin, which is guaranteed to  */
90             /* only see user pthreads                                      */
91             thread_table[i].in_use = FALSE;
92             CloseHandle(thread_table[i].handle);
93             BZERO((void *)(&thread_table[i].context), sizeof(CONTEXT));
94 #         endif
95           continue;
96         }
97         if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
98           ABORT("SuspendThread failed");
99 #     endif
100       thread_table[i].suspended = TRUE;
101     }
102 }
103
104 void GC_start_world()
105 {
106   DWORD thread_id = GetCurrentThreadId();
107   int i;
108   for (i = 0; i < MAX_THREADS; i++)
109     if (thread_table[i].stack != 0 && thread_table[i].suspended
110         && thread_table[i].id != thread_id) {
111       if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
112         ABORT("ResumeThread failed");
113       thread_table[i].suspended = FALSE;
114     }
115   GC_please_stop = FALSE;
116 }
117
118 # ifdef _MSC_VER
119 #   pragma warning(disable:4715)
120 # endif
121 ptr_t GC_current_stackbottom()
122 {
123   DWORD thread_id = GetCurrentThreadId();
124   int i;
125   for (i = 0; i < MAX_THREADS; i++)
126     if (thread_table[i].stack && thread_table[i].id == thread_id)
127       return thread_table[i].stack;
128   ABORT("no thread table entry for current thread");
129 }
130 # ifdef _MSC_VER
131 #   pragma warning(default:4715)
132 # endif
133
134 # ifdef MSWINCE
135     /* The VirtualQuery calls below won't work properly on WinCE, but   */
136     /* since each stack is restricted to an aligned 64K region of       */
137     /* virtual memory we can just take the next lowest multiple of 64K. */
138 #   define GC_get_lo_stack_addr(s) \
139         ((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000))
140 # else
141     static ptr_t GC_get_lo_stack_addr(ptr_t s)
142     {
143         ptr_t bottom;
144         MEMORY_BASIC_INFORMATION info;
145         VirtualQuery(s, &info, sizeof(info));
146         do {
147             bottom = info.BaseAddress;
148             VirtualQuery(bottom - 1, &info, sizeof(info));
149         } while ((info.Protect & PAGE_READWRITE)
150                  && !(info.Protect & PAGE_GUARD));
151         return(bottom);
152     }
153 # endif
154
155 void GC_push_all_stacks()
156 {
157   DWORD thread_id = GetCurrentThreadId();
158   int i;
159   for (i = 0; i < MAX_THREADS; i++)
160     if (thread_table[i].stack) {
161       ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack);
162       if (thread_table[i].id == thread_id)
163         GC_push_all_stack((ptr_t)&i, thread_table[i].stack);
164       else {
165         thread_table[i].context.ContextFlags
166                         = (CONTEXT_INTEGER|CONTEXT_CONTROL);
167         if (!GetThreadContext(thread_table[i].handle,
168                                 /* cast away volatile qualifier */
169                                 (LPCONTEXT)&thread_table[i].context))
170           ABORT("GetThreadContext failed");
171 #       ifdef I386
172           GC_push_one ((word) thread_table[i].context.Edi);
173           GC_push_one ((word) thread_table[i].context.Esi);
174           GC_push_one ((word) thread_table[i].context.Ebp);
175           GC_push_one ((word) thread_table[i].context.Ebx);
176           GC_push_one ((word) thread_table[i].context.Edx);
177           GC_push_one ((word) thread_table[i].context.Ecx);
178           GC_push_one ((word) thread_table[i].context.Eax);
179           if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack
180               || thread_table[i].context.Esp < (DWORD)bottom) {
181               WARN("Thread stack pointer 0x%lx out of range, pushing everything",
182                    thread_table[i].context.Esp);
183               GC_push_all_stack((char *) bottom, thread_table[i].stack);
184           } else {
185               GC_push_all_stack((char *) thread_table[i].context.Esp,
186                                 thread_table[i].stack);
187           }
188 #       else
189 #       ifdef ARM32
190           if (thread_table[i].context.Sp >= (DWORD)thread_table[i].stack
191               || thread_table[i].context.Sp < (DWORD)bottom)
192               ABORT("Thread stack pointer out of range");
193           GC_push_one ((word) thread_table[i].context.R0);
194           GC_push_one ((word) thread_table[i].context.R1);
195           GC_push_one ((word) thread_table[i].context.R2);
196           GC_push_one ((word) thread_table[i].context.R3);
197           GC_push_one ((word) thread_table[i].context.R4);
198           GC_push_one ((word) thread_table[i].context.R5);
199           GC_push_one ((word) thread_table[i].context.R6);
200           GC_push_one ((word) thread_table[i].context.R7);
201           GC_push_one ((word) thread_table[i].context.R8);
202           GC_push_one ((word) thread_table[i].context.R9);
203           GC_push_one ((word) thread_table[i].context.R10);
204           GC_push_one ((word) thread_table[i].context.R11);
205           GC_push_one ((word) thread_table[i].context.R12);
206           GC_push_all_stack((char *) thread_table[i].context.Sp,
207                             thread_table[i].stack);
208 #       else
209 #       ifdef SHx
210           if (thread_table[i].context.R15 >= (DWORD)thread_table[i].stack
211               || thread_table[i].context.R15 < (DWORD)bottom)
212               ABORT("Thread stack pointer out of range");
213           GC_push_one ((word) thread_table[i].context.R0);
214           GC_push_one ((word) thread_table[i].context.R1);
215           GC_push_one ((word) thread_table[i].context.R2);
216           GC_push_one ((word) thread_table[i].context.R3);
217           GC_push_one ((word) thread_table[i].context.R4);
218           GC_push_one ((word) thread_table[i].context.R5);
219           GC_push_one ((word) thread_table[i].context.R6);
220           GC_push_one ((word) thread_table[i].context.R7);
221           GC_push_one ((word) thread_table[i].context.R8);
222           GC_push_one ((word) thread_table[i].context.R9);
223           GC_push_one ((word) thread_table[i].context.R10);
224           GC_push_one ((word) thread_table[i].context.R11);
225           GC_push_one ((word) thread_table[i].context.R12);
226           GC_push_one ((word) thread_table[i].context.R13);
227           GC_push_one ((word) thread_table[i].context.R14);
228           GC_push_all_stack((char *) thread_table[i].context.R15,
229                             thread_table[i].stack);
230 #       else
231 #       ifdef MIPS
232           if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
233               || thread_table[i].context.IntSp < (DWORD)bottom)
234               ABORT("Thread stack pointer out of range");
235           GC_push_one ((word) thread_table[i].context.IntAt);
236           GC_push_one ((word) thread_table[i].context.IntV0);
237           GC_push_one ((word) thread_table[i].context.IntV1);
238           GC_push_one ((word) thread_table[i].context.IntA0);
239           GC_push_one ((word) thread_table[i].context.IntA1);
240           GC_push_one ((word) thread_table[i].context.IntA2);
241           GC_push_one ((word) thread_table[i].context.IntA3);
242           GC_push_one ((word) thread_table[i].context.IntT0);
243           GC_push_one ((word) thread_table[i].context.IntT1);
244           GC_push_one ((word) thread_table[i].context.IntT2);
245           GC_push_one ((word) thread_table[i].context.IntT3);
246           GC_push_one ((word) thread_table[i].context.IntT4);
247           GC_push_one ((word) thread_table[i].context.IntT5);
248           GC_push_one ((word) thread_table[i].context.IntT6);
249           GC_push_one ((word) thread_table[i].context.IntT7);
250           GC_push_one ((word) thread_table[i].context.IntS0);
251           GC_push_one ((word) thread_table[i].context.IntS1);
252           GC_push_one ((word) thread_table[i].context.IntS2);
253           GC_push_one ((word) thread_table[i].context.IntS3);
254           GC_push_one ((word) thread_table[i].context.IntS4);
255           GC_push_one ((word) thread_table[i].context.IntS5);
256           GC_push_one ((word) thread_table[i].context.IntS6);
257           GC_push_one ((word) thread_table[i].context.IntS7);
258           GC_push_one ((word) thread_table[i].context.IntT8);
259           GC_push_one ((word) thread_table[i].context.IntT9);
260           GC_push_one ((word) thread_table[i].context.IntK0);
261           GC_push_one ((word) thread_table[i].context.IntK1);
262           GC_push_one ((word) thread_table[i].context.IntS8);
263           GC_push_all_stack((char *) thread_table[i].context.IntSp,
264                             thread_table[i].stack);
265 #       else
266 #       ifdef PPC
267           if (thread_table[i].context.Gpr1 >= (DWORD)thread_table[i].stack
268               || thread_table[i].context.Gpr1 < (DWORD)bottom)
269               ABORT("Thread stack pointer out of range");
270           GC_push_one ((word) thread_table[i].context.Gpr0);
271           /* Gpr1 is stack pointer */
272           /* Gpr2 is global pointer */
273           GC_push_one ((word) thread_table[i].context.Gpr3);
274           GC_push_one ((word) thread_table[i].context.Gpr4);
275           GC_push_one ((word) thread_table[i].context.Gpr5);
276           GC_push_one ((word) thread_table[i].context.Gpr6);
277           GC_push_one ((word) thread_table[i].context.Gpr7);
278           GC_push_one ((word) thread_table[i].context.Gpr8);
279           GC_push_one ((word) thread_table[i].context.Gpr9);
280           GC_push_one ((word) thread_table[i].context.Gpr10);
281           GC_push_one ((word) thread_table[i].context.Gpr11);
282           GC_push_one ((word) thread_table[i].context.Gpr12);
283           /* Gpr13 is reserved for the kernel */
284           GC_push_one ((word) thread_table[i].context.Gpr14);
285           GC_push_one ((word) thread_table[i].context.Gpr15);
286           GC_push_one ((word) thread_table[i].context.Gpr16);
287           GC_push_one ((word) thread_table[i].context.Gpr17);
288           GC_push_one ((word) thread_table[i].context.Gpr18);
289           GC_push_one ((word) thread_table[i].context.Gpr19);
290           GC_push_one ((word) thread_table[i].context.Gpr20);
291           GC_push_one ((word) thread_table[i].context.Gpr21);
292           GC_push_one ((word) thread_table[i].context.Gpr22);
293           GC_push_one ((word) thread_table[i].context.Gpr23);
294           GC_push_one ((word) thread_table[i].context.Gpr24);
295           GC_push_one ((word) thread_table[i].context.Gpr25);
296           GC_push_one ((word) thread_table[i].context.Gpr26);
297           GC_push_one ((word) thread_table[i].context.Gpr27);
298           GC_push_one ((word) thread_table[i].context.Gpr28);
299           GC_push_one ((word) thread_table[i].context.Gpr29);
300           GC_push_one ((word) thread_table[i].context.Gpr30);
301           GC_push_one ((word) thread_table[i].context.Gpr31);
302           GC_push_all_stack((char *) thread_table[i].context.Gpr1,
303                             thread_table[i].stack);
304 #       else
305 #       ifdef ALPHA
306           if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
307               || thread_table[i].context.IntSp < (DWORD)bottom)
308               ABORT("Thread stack pointer out of range");
309           GC_push_one ((word) thread_table[i].context.IntV0);
310           GC_push_one ((word) thread_table[i].context.IntT0);
311           GC_push_one ((word) thread_table[i].context.IntT1);
312           GC_push_one ((word) thread_table[i].context.IntT2);
313           GC_push_one ((word) thread_table[i].context.IntT3);
314           GC_push_one ((word) thread_table[i].context.IntT4);
315           GC_push_one ((word) thread_table[i].context.IntT5);
316           GC_push_one ((word) thread_table[i].context.IntT6);
317           GC_push_one ((word) thread_table[i].context.IntT7);
318           GC_push_one ((word) thread_table[i].context.IntS0);
319           GC_push_one ((word) thread_table[i].context.IntS1);
320           GC_push_one ((word) thread_table[i].context.IntS2);
321           GC_push_one ((word) thread_table[i].context.IntS3);
322           GC_push_one ((word) thread_table[i].context.IntS4);
323           GC_push_one ((word) thread_table[i].context.IntS5);
324           GC_push_one ((word) thread_table[i].context.IntFp);
325           GC_push_one ((word) thread_table[i].context.IntA0);
326           GC_push_one ((word) thread_table[i].context.IntA1);
327           GC_push_one ((word) thread_table[i].context.IntA2);
328           GC_push_one ((word) thread_table[i].context.IntA3);
329           GC_push_one ((word) thread_table[i].context.IntA4);
330           GC_push_one ((word) thread_table[i].context.IntA5);
331           GC_push_one ((word) thread_table[i].context.IntT8);
332           GC_push_one ((word) thread_table[i].context.IntT9);
333           GC_push_one ((word) thread_table[i].context.IntT10);
334           GC_push_one ((word) thread_table[i].context.IntT11);
335           GC_push_one ((word) thread_table[i].context.IntT12);
336           GC_push_one ((word) thread_table[i].context.IntAt);
337           GC_push_all_stack((char *) thread_table[i].context.IntSp,
338                             thread_table[i].stack);
339 #       else
340               --> architecture not supported
341 #       endif /* !ALPHA */
342 #       endif /* !PPC */
343 #       endif /* !MIPS */
344 #       endif /* !SHx */
345 #       endif /* !ARM32 */
346 #       endif /* !I386 */
347       }
348     }
349 }
350
351 void GC_get_next_stack(char *start, char **lo, char **hi)
352 {
353     int i;
354 #   define ADDR_LIMIT (char *)(-1L)
355     char * current_min = ADDR_LIMIT;
356
357     for (i = 0; i < MAX_THREADS; i++) {
358         char * s = (char *)thread_table[i].stack;
359
360         if (0 != s && s > start && s < current_min) {
361             current_min = s;
362         }
363     }
364     *hi = current_min;
365     if (current_min == ADDR_LIMIT) {
366         *lo = ADDR_LIMIT;
367         return;
368     }
369     *lo = GC_get_lo_stack_addr(current_min);
370     if (*lo < start) *lo = start;
371 }
372
373 #if !defined(CYGWIN32)
374
375 #if !defined(MSWINCE) && defined(GC_DLL)
376
377 /* We register threads from DllMain */
378
379 GC_API HANDLE WINAPI GC_CreateThread(
380     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
381     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
382     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
383 {
384     return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
385                         lpParameter, dwCreationFlags, lpThreadId);
386 }
387
388 #else /* defined(MSWINCE) || !defined(GC_DLL))  */
389
390 /* We have no DllMain to take care of new threads.  Thus we     */
391 /* must properly intercept thread creation.                     */
392
393 typedef struct {
394     HANDLE child_ready_h, parent_ready_h;
395     volatile struct thread_entry * entry;
396     LPTHREAD_START_ROUTINE start;
397     LPVOID param;
398 } thread_args;
399
400 static DWORD WINAPI thread_start(LPVOID arg);
401
402 GC_API HANDLE WINAPI GC_CreateThread(
403     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
404     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
405     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
406 {
407     HANDLE thread_h = NULL;
408     HANDLE child_ready_h, parent_ready_h;
409
410     int i;
411     thread_args args;
412
413     /* allocate thread slot */
414     LOCK();
415     for (i = 0; i != MAX_THREADS && thread_table[i].in_use; i++)
416         ;
417     if (i != MAX_THREADS) {
418         thread_table[i].in_use = TRUE;
419     }
420     UNLOCK();
421
422     if (i != MAX_THREADS) {
423
424         /* create unnamed unsignalled events */
425         if (child_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
426             if (parent_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
427
428                 /* set up thread arguments */
429                 args.child_ready_h = child_ready_h;
430                 args.parent_ready_h = parent_ready_h;
431                 args.entry = &thread_table[i];
432                 args.start = lpStartAddress;
433                 args.param = lpParameter;
434
435                 thread_h = CreateThread(lpThreadAttributes,
436                                         dwStackSize, thread_start,
437                                         &args,
438                                         dwCreationFlags & ~CREATE_SUSPENDED,
439                                         lpThreadId);
440
441                 if (thread_h) {
442
443                     /* fill in ID and handle; tell child this is done */
444                     thread_table[i].id = *lpThreadId;
445                     if (!DuplicateHandle(GetCurrentProcess(),
446                                          thread_h,
447                                          GetCurrentProcess(),
448                                          (PHANDLE) &thread_table[i].handle,
449                                          0,
450                                          0,
451                                          DUPLICATE_SAME_ACCESS)) {
452                         DWORD last_error = GetLastError();
453                         GC_printf1("Last error code: %lx\n", last_error);
454                         ABORT("DuplicateHandle failed");
455                     }
456                     SetEvent (parent_ready_h);
457
458                     /* wait for child to fill in stack and copy args */
459                     WaitForSingleObject (child_ready_h, INFINITE);
460
461                     /* suspend the child if requested */
462                     if (dwCreationFlags & CREATE_SUSPENDED)
463                         SuspendThread (thread_h);
464
465                     /* let child call given function now (or when resumed) */
466                     SetEvent (parent_ready_h);
467
468                 } else {
469                     CloseHandle (parent_ready_h);
470                 }
471             }
472         }
473
474         CloseHandle (child_ready_h);
475
476         if (thread_h == NULL)
477             thread_table[i].in_use = FALSE;
478
479     } else { /* no thread slot found */
480         SetLastError (ERROR_TOO_MANY_TCBS);
481     }
482
483     return thread_h;
484 }
485
486 static DWORD WINAPI thread_start(LPVOID arg)
487 {
488     DWORD ret = 0;
489     thread_args args = *(thread_args *)arg;
490
491     /* wait for parent to fill in ID and handle */
492     WaitForSingleObject (args.parent_ready_h, INFINITE);
493     ResetEvent (args.parent_ready_h);
494
495     /* fill in stack; tell parent this is done */
496     args.entry->stack = GC_get_stack_base();
497     SetEvent (args.child_ready_h);
498
499     /* wait for parent to tell us to go (in case it needs to suspend us) */
500     WaitForSingleObject (args.parent_ready_h, INFINITE);
501     CloseHandle (args.parent_ready_h);
502
503     /* Clear the thread entry even if we exit with an exception.        */
504     /* This is probably pointless, since an uncaught exception is       */
505     /* supposed to result in the process being killed.                  */
506 #ifndef __GNUC__
507     __try {
508 #endif /* __GNUC__ */
509         ret = args.start (args.param);
510 #ifndef __GNUC__
511     } __finally {
512 #endif /* __GNUC__ */
513         LOCK();
514         args.entry->stack = 0;
515         args.entry->in_use = FALSE;
516               /* cast away volatile qualifier */
517         BZERO((void *) &args.entry->context, sizeof(CONTEXT));
518         UNLOCK();
519 #ifndef __GNUC__
520     }
521 #endif /* __GNUC__ */
522
523     return ret;
524 }
525 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))  */
526
527 #endif /* !CYGWIN32 */
528
529 #ifdef MSWINCE
530
531 typedef struct {
532     HINSTANCE hInstance;
533     HINSTANCE hPrevInstance;
534     LPWSTR lpCmdLine;
535     int nShowCmd;
536 } main_thread_args;
537
538 DWORD WINAPI main_thread_start(LPVOID arg);
539
540 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
541                    LPWSTR lpCmdLine, int nShowCmd)
542 {
543     DWORD exit_code = 1;
544
545     main_thread_args args = {
546         hInstance, hPrevInstance, lpCmdLine, nShowCmd
547     };
548     HANDLE thread_h;
549     DWORD thread_id;
550
551     /* initialize everything */
552     InitializeCriticalSection(&GC_allocate_ml);
553     GC_init();
554
555     /* start the main thread */
556     thread_h = GC_CreateThread(
557         NULL, 0, main_thread_start, &args, 0, &thread_id);
558
559     if (thread_h != NULL)
560     {
561         WaitForSingleObject (thread_h, INFINITE);
562         GetExitCodeThread (thread_h, &exit_code);
563         CloseHandle (thread_h);
564     }
565
566     GC_deinit();
567     DeleteCriticalSection(&GC_allocate_ml);
568
569     return (int) exit_code;
570 }
571
572 DWORD WINAPI main_thread_start(LPVOID arg)
573 {
574     main_thread_args * args = (main_thread_args *) arg;
575
576     return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance,
577                                args->lpCmdLine, args->nShowCmd);
578 }
579
580 # else /* !MSWINCE */
581
582 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
583
584 /* threadAttach/threadDetach routines used by both CYGWIN and DLL
585  * implementation, since both recieve explicit notification on thread
586  * creation/destruction.
587  */
588 static void threadAttach() {
589   int i;
590   /* It appears to be unsafe to acquire a lock here, since this */
591   /* code is apparently not preeemptible on some systems.       */
592   /* (This is based on complaints, not on Microsoft's official  */
593   /* documentation, which says this should perform "only simple */
594   /* inititalization tasks".)                                   */
595   /* Hence we make do with nonblocking synchronization.         */
596
597   /* The following should be a noop according to the win32      */
598   /* documentation.  There is empirical evidence that it        */
599   /* isn't.             - HB                                    */
600 # if defined(MPROTECT_VDB)
601    if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
602 # endif
603                 /* cast away volatile qualifier */
604   for (i = 0; InterlockedExchange((LONG*)&thread_table[i].in_use,1) != 0; i++) {
605     /* Compare-and-swap would make this cleaner, but that's not         */
606     /* supported before Windows 98 and NT 4.0.  In Windows 2000,        */
607     /* InterlockedExchange is supposed to be replaced by                */
608     /* InterlockedExchangePointer, but that's not really what I */
609     /* want here.                                                       */
610     if (i == MAX_THREADS - 1)
611       ABORT("too many threads");
612   }
613   thread_table[i].id = GetCurrentThreadId();
614 # ifdef CYGWIN32
615     thread_table[i].pthread_id = pthread_self();
616 # endif
617   if (!DuplicateHandle(GetCurrentProcess(),
618                        GetCurrentThread(),
619                        GetCurrentProcess(),
620                        (HANDLE*)&thread_table[i].handle,
621                        0,
622                        0,
623                        DUPLICATE_SAME_ACCESS)) {
624         DWORD last_error = GetLastError();
625         GC_printf1("Last error code: %lx\n", last_error);
626         ABORT("DuplicateHandle failed");
627   }
628   thread_table[i].stack = GC_get_stack_base();
629   if (thread_table[i].stack == NULL) 
630     ABORT("Failed to find stack base in threadAttach");
631   /* If this thread is being created while we are trying to stop        */
632   /* the world, wait here.  Hopefully this can't happen on any  */
633   /* systems that don't allow us to block here.                 */
634   while (GC_please_stop) Sleep(20);
635 }
636
637 static void threadDetach(DWORD thread_id) {
638   int i;
639
640   LOCK();
641   for (i = 0;
642        i < MAX_THREADS &&
643        (!thread_table[i].in_use || thread_table[i].id != thread_id);
644        i++) {}
645   if (i >= MAX_THREADS ) {
646     WARN("thread %ld not found on detach", (GC_word)thread_id);
647   } else {
648     thread_table[i].stack = 0;
649     thread_table[i].in_use = FALSE;
650     CloseHandle(thread_table[i].handle);
651       /* cast away volatile qualifier */
652     BZERO((void *)&thread_table[i].context, sizeof(CONTEXT));
653   }
654   UNLOCK();
655 }
656
657 #ifdef CYGWIN32
658
659 /* Called by GC_init() - we hold the allocation lock.   */
660 void GC_thr_init() {
661     if (GC_thr_initialized) return;
662     GC_thr_initialized = TRUE;
663
664 #if 0
665     /* this might already be handled in GC_init... */
666     InitializeCriticalSection(&GC_allocate_ml);
667 #endif
668
669     /* Add the initial thread, so we can stop it.       */
670     threadAttach();
671 }
672
673 struct start_info {
674     void *(*start_routine)(void *);
675     void *arg;
676 };
677
678 int GC_pthread_join(pthread_t pthread_id, void **retval) {
679     int result;
680     int i;
681
682 #   if DEBUG_CYGWIN_THREADS
683       GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",(int)pthread_self(),
684                  GetCurrentThreadId(), (int)pthread_id);
685 #   endif
686
687     /* Can't do any table lookups here, because thread being joined 
688        might not have registered itself yet */
689
690     result = pthread_join(pthread_id, retval);
691
692     LOCK();
693     for (i = 0; !thread_table[i].in_use || thread_table[i].pthread_id != pthread_id;
694          i++) {
695       if (i == MAX_THREADS - 1) {
696         GC_printf1("Failed to find thread 0x%x in pthread_join()\n", pthread_id);
697         ABORT("thread not found on detach");
698       }
699     }
700     UNLOCK();
701     threadDetach(thread_table[i].id);
702
703 #   if DEBUG_CYGWIN_THREADS
704       GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
705                  (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
706 #   endif
707
708     return result;
709 }
710
711 /* Cygwin-pthreads calls CreateThread internally, but it's not
712  * easily interceptible by us..
713  *   so intercept pthread_create instead
714  */
715 int
716 GC_pthread_create(pthread_t *new_thread,
717                   const pthread_attr_t *attr,
718                   void *(*start_routine)(void *), void *arg) {
719     int result;
720     struct start_info * si;
721
722     if (!GC_is_initialized) GC_init();
723                 /* make sure GC is initialized (i.e. main thread is attached) */
724     
725     /* This is otherwise saved only in an area mmapped by the thread */
726     /* library, which isn't visible to the collector.            */
727     si = GC_malloc_uncollectable(sizeof(struct start_info)); 
728     if (0 == si) return(EAGAIN);
729
730     si -> start_routine = start_routine;
731     si -> arg = arg;
732
733 #   if DEBUG_CYGWIN_THREADS
734       GC_printf2("About to create a thread from 0x%x(0x%x)\n",(int)pthread_self(),
735                                                               GetCurrentThreadId);
736 #   endif
737     result = pthread_create(new_thread, attr, GC_start_routine, si); 
738
739     if (result) { /* failure */
740         GC_free(si);
741     } 
742
743     return(result);
744 }
745
746 void * GC_start_routine(void * arg)
747 {
748     struct start_info * si = arg;
749     void * result;
750     void *(*start)(void *);
751     void *start_arg;
752     pthread_t pthread_id;
753     int i;
754
755 #   if DEBUG_CYGWIN_THREADS
756       GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(),
757                                                    GetCurrentThreadId());
758 #   endif
759
760     /* If a GC occurs before the thread is registered, that GC will     */
761     /* ignore this thread.  That's fine, since it will block trying to  */
762     /* acquire the allocation lock, and won't yet hold interesting      */
763     /* pointers.                                                        */
764     LOCK();
765     /* We register the thread here instead of in the parent, so that    */
766     /* we don't need to hold the allocation lock during pthread_create. */
767     threadAttach();
768     UNLOCK();
769
770     start = si -> start_routine;
771     start_arg = si -> arg;
772     pthread_id = pthread_self();
773
774     GC_free(si); /* was allocated uncollectable */
775
776     pthread_cleanup_push(GC_thread_exit_proc, pthread_id);
777     result = (*start)(start_arg);
778     pthread_cleanup_pop(0);
779
780 #   if DEBUG_CYGWIN_THREADS
781       GC_printf2("thread 0x%x(0x%x) returned from start routine.\n",
782                  (int)pthread_self(),GetCurrentThreadId());
783 #   endif
784
785     LOCK();
786     for (i = 0; thread_table[i].pthread_id != pthread_id; i++) {
787       if (i == MAX_THREADS - 1)
788         ABORT("thread not found on exit");
789     }
790     thread_table[i].status = result;
791     UNLOCK();
792
793     return(result);
794 }
795
796 void GC_thread_exit_proc(void *arg)
797 {
798     pthread_t pthread_id = (pthread_t)arg;
799     int i;
800
801 #   if DEBUG_CYGWIN_THREADS
802       GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",
803                  (int)pthread_self(),GetCurrentThreadId());
804 #   endif
805
806     LOCK();
807     for (i = 0; thread_table[i].pthread_id != pthread_id; i++) {
808       if (i == MAX_THREADS - 1)
809         ABORT("thread not found on exit");
810     }
811     UNLOCK();
812
813 #if 0
814     /* TODO: we need a way to get the exit value after a pthread_exit so we can stash it safely away */
815     thread_table[i].status = ???
816 #endif
817 }
818
819 /* nothing required here... */
820 int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) {
821   return pthread_sigmask(how, set, oset);
822 }
823 int GC_pthread_detach(pthread_t thread) {
824   return pthread_detach(thread);
825 }
826 #else /* !CYGWIN32 */
827
828 /*
829  * We avoid acquiring locks here, since this doesn't seem to be preemptable.
830  * Pontus Rydin suggests wrapping the thread start routine instead.
831  */
832 #ifdef GC_DLL
833 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
834 {
835   switch (reason) {
836   case DLL_PROCESS_ATTACH:
837     InitializeCriticalSection(&GC_allocate_ml);
838     GC_init();  /* Force initialization before thread attach.   */
839     /* fall through */
840   case DLL_THREAD_ATTACH:
841     threadAttach();
842     break;
843
844   case DLL_THREAD_DETACH:
845     threadDetach(GetCurrentThreadId());
846     break;
847
848   case DLL_PROCESS_DETACH:
849     {
850       int i;
851
852       LOCK();
853       for (i = 0; i < MAX_THREADS; ++i)
854       {
855           if (thread_table[i].in_use)
856           {
857               thread_table[i].stack = 0;
858               thread_table[i].in_use = FALSE;
859               CloseHandle(thread_table[i].handle);
860               BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
861           }
862       }
863       UNLOCK();
864
865       GC_deinit();
866       DeleteCriticalSection(&GC_allocate_ml);
867     }
868     break;
869
870   }
871   return TRUE;
872 }
873 #endif /* GC_DLL */
874 #endif /* !CYGWIN32 */
875
876 # endif /* !MSWINCE */
877
878 #endif /* GC_WIN32_THREADS */