f6f74bd1111939d847e3285cf0c2657a13e98324
[fw/sdcc] / support / gc / win32_threads.c
1 #ifdef WIN32_THREADS
2
3 #include "gc_priv.h"
4
5 #define STRICT
6 #include <windows.h>
7
8 #define MAX_THREADS 64
9
10 struct thread_entry {
11   LONG in_use;
12   DWORD id;
13   HANDLE handle;
14   void *stack;          /* The cold end of the stack.   */
15                         /* 0 ==> entry not valid.       */
16                         /* !in_use ==> stack == 0       */
17   CONTEXT context;
18   GC_bool suspended;
19 };
20
21 volatile GC_bool GC_please_stop = FALSE;
22
23 volatile struct thread_entry thread_table[MAX_THREADS];
24
25 void GC_stop_world()
26 {
27   DWORD thread_id = GetCurrentThreadId();
28   int i;
29
30   GC_please_stop = TRUE;
31   for (i = 0; i < MAX_THREADS; i++)
32     if (thread_table[i].stack != 0
33         && thread_table[i].id != thread_id) {
34       if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
35         ABORT("SuspendThread failed");
36       thread_table[i].suspended = TRUE;
37     }
38 }
39
40 void GC_start_world()
41 {
42   DWORD thread_id = GetCurrentThreadId();
43   int i;
44   for (i = 0; i < MAX_THREADS; i++)
45     if (thread_table[i].stack != 0 && thread_table[i].suspended
46         && thread_table[i].id != thread_id) {
47       if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
48         ABORT("ResumeThread failed");
49       thread_table[i].suspended = FALSE;
50     }
51   GC_please_stop = FALSE;
52 }
53
54 ptr_t GC_current_stackbottom()
55 {
56   DWORD thread_id = GetCurrentThreadId();
57   int i;
58   for (i = 0; i < MAX_THREADS; i++)
59     if (thread_table[i].stack && thread_table[i].id == thread_id)
60       return thread_table[i].stack;
61   ABORT("no thread table entry for current thread");
62 }
63
64 ptr_t GC_get_lo_stack_addr(ptr_t s)
65 {
66     ptr_t bottom;
67     MEMORY_BASIC_INFORMATION info;
68     VirtualQuery(s, &info, sizeof(info));
69     do {
70         bottom = info.BaseAddress;
71         VirtualQuery(bottom - 1, &info, sizeof(info));
72     } while ((info.Protect & PAGE_READWRITE) && !(info.Protect & PAGE_GUARD));
73     return(bottom);
74 }
75
76 void GC_push_all_stacks()
77 {
78   DWORD thread_id = GetCurrentThreadId();
79   int i;
80   for (i = 0; i < MAX_THREADS; i++)
81     if (thread_table[i].stack) {
82       ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack);
83       if (thread_table[i].id == thread_id)
84         GC_push_all(&i, thread_table[i].stack);
85       else {
86         thread_table[i].context.ContextFlags
87                         = (CONTEXT_INTEGER|CONTEXT_CONTROL);
88         if (!GetThreadContext(thread_table[i].handle,
89                               &thread_table[i].context))
90           ABORT("GetThreadContext failed");
91         if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack
92             || thread_table[i].context.Esp < (DWORD)bottom)
93             ABORT("Thread stack pointer out of range");
94         GC_push_one ((word) thread_table[i].context.Edi);
95         GC_push_one ((word) thread_table[i].context.Esi);
96         GC_push_one ((word) thread_table[i].context.Ebx);
97         GC_push_one ((word) thread_table[i].context.Edx);
98         GC_push_one ((word) thread_table[i].context.Ecx);
99         GC_push_one ((word) thread_table[i].context.Eax);
100         GC_push_all_stack(thread_table[i].context.Esp, thread_table[i].stack);
101       }
102     }
103 }
104
105 void GC_get_next_stack(char *start, char **lo, char **hi)
106 {
107     int i;
108 #   define ADDR_LIMIT (char *)(-1L)
109     char * current_min = ADDR_LIMIT;
110
111     for (i = 0; i < MAX_THREADS; i++) {
112         char * s = (char *)thread_table[i].stack;
113
114         if (0 != s && s > start && s < current_min) {
115             current_min = s;
116         }
117     }
118     *hi = current_min;
119     if (current_min == ADDR_LIMIT) {
120         *lo = ADDR_LIMIT;
121         return;
122     }
123     *lo = GC_get_lo_stack_addr(current_min);
124     if (*lo < start) *lo = start;
125 }
126
127 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
128
129 /*
130  * This isn't generally safe, since DllMain is not premptible.
131  * If another thread holds the lock while this runs we're in trouble.
132  * Pontus Rydin suggests wrapping the thread start routine instead.
133  */
134 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
135 {
136   switch (reason) {
137   case DLL_PROCESS_ATTACH:
138     InitializeCriticalSection(&GC_allocate_ml);
139     GC_init();  /* Force initialization before thread attach.   */
140     /* fall through */
141   case DLL_THREAD_ATTACH:
142     {
143       int i;
144       /* It appears to be unsafe to acquire a lock here, since this     */
145       /* code is apparently not preeemptible on some systems.           */
146       /* (This is based on complaints, not on Microsoft's official      */
147       /* documentation, which says this should perform "only simple     */
148       /* inititalization tasks".)                                       */
149       /* Hence we make do with nonblocking synchronization.             */
150
151       /* The following should be a noop according to the win32  */
152       /* documentation.  There is empirical evidence that it    */
153       /* isn't.         - HB                                    */
154 #     ifndef SMALL_CONFIG
155        if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
156 #     endif
157
158       for (i = 0; InterlockedExchange(&thread_table[i].in_use,1) != 0; i++) {
159         /* Compare-and-swap would make this cleaner, but that's not     */
160         /* supported before Windows 98 and NT 4.0.  In Windows 2000,    */
161         /* InterlockedExchange is supposed to be replaced by            */
162         /* InterlockedExchangePointer, but that's not really what I     */
163         /* want here.                                                   */
164         if (i == MAX_THREADS - 1)
165           ABORT("too many threads");
166       }
167       thread_table[i].id = GetCurrentThreadId();
168       if (!DuplicateHandle(GetCurrentProcess(),
169                            GetCurrentThread(),
170                            GetCurrentProcess(),
171                            &thread_table[i].handle,
172                            0,
173                            0,
174                            DUPLICATE_SAME_ACCESS)) {
175             DWORD last_error = GetLastError();
176             GC_printf1("Last error code: %lx\n", last_error);
177             ABORT("DuplicateHandle failed");
178       }
179       thread_table[i].stack = GC_get_stack_base();
180       /* If this thread is being created while we are trying to stop    */
181       /* the world, wait here.  Hopefully this can't happen on any      */
182       /* systems that don't allow us to block here.                     */
183       while (GC_please_stop) Sleep(20);
184     }
185     break;
186   case DLL_PROCESS_DETACH:
187   case DLL_THREAD_DETACH:
188     {
189       int i;
190       DWORD thread_id = GetCurrentThreadId();
191       LOCK();
192       for (i = 0;
193            thread_table[i].stack == 0 || thread_table[i].id != thread_id;
194            i++) {
195         if (i == MAX_THREADS - 1)
196           ABORT("thread not found on detach");
197       }
198       thread_table[i].stack = 0;
199       thread_table[i].in_use = FALSE;
200       CloseHandle(thread_table[i].handle);
201       BZERO(&thread_table[i].context, sizeof(CONTEXT));
202       UNLOCK();
203     }
204     break;
205   }
206   return TRUE;
207 }
208
209 #endif /* WIN32_THREADS */