📄 aix_irix_threads.c
字号:
pthread_mutex_unlock(&GC_suspend_lock); pthread_cond_broadcast(&GC_continue_cv);}#endif /* GC_AIX_THREADS *//* We hold allocation lock. Should do exactly the right thing if the *//* world is stopped. Should not fail if it isn't. */void GC_push_all_stacks(){ register int i; register GC_thread p; register ptr_t hot, cold; pthread_t me = pthread_self(); /* GC_init() should have been called before GC_push_all_stacks is * invoked, and GC_init calls GC_thr_init(), which sets * GC_thr_initialized. */ GC_ASSERT(GC_thr_initialized); /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */ 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 -> flags & FINISHED) continue; cold = p->stack_cold; if (!cold) cold=GC_stackbottom; /* 0 indicates 'original stack' */ if (pthread_equal(p -> id, me)) { hot = GC_approx_sp(); } else {# ifdef GC_AIX_THREADS /* AIX doesn't use signals to suspend, so we need to get an */ /* accurate hot stack pointer. */ /* See http://publib16.boulder.ibm.com/pseries/en_US/libs/basetrf1/pthread_getthrds_np.htm */ pthread_t id = p -> id; struct __pthrdsinfo pinfo; int regbuf[64]; int val = sizeof(regbuf); int retval = pthread_getthrds_np(&id, PTHRDSINFO_QUERY_ALL, &pinfo, sizeof(pinfo), regbuf, &val); if (retval != 0) { printf("ERROR: pthread_getthrds_np() failed in GC\n"); abort(); } /* according to the AIX ABI, "the lowest possible valid stack address is 288 bytes (144 + 144) less than the current value of the stack pointer. Functions may use this stack space as volatile storage which is not preserved across function calls." ftp://ftp.penguinppc64.org/pub/people/amodra/PPC-elf64abi.txt.gz */ hot = (ptr_t)(unsigned long)pinfo.__pi_ustk-288; cold = (ptr_t)pinfo.__pi_stackend; /* more precise */ /* push the registers too, because they won't be on stack */ GC_push_all_eager((ptr_t)&pinfo.__pi_context, (ptr_t)((&pinfo.__pi_context)+1)); GC_push_all_eager((ptr_t)regbuf, ((ptr_t)regbuf)+val);# else hot = p -> stack_hot;# endif }# ifdef STACK_GROWS_UP GC_push_all_stack(cold, hot);# else /* printf("thread 0x%x: hot=0x%08x cold=0x%08x\n", p -> id, hot, cold); */ GC_push_all_stack(hot, cold);# endif } }}/* We hold the allocation lock. */void GC_thr_init(){ GC_thread t; struct sigaction act; if (GC_thr_initialized) return; GC_ASSERT(I_HOLD_LOCK()); GC_thr_initialized = TRUE;#ifndef GC_AIX_THREADS (void) sigaction(SIG_SUSPEND, 0, &act); if (act.sa_handler != SIG_DFL) ABORT("Previously installed SIG_SUSPEND handler"); /* Install handler. */ act.sa_handler = GC_suspend_handler; act.sa_flags = SA_RESTART; (void) sigemptyset(&act.sa_mask); if (0 != sigaction(SIG_SUSPEND, &act, 0)) ABORT("Failed to install SIG_SUSPEND handler");#endif /* Add the initial thread, so we can stop it. */ t = GC_new_thread(pthread_self()); /* use '0' to indicate GC_stackbottom, since GC_init() has not * completed by the time we are called (from GC_init_inner()) */ t -> stack_cold = 0; /* the original stack. */ t -> stack_hot = (ptr_t)(&t); t -> flags = DETACHED;}int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset){ sigset_t fudged_set; #ifdef GC_AIX_THREADS return(pthread_sigmask(how, set, oset));#endif if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) { fudged_set = *set; sigdelset(&fudged_set, SIG_SUSPEND); set = &fudged_set; } return(pthread_sigmask(how, set, oset));}struct start_info { void *(*start_routine)(void *); void *arg; word flags; pthread_mutex_t registeredlock; pthread_cond_t registered; int volatile registereddone;};void GC_thread_exit_proc(void *arg){ GC_thread me; LOCK(); me = GC_lookup_thread(pthread_self()); me -> flags |= FINISHED; /* reclaim DETACHED thread right away; otherwise wait until join() */ if (me -> flags & DETACHED) { GC_delete_gc_thread(pthread_self(), me); } UNLOCK();}int GC_pthread_join(pthread_t thread, void **retval){ int result; GC_thread thread_gc_id; LOCK(); thread_gc_id = GC_lookup_thread(thread); /* This is guaranteed to be the intended one, since the thread id */ /* cant have been recycled by pthreads. */ UNLOCK(); GC_ASSERT(!(thread_gc_id->flags & DETACHED)); result = pthread_join(thread, retval); /* Some versions of the Irix pthreads library can erroneously */ /* return EINTR when the call succeeds. */ if (EINTR == result) result = 0; GC_ASSERT(thread_gc_id->flags & FINISHED); LOCK(); /* Here the pthread thread id may have been recycled. */ GC_delete_gc_thread(thread, thread_gc_id); UNLOCK(); return result;}void * GC_start_routine(void * arg){ int dummy; struct start_info * si = arg; void * result; GC_thread me; pthread_t my_pthread; void *(*start)(void *); void *start_arg; my_pthread = pthread_self(); /* 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. */ /* Holding the allocation lock there would make REDIRECT_MALLOC */ /* impossible. It probably still doesn't work, but we're a little */ /* closer ... */ /* This unfortunately means that we have to be careful the parent */ /* doesn't try to do a pthread_join before we're registered. */ me = GC_new_thread(my_pthread); me -> flags = si -> flags; me -> stack_cold = (ptr_t) &dummy; /* this now the 'start of stack' */ me -> stack_hot = me->stack_cold;/* this field should always be sensible */ UNLOCK(); start = si -> start_routine; start_arg = si -> arg; pthread_mutex_lock(&(si->registeredlock)); si->registereddone = 1; pthread_cond_signal(&(si->registered)); pthread_mutex_unlock(&(si->registeredlock)); /* si went away as soon as we did this unlock */ pthread_cleanup_push(GC_thread_exit_proc, 0); result = (*start)(start_arg); me -> status = result; pthread_cleanup_pop(1); /* This involves acquiring the lock, ensuring that we can't exit */ /* while a collection that thinks we're alive is trying to stop */ /* us. */ return(result);}intGC_pthread_create(pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg){ int result; GC_thread t; int detachstate; word my_flags = 0; struct start_info * si; /* This is otherwise saved only in an area mmapped by the thread */ /* library, which isn't visible to the collector. */ LOCK(); /* GC_INTERNAL_MALLOC implicitly calls GC_init() if required */ si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info), NORMAL); GC_ASSERT(GC_thr_initialized); /* initialized by GC_init() */ UNLOCK(); if (0 == si) return(ENOMEM); pthread_mutex_init(&(si->registeredlock), NULL); pthread_cond_init(&(si->registered),NULL); pthread_mutex_lock(&(si->registeredlock)); si -> start_routine = start_routine; si -> arg = arg; pthread_attr_getdetachstate(attr, &detachstate); if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; si -> flags = my_flags; result = pthread_create(new_thread, attr, GC_start_routine, si); /* Wait until child has been added to the thread table. */ /* This also ensures that we hold onto si until the child is done */ /* with it. Thus it doesn't matter whether it is otherwise */ /* visible to the collector. */ if (0 == result) { si->registereddone = 0; while (!si->registereddone) pthread_cond_wait(&(si->registered), &(si->registeredlock)); } pthread_mutex_unlock(&(si->registeredlock)); pthread_cond_destroy(&(si->registered)); pthread_mutex_destroy(&(si->registeredlock)); LOCK(); GC_INTERNAL_FREE(si); UNLOCK(); return(result);}/* For now we use the pthreads locking primitives on HP/UX */VOLATILE GC_bool GC_collecting = 0; /* A hint that we're in the collector and */ /* holding the allocation lock for an */ /* extended period. *//* Reasonably fast spin locks. Basically the same implementation *//* as STL alloc.h. */#define SLEEP_THRESHOLD 3volatile unsigned int GC_allocate_lock = 0;#define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)#define GC_LOCK_TAKEN GC_allocate_lockvoid GC_lock(){# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */# define high_spin_max 1000 /* spin cycles for multiprocessor */ static unsigned spin_max = low_spin_max; unsigned my_spin_max; static unsigned last_spins = 0; unsigned my_last_spins; volatile unsigned junk;# define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk int i; if (GC_TRY_LOCK()) { return; } junk = 0; my_spin_max = spin_max; my_last_spins = last_spins; for (i = 0; i < my_spin_max; i++) { if (GC_collecting) goto yield; if (i < my_last_spins/2 || GC_LOCK_TAKEN) { PAUSE; continue; } if (GC_TRY_LOCK()) { /* * got it! * Spinning worked. Thus we're probably not being scheduled * against the other process with which we were contending. * Thus it makes sense to spin longer the next time. */ last_spins = i; spin_max = high_spin_max; return; } } /* We are probably being scheduled against the other process. Sleep. */ spin_max = low_spin_max;yield: for (i = 0;; ++i) { if (GC_TRY_LOCK()) { return; } if (i < SLEEP_THRESHOLD) { sched_yield(); } else { struct timespec ts; if (i > 26) i = 26; /* Don't wait for more than about 60msecs, even */ /* under extreme contention. */ ts.tv_sec = 0; ts.tv_nsec = 1 << i; nanosleep(&ts, 0); } }}# else /* !GC_IRIX_THREADS && !GC_AIX_THREADS */#ifndef LINT int GC_no_Irix_threads;#endif# endif /* IRIX_THREADS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -