📄 win32_threads.c
字号:
me -> id = thread_id;# if defined(THREAD_LOCAL_ALLOC) GC_init_thread_local((GC_tlfs)(&(me->tlfs)));# endif if (me -> stack_base == NULL) ABORT("Bad stack base in GC_register_my_thread_inner"); if (GC_win32_dll_threads) { if (GC_please_stop) { AO_store(&GC_attached_thread, TRUE); AO_nop_full(); // Later updates must become visible after this. } /* We'd like to wait here, but can't, since waiting in DllMain */ /* provokes deadlocks. */ /* Thus we force marking to be restarted instead. */ } else { GC_ASSERT(!GC_please_stop); /* Otherwise both we and the thread stopping code would be */ /* holding the allocation lock. */ } return (GC_thread)(me);}/* * GC_max_thread_index may temporarily be larger than MAX_THREADS. * To avoid subscript errors, we check on access. */#ifdef __GNUC____inline__#endifLONG GC_get_max_thread_index(){ LONG my_max = GC_max_thread_index; if (my_max >= MAX_THREADS) return MAX_THREADS-1; return my_max;}/* Return the GC_thread corresponding to a thread id. May be called *//* without a lock, but should be called in contexts in which the *//* requested thread cannot be asynchronously deleted, e.g. from the *//* thread itself. *//* This version assumes that either GC_win32_dll_threads is set, or *//* we hold the allocator lock. *//* Also used (for assertion checking only) from thread_local_alloc.c. */GC_thread GC_lookup_thread_inner(DWORD thread_id) { if (GC_win32_dll_threads) { int i; LONG my_max = GC_get_max_thread_index(); for (i = 0; i <= my_max && (!AO_load_acquire(&(dll_thread_table[i].in_use)) || dll_thread_table[i].id != thread_id); /* Must still be in_use, since nobody else can store our thread_id. */ i++) {} if (i > my_max) { return 0; } else { return (GC_thread)(dll_thread_table + i); } } else { word hv = ((word)thread_id) % THREAD_TABLE_SZ; register GC_thread p = GC_threads[hv]; GC_ASSERT(I_HOLD_LOCK()); while (p != 0 && p -> id != thread_id) p = p -> next; return(p); }}/* A version of the above that acquires the lock if necessary. Note *//* that the identically named function for pthreads is different, and *//* just assumes we hold the lock. *//* Also used (for assertion checking only) from thread_local_alloc.c. */static GC_thread GC_lookup_thread(DWORD thread_id){ if (GC_win32_dll_threads) { return GC_lookup_thread_inner(thread_id); } else { GC_thread result; LOCK(); result = GC_lookup_thread_inner(thread_id); UNLOCK(); return result; }}/* If a thread has been joined, but we have not yet *//* been notified, then there may be more than one thread *//* in the table with the same win32 id. *//* This is OK, but we need a way to delete a specific one. *//* Assumes we hold the allocation lock unless *//* GC_win32_dll_threads is set. *//* If GC_win32_dll_threads is set it should be called from the *//* thread being deleted. */void GC_delete_gc_thread(GC_vthread gc_id){ if (GC_win32_dll_threads) { /* This is intended to be lock-free. */ /* It is either called synchronously from the thread being deleted, */ /* or by the joining thread. */ /* In this branch asynchronosu changes to *gc_id are possible. */ CloseHandle(gc_id->handle); gc_id -> stack_base = 0; gc_id -> id = 0;# ifdef CYGWIN32 gc_id -> pthread_id = 0;# endif /* CYGWIN32 */# ifdef GC_WIN32_PTHREADS gc_id -> pthread_id.p = NULL;# endif /* GC_WIN32_PTHREADS */ AO_store_release(&(gc_id->in_use), FALSE); } else { /* Cast away volatile qualifier, since we have lock. */ GC_thread gc_nvid = (GC_thread)gc_id; DWORD id = gc_nvid -> id; word hv = ((word)id) % THREAD_TABLE_SZ; register GC_thread p = GC_threads[hv]; register GC_thread prev = 0; GC_ASSERT(I_HOLD_LOCK()); while (p != gc_nvid) { prev = p; p = p -> next; } if (prev == 0) { GC_threads[hv] = p -> next; } else { prev -> next = p -> next; } GC_INTERNAL_FREE(p); }}/* Delete a thread from GC_threads. We assume it is there. *//* (The code intentionally traps if it wasn't.) *//* Assumes we hold the allocation lock unless *//* GC_win32_dll_threads is set. *//* If GC_win32_dll_threads is set it should be called from the *//* thread being deleted. */void GC_delete_thread(DWORD id){ if (GC_win32_dll_threads) { GC_thread t = GC_lookup_thread_inner(id); if (0 == t) { WARN("Removing nonexistent thread %ld\n", (GC_word)id); } else { GC_delete_gc_thread(t); } } else { word hv = ((word)id) % THREAD_TABLE_SZ; register GC_thread p = GC_threads[hv]; register GC_thread prev = 0; GC_ASSERT(I_HOLD_LOCK()); while (p -> id != id) { prev = p; p = p -> next; } if (prev == 0) { GC_threads[hv] = p -> next; } else { prev -> next = p -> next; } GC_INTERNAL_FREE(p); }}int GC_register_my_thread(struct GC_stack_base *sb) { DWORD t = GetCurrentThreadId(); if (0 == GC_lookup_thread(t)) { /* We lock here, since we want to wait for an ongoing GC. */ LOCK(); GC_register_my_thread_inner(sb, t); UNLOCK(); return GC_SUCCESS; } else { return GC_DUPLICATE; }}int GC_unregister_my_thread(void){ DWORD t = GetCurrentThreadId();# if defined(THREAD_LOCAL_ALLOC) LOCK(); { GC_thread me = GC_lookup_thread_inner(t); GC_destroy_thread_local(&(me->tlfs)); } UNLOCK();# endif if (GC_win32_dll_threads) { /* Should we just ignore this? */ GC_delete_thread(t); } else { LOCK(); GC_delete_thread(t); UNLOCK(); } return GC_SUCCESS;}#ifdef GC_PTHREADS/* A quick-and-dirty cache of the mapping between pthread_t *//* and win32 thread id. */#define PTHREAD_MAP_SIZE 512DWORD GC_pthread_map_cache[PTHREAD_MAP_SIZE];#define HASH(pthread_id) ((NUMERIC_THREAD_ID(pthread_id) >> 5) % PTHREAD_MAP_SIZE) /* It appears pthread_t is really a pointer type ... */#define SET_PTHREAD_MAP_CACHE(pthread_id, win32_id) \ GC_pthread_map_cache[HASH(pthread_id)] = (win32_id);#define GET_PTHREAD_MAP_CACHE(pthread_id) \ GC_pthread_map_cache[HASH(pthread_id)]/* Return a GC_thread corresponding to a given pthread_t. *//* Returns 0 if it's not there. *//* We assume that this is only called for pthread ids that *//* have not yet terminated or are still joinable, and *//* cannot be concurrently terminated. *//* Assumes we do NOT hold the allocation lock. */static GC_thread GC_lookup_pthread(pthread_t id){ if (GC_win32_dll_threads) { int i; LONG my_max = GC_get_max_thread_index(); for (i = 0; i <= my_max && (!AO_load_acquire(&(dll_thread_table[i].in_use)) || THREAD_EQUAL(dll_thread_table[i].pthread_id, id)); /* Must still be in_use, since nobody else can store our thread_id. */ i++); if (i > my_max) return 0; return (GC_thread)(dll_thread_table + i); } else { /* We first try the cache. If that fails, we use a very slow */ /* approach. */ int hv_guess = GET_PTHREAD_MAP_CACHE(id) % THREAD_TABLE_SZ; int hv; GC_thread p; LOCK(); for (p = GC_threads[hv_guess]; 0 != p; p = p -> next) { if (THREAD_EQUAL(p -> pthread_id, id)) goto foundit; } for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { for (p = GC_threads[hv]; 0 != p; p = p -> next) { if (THREAD_EQUAL(p -> pthread_id, id)) goto foundit; } } p = 0; foundit: UNLOCK(); return p; }}#endif /* GC_PTHREADS */void GC_push_thread_structures(void){ GC_ASSERT(I_HOLD_LOCK()); if (GC_win32_dll_threads) { /* Unlike the other threads implementations, the thread table here */ /* contains no pointers to the collectable heap. Thus we have */ /* no private structures we need to preserve. */# ifdef GC_PTHREADS { int i; /* pthreads may keep a pointer in the thread exit value */ LONG my_max = GC_get_max_thread_index(); for (i = 0; i <= my_max; i++) if (dll_thread_table[i].in_use) GC_push_all((ptr_t)&(dll_thread_table[i].status), (ptr_t)(&(dll_thread_table[i].status)+1)); }# endif } else { GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads)); }# if defined(THREAD_LOCAL_ALLOC) GC_push_all((ptr_t)(&GC_thread_key), (ptr_t)(&GC_thread_key)+sizeof(&GC_thread_key)); /* Just in case we ever use our own TLS implementation. */# endif}/* Suspend the given thread, if it's still active. */void GC_suspend(GC_thread t){# ifdef MSWINCE /* SuspendThread will fail if thread is running kernel code */ while (SuspendThread(t -> handle) == (DWORD)-1) Sleep(10);# else /* Apparently the Windows 95 GetOpenFileName call creates */ /* a thread that does not properly get cleaned up, and */ /* SuspendThread on its descriptor may provoke a crash. */ /* This reduces the probability of that event, though it still */ /* appears there's a race here. */ DWORD exitCode; if (GetExitCodeThread(t -> handle, &exitCode) && exitCode != STILL_ACTIVE) { t -> stack_base = 0; /* prevent stack from being pushed */# ifndef GC_PTHREADS /* this breaks pthread_join on Cygwin, which is guaranteed to */ /* only see user pthreads */ AO_store(&(t -> in_use), FALSE); CloseHandle(t -> handle);# endif return; } if (SuspendThread(t -> handle) == (DWORD)-1) ABORT("SuspendThread failed");# endif t -> suspended = TRUE;}/* Defined in misc.c */#ifndef CYGWIN32 extern CRITICAL_SECTION GC_write_cs;#endifvoid GC_stop_world(void){ DWORD thread_id = GetCurrentThreadId(); int i; if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()"); GC_ASSERT(I_HOLD_LOCK()); GC_please_stop = TRUE;# ifndef CYGWIN32 EnterCriticalSection(&GC_write_cs);# endif if (GC_win32_dll_threads) { /* Any threads being created during this loop will end up setting */ /* GC_attached_thread when they start. This will force marking to */ /* restart. */ /* This is not ideal, but hopefully correct. */ GC_attached_thread = FALSE; for (i = 0; i <= GC_get_max_thread_index(); i++) { GC_vthread t = dll_thread_table + i; if (t -> stack_base != 0 && t -> id != thread_id) { GC_suspend((GC_thread)t); } } } else { GC_thread t; int i; for (i = 0; i < THREAD_TABLE_SZ; i++) { for (t = GC_threads[i]; t != 0; t = t -> next) { if (t -> stack_base != 0 && !KNOWN_FINISHED(t) && t -> id != thread_id) { GC_suspend(t); } } } }# ifndef CYGWIN32 LeaveCriticalSection(&GC_write_cs);# endif }void GC_start_world(void){ DWORD thread_id = GetCurrentThreadId(); int i; LONG my_max = GC_get_max_thread_index(); GC_ASSERT(I_HOLD_LOCK()); if (GC_win32_dll_threads) { for (i = 0; i <= my_max; i++) { GC_thread t = (GC_thread)(dll_thread_table + i); if (t -> stack_base != 0 && t -> suspended && t -> id != thread_id) { if (ResumeThread(t -> handle) == (DWORD)-1) ABORT("ResumeThread failed"); t -> suspended = FALSE; } } } else { GC_thread t; int i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -