| [2] | 1 | /*
|
|---|
| 2 | * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
|
|---|
| 3 | * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
|
|---|
| 4 | * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
|
|---|
| 5 | *
|
|---|
| 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
|
|---|
| 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
|
|---|
| 8 | *
|
|---|
| 9 | * Permission is hereby granted to use or copy this program
|
|---|
| 10 | * for any purpose, provided the above notices are retained on all copies.
|
|---|
| 11 | * Permission to modify the code and to distribute modified code is granted,
|
|---|
| 12 | * provided the above notices are retained, and a notice that the code was
|
|---|
| 13 | * modified is included with the above copyright notice.
|
|---|
| 14 | */
|
|---|
| 15 | /*
|
|---|
| 16 | * Support code for Irix (>=6.2) Pthreads. This relies on properties
|
|---|
| 17 | * not guaranteed by the Pthread standard. It may or may not be portable
|
|---|
| 18 | * to other implementations.
|
|---|
| 19 | *
|
|---|
| 20 | * This now also includes an initial attempt at thread support for
|
|---|
| 21 | * HP/UX 11.
|
|---|
| 22 | *
|
|---|
| 23 | * Note that there is a lot of code duplication between linux_threads.c
|
|---|
| 24 | * and irix_threads.c; any changes made here may need to be reflected
|
|---|
| 25 | * there too.
|
|---|
| 26 | */
|
|---|
| 27 |
|
|---|
| 28 | # if defined(GC_IRIX_THREADS)
|
|---|
| 29 |
|
|---|
| 30 | # include "private/gc_priv.h"
|
|---|
| 31 | # include <pthread.h>
|
|---|
| 32 | # include <semaphore.h>
|
|---|
| 33 | # include <time.h>
|
|---|
| 34 | # include <errno.h>
|
|---|
| 35 | # include <unistd.h>
|
|---|
| 36 | # include <sys/mman.h>
|
|---|
| 37 | # include <sys/time.h>
|
|---|
| 38 |
|
|---|
| 39 | #undef pthread_create
|
|---|
| 40 | #undef pthread_sigmask
|
|---|
| 41 | #undef pthread_join
|
|---|
| 42 | #undef pthread_detach
|
|---|
| 43 |
|
|---|
| 44 | void GC_thr_init();
|
|---|
| 45 |
|
|---|
| 46 | #if 0
|
|---|
| 47 | void GC_print_sig_mask()
|
|---|
| 48 | {
|
|---|
| 49 | sigset_t blocked;
|
|---|
| 50 | int i;
|
|---|
| 51 |
|
|---|
| 52 | if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
|
|---|
| 53 | ABORT("pthread_sigmask");
|
|---|
| 54 | GC_printf0("Blocked: ");
|
|---|
| 55 | for (i = 1; i <= MAXSIG; i++) {
|
|---|
| 56 | if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
|
|---|
| 57 | }
|
|---|
| 58 | GC_printf0("\n");
|
|---|
| 59 | }
|
|---|
| 60 | #endif
|
|---|
| 61 |
|
|---|
| 62 | /* We use the allocation lock to protect thread-related data structures. */
|
|---|
| 63 |
|
|---|
| 64 | /* The set of all known threads. We intercept thread creation and */
|
|---|
| 65 | /* joins. We never actually create detached threads. We allocate all */
|
|---|
| 66 | /* new thread stacks ourselves. These allow us to maintain this */
|
|---|
| 67 | /* data structure. */
|
|---|
| 68 | /* Protected by GC_thr_lock. */
|
|---|
| 69 | /* Some of this should be declared volatile, but that's incosnsistent */
|
|---|
| 70 | /* with some library routine declarations. */
|
|---|
| 71 | typedef struct GC_Thread_Rep {
|
|---|
| 72 | struct GC_Thread_Rep * next; /* More recently allocated threads */
|
|---|
| 73 | /* with a given pthread id come */
|
|---|
| 74 | /* first. (All but the first are */
|
|---|
| 75 | /* guaranteed to be dead, but we may */
|
|---|
| 76 | /* not yet have registered the join.) */
|
|---|
| 77 | pthread_t id;
|
|---|
| 78 | word stop;
|
|---|
| 79 | # define NOT_STOPPED 0
|
|---|
| 80 | # define PLEASE_STOP 1
|
|---|
| 81 | # define STOPPED 2
|
|---|
| 82 | word flags;
|
|---|
| 83 | # define FINISHED 1 /* Thread has exited. */
|
|---|
| 84 | # define DETACHED 2 /* Thread is intended to be detached. */
|
|---|
| 85 | # define CLIENT_OWNS_STACK 4
|
|---|
| 86 | /* Stack was supplied by client. */
|
|---|
| 87 | ptr_t stack;
|
|---|
| 88 | ptr_t stack_ptr; /* Valid only when stopped. */
|
|---|
| 89 | /* But must be within stack region at */
|
|---|
| 90 | /* all times. */
|
|---|
| 91 | size_t stack_size; /* 0 for original thread. */
|
|---|
| 92 | void * status; /* Used only to avoid premature */
|
|---|
| 93 | /* reclamation of any data it might */
|
|---|
| 94 | /* reference. */
|
|---|
| 95 | } * GC_thread;
|
|---|
| 96 |
|
|---|
| 97 | GC_thread GC_lookup_thread(pthread_t id);
|
|---|
| 98 |
|
|---|
| 99 | /*
|
|---|
| 100 | * The only way to suspend threads given the pthread interface is to send
|
|---|
| 101 | * signals. Unfortunately, this means we have to reserve
|
|---|
| 102 | * a signal, and intercept client calls to change the signal mask.
|
|---|
| 103 | * We use SIG_SUSPEND, defined in gc_priv.h.
|
|---|
| 104 | */
|
|---|
| 105 |
|
|---|
| 106 | pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
|
|---|
| 107 | /* Number of threads stopped so far */
|
|---|
| 108 | pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;
|
|---|
| 109 | pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;
|
|---|
| 110 |
|
|---|
| 111 | void GC_suspend_handler(int sig)
|
|---|
| 112 | {
|
|---|
| 113 | int dummy;
|
|---|
| 114 | GC_thread me;
|
|---|
| 115 | sigset_t all_sigs;
|
|---|
| 116 | sigset_t old_sigs;
|
|---|
| 117 | int i;
|
|---|
| 118 |
|
|---|
| 119 | if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
|
|---|
| 120 | me = GC_lookup_thread(pthread_self());
|
|---|
| 121 | /* The lookup here is safe, since I'm doing this on behalf */
|
|---|
| 122 | /* of a thread which holds the allocation lock in order */
|
|---|
| 123 | /* to stop the world. Thus concurrent modification of the */
|
|---|
| 124 | /* data structure is impossible. */
|
|---|
| 125 | if (PLEASE_STOP != me -> stop) {
|
|---|
| 126 | /* Misdirected signal. */
|
|---|
| 127 | pthread_mutex_unlock(&GC_suspend_lock);
|
|---|
| 128 | return;
|
|---|
| 129 | }
|
|---|
| 130 | pthread_mutex_lock(&GC_suspend_lock);
|
|---|
| 131 | me -> stack_ptr = (ptr_t)(&dummy);
|
|---|
| 132 | me -> stop = STOPPED;
|
|---|
| 133 | pthread_cond_signal(&GC_suspend_ack_cv);
|
|---|
| 134 | pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
|
|---|
| 135 | pthread_mutex_unlock(&GC_suspend_lock);
|
|---|
| 136 | /* GC_printf1("Continuing 0x%x\n", pthread_self()); */
|
|---|
| 137 | }
|
|---|
| 138 |
|
|---|
| 139 |
|
|---|
| 140 | GC_bool GC_thr_initialized = FALSE;
|
|---|
| 141 |
|
|---|
| 142 | size_t GC_min_stack_sz;
|
|---|
| 143 |
|
|---|
| 144 | # define N_FREE_LISTS 25
|
|---|
| 145 | ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 };
|
|---|
| 146 | /* GC_stack_free_lists[i] is free list for stacks of */
|
|---|
| 147 | /* size GC_min_stack_sz*2**i. */
|
|---|
| 148 | /* Free lists are linked through first word. */
|
|---|
| 149 |
|
|---|
| 150 | /* Return a stack of size at least *stack_size. *stack_size is */
|
|---|
| 151 | /* replaced by the actual stack size. */
|
|---|
| 152 | /* Caller holds allocation lock. */
|
|---|
| 153 | ptr_t GC_stack_alloc(size_t * stack_size)
|
|---|
| 154 | {
|
|---|
| 155 | register size_t requested_sz = *stack_size;
|
|---|
| 156 | register size_t search_sz = GC_min_stack_sz;
|
|---|
| 157 | register int index = 0; /* = log2(search_sz/GC_min_stack_sz) */
|
|---|
| 158 | register ptr_t result;
|
|---|
| 159 |
|
|---|
| 160 | while (search_sz < requested_sz) {
|
|---|
| 161 | search_sz *= 2;
|
|---|
| 162 | index++;
|
|---|
| 163 | }
|
|---|
| |
|---|