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