summaryrefslogtreecommitdiff
path: root/win32_threads.c
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2022-12-06 09:54:13 +0300
committerIvan Maidanski <ivmai@mail.ru>2022-12-06 09:54:13 +0300
commitbc1866b1405ce88d1acfcc05289f75b7ca4ad4e4 (patch)
tree40c702b9db55b3b454badd4213246c93cccd3070 /win32_threads.c
parent92ba44158ca276ff397fb55081ff371811a7b939 (diff)
downloadbdwgc-bc1866b1405ce88d1acfcc05289f75b7ca4ad4e4.tar.gz
Avoid code duplication between pthread_support.c and win32_threads.c
(refactoring) * CMakeLists.txt [CMAKE_USE_PTHREADS_INIT || CMAKE_USE_WIN32_THREADS_INIT] (SRC): Include pthread_start.c and pthread_support.c for Win32 platform. * Makefile.am [THREADS] (libgc_la_SOURCES): Likewise. * NT_MAKEFILE [ENABLE_STATIC] (OBJS): Add pthread_start.obj, pthread_support.obj. * include/private/gc_locks.h [LINT2 || GC_WIN32_THREADS] (NO_PTHREAD_TRYLOCK): Do not define if already defined; add comment. * include/private/gcconfig.h [GC_PTHREADS && !GC_PTHREADS_PARAMARK && !__MINGW32__] (GC_PTHREADS_PARAMARK): Do not define unless PARALLEL_MARK. * include/private/pthread_support.h (thread_id_self, THREAD_ID_EQUAL, ADDR_LIMIT, MAX_MARKERS, GC_PTHREAD_PTRVAL): Define macro. * include/private/pthread_support.h (GC_win32_dll_threads, GC_available_markers_m1, GC_required_markers_cnt, GC_marker_sp, GC_marker_last_stack_min, GC_marker_Id): Declare global variable. * include/private/pthread_support.h (GC_init_win32_thread_naming, GC_mark_thread, GC_new_thread, GC_record_stack_base, GC_register_my_thread_inner, GC_lookup_by_pthread, GC_setup_atfork, GC_win32_cache_self_pthread, GC_delete_gc_thread_no_free, GC_win32_dll_lookup_thread, GC_delete_thread, GC_win32_unprotect_thread, GC_wait_for_gc_completion): Declare. * include/private/pthread_support.h [GC_PTHREADS] (GC_pthread_start_inner, GC_start_rtn_prepare_thread): Declare even for GC_WIN32_THREADS. * misc.c [!THREADS] (GC_call_with_gc_active, GC_do_blocking_inner): Update comment to refer to pthread_support.c. * pthread_start.c [GC_PTHREADS] (GC_pthread_start_inner): Define even for GC_WIN32_THREADS. * pthread_support.c: Do not skip this file for GC_WIN32_THREADS; adjust includes for Win32. * pthread_support.c (GC_INNER_WIN32THREAD): New macro. * pthread_support.c (GC_init_win32_thread_naming, GC_win32_unprotect_thread): New function. * pthread_support.c (setThreadDescription_fn, set_marker_thread_name): Move from win32_threads.c. * pthread_support.c (available_markers_m1): Rename to GC_available_markers_m1. * win32_threads.c (available_markers_m1): Likewise. * pthread_support.c (required_markers_cnt): Rename to GC_required_markers_cnt. * win32_threads.c (required_markers_cnt): Likewise. * pthread_support.c (GC_mark_thread, GC_start_mark_threads_inner, GC_push_thread_structures, GC_count_threads, GC_new_thread, GC_delete_thread, GC_delete_gc_thread_no_free, GC_lookup_thread, GC_reset_finalizer_nested, GC_check_finalizer_nested, GC_is_thread_tsd_valid, GC_thread_is_registered, GC_register_altstack, GC_segment_is_thread_stack, GC_wait_for_gc_completion, GC_remove_all_threads_but_me, fork_child_proc, GC_record_stack_base, GC_init_parallel, GC_pthread_sigmask, GC_set_stackbottom, GC_get_my_stackbottom, GC_call_with_gc_active, GC_unregister_my_thread, GC_unregister_my_thread_inner, GC_thread_exit_proc, GC_pthread_join, GC_pthread_detach, GC_pthread_sigmask, GC_pthread_create): Adjust function for Win32. * win32_threads.c (GC_lock_holder, GC_win32_dll_threads, IE_t, GC_thr_initialized, GC_need_to_lock, ADDR_LIMIT, GC_thread, GC_vthread, GC_threads, first_thread, GC_new_thread, GC_in_thread_creation, GC_record_stack_base, GC_lookup_thread, CHECK_LOOKUP_MY_THREAD, GC_reset_finalizer_nested, GC_check_finalizer_nested, GC_is_thread_tsd_valid, GC_thread_is_registered, GC_register_altstack, UNPROTECT_THREAD, GC_PTHREAD_PTRVAL, GC_delete_gc_thread_no_free, GC_delete_thread, GC_allow_register_threads, GC_register_my_thread, GC_set_stackbottom, GC_wait_for_gc_completion, GC_unregister_my_thread, GC_do_blocking_inner, GC_call_with_gc_active, GC_get_my_stackbottom, GC_remove_all_threads_but_me, fork_prepare_proc, fork_parent_proc, fork_child_proc, GC_atfork_prepare, GC_atfork_parent, GC_atfork_child, GC_setup_atfork, GC_push_thread_structures, marker_sp, set_marker_thread_name, setThreadDescription_fn, GC_mark_thread, required_markers_cnt, GC_set_markers_count, START_MARK_THREADS, start_info, GC_pthread_join, GC_pthread_create, GC_pthread_start_inner, GC_pthread_start, GC_thread_exit_proc, GC_pthread_detach, GC_check_tls, GC_init_parallel, GC_lock, GC_mark_thread_local_free_lists): Remove. * win32_threads.c [GC_PTHREADS_PARAMARK] (mark_cv, GC_start_mark_threads_inner, GC_mark_lock_holder, SET_MARK_LOCK_HOLDER, UNSET_MARK_LOCK_HOLDER, mark_mutex, builder_cv, GC_acquire_mark_lock, GC_release_mark_lock, GC_wait_builder, GC_wait_for_reclaim, GC_notify_all_builder, GC_wait_marker, GC_notify_all_marker, * win32_threads.c [GC_PTHREADS] (pthread_create, pthread_join, pthread_detach, pthread_sigmask): Do not undefine. * win32_threads.c [CAN_CALL_ATFORK]: Do not include unistd.h. * win32_threads.c (GC_register_my_thread_inner): Change type of me local variable from GC_vthread to GC_thread. * win32_threads.c (GC_win32_dll_lookup_thread, GC_win32_cache_self_pthread): New GC_INNER function. * win32_threads.c (GC_suspend, GC_start_world, GC_push_stack_for): Use GC_win32_unprotect_thread() instead of UNPROTECT_THREAD(). * win32_threads.c (marker_last_stack_min): Rename to GC_marker_last_stack_min. * win32_threads.c (GC_thr_init): Call GC_init_win32_thread_naming().
Diffstat (limited to 'win32_threads.c')
-rw-r--r--win32_threads.c1576
1 files changed, 114 insertions, 1462 deletions
diff --git a/win32_threads.c b/win32_threads.c
index d827de43..4ff7c9cb 100644
--- a/win32_threads.c
+++ b/win32_threads.c
@@ -21,15 +21,8 @@
#if defined(GC_WIN32_THREADS)
/* Allocation lock declarations. */
-#if !defined(USE_PTHREAD_LOCKS)
+#ifndef USE_PTHREAD_LOCKS
GC_INNER CRITICAL_SECTION GC_allocate_ml;
-#else
- GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-#ifdef GC_ASSERTIONS
- GC_INNER unsigned long GC_lock_holder = NO_THREAD;
- /* Thread id for current holder of allocation lock */
#endif
#undef CreateThread
@@ -37,29 +30,10 @@
#undef _beginthreadex
#undef _endthreadex
-#ifdef GC_PTHREADS
-# include <errno.h> /* for EAGAIN */
-
- /* Cygwin-specific forward decls */
-# undef pthread_create
-# undef pthread_join
-# undef pthread_detach
-
-# ifndef GC_NO_PTHREAD_SIGMASK
-# undef pthread_sigmask
-# endif
-
- STATIC void * GC_pthread_start(void * arg);
-
-# ifdef CAN_CALL_ATFORK
-# include <unistd.h>
-# endif
-
-#elif !defined(MSWINCE)
-# include <process.h> /* For _beginthreadex, _endthreadex */
+#if !defined(GC_PTHREADS) && !defined(MSWINCE)
# include <errno.h> /* for errno, EAGAIN */
-
-#endif /* !GC_PTHREADS && !MSWINCE */
+# include <process.h> /* For _beginthreadex, _endthreadex */
+#endif
static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext);
@@ -74,19 +48,17 @@ static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext);
/* In this mode access to the thread table is lock-free. */
/* Hence there is a static limit on the number of threads. */
-# ifdef GC_DISCOVER_TASK_THREADS
- /* GC_DISCOVER_TASK_THREADS should be used if DllMain-based */
- /* thread registration is required but it is impossible to */
- /* call GC_use_threads_discovery before other GC routines. */
-# define GC_win32_dll_threads TRUE
-# else
- STATIC GC_bool GC_win32_dll_threads = FALSE;
+ /* GC_DISCOVER_TASK_THREADS should be used if DllMain-based */
+ /* thread registration is required but it is impossible to */
+ /* call GC_use_threads_discovery before other GC routines. */
+
+# ifndef GC_DISCOVER_TASK_THREADS
/* GC_win32_dll_threads must be set (if needed) at the */
/* application initialization time, i.e. before any */
/* collector or thread calls. We make it a "dynamic" */
/* option only to avoid multiple library versions. */
+ GC_INNER GC_bool GC_win32_dll_threads = FALSE;
# endif
-
#else
/* If GC_win32_dll_threads is FALSE (or the collector is */
/* built without GC_DLL defined), things operate in a way */
@@ -102,7 +74,6 @@ static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext);
/* the basic collector rely on such facilities, but an */
/* optional package that intercepts thread calls this way */
/* would probably be nice. */
-# define GC_win32_dll_threads FALSE
# undef MAX_THREADS
# define MAX_THREADS 1 /* dll_thread_table[] is always empty. */
#endif /* GC_NO_THREADS_DISCOVERY */
@@ -114,19 +85,6 @@ static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext);
/* called. The main thread will be added on */
/* initialization. */
-/* The type of the first argument to InterlockedExchange. */
-/* Documented to be LONG volatile *, but at least gcc likes */
-/* this better. */
-typedef LONG * IE_t;
-
-#ifdef GC_ASSERTIONS
- GC_INNER GC_bool GC_thr_initialized = FALSE;
-#endif
-
-#ifndef GC_ALWAYS_MULTITHREADED
- GC_INNER GC_bool GC_need_to_lock = FALSE;
-#endif
-
/* GC_use_threads_discovery() is currently incompatible with pthreads */
/* and WinCE. It might be possible to get DllMain-based thread */
/* registration to work with Cygwin, but if you try it then you are on */
@@ -144,14 +102,12 @@ GC_API void GC_CALL GC_use_threads_discovery(void)
GC_win32_dll_threads = TRUE;
# endif
GC_init();
+# ifdef CPPCHECK
+ GC_noop1((word)&GC_DllMain);
+# endif
# endif
}
-#define ADDR_LIMIT ((ptr_t)GC_WORD_MAX)
-
-typedef struct GC_Thread_Rep * GC_thread;
-typedef volatile struct GC_Thread_Rep * GC_vthread;
-
#ifndef GC_NO_THREADS_DISCOVERY
static thread_id_t main_thread_id;
@@ -208,77 +164,16 @@ STATIC volatile LONG GC_max_thread_index = 0;
/* Largest index in dll_thread_table */
/* that was ever used. */
-/* And now the version used if GC_win32_dll_threads is not set. */
-/* This is a chained hash table, with much of the code borrowed */
-/* from the Posix implementation. */
-GC_INNER GC_thread GC_threads[THREAD_TABLE_SZ] = {0};
-
-/* It may not be safe to allocate when we register the first thread. */
-/* Thus we allocated one statically. It does not contain any pointer */
-/* field we need to push ("next" and "status" fields are unused). */
-static struct GC_Thread_Rep first_thread;
-static GC_bool first_thread_used = FALSE;
-
-/* Add a thread to GC_threads. We assume it wasn't already there. */
-STATIC GC_thread GC_new_thread(thread_id_t id)
-{
- int hv = THREAD_TABLE_INDEX(id);
- GC_thread result;
-
- GC_ASSERT(I_HOLD_LOCK());
-# ifdef DEBUG_THREADS
- GC_log_printf("Creating thread 0x%lx\n", (long)id);
- if (GC_threads[hv] != NULL)
- GC_log_printf("Hash collision at GC_threads[%d]\n", hv);
-# endif
- if (EXPECT(!first_thread_used, FALSE)) {
- result = &first_thread;
- first_thread_used = TRUE;
- GC_ASSERT(NULL == GC_threads[hv]);
-# if defined(GC_NO_FINALIZATION) && defined(CPPCHECK)
- GC_noop1(result -> no_fnlz_pad[0]);
-# endif
- } else {
- GC_ASSERT(!GC_win32_dll_threads);
- result = (struct GC_Thread_Rep *)
- GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
- if (EXPECT(NULL == result, FALSE)) return NULL;
- }
- /* The id field is set by the caller. */
- result -> tm.next = GC_threads[hv];
- GC_threads[hv] = result;
- GC_ASSERT(0 == result -> flags);
- if (EXPECT(result != &first_thread, TRUE))
- GC_dirty(result);
- return result;
-}
-
-GC_INNER GC_bool GC_in_thread_creation = FALSE;
- /* Protected by allocation lock. */
-
-GC_INLINE void GC_record_stack_base(GC_thread me,
- const struct GC_stack_base *sb)
-{
- me -> stack_end = (ptr_t)sb->mem_base;
-# ifdef IA64
- me -> backing_store_end = (ptr_t)sb->reg_base;
-# elif defined(I386)
- me -> initial_stack_base = (ptr_t)sb->mem_base;
-# endif
- if (NULL == me -> stack_end)
- ABORT("Bad stack base in GC_register_my_thread");
-}
-
/* This may be called from DllMain, and hence operates under unusual */
/* constraints. In particular, it must be lock-free if */
/* GC_win32_dll_threads is set. Always called from the thread being */
/* added. If GC_win32_dll_threads is not set, we already hold the */
/* allocation lock except possibly during single-threaded startup code. */
/* Does not initialize thread local free lists. */
-STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb,
- thread_id_t id)
+GC_INNER GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb,
+ thread_id_t self_id)
{
- GC_vthread me;
+ GC_thread me;
/* The following should be a no-op according to the Win32 */
/* documentation. There is empirical evidence that it */
@@ -325,20 +220,21 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb,
/* Unfortunately, GC_max_thread_index may be temporarily out of */
/* bounds, so readers have to compensate. */
while (i > GC_max_thread_index) {
- InterlockedIncrement((IE_t)&GC_max_thread_index);
+ InterlockedIncrement((LONG *)&GC_max_thread_index);
+ /* Cast away volatile for older versions of Win32 headers. */
}
if (EXPECT(GC_max_thread_index >= MAX_THREADS, FALSE)) {
/* We overshot due to simultaneous increments. */
/* Setting it to MAX_THREADS-1 is always safe. */
GC_max_thread_index = MAX_THREADS - 1;
}
- me = dll_thread_table + i;
+ me = (GC_thread)(dll_thread_table + i);
} else
# endif
/* else */ /* Not using DllMain */ {
GC_ASSERT(I_HOLD_LOCK());
GC_in_thread_creation = TRUE; /* OK to collect from unknown thread. */
- me = GC_new_thread(id);
+ me = GC_new_thread(self_id);
GC_in_thread_creation = FALSE;
if (NULL == me)
ABORT("Failed to allocate memory for thread registering");
@@ -358,12 +254,12 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb,
}
# endif
me -> last_stack_min = ADDR_LIMIT;
- GC_record_stack_base((GC_thread)me, sb);
+ GC_record_stack_base(me, sb);
/* Up until this point, GC_push_all_stacks considers this thread */
/* invalid. */
/* Up until this point, this entry is viewed as reserved but invalid */
/* by GC_delete_thread. */
- me -> id = id;
+ ((volatile struct GC_Thread_Rep *)me) -> id = self_id;
# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
if (GC_please_stop) {
@@ -380,7 +276,7 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb,
/* Otherwise both we and the thread stopping code would be */
/* holding the allocation lock. */
}
- return (GC_thread)me;
+ return me;
}
/*
@@ -394,516 +290,29 @@ GC_INLINE LONG GC_get_max_thread_index(void)
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. */
-GC_INNER GC_thread GC_lookup_thread(thread_id_t id)
-{
-# ifndef GC_NO_THREADS_DISCOVERY
- if (GC_win32_dll_threads) {
+#ifndef GC_NO_THREADS_DISCOVERY
+ /* Search in dll_thread_table and return the GC_thread entity */
+ /* corresponding to the given 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. */
+ GC_INNER GC_thread GC_win32_dll_lookup_thread(thread_id_t id)
+ {
int i;
LONG my_max = GC_get_max_thread_index();
+ GC_ASSERT(GC_win32_dll_threads);
for (i = 0; i <= my_max; i++) {
- GC_vthread t = dll_thread_table + i;
- if (AO_load_acquire(&(t -> tm.in_use)) && t -> id == id)
+ if (AO_load_acquire(&dll_thread_table[i].tm.in_use)
+ && dll_thread_table[i].id == id)
break; /* Must still be in use, since nobody else can */
/* store our thread id. */
}
return i <= my_max ? (GC_thread)(dll_thread_table + i) : NULL;
- } else
-# endif
- /* else */ {
- GC_thread p;
-
- GC_ASSERT(I_HOLD_LOCK());
- for (p = GC_threads[THREAD_TABLE_INDEX(id)];
- p != NULL; p = p -> tm.next) {
- if (p -> id == id) break;
- }
- return p;
- }
-}
-
-#ifdef LINT2
-# define CHECK_LOOKUP_MY_THREAD(me) \
- if (!(me)) ABORT("GC_lookup_thread(GetCurrentThreadId) failed")
-#else
-# define CHECK_LOOKUP_MY_THREAD(me) /* empty */
-#endif
-
-#ifndef GC_NO_FINALIZATION
- /* Called by GC_finalize() (in case of an allocation failure observed). */
- /* GC_reset_finalizer_nested() is the same as in pthread_support.c. */
- GC_INNER void GC_reset_finalizer_nested(void)
- {
- GC_thread me = GC_lookup_thread(GetCurrentThreadId());
-
- CHECK_LOOKUP_MY_THREAD(me);
- me->finalizer_nested = 0;
- }
-
- /* GC_check_finalizer_nested() is the same as in pthread_support.c. */
- GC_INNER unsigned char *GC_check_finalizer_nested(void)
- {
- GC_thread me;
- unsigned nesting_level;
-
- GC_ASSERT(I_HOLD_LOCK());
- me = GC_lookup_thread(GetCurrentThreadId());
- CHECK_LOOKUP_MY_THREAD(me);
- nesting_level = me->finalizer_nested;
- if (nesting_level) {
- /* We are inside another GC_invoke_finalizers(). */
- /* Skip some implicitly-called GC_invoke_finalizers() */
- /* depending on the nesting (recursion) level. */
- if (++me->finalizer_skipped < (1U << nesting_level)) return NULL;
- me->finalizer_skipped = 0;
- }
- me->finalizer_nested = (unsigned char)(nesting_level + 1);
- return &me->finalizer_nested;
- }
-#endif /* !GC_NO_FINALIZATION */
-
-#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC)
- /* This is called from thread-local GC_malloc(). */
- GC_bool GC_is_thread_tsd_valid(void *tsd)
- {
- GC_thread me;
- DCL_LOCK_STATE;
-
- LOCK();
- me = GC_lookup_thread(GetCurrentThreadId());
- UNLOCK();
- return (word)tsd >= (word)(&me->tlfs)
- && (word)tsd < (word)(&me->tlfs) + sizeof(me->tlfs);
- }
-#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
-
-GC_API int GC_CALL GC_thread_is_registered(void)
-{
- thread_id_t self_id = GetCurrentThreadId();
- GC_thread me;
- DCL_LOCK_STATE;
-
- LOCK();
- me = GC_lookup_thread(self_id);
- UNLOCK();
- return me != NULL;
-}
-
-GC_API void GC_CALL GC_register_altstack(void *normstack,
- GC_word normstack_size, void *altstack, GC_word altstack_size)
-{
- /* TODO: Implement */
- UNUSED_ARG(normstack);
- UNUSED_ARG(normstack_size);
- UNUSED_ARG(altstack);
- UNUSED_ARG(altstack_size);
-}
-
-/* Make sure thread descriptor t is not protected by the VDB */
-/* implementation. */
-/* Used to prevent write faults when the world is (partially) stopped, */
-/* since it may have been stopped with a system lock held, and that */
-/* lock may be required for fault handling. */
-#if defined(MPROTECT_VDB)
-# define UNPROTECT_THREAD(t) \
- if (!GC_win32_dll_threads && GC_auto_incremental \
- && t != &first_thread) { \
- GC_ASSERT(SMALL_OBJ(GC_size(t))); \
- GC_remove_protection(HBLKPTR(t), 1, FALSE); \
- } else (void)0
-#else
-# define UNPROTECT_THREAD(t) (void)0
-#endif
-
-#ifdef CYGWIN32
-# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id
-#elif defined(GC_WIN32_PTHREADS) || defined(GC_PTHREADS_PARAMARK)
-# if defined(__WINPTHREADS_VERSION_MAJOR)
-# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id
-# else
-# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id.p
-# endif
-#endif
-
-/* 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 thread id. */
-/* This is OK, but we need a way to delete a specific one. */
-/* Does not actually free GC_thread entry, only unlinks it. */
-/* If GC_win32_dll_threads is set it should be called from the */
-/* thread being deleted. */
-STATIC void GC_delete_gc_thread_no_free(GC_vthread t)
-{
-# ifndef MSWINCE
- CloseHandle(t->handle);
-# endif
-# ifndef GC_NO_THREADS_DISCOVERY
- 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 asynchronous changes to (*t) are possible. */
- /* It's not allowed to call GC_printf (and the friends) here, */
- /* see GC_stop_world() for the information. */
- t -> stack_end = NULL;
- t -> id = 0;
- t -> flags = 0; /* !IS_SUSPENDED */
-# ifdef RETRY_GET_THREAD_CONTEXT
- t -> context_sp = NULL;
-# endif
- AO_store_release(&t->tm.in_use, FALSE);
- } else
-# endif
- /* else */ {
- /* Cast away volatile qualifier, since we have lock. */
- int hv = THREAD_TABLE_INDEX(((GC_thread)t) -> id);
- GC_thread p = GC_threads[hv];
- GC_thread prev = NULL;
-
- GC_ASSERT(I_HOLD_LOCK());
- while (p != (GC_thread)t) {
- prev = p;
- p = p -> tm.next;
- }
- if (prev == 0) {
- GC_threads[hv] = p -> tm.next;
- } else {
- GC_ASSERT(prev != &first_thread);
- prev -> tm.next = p -> tm.next;
- GC_dirty(prev);
- }
- }
-}
-
-/* Delete a thread from GC_threads. We assume it is there. */
-/* (The code intentionally traps if it wasn't.) */
-/* If GC_win32_dll_threads is set then it should be called from */
-/* the thread being deleted. It is also safe to delete the */
-/* main thread (unless GC_win32_dll_threads). */
-STATIC void GC_delete_thread(thread_id_t id)
-{
- if (GC_win32_dll_threads) {
- GC_vthread t = GC_lookup_thread(id);
-
- if (EXPECT(NULL == t, FALSE)) {
- WARN("Removing nonexistent thread, id= %" WARN_PRIuPTR "\n", id);
- } else {
- GC_delete_gc_thread_no_free(t);
- }
- } else {
- int hv = THREAD_TABLE_INDEX(id);
- GC_thread p;
- GC_thread prev = NULL;
-
- GC_ASSERT(I_HOLD_LOCK());
- for (p = GC_threads[hv]; ; p = p -> tm.next) {
- if (p -> id == id) break;
- prev = p;
- }
-# ifndef MSWINCE
- CloseHandle(p->handle);
-# endif
- if (NULL == prev) {
- GC_threads[hv] = p -> tm.next;
- } else {
- GC_ASSERT(prev != &first_thread);
- prev -> tm.next = p -> tm.next;
- GC_dirty(prev);
- }
- if (EXPECT(p != &first_thread, TRUE)) {
- GC_INTERNAL_FREE(p);
- }
- }
-}
-
-GC_API void GC_CALL GC_allow_register_threads(void)
-{
-# ifdef GC_ASSERTIONS
- DCL_LOCK_STATE;
-
- /* Check GC is initialized and the current thread is registered. */
- LOCK();
- GC_ASSERT(GC_lookup_thread(GetCurrentThreadId()) != 0);
- UNLOCK();
-# endif
- GC_start_mark_threads();
- set_need_to_lock();
-}
-
-GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb)
-{
- GC_thread me;
- thread_id_t self_id = GetCurrentThreadId();
- DCL_LOCK_STATE;
-
- if (GC_need_to_lock == FALSE)
- ABORT("Threads explicit registering is not previously enabled");
-
- /* We lock here, since we want to wait for an ongoing GC. */
- LOCK();
- me = GC_lookup_thread(self_id);
- if (EXPECT(NULL == me, TRUE)) {
- me = GC_register_my_thread_inner(sb, self_id);
-# ifdef GC_PTHREADS
-# if defined(CPPCHECK)
- GC_noop1(me->flags);
-# endif
- me -> flags |= DETACHED;
- /* Treat as detached, since we do not need to worry about */
- /* pointer results. */
-# else
- (void)me;
-# endif
- } else
-# ifdef GC_PTHREADS
- /* else */ if (KNOWN_FINISHED(me)) {
- GC_record_stack_base(me, sb);
- me -> flags &= ~FINISHED; /* but not DETACHED */
- } else
-# endif
- /* else */ {
- UNLOCK();
- return GC_DUPLICATE;
- }
-
-# ifdef THREAD_LOCAL_ALLOC
- GC_init_thread_local(&me->tlfs);
-# endif
- UNLOCK();
- return GC_SUCCESS;
-}
-
-#ifdef GC_DISABLE_INCREMENTAL
-# define GC_wait_for_gc_completion(wait_for_all) (void)(wait_for_all)
-#else
-/* Similar to that in pthread_support.c. */
-STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all)
-{
- GC_ASSERT(I_HOLD_LOCK());
- if (GC_incremental && GC_collection_in_progress()) {
- word old_gc_no = GC_gc_no;
-
- /* Make sure that no part of our stack is still on the mark stack, */
- /* since it's about to be unmapped. */
- do {
- ENTER_GC();
- GC_in_thread_creation = TRUE;
- GC_collect_a_little_inner(1);
- GC_in_thread_creation = FALSE;
- EXIT_GC();
-
- UNLOCK();
- Sleep(0); /* yield */
- LOCK();
- } while (GC_incremental && GC_collection_in_progress()
- && (wait_for_all || old_gc_no == GC_gc_no));
- }
-}
-#endif /* !GC_DISABLE_INCREMENTAL */
-
-GC_API int GC_CALL GC_unregister_my_thread(void)
-{
- DCL_LOCK_STATE;
-
-# ifdef DEBUG_THREADS
- GC_log_printf("Unregistering thread 0x%lx\n", (long)GetCurrentThreadId());
-# endif
-
- if (GC_win32_dll_threads) {
-# if defined(THREAD_LOCAL_ALLOC)
- /* Can't happen: see GC_use_threads_discovery(). */
- GC_ASSERT(FALSE);
-# else
- /* FIXME: Should we just ignore this? */
- GC_delete_thread(GetCurrentThreadId());
-# endif
- } else {
-# if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS)
- GC_thread me;
-# endif
- thread_id_t self_id = GetCurrentThreadId();
-
- LOCK();
- GC_wait_for_gc_completion(FALSE);
-# if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS)
- me = GC_lookup_thread(self_id);
- CHECK_LOOKUP_MY_THREAD(me);
- GC_ASSERT(!KNOWN_FINISHED(me));
-# endif
-# if defined(THREAD_LOCAL_ALLOC)
- GC_ASSERT(GC_getspecific(GC_thread_key) == &me->tlfs);
- GC_destroy_thread_local(&me->tlfs);
-# endif
-# ifdef GC_PTHREADS
- if ((me -> flags & DETACHED) == 0) {
- me -> flags |= FINISHED;
- } else
-# endif
- /* else */ {
- GC_delete_thread(self_id);
- }
-# if defined(THREAD_LOCAL_ALLOC)
- /* It is required to call remove_specific defined in specific.c. */
- GC_remove_specific(GC_thread_key);
-# endif
- UNLOCK();
}
- return GC_SUCCESS;
-}
-
-/* Wrapper for functions that are likely to block for an appreciable */
-/* length of time. */
-
-/* GC_do_blocking_inner() is nearly the same as in pthread_support.c */
-GC_INNER void GC_do_blocking_inner(ptr_t data, void *context)
-{
- struct blocking_data * d = (struct blocking_data *)data;
- thread_id_t self_id = GetCurrentThreadId();
- GC_thread me;
-# ifdef IA64
- ptr_t bs_hi = GC_save_regs_in_stack();
-# endif
- DCL_LOCK_STATE;
-
- UNUSED_ARG(context);
- LOCK();
- me = GC_lookup_thread(self_id);
- CHECK_LOOKUP_MY_THREAD(me);
- GC_ASSERT((me -> flags & DO_BLOCKING) == 0);
-# ifdef IA64
- me -> backing_store_ptr = bs_hi;
-# endif
- me -> stack_ptr = (ptr_t)(&d); /* save approx. sp */
- /* Save context here if we want to support precise stack marking */
- me -> flags |= DO_BLOCKING;
- UNLOCK();
- d -> client_data = (d -> fn)(d -> client_data);
- LOCK(); /* This will block if the world is stopped. */
- me -> flags &= ~DO_BLOCKING;
- UNLOCK();
-}
-
-/* GC_call_with_gc_active() has the opposite to GC_do_blocking() */
-/* functionality. It might be called from a user function invoked by */
-/* GC_do_blocking() to temporarily back allow calling any GC function */
-/* and/or manipulating pointers to the garbage collected heap. */
-GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
- void * client_data)
-{
- struct GC_traced_stack_sect_s stacksect;
- thread_id_t self_id = GetCurrentThreadId();
- GC_thread me;
- DCL_LOCK_STATE;
-
- LOCK(); /* This will block if the world is stopped. */
- me = GC_lookup_thread(self_id);
- CHECK_LOOKUP_MY_THREAD(me);
- /* Adjust our stack bottom pointer (this could happen unless */
- /* GC_get_stack_base() was used which returned GC_SUCCESS). */
- GC_ASSERT(me -> stack_end != NULL);
- if ((word)me -> stack_end < (word)(&stacksect)) {
- me -> stack_end = (ptr_t)(&stacksect);
-# if defined(I386)
- me -> initial_stack_base = me -> stack_end;
-# endif
- }
-
- if ((me -> flags & DO_BLOCKING) == 0) {
- /* We are not inside GC_do_blocking() - do nothing more. */
- UNLOCK();
- client_data = fn(client_data);
- /* Prevent treating the above as a tail call. */
- GC_noop1(COVERT_DATAFLOW(&stacksect));
- return client_data; /* result */
- }
-
- /* Setup new "stack section". */
- stacksect.saved_stack_ptr = me -> stack_ptr;
-# ifdef IA64
- /* This is the same as in GC_call_with_stack_base(). */
- stacksect.backing_store_end = GC_save_regs_in_stack();
- /* Unnecessarily flushes register stack, */
- /* but that probably doesn't hurt. */
- stacksect.saved_backing_store_ptr = me -> backing_store_ptr;
-# endif
- stacksect.prev = me -> traced_stack_sect;
- me -> flags &= ~DO_BLOCKING;
- me -> traced_stack_sect = &stacksect;
-
- UNLOCK();
- client_data = fn(client_data);
- GC_ASSERT((me -> flags & DO_BLOCKING) == 0);
- GC_ASSERT(me -> traced_stack_sect == &stacksect);
-
- /* Restore original "stack section". */
- LOCK();
-# if defined(CPPCHECK)
- GC_noop1((word)me->traced_stack_sect);
-# endif
- me -> traced_stack_sect = stacksect.prev;
-# ifdef IA64
- me -> backing_store_ptr = stacksect.saved_backing_store_ptr;
-# endif
- me -> flags |= DO_BLOCKING;
- me -> stack_ptr = stacksect.saved_stack_ptr;
- UNLOCK();
-
- return client_data; /* result */
-}
-
-GC_API void GC_CALL GC_set_stackbottom(void *gc_thread_handle,
- const struct GC_stack_base *sb)
-{
- GC_thread t = (GC_thread)gc_thread_handle;
-
- GC_ASSERT(sb -> mem_base != NULL);
- if (!EXPECT(GC_is_initialized, TRUE)) {
- GC_ASSERT(NULL == t);
- GC_stackbottom = (char *)sb->mem_base;
-# ifdef IA64
- GC_register_stackbottom = (ptr_t)sb->reg_base;
-# endif
- return;
- }
-
- GC_ASSERT(I_HOLD_LOCK());
- if (NULL == t) { /* current thread? */
- t = GC_lookup_thread(GetCurrentThreadId());
- CHECK_LOOKUP_MY_THREAD(t);
- }
- GC_ASSERT(!KNOWN_FINISHED(t));
- GC_ASSERT((t -> flags & DO_BLOCKING) == 0
- && NULL == t -> traced_stack_sect); /* for now */
- t -> stack_end = (ptr_t)sb->mem_base;
- t -> last_stack_min = ADDR_LIMIT; /* reset the known minimum */
-# ifdef IA64
- t -> backing_store_end = (ptr_t)sb->reg_base;
-# endif
-}
-
-GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb)
-{
- thread_id_t self_id = GetCurrentThreadId();
- GC_thread me;
- DCL_LOCK_STATE;
-
- LOCK();
- me = GC_lookup_thread(self_id);
- CHECK_LOOKUP_MY_THREAD(me); /* the thread is assumed to be registered */
- sb -> mem_base = me -> stack_end;
-# ifdef IA64
- sb -> reg_base = me -> backing_store_end;
-# endif
- UNLOCK();
- return (void *)me; /* gc_thread_handle */
-}
+#endif /* !GC_NO_THREADS_DISCOVERY */
#ifdef GC_PTHREADS
-
/* A quick-and-dirty cache of the mapping between pthread_t */
/* and Win32 thread id. */
# define PTHREAD_MAP_SIZE 512
@@ -916,12 +325,18 @@ GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb)
# define GET_PTHREAD_MAP_CACHE(pthread_id) \
GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)]
- /* Return a GC_thread corresponding to a given pthread_t. */
- /* Returns NULL if it is not there. */
- /* We assume that this is only called for pthread ids that */
- /* have not yet terminated or are still joinable, and */
- /* cannot be terminated concurrently. */
- STATIC GC_thread GC_lookup_by_pthread(pthread_t thread)
+ GC_INNER void GC_win32_cache_self_pthread(thread_id_t self_id)
+ {
+ pthread_t self = pthread_self();
+
+ SET_PTHREAD_MAP_CACHE(self, self_id);
+ }
+
+ /* Return a GC_thread corresponding to a given pthread_t, or */
+ /* NULL if it is not there. We assume that this is only */
+ /* called for pthread ids that have not yet terminated or are */
+ /* still joinable, and cannot be terminated concurrently. */
+ GC_INNER GC_thread GC_lookup_by_pthread(pthread_t thread)
{
/* TODO: search in dll_thread_table instead when DllMain-based */
/* thread registration is made compatible with pthreads (and */
@@ -953,164 +368,8 @@ GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb)
UNLOCK();
return p;
}
-
#endif /* GC_PTHREADS */
-#ifdef CAN_HANDLE_FORK
- /* Similar to that in pthread_support.c but also rehashes the table */
- /* since hash map key (thread id) differs from that in the parent. */
- STATIC void GC_remove_all_threads_but_me(void)
- {
- int hv;
- GC_thread me = NULL;
- thread_id_t self_id;
- pthread_t self = pthread_self(); /* same as in parent */
-
- GC_ASSERT(!GC_win32_dll_threads);
- for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
- GC_thread p, next;
-
- for (p = GC_threads[hv]; p != NULL; p = next) {
- next = p -> tm.next;
- if (THREAD_EQUAL(p -> pthread_id, self)
- && me == NULL) { /* ignore dead threads with the same id */
- me = p;
- p -> tm.next = 0;
- } else {
-# ifdef THREAD_LOCAL_ALLOC
- if (!KNOWN_FINISHED(p)) {
- /* Cannot call GC_destroy_thread_local here (see the */
- /* corresponding comment in pthread_support.c). */
- GC_remove_specific_after_fork(GC_thread_key, p -> pthread_id);
- }
-# endif
- if (&first_thread != p)
- GC_INTERNAL_FREE(p);
- }
- }
- GC_threads[hv] = NULL;
- }
-
- /* Put "me" back to GC_threads. */
- GC_ASSERT(me != NULL);
- self_id = GetCurrentThreadId(); /* differs from that in parent */
- GC_threads[THREAD_TABLE_INDEX(self_id)] = me;
-
- /* Update Win32 thread Id and handle. */
- me -> id = self_id;
-# ifndef MSWINCE
- if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
- GetCurrentProcess(), (HANDLE *)&me->handle,
- 0 /* dwDesiredAccess */, FALSE /* bInheritHandle */,
- DUPLICATE_SAME_ACCESS))
- ABORT("DuplicateHandle failed");
-# endif
-
-# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC)
- /* For Cygwin, we need to re-assign thread-local pointer to */
- /* 'tlfs' (it is OK to call GC_destroy_thread_local and */
- /* GC_free_internal before this action). */
- if (GC_setspecific(GC_thread_key, &me->tlfs) != 0)
- ABORT("GC_setspecific failed (in child)");
-# endif
- }
-
- static void fork_prepare_proc(void)
- {
- LOCK();
-# ifdef PARALLEL_MARK
- if (GC_parallel)
- GC_wait_for_reclaim();
-# endif
- GC_wait_for_gc_completion(TRUE);
-# ifdef PARALLEL_MARK
- if (GC_parallel)
- GC_acquire_mark_lock();
-# endif
- }
-
- static void fork_parent_proc(void)
- {
-# ifdef PARALLEL_MARK
- if (GC_parallel)
- GC_release_mark_lock();
-# endif
- UNLOCK();
- }
-
- static void fork_child_proc(void)
- {
-# ifdef PARALLEL_MARK
- if (GC_parallel) {
- GC_release_mark_lock();
- GC_parallel = FALSE; /* or GC_markers_m1 = 0 */
- /* Turn off parallel marking in the child, since we are */
- /* probably just going to exec, and we would have to */
- /* restart mark threads. */
- }
-# endif
- GC_remove_all_threads_but_me();
- UNLOCK();
- }
-
- /* Routines for fork handling by client (no-op if pthread_atfork works). */
- GC_API void GC_CALL GC_atfork_prepare(void)
- {
- if (!EXPECT(GC_is_initialized, TRUE)) GC_init();
- if (GC_handle_fork <= 0)
- fork_prepare_proc();
- }
-
- GC_API void GC_CALL GC_atfork_parent(void)
- {
- if (GC_handle_fork <= 0)
- fork_parent_proc();
- }
-
- GC_API void GC_CALL GC_atfork_child(void)
- {
- if (GC_handle_fork <= 0)
- fork_child_proc();
- }
-
- /* Prepare for forks if requested. */
- STATIC void GC_setup_atfork(void)
- {
- if (GC_handle_fork) {
-# ifdef CAN_CALL_ATFORK
- if (pthread_atfork(fork_prepare_proc, fork_parent_proc,
- fork_child_proc) == 0) {
- /* Handlers successfully registered. */
- GC_handle_fork = 1;
- } else
-# endif
- /* else */ if (GC_handle_fork != -1)
- ABORT("pthread_atfork failed");
- }
- }
-#endif /* CAN_HANDLE_FORK */
-
-void GC_push_thread_structures(void)
-{
- GC_ASSERT(I_HOLD_LOCK());
-# ifndef GC_NO_THREADS_DISCOVERY
- if (GC_win32_dll_threads) {
- /* Unlike the other threads implementations, the thread table */
- /* here contains no pointers to the collectible heap (note also */
- /* that GC_PTHREADS is incompatible with DllMain-based thread */
- /* registration). Thus we have no private structures we need */
- /* to preserve. */
- } else
-# endif
- /* else */ {
- GC_PUSH_ALL_SYM(GC_threads);
- }
-# if defined(THREAD_LOCAL_ALLOC) && defined(USE_CUSTOM_SPECIFIC)
- GC_PUSH_ALL_SYM(GC_thread_key);
- /* Just in case we ever use our own TLS implementation. */
-# endif
-}
-
#ifdef WOW64_THREAD_CONTEXT_WORKAROUND
# ifndef CONTEXT_EXCEPTION_ACTIVE
# define CONTEXT_EXCEPTION_ACTIVE 0x08000000
@@ -1140,7 +399,7 @@ STATIC void GC_suspend(GC_thread t)
# ifdef DEBUG_THREADS
GC_log_printf("Suspending 0x%x\n", (int)t->id);
# endif
- UNPROTECT_THREAD(t);
+ GC_win32_unprotect_thread(t);
GC_acquire_dirty_lock();
# ifdef MSWINCE
@@ -1171,11 +430,11 @@ STATIC void GC_suspend(GC_thread t)
return;
}
- if (SuspendThread(t->handle) != (DWORD)-1) {
+ if (SuspendThread(t -> handle) != (DWORD)-1) {
CONTEXT context;
context.ContextFlags = GET_THREAD_CONTEXT_FLAGS;
- if (GetThreadContext(t->handle, &context)) {
+ if (GetThreadContext(t -> handle, &context)) {
/* TODO: WoW64 extra workaround: if CONTEXT_EXCEPTION_ACTIVE */
/* then Sleep(1) and retry. */
t->context_sp = copy_ptr_regs(t->context_regs, &context);
@@ -1183,7 +442,7 @@ STATIC void GC_suspend(GC_thread t)
}
/* Resume the thread, try to suspend it in a better location. */
- if (ResumeThread(t->handle) == (DWORD)-1)
+ if (ResumeThread(t -> handle) == (DWORD)-1)
ABORT("ResumeThread failed in suspend loop");
}
if (retry_cnt > 1) {
@@ -1333,7 +592,7 @@ GC_INNER void GC_start_world(void)
GC_ASSERT(p -> stack_end != NULL && p -> id != self_id);
if (ResumeThread(THREAD_HANDLE(p)) == (DWORD)-1)
ABORT("ResumeThread failed");
- UNPROTECT_THREAD(p);
+ GC_win32_unprotect_thread(p);
p -> flags &= ~IS_SUSPENDED;
if (GC_on_thread_event)
GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, THREAD_HANDLE(p));
@@ -1601,7 +860,7 @@ STATIC word GC_push_stack_for(GC_thread thread, thread_id_t self_id,
/* else */ {
stack_min = GC_get_stack_min(traced_stack_sect != NULL ?
(ptr_t)traced_stack_sect : thread -> stack_end);
- UNPROTECT_THREAD(thread);
+ GC_win32_unprotect_thread(thread);
thread -> last_stack_min = stack_min;
}
} else {
@@ -1609,7 +868,7 @@ STATIC word GC_push_stack_for(GC_thread thread, thread_id_t self_id,
/* are inside GC_call_with_gc_active(). */
if (traced_stack_sect != NULL &&
(word)thread->last_stack_min > (word)traced_stack_sect) {
- UNPROTECT_THREAD(thread);
+ GC_win32_unprotect_thread(thread);
thread -> last_stack_min = (ptr_t)traced_stack_sect;
}
@@ -1629,7 +888,7 @@ STATIC word GC_push_stack_for(GC_thread thread, thread_id_t self_id,
/* Stack shrunk? Is this possible? */
stack_min = GC_get_stack_min(thread -> stack_end);
}
- UNPROTECT_THREAD(thread);
+ GC_win32_unprotect_thread(thread);
thread -> last_stack_min = stack_min;
}
}
@@ -1721,15 +980,7 @@ GC_INNER void GC_push_all_stacks(void)
}
#ifdef PARALLEL_MARK
-
-# ifndef MAX_MARKERS
-# define MAX_MARKERS 16
-# endif
-
- static ptr_t marker_sp[MAX_MARKERS - 1]; /* The cold end of the stack */
- /* for markers. */
-
- static ptr_t marker_last_stack_min[MAX_MARKERS - 1];
+ GC_INNER ptr_t GC_marker_last_stack_min[MAX_MARKERS - 1] = {0};
/* Last known minimum (hottest) address */
/* in stack (or ADDR_LIMIT if unset) */
/* for markers. */
@@ -1765,7 +1016,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
plast_stack_min = (ptr_t * /* no volatile */)
&dll_thread_table[i].last_stack_min;
current_min = s;
-# if defined(CPPCHECK)
+# ifdef CPPCHECK
/* To avoid a warning that thread is always null. */
thread = (GC_thread)&dll_thread_table[i];
# endif
@@ -1788,13 +1039,13 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
}
# ifdef PARALLEL_MARK
for (i = 0; i < GC_markers_m1; ++i) {
- ptr_t s = marker_sp[i];
+ ptr_t s = GC_marker_sp[i];
# ifdef IA64
/* FIXME: not implemented */
# endif
if ((word)s > (word)start && (word)s < (word)current_min) {
- GC_ASSERT(marker_last_stack_min[i] != NULL);
- plast_stack_min = &marker_last_stack_min[i];
+ GC_ASSERT(GC_marker_last_stack_min[i] != NULL);
+ plast_stack_min = &GC_marker_last_stack_min[i];
current_min = s;
thread = NULL; /* Not a thread's hash table entry. */
}
@@ -1837,308 +1088,26 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
}
/* Remember current stack_min value. */
- if (thread != NULL) {
- UNPROTECT_THREAD(thread);
- }
+ if (thread != NULL)
+ GC_win32_unprotect_thread(thread);
*plast_stack_min = *lo;
}
-#ifdef PARALLEL_MARK
+#if defined(PARALLEL_MARK) && !defined(GC_PTHREADS_PARAMARK)
+
+# ifndef MARK_THREAD_STACK_SIZE
+# define MARK_THREAD_STACK_SIZE 0 /* default value */
+# endif
-# if !defined(GC_PTHREADS_PARAMARK)
STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1] = {0};
/* Events with manual reset (one for each */
/* mark helper). */
- STATIC thread_id_t GC_marker_Id[MAX_MARKERS - 1] = {0};
+ GC_INNER thread_id_t GC_marker_Id[MAX_MARKERS - 1] = {0};
/* This table is used for mapping helper */
/* threads ID to mark helper index (linear */
/* search is used since the mapping contains */
/* only a few entries). */
-# endif
-
-# if defined(GC_PTHREADS) && defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)
- static void set_marker_thread_name(unsigned id)
- {
- /* This code is the same as in pthread_support.c. */
- char name_buf[16]; /* pthread_setname_np may fail for longer names */
- int len = sizeof("GC-marker-") - 1;
-
- /* Compose the name manually as snprintf may be unavailable or */
- /* "%u directive output may be truncated" warning may occur. */
- BCOPY("GC-marker-", name_buf, len);
- if (id >= 10)
- name_buf[len++] = (char)('0' + (id / 10) % 10);
- name_buf[len] = (char)('0' + id % 10);
- name_buf[len + 1] = '\0';
-
- if (pthread_setname_np(pthread_self(), name_buf) != 0)
- WARN("pthread_setname_np failed\n", 0);
- }
-
-# elif !defined(MSWINCE)
- /* A pointer to SetThreadDescription() which is available since */
- /* Windows 10. The function prototype is in processthreadsapi.h. */
- static FARPROC setThreadDescription_fn;
-
- static void set_marker_thread_name(unsigned id)
- {
- WCHAR name_buf[16];
- int len = sizeof(L"GC-marker-") / sizeof(WCHAR) - 1;
- HRESULT hr;
-
- if (!setThreadDescription_fn) return; /* missing SetThreadDescription */
-
- /* Compose the name manually as swprintf may be unavailable. */
- BCOPY(L"GC-marker-", name_buf, len * sizeof(WCHAR));
- if (id >= 10)
- name_buf[len++] = (WCHAR)('0' + (id / 10) % 10);
- name_buf[len] = (WCHAR)('0' + id % 10);
- name_buf[len + 1] = 0;
-
- /* Invoke SetThreadDescription(). Cast the function pointer to word */
- /* first to avoid "incompatible function types" compiler warning. */
- hr = (*(HRESULT (WINAPI *)(HANDLE, const WCHAR *))
- (word)setThreadDescription_fn)(GetCurrentThread(), name_buf);
- if (FAILED(hr))
- WARN("SetThreadDescription failed\n", 0);
- }
-# else
-# define set_marker_thread_name(id) (void)(id)
-# endif
-
- /* GC_mark_thread() is the same as in pthread_support.c */
-# ifdef GC_PTHREADS_PARAMARK
- STATIC void * GC_mark_thread(void * id)
-# elif defined(MSWINCE)
- STATIC DWORD WINAPI GC_mark_thread(LPVOID id)
-# else
- STATIC unsigned __stdcall GC_mark_thread(void * id)
-# endif
- {
- word my_mark_no = 0;
-
- if ((word)id == GC_WORD_MAX) return 0; /* to prevent a compiler warning */
- set_marker_thread_name((unsigned)(word)id);
- marker_sp[(word)id] = GC_approx_sp();
-# if !defined(GC_PTHREADS_PARAMARK)
- GC_marker_Id[(word)id] = GetCurrentThreadId();
-# endif
-
- /* Inform GC_start_mark_threads about completion of marker data init. */
- GC_acquire_mark_lock();
- if (0 == --GC_fl_builder_count) /* count may have a negative value */
- GC_notify_all_builder();
-
- for (;; ++my_mark_no) {
- if (my_mark_no - GC_mark_no > (word)2) {
- /* resynchronize if we get far off, e.g. because GC_mark_no */
- /* wrapped. */
- my_mark_no = GC_mark_no;
- }
-# ifdef DEBUG_THREADS
- GC_log_printf("Starting helper for mark number %lu (thread %u)\n",
- (unsigned long)my_mark_no, (unsigned)(word)id);
-# endif
- GC_help_marker(my_mark_no);
- }
- }
-
-# ifndef GC_ASSERTIONS
-# define SET_MARK_LOCK_HOLDER (void)0
-# define UNSET_MARK_LOCK_HOLDER (void)0
-# endif
-
- static int available_markers_m1 = 0;
-
-# ifdef GC_PTHREADS_PARAMARK
-
-# if defined(GC_ASSERTIONS) && !defined(USE_PTHREAD_LOCKS)
-# define NUMERIC_THREAD_ID(id) (unsigned long)(word)GC_PTHREAD_PTRVAL(id)
- /* Id not guaranteed to be unique. */
-# endif
-
-# ifdef CAN_HANDLE_FORK
- static pthread_cond_t mark_cv;
- /* initialized by GC_start_mark_threads_inner */
-# else
- static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER;
-# endif
-
- /* GC_start_mark_threads is the same as in pthread_support.c except */
- /* for thread stack that is assumed to be large enough. */
-
- GC_INNER void GC_start_mark_threads_inner(void)
- {
- int i;
- pthread_attr_t attr;
- pthread_t new_thread;
-# ifndef NO_MARKER_SPECIAL_SIGMASK
- sigset_t set, oldset;
-# endif
-
- GC_ASSERT(I_HOLD_LOCK());
- ASSERT_CANCEL_DISABLED();
- if (available_markers_m1 <= 0 || GC_parallel) return;
- /* Skip if parallel markers disabled or already started. */
- GC_wait_for_gc_completion(TRUE);
-
-# ifdef CAN_HANDLE_FORK
- /* Reset mark_cv state after forking (as in pthread_support.c). */
- {
- pthread_cond_t mark_cv_local = PTHREAD_COND_INITIALIZER;
- BCOPY(&mark_cv_local, &mark_cv, sizeof(mark_cv));
- }
-# endif
-
- GC_ASSERT(GC_fl_builder_count == 0);
- if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed");
- if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
- ABORT("pthread_attr_setdetachstate failed");
-
-# ifndef NO_MARKER_SPECIAL_SIGMASK
- /* Apply special signal mask to GC marker threads, and don't drop */
- /* user defined signals by GC marker threads. */
- if (sigfillset(&set) != 0)
- ABORT("sigfillset failed");
- if (EXPECT(pthread_sigmask(SIG_BLOCK, &set, &oldset) < 0, FALSE)) {
- WARN("pthread_sigmask set failed, no markers started\n", 0);
- GC_markers_m1 = 0;
- (void)pthread_attr_destroy(&attr);
- return;
- }
-# endif /* !NO_MARKER_SPECIAL_SIGMASK */
-
- /* To have proper GC_parallel value in GC_help_marker. */
- GC_markers_m1 = available_markers_m1;
-
- for (i = 0; i < available_markers_m1; ++i) {
- marker_last_stack_min[i] = ADDR_LIMIT;
- if (EXPECT(0 != pthread_create(&new_thread, &attr, GC_mark_thread,
- (void *)(word)i), FALSE)) {
- WARN("Marker thread %" WARN_PRIdPTR " creation failed\n",
- (signed_word)i);
- /* Don't try to create other marker threads. */
- GC_markers_m1 = i;
- break;
- }
- }
-
-# ifndef NO_MARKER_SPECIAL_SIGMASK
- /* Restore previous signal mask. */
- if (EXPECT(pthread_sigmask(SIG_SETMASK, &oldset, NULL) < 0, FALSE)) {
- WARN("pthread_sigmask restore failed\n", 0);
- }
-# endif
-
- (void)pthread_attr_destroy(&attr);
- GC_wait_for_markers_init();
- GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1);
- }
-
-# ifdef GC_ASSERTIONS
- STATIC unsigned long GC_mark_lock_holder = NO_THREAD;
-# define SET_MARK_LOCK_HOLDER \
- (void)(GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self()))
-# define UNSET_MARK_LOCK_HOLDER \
- do { \
- GC_ASSERT(GC_mark_lock_holder \
- == NUMERIC_THREAD_ID(pthread_self())); \
- GC_mark_lock_holder = NO_THREAD; \
- } while (0)
-# endif /* GC_ASSERTIONS */
-
- static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER;
-
- static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
-
- /* GC_acquire/release_mark_lock(), GC_wait_builder/marker(), */
- /* GC_wait_for_reclaim(), GC_notify_all_builder/marker() are the same */
- /* as in pthread_support.c except that GC_generic_lock() is not used. */
-
-# ifdef LOCK_STATS
- volatile AO_t GC_block_count = 0;
-# endif
-
- GC_INNER void GC_acquire_mark_lock(void)
- {
-# ifdef NUMERIC_THREAD_ID_UNIQUE
- GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self()));
-# endif
- if (pthread_mutex_lock(&mark_mutex) != 0) {
- ABORT("pthread_mutex_lock failed");
- }
-# ifdef LOCK_STATS
- (void)AO_fetch_and_add1(&GC_block_count);
-# endif
- /* GC_generic_lock(&mark_mutex); */
- SET_MARK_LOCK_HOLDER;
- }
-
- GC_INNER void GC_release_mark_lock(void)
- {
- UNSET_MARK_LOCK_HOLDER;
- if (pthread_mutex_unlock(&mark_mutex) != 0) {
- ABORT("pthread_mutex_unlock failed");
- }
- }
-
- /* Collector must wait for a freelist builders for 2 reasons: */
- /* 1) Mark bits may still be getting examined without lock. */
- /* 2) Partial free lists referenced only by locals may not be */
- /* scanned correctly, e.g. if they contain "pointer-free" objects, */
- /* since the free-list link may be ignored. */
- STATIC void GC_wait_builder(void)
- {
- UNSET_MARK_LOCK_HOLDER;
- if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0) {
- ABORT("pthread_cond_wait failed");
- }
- GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
- SET_MARK_LOCK_HOLDER;
- }
-
- GC_INNER void GC_wait_for_reclaim(void)
- {
- GC_acquire_mark_lock();
- while (GC_fl_builder_count > 0) {
- GC_wait_builder();
- }
- GC_release_mark_lock();
- }
-
- GC_INNER void GC_notify_all_builder(void)
- {
- GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
- if (pthread_cond_broadcast(&builder_cv) != 0) {
- ABORT("pthread_cond_broadcast failed");
- }
- }
-
- GC_INNER void GC_wait_marker(void)
- {
- GC_ASSERT(GC_parallel);
- UNSET_MARK_LOCK_HOLDER;
- if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) {
- ABORT("pthread_cond_wait failed");
- }
- GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
- SET_MARK_LOCK_HOLDER;
- }
-
- GC_INNER void GC_notify_all_marker(void)
- {
- GC_ASSERT(GC_parallel);
- if (pthread_cond_broadcast(&mark_cv) != 0) {
- ABORT("pthread_cond_broadcast failed");
- }
- }
-
-# else /* ! GC_PTHREADS_PARAMARK */
-
-# ifndef MARK_THREAD_STACK_SIZE
-# define MARK_THREAD_STACK_SIZE 0 /* default value */
-# endif
/* mark_mutex_event, builder_cv, mark_cv are initialized in GC_thr_init */
static HANDLE mark_mutex_event = (HANDLE)0; /* Event with auto-reset. */
@@ -2151,13 +1120,13 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
GC_ASSERT(I_HOLD_LOCK());
ASSERT_CANCEL_DISABLED();
- if (available_markers_m1 <= 0 || GC_parallel) return;
+ if (GC_available_markers_m1 <= 0 || GC_parallel) return;
GC_wait_for_gc_completion(TRUE);
GC_ASSERT(GC_fl_builder_count == 0);
/* Initialize GC_marker_cv[] fully before starting the */
/* first helper thread. */
- GC_markers_m1 = available_markers_m1;
+ GC_markers_m1 = GC_available_markers_m1;
for (i = 0; i < GC_markers_m1; ++i) {
if ((GC_marker_cv[i] = CreateEvent(NULL /* attrs */,
TRUE /* isManualReset */,
@@ -2171,7 +1140,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
HANDLE handle;
DWORD thread_id;
- marker_last_stack_min[i] = ADDR_LIMIT;
+ GC_marker_last_stack_min[i] = ADDR_LIMIT;
/* There is no _beginthreadex() in WinCE. */
handle = CreateThread(NULL /* lpsa */,
MARK_THREAD_STACK_SIZE /* ignored */,
@@ -2190,7 +1159,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
GC_uintptr_t handle;
unsigned thread_id;
- marker_last_stack_min[i] = ADDR_LIMIT;
+ GC_marker_last_stack_min[i] = ADDR_LIMIT;
handle = _beginthreadex(NULL /* security_attr */,
MARK_THREAD_STACK_SIZE, GC_mark_thread,
(void *)(word)i, 0 /* flags */, &thread_id);
@@ -2228,7 +1197,10 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
GC_ASSERT(GC_mark_lock_holder == GetCurrentThreadId()); \
GC_mark_lock_holder = NO_THREAD; \
} while (0)
-# endif /* GC_ASSERTIONS */
+# else
+# define SET_MARK_LOCK_HOLDER (void)0
+# define UNSET_MARK_LOCK_HOLDER (void)0
+# endif /* !GC_ASSERTIONS */
STATIC /* volatile */ LONG GC_mark_mutex_state = 0;
/* Mutex state: 0 - unlocked, */
@@ -2236,7 +1208,6 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
/* -1 - locked and waiters may exist. */
/* Accessed by InterlockedExchange(). */
- /* #define LOCK_STATS */
# ifdef LOCK_STATS
volatile AO_t GC_block_count = 0;
volatile AO_t GC_unlocked_count = 0;
@@ -2346,37 +1317,19 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
}
}
-# endif /* ! GC_PTHREADS_PARAMARK */
-
- static unsigned required_markers_cnt = 0;
- /* The default value (0) means the number of */
- /* markers should be selected automatically. */
-
- GC_API void GC_CALL GC_set_markers_count(unsigned markers)
- {
- /* The same implementation as in pthread_support.c. */
- required_markers_cnt = markers < MAX_MARKERS ? markers : MAX_MARKERS;
- }
-
-# define START_MARK_THREADS() \
- if (EXPECT(GC_parallel || available_markers_m1 <= 0, TRUE)) {} \
- else GC_start_mark_threads()
-#else
-
-# define START_MARK_THREADS() (void)0
-#endif /* !PARALLEL_MARK */
+#endif /* PARALLEL_MARK && !GC_PTHREADS_PARAMARK */
- /* We have no DllMain to take care of new threads. Thus, we */
- /* must properly intercept thread creation. */
+/* We have no DllMain to take care of new threads. Thus, we */
+/* must properly intercept thread creation. */
- struct win32_start_info {
- LPTHREAD_START_ROUTINE start_routine;
- LPVOID arg;
- };
+struct win32_start_info {
+ LPTHREAD_START_ROUTINE start_routine;
+ LPVOID arg;
+};
- STATIC void * GC_CALLBACK GC_win32_start_inner(struct GC_stack_base *sb,
- void *arg)
- {
+STATIC void *GC_CALLBACK GC_win32_start_inner(struct GC_stack_base *sb,
+ void *arg)
+{
void * ret;
LPTHREAD_START_ROUTINE start_routine =
((struct win32_start_info *)arg) -> start_routine;
@@ -2410,20 +1363,20 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
(long)GetCurrentThreadId());
# endif
return ret;
- }
+}
- STATIC DWORD WINAPI GC_win32_start(LPVOID arg)
- {
+STATIC DWORD WINAPI GC_win32_start(LPVOID arg)
+{
return (DWORD)(word)GC_call_with_stack_base(GC_win32_start_inner, arg);
- }
+}
- GC_API HANDLE WINAPI GC_CreateThread(
+GC_API HANDLE WINAPI GC_CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
GC_WIN32_SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlags,
LPDWORD lpThreadId)
- {
+{
if (!EXPECT(GC_is_initialized, TRUE)) GC_init();
GC_ASSERT(GC_thr_initialized);
/* Make sure GC is initialized (i.e. main thread is attached, */
@@ -2455,23 +1408,26 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
GC_dirty(psi);
REACHABLE_AFTER_DIRTY(lpParameter);
- START_MARK_THREADS();
+# ifdef PARALLEL_MARK
+ if (EXPECT(!GC_parallel && GC_available_markers_m1 > 0, FALSE))
+ GC_start_mark_threads();
+# endif
set_need_to_lock();
thread_h = CreateThread(lpThreadAttributes, dwStackSize, GC_win32_start,
psi, dwCreationFlags, lpThreadId);
if (EXPECT(0 == thread_h, FALSE)) GC_free(psi);
return thread_h;
}
- }
+}
- GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread(DWORD dwExitCode)
- {
+GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread(DWORD dwExitCode)
+{
GC_unregister_my_thread();
ExitThread(dwExitCode);
- }
+}
-# if !defined(CYGWIN32) && !defined(MSWINCE) && !defined(MSWIN_XBOX1) \
- && !defined(NO_CRT)
+#if !defined(CYGWIN32) && !defined(MSWINCE) && !defined(MSWIN_XBOX1) \
+ && !defined(NO_CRT)
GC_API GC_uintptr_t GC_CALL GC_beginthreadex(
void *security, unsigned stack_size,
unsigned (__stdcall *start_address)(void *),
@@ -2510,7 +1466,10 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
GC_dirty(psi);
REACHABLE_AFTER_DIRTY(arglist);
- START_MARK_THREADS();
+# ifdef PARALLEL_MARK
+ if (EXPECT(!GC_parallel && GC_available_markers_m1 > 0, FALSE))
+ GC_start_mark_threads();
+# endif
set_need_to_lock();
thread_h = _beginthreadex(security, stack_size,
(unsigned (__stdcall *)(void *))GC_win32_start,
@@ -2525,7 +1484,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
GC_unregister_my_thread();
_endthreadex(retval);
}
-# endif /* !CYGWIN32 && !MSWINCE && !MSWIN_XBOX1 && !NO_CRT */
+#endif /* !CYGWIN32 && !MSWINCE && !MSWIN_XBOX1 && !NO_CRT */
#ifdef GC_WINMAIN_REDIRECT
/* This might be useful on WinCE. Shouldn't be used with GC_DLL. */
@@ -2656,7 +1615,7 @@ GC_INNER void GC_thr_init(void)
# if defined(PARALLEL_MARK)
{
char * markers_string = GETENV("GC_MARKERS");
- int markers = required_markers_cnt;
+ int markers = GC_required_markers_cnt;
if (markers_string != NULL) {
markers = atoi(markers_string);
@@ -2705,11 +1664,11 @@ GC_INNER void GC_thr_init(void)
if (markers > MAX_MARKERS)
markers = MAX_MARKERS; /* silently limit the value */
}
- available_markers_m1 = markers - 1;
+ GC_available_markers_m1 = markers - 1;
}
/* Check whether parallel mode could be enabled. */
- if (GC_win32_dll_threads || available_markers_m1 <= 0) {
+ if (GC_win32_dll_threads || GC_available_markers_m1 <= 0) {
/* Disable parallel marking. */
GC_parallel = FALSE;
GC_COND_LOG_PRINTF(
@@ -2730,9 +1689,7 @@ GC_INNER void GC_thr_init(void)
ABORT("CreateEvent failed");
# endif
# if !defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) && !defined(MSWINCE)
- if (hK32)
- setThreadDescription_fn = GetProcAddress(hK32,
- "SetThreadDescription");
+ GC_init_win32_thread_naming(hK32);
# endif
}
# endif /* PARALLEL_MARK */
@@ -2742,227 +1699,7 @@ GC_INNER void GC_thr_init(void)
# undef main_thread_id
}
-#ifdef GC_PTHREADS
-
- struct start_info {
- void *(*start_routine)(void *);
- void *arg;
- int detached;
- };
-
- GC_API int GC_pthread_join(pthread_t thread, void **retval)
- {
- int result;
-# ifndef GC_WIN32_PTHREADS
- GC_thread t;
-# endif
- DCL_LOCK_STATE;
-
- GC_ASSERT(!GC_win32_dll_threads);
-# ifdef DEBUG_THREADS
- GC_log_printf("thread %p(0x%lx) is joining thread %p\n",
- (void *)GC_PTHREAD_PTRVAL(pthread_self()),
- (long)GetCurrentThreadId(),
- (void *)GC_PTHREAD_PTRVAL(thread));
-# endif
-
- /* Thread being joined might not have registered itself yet. */
- /* After the join, thread id may have been recycled. */
- /* FIXME: It would be better if this worked more like */
- /* pthread_support.c. */
-# ifndef GC_WIN32_PTHREADS
- while ((t = GC_lookup_by_pthread(thread)) == 0)
- Sleep(10);
-# endif
- result = pthread_join(thread, retval);
- if (EXPECT(0 == result, TRUE)) {
-# ifdef GC_WIN32_PTHREADS
- /* pthreads-win32 and winpthreads id are unique (not recycled). */
- GC_thread t = GC_lookup_by_pthread(thread);
- if (NULL == t) ABORT("Thread not registered");
-# endif
-
- LOCK();
- if (KNOWN_FINISHED(t)) {
- GC_delete_gc_thread_no_free(t);
- GC_INTERNAL_FREE(t);
- }
- UNLOCK();
- }
-
-# ifdef DEBUG_THREADS
- GC_log_printf("thread %p(0x%lx) join with thread %p %s\n",
- (void *)GC_PTHREAD_PTRVAL(pthread_self()),
- (long)GetCurrentThreadId(),
- (void *)GC_PTHREAD_PTRVAL(thread),
- result != 0 ? "failed" : "succeeded");
-# endif
- return result;
- }
-
- /* Cygwin-pthreads calls CreateThread internally, but it's not easily */
- /* interceptable by us..., so intercept pthread_create instead. */
- GC_API int GC_pthread_create(pthread_t *new_thread,
- GC_PTHREAD_CREATE_CONST pthread_attr_t *attr,
- void *(*start_routine)(void *), void *arg)
- {
- int result;
- struct start_info * si;
-
- if (!EXPECT(GC_is_initialized, TRUE)) GC_init();
- GC_ASSERT(GC_thr_initialized);
- GC_ASSERT(!GC_win32_dll_threads);
-
- /* This is otherwise saved only in an area mmapped by the thread */
- /* library, which isn't visible to the collector. */
- si = (struct start_info *)GC_malloc_uncollectable(
- sizeof(struct start_info));
- if (EXPECT(NULL == si, FALSE)) return EAGAIN;
-
- si -> start_routine = start_routine;
- si -> arg = arg;
- GC_dirty(si);
- REACHABLE_AFTER_DIRTY(arg);
- if (attr != NULL
- && pthread_attr_getdetachstate(attr, &(si -> detached)) != 0)
- ABORT("pthread_attr_getdetachstate failed");
-# ifdef DEBUG_THREADS
- GC_log_printf("About to create a thread from %p(0x%lx)\n",
- (void *)GC_PTHREAD_PTRVAL(pthread_self()),
- (long)GetCurrentThreadId());
-# endif
- START_MARK_THREADS();
- set_need_to_lock();
- result = pthread_create(new_thread, attr, GC_pthread_start, si);
- if (EXPECT(result != 0, FALSE)) GC_free(si); /* failure */
- return result;
- }
-
- STATIC void * GC_CALLBACK GC_pthread_start_inner(struct GC_stack_base *sb,
- void * arg)
- {
- struct start_info * si = (struct start_info *)arg;
- void * result;
- void *(*start)(void *);
- void *start_arg;
- thread_id_t self_id = GetCurrentThreadId();
- pthread_t self = pthread_self();
- GC_thread me;
- DCL_LOCK_STATE;
-
-# ifdef DEBUG_THREADS
- GC_log_printf("thread %p(0x%x) starting...\n",
- (void *)GC_PTHREAD_PTRVAL(self), (int)self_id);
-# endif
-
- GC_ASSERT(!GC_win32_dll_threads);
- /* If a GC occurs before the thread is registered, that GC will */
- /* ignore this thread. That's fine, since it will block trying to */
- /* acquire the allocation lock, and won't yet hold interesting */
- /* pointers. */
- LOCK();
- /* We register the thread here instead of in the parent, so that */
- /* we don't need to hold the allocation lock during pthread_create. */
- me = GC_register_my_thread_inner(sb, self_id);
- SET_PTHREAD_MAP_CACHE(self, self_id);
- GC_ASSERT(me != &first_thread);
- me -> pthread_id = self;
- if (si->detached) me -> flags |= DETACHED;
-# ifdef THREAD_LOCAL_ALLOC
- GC_init_thread_local(&me->tlfs);
-# endif
- UNLOCK();
-
- start = si -> start_routine;
- start_arg = si -> arg;
-
- GC_free(si); /* was allocated uncollectible */
-
- pthread_cleanup_push(GC_thread_exit_proc, (void *)me);
- result = (*start)(start_arg);
- me -> status = result;
- GC_dirty(me);
- pthread_cleanup_pop(1);
-
-# ifdef DEBUG_THREADS
- GC_log_printf("thread %p(0x%x) returned from start routine\n",
- (void *)GC_PTHREAD_PTRVAL(self), (int)self_id);
-# endif
- return result;
- }
-
- STATIC void * GC_pthread_start(void * arg)
- {
- return GC_call_with_stack_base(GC_pthread_start_inner, arg);
- }
-
- GC_INNER_PTHRSTART void GC_thread_exit_proc(void *arg)
- {
- GC_thread me = (GC_thread)arg;
- DCL_LOCK_STATE;
-
- GC_ASSERT(!GC_win32_dll_threads);
-# ifdef DEBUG_THREADS
- GC_log_printf("thread %p(0x%lx) called pthread_exit()\n",
- (void *)GC_PTHREAD_PTRVAL(pthread_self()),
- (long)GetCurrentThreadId());
-# endif
-
- LOCK();
- GC_wait_for_gc_completion(FALSE);
-# if defined(THREAD_LOCAL_ALLOC)
- GC_ASSERT(GC_getspecific(GC_thread_key) == &me->tlfs);
- GC_destroy_thread_local(&me->tlfs);
-# endif
- if (me -> flags & DETACHED) {
- GC_delete_thread(GetCurrentThreadId());
- } else {
- /* deallocate it as part of join */
- me -> flags |= FINISHED;
- }
-# if defined(THREAD_LOCAL_ALLOC)
- /* It is required to call remove_specific defined in specific.c. */
- GC_remove_specific(GC_thread_key);
-# endif
- UNLOCK();
- }
-
-# ifndef GC_NO_PTHREAD_SIGMASK
- /* pthreads-win32 does not support sigmask. */
- /* So, nothing required here... */
- GC_API int GC_pthread_sigmask(int how, const sigset_t *set,
- sigset_t *oset)
- {
- return pthread_sigmask(how, set, oset);
- }
-# endif /* !GC_NO_PTHREAD_SIGMASK */
-
- GC_API int GC_pthread_detach(pthread_t thread)
- {
- int result;
- GC_thread t;
- DCL_LOCK_STATE;
-
- GC_ASSERT(!GC_win32_dll_threads);
- /* The thread might not have registered itself yet. */
- /* TODO: Wait for registration of the created thread in pthread_create. */
- while ((t = GC_lookup_by_pthread(thread)) == NULL)
- Sleep(10);
- result = pthread_detach(thread);
- if (EXPECT(0 == result, TRUE)) {
- LOCK();
- t -> flags |= DETACHED;
- /* Here the pthread id may have been recycled. */
- if (KNOWN_FINISHED(t)) {
- GC_delete_gc_thread_no_free(t);
- GC_INTERNAL_FREE(t);
- }
- UNLOCK();
- }
- return result;
- }
-
-#elif !defined(GC_NO_THREADS_DISCOVERY)
+#ifndef GC_NO_THREADS_DISCOVERY
/* We avoid acquiring locks here, since this doesn't seem to be */
/* preemptible. This may run with an uninitialized collector, in */
/* which case we don't do much. This implies that no threads other */
@@ -3029,7 +1766,7 @@ GC_INNER void GC_thr_init(void)
for (i = 0; i <= my_max; ++i) {
if (AO_load(&(dll_thread_table[i].tm.in_use)))
- GC_delete_gc_thread_no_free(&dll_thread_table[i]);
+ GC_delete_gc_thread_no_free((GC_thread)&dll_thread_table[i]);
}
GC_deinit();
}
@@ -3037,92 +1774,7 @@ GC_INNER void GC_thr_init(void)
}
return TRUE;
}
-#endif /* !GC_NO_THREADS_DISCOVERY && !GC_PTHREADS */
-
-/* Perform all initializations, including those that may require */
-/* allocation, e.g. initialize thread local free lists if used. */
-GC_INNER void GC_init_parallel(void)
-{
-# ifdef THREAD_LOCAL_ALLOC
- GC_thread me;
- DCL_LOCK_STATE;
-
- GC_ASSERT(GC_is_initialized);
- LOCK();
- me = GC_lookup_thread(GetCurrentThreadId());
- CHECK_LOOKUP_MY_THREAD(me);
- GC_init_thread_local(&me->tlfs);
- UNLOCK();
-# endif
-# if defined(CPPCHECK) && !defined(GC_NO_THREADS_DISCOVERY)
- GC_noop1((word)&GC_DllMain);
-# endif
- if (GC_win32_dll_threads) {
- set_need_to_lock();
- /* Cannot intercept thread creation. Hence we don't know if */
- /* other threads exist. However, client is not allowed to */
- /* create other threads before collector initialization. */
- /* Thus it's OK not to lock before this. */
- }
-}
-
-#if defined(USE_PTHREAD_LOCKS)
- /* Support for pthread locking code. */
- /* pthread_mutex_trylock may not win here, */
- /* due to builtin support for spinning first? */
-
- GC_INNER void GC_lock(void)
- {
- pthread_mutex_lock(&GC_allocate_ml);
- }
-#endif /* USE_PTHREAD_LOCKS */
-
-#if defined(THREAD_LOCAL_ALLOC)
-
- /* Add thread-local allocation support. VC++ uses __declspec(thread). */
-
- /* We must explicitly mark ptrfree and gcj free lists, since the free */
- /* list links wouldn't otherwise be found. We also set them in the */
- /* normal free lists, since that involves touching less memory than if */
- /* we scanned them normally. */
- GC_INNER void GC_mark_thread_local_free_lists(void)
- {
- int i;
- GC_thread p;
-
- for (i = 0; i < THREAD_TABLE_SZ; ++i) {
- for (p = GC_threads[i]; p != NULL; p = p -> tm.next)
- if (!KNOWN_FINISHED(p)) {
-# ifdef DEBUG_THREADS
- GC_log_printf("Marking thread locals for 0x%x\n", (int)p->id);
-# endif
- GC_mark_thread_local_fls_for(&p->tlfs);
- }
- }
- }
-
-# if defined(GC_ASSERTIONS)
- /* Check that all thread-local free-lists are completely marked. */
- /* also check that thread-specific-data structures are marked. */
- void GC_check_tls(void)
- {
- int i;
- GC_thread p;
-
- for (i = 0; i < THREAD_TABLE_SZ; ++i) {
- for (p = GC_threads[i]; p != NULL; p = p -> tm.next) {
- if (!KNOWN_FINISHED(p))
- GC_check_tls_for(&p->tlfs);
- }
- }
-# if defined(USE_CUSTOM_SPECIFIC)
- if (GC_thread_key != 0)
- GC_check_tsd_marks(GC_thread_key);
-# endif
- }
-# endif /* GC_ASSERTIONS */
-
-#endif /* THREAD_LOCAL_ALLOC ... */
+#endif /* !GC_NO_THREADS_DISCOVERY */
# ifndef GC_NO_THREAD_REDIRECTS
/* Restore thread calls redirection. */