📄 pthread_support.c
字号:
/* * Copyright (c) 1994 by Xerox Corporation. All rights reserved. * Copyright (c) 1996 by Silicon Graphics. All rights reserved. * Copyright (c) 1998 by Fergus Henderson. All rights reserved. * Copyright (c) 2000-2005 by Hewlett-Packard Company. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. * * Permission is hereby granted to use or copy this program * for any purpose, provided the above notices are retained on all copies. * Permission to modify the code and to distribute modified code is granted, * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. *//* * Support code originally for LinuxThreads, the clone()-based kernel * thread package for Linux which is included in libc6. * * This code no doubt makes some assumptions beyond what is * guaranteed by the pthread standard, though it now does * very little of that. It now also supports NPTL, and many * other Posix thread implementations. We are trying to merge * all flavors of pthread dupport code into this file. */ /* DG/UX ix86 support <takis@xfree86.org> *//* * Linux_threads.c now also includes some code to support HPUX and * OSF1 (Compaq Tru64 Unix, really). The OSF1 support is based on Eric Benson's * patch. * * Eric also suggested an alternate basis for a lock implementation in * his code: * + #elif defined(OSF1) * + unsigned long GC_allocate_lock = 0; * + msemaphore GC_allocate_semaphore; * + # define GC_TRY_LOCK() \ * + ((msem_lock(&GC_allocate_semaphore, MSEM_IF_NOWAIT) == 0) \ * + ? (GC_allocate_lock = 1) \ * + : 0) * + # define GC_LOCK_TAKEN GC_allocate_lock *//*#define DEBUG_THREADS 1*/# include "private/pthread_support.h"# if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS)# if defined(GC_DGUX386_THREADS) && !defined(_POSIX4A_DRAFT10_SOURCE)# define _POSIX4A_DRAFT10_SOURCE 1# endif# if defined(GC_DGUX386_THREADS) && !defined(_USING_POSIX4A_DRAFT10)# define _USING_POSIX4A_DRAFT10 1# endif# include <stdlib.h># include <pthread.h># include <sched.h># include <time.h># include <errno.h># include <unistd.h># include <sys/mman.h># include <sys/time.h># include <sys/types.h># include <sys/stat.h># include <fcntl.h># include <signal.h># include "gc_inline.h"#if defined(GC_DARWIN_THREADS)# include "private/darwin_semaphore.h"#else# include <semaphore.h>#endif /* !GC_DARWIN_THREADS */#if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS)# include <sys/sysctl.h>#endif /* GC_DARWIN_THREADS */#if defined(GC_NETBSD_THREADS)# include <sys/param.h># include <sys/sysctl.h>#endif /* GC_NETBSD_THREADS *//* Allocator lock definitions. */#if !defined(USE_SPIN_LOCK) pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;#endifunsigned long GC_lock_holder = NO_THREAD; /* Used only for assertions, and to prevent */ /* recursive reentry in the system call wrapper. */#if defined(GC_DGUX386_THREADS)# include <sys/dg_sys_info.h># include <sys/_int_psem.h> /* sem_t is an uint in DG/UX */ typedef unsigned int sem_t;#endif /* GC_DGUX386_THREADS */#ifndef __GNUC__# define __inline__#endif/* Undefine macros used to redirect pthread primitives. */# undef pthread_create# if !defined(GC_DARWIN_THREADS)# undef pthread_sigmask# endif# undef pthread_join# undef pthread_detach# if defined(GC_OSF1_THREADS) && defined(_PTHREAD_USE_MANGLED_NAMES_) \ && !defined(_PTHREAD_USE_PTDNAM_) /* Restore the original mangled names on Tru64 UNIX. */# define pthread_create __pthread_create# define pthread_join __pthread_join# define pthread_detach __pthread_detach# endif#ifdef GC_USE_LD_WRAP# define WRAP_FUNC(f) __wrap_##f# define REAL_FUNC(f) __real_##f#else# ifdef GC_USE_DLOPEN_WRAP# include <dlfcn.h># define WRAP_FUNC(f) f# define REAL_FUNC(f) GC_real_##f /* We define both GC_f and plain f to be the wrapped function. */ /* In that way plain calls work, as do calls from files that */ /* included gc.h, wich redefined f to GC_f. */ /* FIXME: Needs work for DARWIN and True64 (OSF1) */ typedef int (* GC_pthread_create_t)(pthread_t *, const pthread_attr_t *, void * (*)(void *), void *); static GC_pthread_create_t GC_real_pthread_create; typedef int (* GC_pthread_sigmask_t)(int, const sigset_t *, sigset_t *); static GC_pthread_sigmask_t GC_real_pthread_sigmask; typedef int (* GC_pthread_join_t)(pthread_t, void **); static GC_pthread_join_t GC_real_pthread_join; typedef int (* GC_pthread_detach_t)(pthread_t); static GC_pthread_detach_t GC_real_pthread_detach;# else# define WRAP_FUNC(f) GC_##f# if !defined(GC_DGUX386_THREADS)# define REAL_FUNC(f) f# else /* GC_DGUX386_THREADS */# define REAL_FUNC(f) __d10_##f# endif /* GC_DGUX386_THREADS */# endif#endif#if defined(GC_USE_DL_WRAP) || defined(GC_USE_DLOPEN_WRAP)/* Define GC_ functions as aliases for the plain ones, which will *//* be intercepted. This allows files which include gc.h, and hence *//* generate referemces to the GC_ symbols, to see the right symbols. */ int GC_pthread_create(pthread_t * t, const pthread_attr_t * a, void * (* fn)(void *), void * arg) { return pthread_create(t, a, fn, arg); } int GC_pthread_sigmask(int how, const sigset_t *mask, sigset_t *old) { return pthread_sigmask(how, mask, old); } int GC_pthread_join(pthread_t t, void **res) { return pthread_join(t, res); } int GC_pthread_detach(pthread_t t) { return pthread_detach(t); }#endif /* Linker-based interception. */#ifdef GC_USE_DLOPEN_WRAP static GC_bool GC_syms_initialized = FALSE; void GC_init_real_syms(void) { void *dl_handle;# define LIBPTHREAD_NAME "libpthread.so.0"# define LIBPTHREAD_NAME_LEN 16 /* incl. trailing 0 */ size_t len = LIBPTHREAD_NAME_LEN - 1; char namebuf[LIBPTHREAD_NAME_LEN]; static char *libpthread_name = LIBPTHREAD_NAME; if (GC_syms_initialized) return;# ifdef RTLD_NEXT dl_handle = RTLD_NEXT;# else dl_handle = dlopen(libpthread_name, RTLD_LAZY); if (NULL == dl_handle) { while (isdigit(libpthread_name[len-1])) --len; if (libpthread_name[len-1] == '.') --len; memcpy(namebuf, libpthread_name, len); namebuf[len] = '\0'; dl_handle = dlopen(namebuf, RTLD_LAZY); } if (NULL == dl_handle) ABORT("Couldn't open libpthread\n");# endif GC_real_pthread_create = (GC_pthread_create_t) dlsym(dl_handle, "pthread_create"); GC_real_pthread_sigmask = (GC_pthread_sigmask_t) dlsym(dl_handle, "pthread_sigmask"); GC_real_pthread_join = (GC_pthread_join_t) dlsym(dl_handle, "pthread_join"); GC_real_pthread_detach = (GC_pthread_detach_t) dlsym(dl_handle, "pthread_detach"); GC_syms_initialized = TRUE; }# define INIT_REAL_SYMS() if (!GC_syms_initialized) GC_init_real_syms();#else# define INIT_REAL_SYMS()#endifvoid GC_thr_init(void);static GC_bool parallel_initialized = FALSE;GC_bool GC_need_to_lock = FALSE;void GC_init_parallel(void);long GC_nprocs = 1; /* Number of processors. We may not have */ /* access to all of them, but this is as good */ /* a guess as any ... */#ifdef THREAD_LOCAL_ALLOC/* 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. */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]; 0 != p; p = p -> next) { 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]; 0 != p; p = p -> next) { 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 */#ifdef PARALLEL_MARK# ifndef MAX_MARKERS# define MAX_MARKERS 16# endifstatic ptr_t marker_sp[MAX_MARKERS] = {0};#ifdef IA64 static ptr_t marker_bsp[MAX_MARKERS] = {0};#endifvoid * GC_mark_thread(void * id){ word my_mark_no = 0; marker_sp[(word)id] = GC_approx_sp();# ifdef IA64 marker_bsp[(word)id] = GC_save_regs_in_stack();# endif for (;; ++my_mark_no) { /* GC_mark_no is passed only to allow GC_help_marker to terminate */ /* promptly. This is important if it were called from the signal */ /* handler or from the GC lock acquisition code. Under Linux, it's */ /* not safe to call it from a signal handler, since it uses mutexes */ /* and condition variables. Since it is called only here, the */ /* argument is unnecessary. */ if (my_mark_no < GC_mark_no || my_mark_no > GC_mark_no + 2) { /* resynchronize if we get far off, e.g. because GC_mark_no */ /* wrapped. */ my_mark_no = GC_mark_no; }# ifdef DEBUG_THREADS GC_printf("Starting mark helper for mark number %lu\n", my_mark_no);# endif GC_help_marker(my_mark_no); }}extern long GC_markers; /* Number of mark threads we would */ /* like to have. Includes the */ /* initiating thread. */pthread_t GC_mark_threads[MAX_MARKERS];#define PTHREAD_CREATE REAL_FUNC(pthread_create)static void start_mark_threads(void){ unsigned i; pthread_attr_t attr; if (GC_markers > MAX_MARKERS) { WARN("Limiting number of mark threads\n", 0); GC_markers = MAX_MARKERS; } 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");# if defined(HPUX) || defined(GC_DGUX386_THREADS) /* Default stack size is usually too small: fix it. */ /* Otherwise marker threads or GC may run out of */ /* space. */# define MIN_STACK_SIZE (8*HBLKSIZE*sizeof(word)) { size_t old_size; int code; if (pthread_attr_getstacksize(&attr, &old_size) != 0) ABORT("pthread_attr_getstacksize failed\n"); if (old_size < MIN_STACK_SIZE) { if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0) ABORT("pthread_attr_setstacksize failed\n"); } }# endif /* HPUX || GC_DGUX386_THREADS */ if (GC_print_stats) { GC_log_printf("Starting %ld marker threads\n", GC_markers - 1); } for (i = 0; i < GC_markers - 1; ++i) { if (0 != PTHREAD_CREATE(GC_mark_threads + i, &attr, GC_mark_thread, (void *)(word)i)) { WARN("Marker thread creation failed, errno = %ld.\n", errno); } }}#endif /* PARALLEL_MARK */GC_bool GC_thr_initialized = FALSE;volatile GC_thread GC_threads[THREAD_TABLE_SZ];void GC_push_thread_structures(void){ GC_ASSERT(I_HOLD_LOCK()); 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));# endif}/* It may not be safe to allocate when we register the first thread. */static struct GC_Thread_Rep first_thread;/* Add a thread to GC_threads. We assume it wasn't already there. *//* Caller holds allocation lock. */GC_thread GC_new_thread(pthread_t id){ int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ; GC_thread result; static GC_bool first_thread_used = FALSE; GC_ASSERT(I_HOLD_LOCK()); if (!first_thread_used) { result = &first_thread; first_thread_used = TRUE; } else { result = (struct GC_Thread_Rep *) GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL); GC_ASSERT(result -> flags == 0); } if (result == 0) return(0); result -> id = id; result -> next = GC_threads[hv]; GC_threads[hv] = result; GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 0); return(result);}/* Delete a thread from GC_threads. We assume it is there. *//* (The code intentionally traps if it wasn't.) */void GC_delete_thread(pthread_t id){ int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ; register GC_thread p = GC_threads[hv]; register GC_thread prev = 0; GC_ASSERT(I_HOLD_LOCK()); while (!THREAD_EQUAL(p -> id, id)) { prev = p; p = p -> next; } if (prev == 0) { GC_threads[hv] = p -> next; } else { prev -> next = p -> next; }# ifdef GC_DARWIN_THREADS mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread);# endif GC_INTERNAL_FREE(p);}/* 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 pthread id. *//* This is OK, but we need a way to delete a specific one. */void GC_delete_gc_thread(GC_thread gc_id){ pthread_t id = gc_id -> id; int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ; register GC_thread p = GC_threads[hv]; register GC_thread prev = 0; GC_ASSERT(I_HOLD_LOCK()); while (p != gc_id) { prev = p; p = p -> next; } if (prev == 0) { GC_threads[hv] = p -> next; } else { prev -> next = p -> next; }# ifdef GC_DARWIN_THREADS mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread);# endif GC_INTERNAL_FREE(p);}/* Return a GC_thread corresponding to a given pthread_t. *//* Returns 0 if it's not there. *//* Caller holds allocation lock or otherwise inhibits *//* updates. *//* If there is more than one thread with the given id we *//* return the most recent one. */GC_thread GC_lookup_thread(pthread_t id){ int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ; register GC_thread p = GC_threads[hv]; while (p != 0 && !THREAD_EQUAL(p -> id, id)) p = p -> next; return(p);}#ifdef HANDLE_FORK/* Remove all entries from the GC_threads table, except the *//* one for the current thread. We need to do this in the child *//* process after a fork(), since only the current thread *//* survives in the child. */void GC_remove_all_threads_but_me(void){ pthread_t self = pthread_self(); int hv; GC_thread p, next, me; for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { me = 0; for (p = GC_threads[hv]; 0 != p; p = next) { next = p -> next; if (THREAD_EQUAL(p -> id, self)) { me = p; p -> next = 0; } else {# ifdef THREAD_LOCAL_ALLOC if (!(p -> flags & FINISHED)) { GC_destroy_thread_local(&(p->tlfs)); }# endif /* THREAD_LOCAL_ALLOC */ if (p != &first_thread) GC_INTERNAL_FREE(p); } } GC_threads[hv] = me; }}#endif /* HANDLE_FORK */#ifdef USE_PROC_FOR_LIBRARIESGC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi){ int i; GC_thread p;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -