| 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 */ | 
|---|