Daily bump.
[official-gcc.git] / boehm-gc / win32_threads.c
blobf6f74bd1111939d847e3285cf0c2657a13e98324
1 #ifdef WIN32_THREADS
3 #include "gc_priv.h"
5 #define STRICT
6 #include <windows.h>
8 #define MAX_THREADS 64
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;
21 volatile GC_bool GC_please_stop = FALSE;
23 volatile struct thread_entry thread_table[MAX_THREADS];
25 void GC_stop_world()
27 DWORD thread_id = GetCurrentThreadId();
28 int i;
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;
40 void GC_start_world()
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;
51 GC_please_stop = FALSE;
54 ptr_t GC_current_stackbottom()
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");
64 ptr_t GC_get_lo_stack_addr(ptr_t s)
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);
76 void GC_push_all_stacks()
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);
105 void GC_get_next_stack(char *start, char **lo, char **hi)
107 int i;
108 # define ADDR_LIMIT (char *)(-1L)
109 char * current_min = ADDR_LIMIT;
111 for (i = 0; i < MAX_THREADS; i++) {
112 char * s = (char *)thread_table[i].stack;
114 if (0 != s && s > start && s < current_min) {
115 current_min = s;
118 *hi = current_min;
119 if (current_min == ADDR_LIMIT) {
120 *lo = ADDR_LIMIT;
121 return;
123 *lo = GC_get_lo_stack_addr(current_min);
124 if (*lo < start) *lo = start;
127 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
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.
134 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
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:
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. */
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
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");
167 thread_table[i].id = GetCurrentThreadId();
168 if (!DuplicateHandle(GetCurrentProcess(),
169 GetCurrentThread(),
170 GetCurrentProcess(),
171 &thread_table[i].handle,
174 DUPLICATE_SAME_ACCESS)) {
175 DWORD last_error = GetLastError();
176 GC_printf1("Last error code: %lx\n", last_error);
177 ABORT("DuplicateHandle failed");
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);
185 break;
186 case DLL_PROCESS_DETACH:
187 case DLL_THREAD_DETACH:
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");
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();
204 break;
206 return TRUE;
209 #endif /* WIN32_THREADS */