📄 aix_irix_threads.c
字号:
/* * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. * Copyright (c) 1999-2003 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 for Irix (>=6.2) Pthreads and for AIX pthreads. * This relies on properties * not guaranteed by the Pthread standard. It may or may not be portable * to other implementations. * * Note that there is a lot of code duplication between this file and * (pthread_support.c, pthread_stop_world.c). They should be merged. * Pthread_support.c should be directly usable. * * Please avoid adding new ports here; use the generic pthread support * as a base instead. */# include "private/gc_priv.h"# if defined(GC_IRIX_THREADS) || defined(GC_AIX_THREADS)# include <pthread.h># include <assert.h># include <semaphore.h># include <time.h># include <errno.h># include <unistd.h># include <sys/mman.h># include <sys/time.h>#undef pthread_create#undef pthread_sigmask#undef pthread_join#if defined(GC_IRIX_THREADS) && !defined(MUTEX_RECURSIVE_NP)#define MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE#endifvoid GC_thr_init();#if 0void GC_print_sig_mask(){ sigset_t blocked; int i; if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) ABORT("pthread_sigmask"); GC_printf0("Blocked: "); for (i = 1; i <= MAXSIG; i++) { if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); } } GC_printf0("\n");}#endif/* We use the allocation lock to protect thread-related data structures. *//* The set of all known threads. We intercept thread creation and *//* joins. We never actually create detached threads. We allocate all *//* new thread stacks ourselves. These allow us to maintain this *//* data structure. *//* Protected by GC_thr_lock. *//* Some of this should be declared volatile, but that's incosnsistent *//* with some library routine declarations. */typedef struct GC_Thread_Rep { struct GC_Thread_Rep * next; /* More recently allocated threads */ /* with a given pthread id come */ /* first. (All but the first are */ /* guaranteed to be dead, but we may */ /* not yet have registered the join.) */ pthread_t id; word stop;# define NOT_STOPPED 0# define PLEASE_STOP 1# define STOPPED 2 word flags;# define FINISHED 1 /* Thread has exited. */# define DETACHED 2 /* Thread is intended to be detached. */ ptr_t stack_cold; /* cold end of the stack */ ptr_t stack_hot; /* Valid only when stopped. */ /* But must be within stack region at */ /* all times. */ void * status; /* Used only to avoid premature */ /* reclamation of any data it might */ /* reference. */} * GC_thread;GC_thread GC_lookup_thread(pthread_t id);/* * The only way to suspend threads given the pthread interface is to send * signals. Unfortunately, this means we have to reserve * a signal, and intercept client calls to change the signal mask. */#if 0 /* DOB: 6.1 */# if defined(GC_AIX_THREADS)# define SIG_SUSPEND SIGUSR1# else# define SIG_SUSPEND (SIGRTMIN + 6)# endif#endifpthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER; /* Number of threads stopped so far */pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;void GC_suspend_handler(int sig){ int dummy; GC_thread me; sigset_t all_sigs; sigset_t old_sigs; int i; if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler"); me = GC_lookup_thread(pthread_self()); /* The lookup here is safe, since I'm doing this on behalf */ /* of a thread which holds the allocation lock in order */ /* to stop the world. Thus concurrent modification of the */ /* data structure is impossible. */ if (PLEASE_STOP != me -> stop) { /* Misdirected signal. */ pthread_mutex_unlock(&GC_suspend_lock); return; } pthread_mutex_lock(&GC_suspend_lock); me -> stack_hot = (ptr_t)(&dummy); me -> stop = STOPPED; pthread_cond_signal(&GC_suspend_ack_cv); pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock); pthread_mutex_unlock(&GC_suspend_lock); /* GC_printf1("Continuing 0x%x\n", pthread_self()); */}GC_bool GC_thr_initialized = FALSE;# define THREAD_TABLE_SZ 128 /* Must be power of 2 */volatile GC_thread GC_threads[THREAD_TABLE_SZ];void GC_push_thread_structures GC_PROTO((void)){ GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));}/* 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 = ((word)id) % THREAD_TABLE_SZ; GC_thread result; static struct GC_Thread_Rep first_thread; static GC_bool first_thread_used = FALSE; GC_ASSERT(I_HOLD_LOCK()); if (!first_thread_used) { result = &first_thread; first_thread_used = TRUE; /* Dont acquire allocation lock, since we may already hold it. */ } else { result = (struct GC_Thread_Rep *) GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL); } if (result == 0) return(0); result -> id = id; result -> next = GC_threads[hv]; GC_threads[hv] = result; /* result -> flags = 0; */ /* result -> stop = 0; */ return(result);}/* Delete a thread from GC_threads. We assume it is there. *//* (The code intentionally traps if it wasn't.) *//* Caller holds allocation lock. *//* We explicitly pass in the GC_thread we're looking for, since *//* 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(pthread_t id, GC_thread gc_id){ int 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_id) { prev = p; p = p -> next; } if (prev == 0) { GC_threads[hv] = p -> next; } else { prev -> next = p -> next; }}/* Return a GC_thread corresponding to a given thread_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 = ((word)id) % THREAD_TABLE_SZ; register GC_thread p = GC_threads[hv]; /* I either hold the lock, or i'm being called from the stop-the-world * handler. */#if defined(GC_AIX_THREADS) GC_ASSERT(I_HOLD_LOCK()); /* no stop-the-world handler needed on AIX */#endif while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next; return(p);}#if defined(GC_AIX_THREADS)void GC_stop_world(){ pthread_t my_thread = pthread_self(); register int i; register GC_thread p; register int result; struct timespec timeout; GC_ASSERT(I_HOLD_LOCK()); for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { if (p -> id != my_thread) { pthread_suspend_np(p->id); } } } /* GC_printf1("World stopped 0x%x\n", pthread_self()); */}void GC_start_world(){ GC_thread p; unsigned i; pthread_t my_thread = pthread_self(); /* GC_printf0("World starting\n"); */ GC_ASSERT(I_HOLD_LOCK()); for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { if (p -> id != my_thread) { pthread_continue_np(p->id); } } }}#else /* GC_AIX_THREADS *//* Caller holds allocation lock. */void GC_stop_world(){ pthread_t my_thread = pthread_self(); register int i; register GC_thread p; register int result; struct timespec timeout; GC_ASSERT(I_HOLD_LOCK()); for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { if (p -> id != my_thread) { if (p -> flags & FINISHED) { p -> stop = STOPPED; continue; } p -> stop = PLEASE_STOP; result = pthread_kill(p -> id, SIG_SUSPEND); /* GC_printf1("Sent signal to 0x%x\n", p -> id); */ switch(result) { case ESRCH: /* Not really there anymore. Possible? */ p -> stop = STOPPED; break; case 0: break; default: ABORT("pthread_kill failed"); } } } } pthread_mutex_lock(&GC_suspend_lock); for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { while (p -> id != my_thread && p -> stop != STOPPED) { clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_nsec += 50000000; /* 50 msecs */ if (timeout.tv_nsec >= 1000000000) { timeout.tv_nsec -= 1000000000; ++timeout.tv_sec; } result = pthread_cond_timedwait(&GC_suspend_ack_cv, &GC_suspend_lock, &timeout); if (result == ETIMEDOUT) { /* Signal was lost or misdirected. Try again. */ /* Duplicate signals should be benign. */ result = pthread_kill(p -> id, SIG_SUSPEND); } } } } pthread_mutex_unlock(&GC_suspend_lock); /* GC_printf1("World stopped 0x%x\n", pthread_self()); */}/* Caller holds allocation lock. */void GC_start_world(){ GC_thread p; unsigned i; /* GC_printf0("World starting\n"); */ GC_ASSERT(I_HOLD_LOCK()); for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { p -> stop = NOT_STOPPED; } } pthread_mutex_lock(&GC_suspend_lock); /* All other threads are at pthread_cond_wait in signal handler. */ /* Otherwise we couldn't have acquired the lock. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -