diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2011-07-26 14:55:44 +0400 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2011-07-26 14:55:44 +0400 |
commit | 2d90eba35546e0aa17a1014026ddbf4f523cad85 (patch) | |
tree | 7b87e1fde4c601c88afa9ebaaa3abf8236b29950 | |
parent | 126a335d1a4b898a0ce69eadfe8ff02c26562f7b (diff) | |
download | bdwgc-gc4_10t3.tar.gz |
gc4.10t3 tarball importgc4_10t3
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | Makefile.pthreads | 67 | ||||
-rw-r--r-- | README.pthreads | 203 | ||||
-rw-r--r-- | alpha_mach_dep.s | 22 | ||||
-rw-r--r-- | config.h | 9 | ||||
-rw-r--r-- | dec_threads.c | 558 | ||||
-rw-r--r-- | dyn_load.c | 138 | ||||
-rw-r--r-- | gc.h | 42 | ||||
-rw-r--r-- | gc_priv.h | 35 | ||||
-rw-r--r-- | misc.c | 34 | ||||
-rw-r--r-- | mit_threads.c | 467 | ||||
-rw-r--r-- | os_dep.c | 66 | ||||
-rw-r--r-- | test.c | 140 |
13 files changed, 1755 insertions, 32 deletions
@@ -13,7 +13,7 @@ AS=as # The above doesn't work with gas, which doesn't run cpp. # Define AS as `gcc -c -x assembler-with-cpp' instead. -CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT +CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT -DSOLARIS_THREADS # Setjmp_test may yield overly optimistic results when compiled # without optimization. @@ -79,9 +79,9 @@ RANLIB= ranlib srcdir = . VPATH = $(srcdir) -OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o typd_mlc.o ptr_chck.o +OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o typd_mlc.o ptr_chck.o mit_threads.o dec_threads.o -CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c typd_mlc.c ptr_chck.c +CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c typd_mlc.c ptr_chck.c mit_threads.c dec_threads.c CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/de.c cord/cordtest.c cord/cord.h cord/ec.h cord/private/cord_pos.h cord/de_win.c cord/de_win.h cord/de_cmds.h cord/de_win.ICO cord/de_win.RC cord/SCOPTIONS.amiga cord/SMakefile.amiga diff --git a/Makefile.pthreads b/Makefile.pthreads new file mode 100644 index 00000000..8981a014 --- /dev/null +++ b/Makefile.pthreads @@ -0,0 +1,67 @@ +# If your make barfs, try gnumake instead. + +# Supported targets: +# <default> - builds gc.a and gctest +# gc.a - builds basic library +# gctest - tests basic library +# c++ - adds C++ interface to library + +# Default threads package -- uncomment ONE of the following + +THR=MIT +#THR=DEC + +ifeq ($(THR),MIT) + +# Definitions for MIT-pthreads +CC=pgcc +IFCC=cc +CXX=pg++ +CFLAGS=-g -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT \ + -DMIT_PTHREADS -DGATHERSTATS +endif + +ifeq ($(THR),DEC) + +# Definitions for DECthreads +CC=cc +IFCC=cc +CXX=gcc +CFLAGS= -g -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT \ + -DDEC_PTHREADS -DGATHERSTATS +THRLIB=-lpthreads -lmach -lc_r +endif + +ifeq ($(THR),SOLARIS) + +# Definitions for Solaris threads +CC=gcc +IFCC=gcc +CXX=gcc +CFLAGS= -g -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT \ + -DSOLARIS_THREADS -DGATHERSTATS +endif + +all: gc.a gctest + +gc.a: pre-built + $(MAKE) -f Makefile CC=$(CC) CXX=$(CXX) CFLAGS="$(CFLAGS)" $@ + +test.o: + $(MAKE) -f Makefile CC=$(CC) CXX=$(CXX) CFLAGS="$(CFLAGS)" $@ + +gctest: pre-built gc.a test.o + $(MAKE) -f Makefile CC=$(CC) CXX=$(CXX) CFLAGS="$(CFLAGS) $(THRLIB)" $@ + +c++: gc.a test.o + $(MAKE) -f Makefile CC=$(CC) CXX=$(CXX) CFLAGS="$(CFLAGS) $(THRLIB)" $@ + +# May need to avoid pgcc for if_not_there and if_mach... + +pre-built: if_not_there if_mach + +if_not_there: if_not_there.c + $(MAKE) -f Makefile CC=$(IFCC) if_not_there + +if_mach: if_mach.c config.h + $(MAKE) -f Makefile CC=$(IFCC) if_mach diff --git a/README.pthreads b/README.pthreads new file mode 100644 index 00000000..f411fc64 --- /dev/null +++ b/README.pthreads @@ -0,0 +1,203 @@ +See the README file for main Copyright notices. + +DECthreads/MIT-pthreads extensions: +Copyright (c) 1995, 1996 by Ian Piumarta and INRIA, with permission +for use under exactly the same terms as the original GC; i.e... + +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. + + +MODIFICATIONS + +The pthreads extensions required the following GC source files to be +modified: + + dyn_load.c + gc.h + gc_priv.h + misc.c + os_dep.c + test.c + +The modified sections are clearly visible, being those affected by the +existence of the preprocessor symbols MIT_PTHREADS and DEC_PTHREADS. +No parts of the original sources outside these sections were modified. +The following files are new: + + README.pthreads + dec_threads.c + mit_threads.c + +The Makefile was also modified very slightly, to include the two new +source files in the OBJS and CSRCS lists. + + +GENERAL DESCRIPTION + +The pthreads extensions allow the GC to be used with DECpthreads +(under Digital Unix, formerly DEC OSF/1) or the user-level +MIT-pthreads implementation by Chris Provenzano (running on a large +variety of platforms). DECthreads is bundled with DEC's Unix +machines; MIT-pthreads is available from: + + ftp://sipb.mit.edu:/pub/pthreads + or ftp://toxicwaste.mit.edu:/pub/archive/pthreads + + +INSTALLATION AND PORTABILITY + +These extensions have been tested (and appear to work) on the +following platforms: + + DECthreads: + DEC Alpha OSF/1 3.2C/3.2D + + MIT-pthreads (tested with pthreads versions 1.60 beta4 and beta5.9): + DEC Alpha OSF/1 3.2C/3.2D + Sparc SunOS 4.1.3/4.1.3_U1/4.1.4 + i586 Red Hat Linux 3.0.3, Kernel 1.99.4 + HP 9000/725 HP-UX A.09.05 + +Very minor additions may be needed to be make the extensions work with +the other architectures supported by both the GC and MIT-pthreads. + +Note that some versions of gcc-2.7 are broken on DEC Alpha. Version +2.7.0 seems to be reliable, and versions 2.7.3 and later might be. + + Unless you're already familiar with this GC: + + Read the installtation notes in the README file! + Read the Makefile! + + To install for DECpthreads: + + Edit "Makefile.pthreads". Uncomment the definition "THR=DEC". + Edit (if necessary) the definitions immediately following the + comment "Definitions for DECthreads" to use the correct + compiler and/or CFLAGS for your environment. See Makefile for + an explanation of the GC-related flags. + + If you are using gcc rather than the DEC C compiler, edit the + Makefile and remove the line indicated in the target + "mark_rts.o". + + Type "make -f Makefile.pthreads" to build the basic library + and gctest program, with pthreads support. + + Run gctest to check that things are working. + + If you require C++ support, type "make -f Makefile.pthreads + c++ THR=DEC". Make sure the test_cpp program does not fail. + + To install for MIT-pthreads: + + Install (if necessary) the MIT-pthreads package (see above). + Versions 1.60beta4 and earlier have a broken "pg++" script + (later versions may be corrected): if necessary change the + definition of "libs" from + libs='-lpthread -lstdc++ -lm -lgcc' + to + libs='-lpthread -lstdc++ -lm -lgcc -lpthread' + + Edit "Makefile.pthreads". Uncomment the definition "THR=MIT". + Edit (if necessary) the definitions immediately following the + comment "Definitions for MIT-pthreads" to use the correct + CFLAGS for your environment (the supplied compiler definitions + should be correct in most situations). See Makefile for an + explanation of the GC-related flags. + + If you are compiling on a DEC Alpha using gcc rather than the + DEC C compiler, edit the Makefile and remove the line + indicated in the target "mark_rts.o". + + Type "make -f Makefile.pthreads" to build the basic library + and gctest program, with pthreads support. + + Run gctest to check that things are working. + + If you require C++ support, type "make -f Makefile.pthreads + c++ THR=MIT". Make sure the test_cpp program does not fail. + + +MODIFICATIONS TO THE PTHREADS INTERFACES + +The extensions rely on intercepting calls to a few pthreads routines. +Any source file using the pthreads routines MUST include "gc.h", even +if there is no GC-related code in the file. + +The following DECthreads routines are affected: + + int pthread_create(pthread_t *thread, pthread_attr_t attr, + pthread_startroutine_t start_routine, + pthread_addr_t arg); + + int pthread_detach(pthread_t *thread); + +The following MIT-pthreads routines are affected: + + int pthread_create(pthread_t *thread, pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); + + +BUGS + +Incremental mode does not work with DECpthreads. The reasons are not +yet fully explored. GC_enable_incremental() is therefore a no-op when +the GC is compiled with DECpthreads support. + +The DECthreads extensions assume a one-to-one correspondance between +each pthread and a kernel (mach) thread. This appears to be true with +round-robin scheduling, but may not be true for other scheduling +policies. (This concerns stopping the world: there is no documented +mechanism to stop the pthreads world, so we have to stop the +underlying kernel threads instead [which is documented]. When asked +about setting the pthreads kernel lock, all that the DEC techincal +people would tell me was: "don't meddle with things that you don't +understand".) + +The extensions have been tested with both versions of pthreads, +although in relatively undemanding situations. Some subtle problems +may still exist in programs which use thread synchronisation +operations (pthread_join/pthread_detach) in bizarre ways, or which use +scheduling policies other than the default round-robin. The more +exotic pthread routines (e.g. executing a call on another thread's +stack) have not been tested very much at all. + +The MITpthreads extensions rely on snooping about in small parts of +the pthreads' private internal structures. This seems unavoidable. + +The extensions appear to work with unmodified versions of the GNU +libgcc and libstdc++ (at least as far as iostreams are concerned), but +large parts of these libraries remain untested in a pthreads/GC +context. + + +BUG REPORTS AND SUGGESTIONS FOR IMPROVEMENTS + +Should be sent to: Ian.Piumarta@inria.fr + +It would be most helpful if you could include a small program which +reproduces the bug. + + +ACKNOWLEDGEMENTS + +I am immensely grateful to David Halls (David.Halls@cl.cam.ac.uk) who +spent many months "alpha testing" the extensions, suffering problems +with the early implementations, and contributing suggestions for +improvements. He also kindly tested the MIT-pthreads extensions under +Linux and provided the code specific to dynamic loading and the HP-PA +architecture. + + +------------------------------- projet SOR ------------------------------- +Ian Piumarta, INRIA Rocquencourt, Internet: Ian.Piumarta@inria.fr +BP105, 78153 Le Chesnay Cedex, FRANCE Voice: +33 1 39 63 52 87 +----------------------- Systemes a Objets Repartis ----------------------- diff --git a/alpha_mach_dep.s b/alpha_mach_dep.s index 265c3141..087baeac 100644 --- a/alpha_mach_dep.s +++ b/alpha_mach_dep.s @@ -1,15 +1,18 @@ # $Id: alpha_mach_dep.s,v 1.2 1993/01/18 22:54:51 dosser Exp $ -# define call_push(x) lda $16, 0(x); jsr $26, GC_push_one - +# define call_push(x) \ + lda $16, 0(x); /* copy x to first argument register */ \ + jsr $26, GC_push_one; /* call GC_push_one, ret addr in $26 */ \ + ldgp $gp, 0($26) /* restore $gp register from $ra */ + .text .align 4 .globl GC_push_regs .ent GC_push_regs 2 GC_push_regs: - ldgp $gp, 0($27) - lda $sp, -32($sp) - stq $26, 8($sp) + ldgp $gp, 0($27) # set gp from the procedure value reg + lda $sp, -32($sp) # make stack frame + stq $26, 8($sp) # save return address .mask 0x04000000, -8 .frame $sp, 16, $26, 0 @@ -51,8 +54,7 @@ GC_push_regs: call_push($29) # Global Pointer # call_push($30) # Stack Pointer - ldgp $gp, 0($26) - ldq $26, 8($sp) - lda $sp, 32($sp) - ret $31, ($26), 1 - .end GC_push_regs + ldq $26, 8($sp) # restore return address + lda $sp, 32($sp) # pop stack frame + ret $31, ($26), 1 # return ($31 == hardwired zero) + .end GC_push_regs @@ -604,18 +604,21 @@ # define HEURISTIC2 # endif # define STACK_GROWS_UP +# define DYNAMIC_LOADING # endif # ifdef ALPHA # define MACH_TYPE "ALPHA" # define ALIGNMENT 8 # define DATASTART ((ptr_t) 0x140000000) -# define HEURISTIC2 +# ifndef DEC_PTHREADS +# define HEURISTIC2 /* Normally HEURISTIC2 is too conervative, since */ /* the text segment immediately follows the stack. */ /* Hence we give an upper pound. */ - extern __start; -# define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1))) + extern __start; +# define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1))) +# endif # define CPP_WORDSZ 64 # define MPROTECT_VDB # define DYNAMIC_LOADING diff --git a/dec_threads.c b/dec_threads.c new file mode 100644 index 00000000..1b323d40 --- /dev/null +++ b/dec_threads.c @@ -0,0 +1,558 @@ +/* dec_threads.c +/* +/* Support for DECthreads in Boehm/Demer/Weiser garbage collector. +/* Depends on a gc.a compiled with -DDEC_PTHREADS, plus local modifications. +/* Final executable must be linked with "-lpthreads -lmach -lc_r". +/* +/* Author: Ian.Piumarta@INRIA.fr +/* +/* Copyright (C) 1996 by INRIA and Ian Piumarta +/* +/* Known problems: +/* +/* - debugging needs to be tidied up +/* +/* - some redundant code should be removed +/* +/* last edited: Wed Nov 13 15:42:21 1996 by piumarta (Ian Piumarta) on xombul +/* +/***************************************************************************/ + +#ifdef DEC_PTHREADS + +/* Turn on debugging output */ +#undef DEBUG + +/* Turn on trace messages for thread creation/deletion */ +#undef TRACE + +/* visual indication of GC: */ +/* * = push stack for GC thread, */ +/* + = push stack for other thread */ +#undef VISUAL_CLUES + +#include <unistd.h> +#include <sys/types.h> +#include <signal.h> +#include <setjmp.h> +#include <pthread.h> +#include <sys/user.h> +#include <sys/table.h> +#include <mach/mach_interface.h> +#include <mach/thread_status.h> + +extern char *GC_malloc_uncollectable(); +extern pthread_mutex_t GC_allocate_ml; +extern int GC_pt_init_ok; + +#define ERR(X) { perror(X); abort(); } + +extern GC_err_printf(); + +#ifdef DEBUG +# define FPRINTF(X) GC_err_printf X +#else +# define FPRINTF(X) +#endif + +#define PRINT_INFO(X,Y,Z) /* or #define PRINT_INFO print_info */ + +#define CHECK_DUPLICATE (2<<0) +#define CHECK_OVERLAP (2<<1) + +#define struct_base(PTR, FLD, TYP) \ + ((TYP*)((char*)(PTR)-(char*)(&(((TYP*)0)->FLD)))) + +typedef struct QUEUE { + struct QUEUE *flink, *blink; +} queue; + +typedef struct GC_PT_THREAD_INFO { + queue q; + /* user thread structure */ + pthread_t thread; + /* start routine and argument */ + pthread_startroutine_t starter; + pthread_addr_t arg; + /* mach port */ + port_t port; + /* stack top */ + void *stack_top; + /* approximate thread status */ + short active; + short exited; + short detached; + /* saved register state */ + struct alpha_thread_state reg_state; +} gc_pt_thread_info; + +#define TID(info) (info->port) + +static queue infos = { &infos, &infos }; + +#define DISABLE_SIGNALS() GC_disable_signals() +#define ENABLE_SIGNALS() GC_enable_signals() + +#define LOCK(ML) \ + if(pthread_mutex_lock(&GC_pt_##ML##_ml)<0) ERR("LOCK(GC_pt_"#ML"_ml)"); + +#define UNLOCK(ML) \ + if(pthread_mutex_unlock(&GC_pt_##ML##_ml)<0) ERR("UNLOCK(GC_pt_"#ML"_ml)"); + +static pthread_mutex_t GC_pt_info_ml; +static pthread_mutex_t GC_pt_init_ml; + +static pthread_key_t info_key; + + +static print_info(gc_pt_thread_info *info, char *msg, long arg) +{ + GC_err_printf("********** INFO structure at %lx size %d for ", + info, sizeof(*info)); + GC_err_printf(msg, arg); + GC_err_printf(":\n"); + /* this is VERY naughty: this structure is supposed to be opaque. */ + GC_err_printf(" thread at %lx = (%lx, %lx)\n", + info->thread, info->thread.field1, info->thread.field2); + GC_err_printf(" startroutine at %lx, arg %lx\n", info->starter, info->arg); + GC_err_printf(" mach port number is %d\n", info->port); + GC_err_printf(" stack_top is %lx\n", info->stack_top); + GC_err_printf(" stack pointer is %lx\n", info->reg_state.r30); + GC_err_printf(" thread is %s", info->active ? "active" : "inactive"); + GC_err_printf(", %s", info->exited ? "exited" : "unexited"); + GC_err_printf(", %s", info->detached ? "detached" : "attached"); + GC_err_printf("\n flink %lx, blink %lx\n", info->q.flink, info->q.blink); +} + + +static print_infos() +{ + queue *ptr= infos.flink; + + GC_err_printf("infos.flink = %lx\n", infos.flink); + print_info(struct_base(ptr, q, gc_pt_thread_info), "flink", 0); + GC_err_printf("infos.blink = %lx\n", infos.flink); + print_info(struct_base(ptr, q, gc_pt_thread_info), "blink", 0); + + while (ptr != &infos) { + GC_err_printf("q ===> %lx:", ptr); + print_info(struct_base(ptr, q, gc_pt_thread_info), "print_infos", 0); + ptr= ptr->flink; + } +} + + +/* GC_pt_check + * + * Check the given gc_pt_thread_info structure is consistent with the others. + * + * Caller must hold the info lock. + */ +static int GC_pt_check(gc_pt_thread_info *info) +{ + queue *ptr= infos.flink; + int fail= 0; + while (ptr != &infos) { + gc_pt_thread_info *old= struct_base(ptr, q, gc_pt_thread_info); +/* + if (info != old && info->port == old->port) { + fail|= CHECK_DUPLICATE; + FPRINTF((stderr, "duplicate thread: %d\n", info->port)); + } +*/ + if (info != old && pthread_equal(info->thread, old->thread)) { + fail|= CHECK_DUPLICATE; + FPRINTF((stderr, "duplicate thread: %d\n", info->port)); + } +/* + if (info != old && + (((char *)info->stack_top > (char *)old->reg_state.r30 && + (char *)info->stack_top < (char *)old->stack_top) || + ((char *)info->reg_state.r30 > (char *)old->reg_state.r30 && + (char *)info->reg_state.r30 < (char *)old->stack_top))) { + fail|= CHECK_OVERLAP; + FPRINTF((stderr, "stack overlaps for threads: %lx and %lx\n", + TID(old), TID(info))); + } +*/ + ptr= ptr->flink; + } + return fail; +} + + +/* This must be called with the info lock set. + */ +static gc_pt_thread_info *GC_pt_find_pthread_info(pthread_t *thread) +{ + queue *ptr= infos.flink; + while (ptr != &infos) { + gc_pt_thread_info *info= struct_base(ptr, q, gc_pt_thread_info); + if (pthread_equal(*thread, info->thread)) + return info; + ptr= ptr->flink; + } + return 0; +} + + +static void GC_pt_info_destroy(pthread_addr_t infop); + + +/* GC_pt_init + * + * Initialises DECthreads-specific data structures. Should be called + * before any threads are created, and before any posibility of GC + * activity. + */ +void GC_pt_init() +{ + gc_pt_thread_info *info; + struct user u; + pthread_mutexattr_t attr; + + if(GC_pt_init_ok) return; + GC_pt_init_ok= 1; + FPRINTF((stderr, "initialising GC_dt...\n")); + FPRINTF((stderr, "ENTER GC_pt_init()\n")); + if (pthread_mutexattr_create(&attr) + || pthread_mutexattr_setkind_np(&attr, MUTEX_RECURSIVE_NP) + || pthread_mutex_init(&GC_pt_info_ml, attr) + || pthread_mutex_init(&GC_pt_init_ml, attr) + || pthread_keycreate(&info_key, GC_pt_info_destroy)) + ERR("GC_pt_init"); + + if (table(TBL_UAREA, getpid(), &u, 1, sizeof(struct user)) < 1) { + GC_err_printf("problem getting u area\n"); + } + + info= (gc_pt_thread_info *)GC_malloc_uncollectable(sizeof(gc_pt_thread_info)); + if (!info) { + GC_err_printf("GC_dt: could not allocate thread_info\n"); + exit(1); + } + + info->port= thread_self(); + info->stack_top= u.u_stack_end; + info->active= 1; + info->exited= 0; + info->detached= 0; + info->q.flink= &infos; + info->q.blink= &infos; + bzero((char *)&info->reg_state, sizeof(info->reg_state)); + + LOCK(info); + infos.flink= infos.blink= &info->q; + UNLOCK(info); + + PRINT_INFO(info, "default thread", 0); + + FPRINTF((stderr, "LEAVE GC_pt_init()\n")); +} + + +static void GC_pt_apply_other_threads(kern_return_t (*func)(port_t)) +{ + port_t me= thread_self(); + + FPRINTF((stderr, "APPLYING for thread %d\n", me)); + + LOCK(info); + { + queue *ptr= infos.flink; + while (ptr != &infos) + { + gc_pt_thread_info *info= struct_base(ptr, q, gc_pt_thread_info); + if (info->port != me && info->active && !info->exited) + { + FPRINTF((stderr, "applying to thread %d:", info->port)); + FPRINTF((stderr, " %s %s %s\n", + info->active ? "active" : "inactive", + info->exited ? "exited" : "unexited", + info->detached ? "detached" : "attached")); + if (func(info->port) != KERN_SUCCESS) + { + UNLOCK(info); + GC_err_printf("GC_pt_apply_other_threads failed:\n"); + print_info(info, "apply failed from thread %d", me); + abort(); + } + } + else + { + FPRINTF((stderr, "NOT applying to thread %d:", info->port)); + FPRINTF((stderr, " %s %s %s\n", + info->active ? "active" : "inactive", + info->exited ? "exited" : "unexited", + info->detached ? "detached" : "attached")); + } + ptr= ptr->flink; + } + } + UNLOCK(info); +} + + +void GC_pt_stop_world() +{ + if (!GC_pt_init_ok) GC_init(); + FPRINTF((stderr, "stopping the world...\n")); + GC_pt_apply_other_threads(thread_suspend); + FPRINTF((stderr, "...stopped!\n")); +} + +void GC_pt_start_world() +{ + FPRINTF((stderr, "starting the world...\n")); + GC_pt_apply_other_threads(thread_resume); + FPRINTF((stderr, "...started!\n")); +} + + +/* GC_pt_push_all_stacks + * + * Pushes all thread stacks, including the default stack. Default stack + * pointer read from /proc, other thread stacks taken from stacks queue. + * Locks the stacks to prevent thread creation/deletion during pushing. + */ +void GC_pt_push_all_stacks() +{ + queue *ptr= infos.flink; + FPRINTF((stderr, "PUSHING THREAD STACKS:\n")); + while (ptr != &infos) + { + gc_pt_thread_info *info= struct_base(ptr, q, gc_pt_thread_info); + if (info->active && !info->exited) + if (info->port == thread_self()) + { +#ifdef VISUAL_CLUES + fputc('*', stderr); +#endif VISUAL_CLUES + FPRINTF((stderr, "[%lx,%lx]\n", &ptr, info->stack_top)); + GC_push_all_stack(&ptr, info->stack_top); + } + else + { + int count= ALPHA_THREAD_STATE_COUNT; + FPRINTF((stderr, "+")); + /* get register state to (a) know sp, and (b) mark registers */ + PRINT_INFO(info, "thread %d PRE-GETSTATE", info->port); + FPRINTF((stderr, " reg_state dumped to %lx, size %d\n", + &(info->reg_state), sizeof(info->reg_state))); + if (thread_get_state(info->port, + ALPHA_THREAD_STATE, + (thread_state_t)&(info->reg_state), + &count) != KERN_SUCCESS) + { + GC_err_printf("problem in thread_get_state"); + exit(1); + } + PRINT_INFO(info, "thread %d POST-GETSTATE", info->port); +#ifdef VISUAL_CLUES + fputc('+', stderr); +#endif VISUAL_CLUES + FPRINTF((stderr, "[%lx,%lx]\n", + info->reg_state.r30, info->stack_top)); + GC_push_all_stack(info->reg_state.r30, info->stack_top); + } + ptr= ptr->flink; + } +} + + +/* GC_pt_thread_init + * + * Creates a gc_pt_thread_info structure describing the calling thread in the + * stacks queue. + */ +static void GC_pt_thread_init(gc_pt_thread_info *info, void *stacktop) +{ + FPRINTF((stderr, "ENTER GC_pt_thread_init(%lx)\n", TID(info))); + { + int status; + info->port= thread_self(); + info->stack_top= stacktop; + PRINT_INFO(info, "thread %d", info->port); + if (status= GC_pt_check(info)) { + if (status & CHECK_DUPLICATE) + GC_err_printf("GC_pt_thread_init: duplicated thread %lx\n", + TID(info)); + if (status & CHECK_OVERLAP) + GC_err_printf("GC_pt_thread_init: stack overlap %lx\n", + TID(info)); + if (status) { + GC_err_printf("GC_pt_thread_init: aborting, sorry...\n"); + abort(); + } + } + } + FPRINTF((stderr, "LEAVE GC_pt_thread_init(%lx)\n", TID(info))); +} + + +/* GC_pt_info_destroy + * + * The thread is finished, and about to exit -- either due to a normal + * return or a cancel request. If it is already detached, remove its + * stack. If not, mark the thread as exited so that its stack will be + * removed when it is eventually detached. + */ +static void GC_pt_info_destroy(pthread_addr_t infop) +{ + gc_pt_thread_info *info= (gc_pt_thread_info *)infop; + + FPRINTF((stderr, "ENTER GC_pt_info_destroy(%lx)\n", infop)); + if (pthread_mutex_lock(&GC_allocate_ml) < 0) + ERR("GC_pt_info_destroy: lock(&GC_allocate_ml)"); + { + /* safety first: check that the info structure really exists */ + queue *target= &info->q; + queue *ptr= infos.flink; + while (ptr != &infos) + { + if (ptr == target) + break; + else + ptr= ptr->flink; + } + if (ptr != target) + { + if (pthread_mutex_unlock(&GC_allocate_ml) < 0) + ERR("GC_pt_info_destroy: unlock(&GC_allocate_ml)"); + GC_err_printf("GC_pt_info_destroy: unknown info -- aborting\n"); + abort(); + } + if (info->detached) + { + /* thread detached => delete stack on exit */ + queue *ptr= &info->q; + ptr->flink->blink= ptr->blink; + ptr->blink->flink= ptr->flink; +#ifdef TRACE + GC_err_printf("DELETE (exit) %lx\n", TID(info)); +#endif TRACE + GC_free(info); + } + else + { + info->exited= 1; /* cancelled thread won't do this otherwise */ + info->active= 0; + } + } + if (pthread_mutex_unlock(&GC_allocate_ml) < 0) + ERR("GC_pt_info_destroy: unlock(&GC_allocate_ml)"); + FPRINTF((stderr, "LEAVE GC_pt_info_destroy(%lx)\n", infop)); +} + + +static pthread_addr_t GC_pt_start_routine(gc_pt_thread_info *info) +{ + pthread_addr_t status; + LOCK(info); + /* wait for GC_pt_pthread_create to finish building info */ + FPRINTF((stderr, "ENTER GC_pt_start_routine(%lx)\n", TID(info))); + GC_pt_thread_init(info, &info); + UNLOCK(info); /* it is now safe to start the next thread */ + pthread_setspecific(info_key, info); /* the reaper for the thread */ + /* this thread might be starting up during a GC! Do not proceed beyond + this point until the allocation lock becomes available. */ + pthread_mutex_lock(&GC_allocate_ml); + info->active= 1; + pthread_mutex_unlock(&GC_allocate_ml); +#ifdef TRACE + GC_err_printf("LAUNCH (start) %lx\n", TID(info)); +#endif TRACE + status= info->starter(info->arg); + info->active= 0; + info->exited= 1; + FPRINTF((stderr, "LEAVE GC_pt_start_routine(%lx)\n", TID(info))); + return status; +} + +/* GC_pt_pthread_create + * + * Creates a new thread, allocates a gc_pt_thread_info object describing the + * new thread, + * + * Get stack information by intercepting thread creation. + */ +int GC_pt_pthread_create(pthread_t *thread, pthread_attr_t attr, + pthread_startroutine_t start_routine, + pthread_addr_t arg) +{ + int result; + gc_pt_thread_info *info; + + FPRINTF((stderr, "ENTER GC_pt_pthread_create(%lx)\n", thread)); + info= (void *)GC_malloc_uncollectable(sizeof(gc_pt_thread_info)); + info->starter= start_routine; + info->arg= arg; + info->active= 0; + info->detached= 0; + info->exited= 0; + LOCK(info); + if (result= pthread_create(thread, attr, + (pthread_startroutine_t)GC_pt_start_routine, + (pthread_addr_t)info)) { + GC_free(info); + FPRINTF((stderr, "LEAVE GC_pt_pthread_create(%lx) ===> ERROR\n", thread)); + goto end; /* something is amiss */ + } + info->thread= *thread; + PRINT_INFO(info, "uninitialized new thread", 0); + /* add to end of info queue */ + { + queue *ptr= &info->q; + ptr->flink= &infos; + ptr->blink= infos.blink; + infos.blink->flink= ptr; + infos.blink= ptr; + } + FPRINTF((stderr, "LEAVE GC_pt_pthread_create(%lx) ===> %lx\n", + thread, TID(info))); + end: + UNLOCK(info); + return result; +} + + +/* GC_pt_pthread_detach + * + * If thread already exited, remove its stack. Otherwise mark the thread + * as detached, so that its stack will be removed when it eventually exits. + */ +int GC_pt_pthread_detach(pthread_t *thread) +{ + int result; + FPRINTF((stderr, "ENTER GC_pt_pthread_detach(%lx)\n", thread)); + LOCK(info); + { + gc_pt_thread_info *info= GC_pt_find_pthread_info(thread); + if (!info) + { + UNLOCK(info); + errno= ESRCH; + return -1; + } + if (info->exited) + { + /* thread exited => delete stack on detach */ + queue *ptr= &info->q; + ptr->flink->blink= ptr->blink; + ptr->blink->flink= ptr->flink; + FPRINTF((stderr, "DELETE (detach) %lx\n", TID(info))); + GC_free(info); + } + else + { + info->detached= 1; + } + } + UNLOCK(info); + result= pthread_detach(thread); + FPRINTF((stderr, "LEAVE GC_pt_pthread_detach(%lx)\n", thread)); + return result; +} + + +#endif DEC_PTHREADS @@ -45,7 +45,7 @@ # endif #if (defined(DYNAMIC_LOADING) || defined(MSWIN32)) && !defined(PCR) -#if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && !defined(MSWIN32) && !defined(ALPHA) +#if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && !defined(MSWIN32) && !defined(ALPHA) && !defined(HP_PA) --> We only know how to find data segments of dynamic libraries under SunOS, --> IRIX5, DRSNX and Win32. Additional SVR4 variants might not be too --> hard to add. @@ -158,7 +158,8 @@ static ptr_t GC_first_common() # if defined(SUNOS4) || defined(SUNOS5DL) /* Add dynamic library data sections to the root set. */ -# if !defined(PCR) && !defined(SOLARIS_THREADS) && defined(THREADS) +# if !defined(PCR) && !defined(SOLARIS_THREADS) && defined(THREADS) \ + && !defined(MIT_PTHREADS) && !defined(DEC_PTHREADS) # ifndef SRC_M3 --> fix mutual exclusion with dlopen # endif /* We assume M3 programs don't call dlopen for now */ @@ -523,6 +524,139 @@ void GC_register_dynamic_libraries() } #endif +#if defined(HP_PA) + +#include <dl.h> +#include <errno.h> + +#ifndef MIT_PTHREADS +extern int errno; +extern char *sys_errlist[]; +extern int sys_nerr; +#endif + +void GC_register_dynamic_libraries() +{ + int status; + int index = 1; /* Ordinal position in shared library search list */ + struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */ + + /* For each dynamic library loaded */ + while (TRUE) { + + /* Get info about next shared library */ + status = shl_get(index, &shl_desc); + + /* Check if this is the end of the list or if some error occured */ + if (status != 0) { + if (errno == EINVAL) { + break; /* Moved past end of shared library list --> finished */ + } else { + if (errno <= sys_nerr) { + GC_printf1("dynamic_load: %s\n", (long) sys_errlist[errno]); + } else { + GC_printf1("dynamic_load: %d\n", (long) errno); + } + ABORT("shl_get failed"); + } + } + +# ifdef VERBOSE + GC_printf0("---Shared library---\n"); + GC_printf1("\tfilename = \"%s\"\n", shl_desc->filename); + GC_printf1("\tindex = %d\n", index); + GC_printf1("\thandle = %08x\n", + (unsigned long) shl_desc->handle); + GC_printf1("\ttext seg. start = %08x\n", shl_desc->tstart); + GC_printf1("\ttext seg. end = %08x\n", shl_desc->tend); + GC_printf1("\tdata seg. start = %08x\n", shl_desc->dstart); + GC_printf1("\tdata seg. end = %08x\n", shl_desc->dend); + GC_printf1("\tref. count = %lu\n", shl_desc->ref_count); +# endif + + /* register shared library's data segment as a garbage collection root */ + GC_add_roots_inner((char *) shl_desc->dstart, + (char *) shl_desc->dend, TRUE); + + index++; + } +} +#endif /* HP_PA */ + + +#if defined(MIT_PTHREADS) || defined(DEC_PTHREADS) +#ifdef HP_PA + + /* We're going to protect the lot, since there is evidence */ + /* that dlopen() isn't the only place where things can break. */ + +#undef shl_load +#undef shl_findsym +#undef shl_unload + +# define GC_DL_DEF(RTYPE, PROTO, FUNC) RTYPE PROTO \ + { RTYPE result; pthread_mutex_lock(&GC_allocate_ml); result= FUNC; \ + pthread_mutex_unlock(&GC_allocate_ml); return (result); } +GC_DL_DEF(shl_t, GC_shl_load(const char *path, int flags, long address), shl_load(path, flags, address)) +GC_DL_DEF(int , GC_shl_findsym(shl_t *handle, const char *sym, short type, void *value), + shl_findsym(handle, sym, type, value)) +GC_DL_DEF(int , GC_shl_unload(shl_t handle), shl_unload(handle)) + + /* unfortunately, MIT PThreads and dynamic loading on HPUX + causes programs to hang (not just for the GC); + we keep everything in but define some null-op stubs; + makes it easier to fix when pthreads is fixed */ + + shl_t shl_load(const char *path, int flags, long address) + { + return NULL; + } + + int shl_findsym(shl_t *handle, const char *symname, short type, void *value) + { + return -1; + } + + int shl_unload(shl_t handle) + { + return -1; + } + + int shl_get(int index, struct shl_descriptor **desc) + { + errno = EINVAL; + return -1; + } + +# define shl_load GC_shl_load +# define shl_findsym GC_shl_findsym +# define shl_unload GC_shl_unload + +#else + /* We're going to protect the lot, since there is evidence */ + /* that dlopen() isn't the only place where things can break. */ + +#undef dlopen +#undef dlsym +#undef dlclose +#undef dlerror + +# define GC_DL_DEF(RTYPE, PROTO, FUNC) RTYPE PROTO \ + { RTYPE result; pthread_mutex_lock(&GC_allocate_ml); result= FUNC; \ + pthread_mutex_unlock(&GC_allocate_ml); return (result); } +GC_DL_DEF(void *, GC_dlopen(const char *path, int mode), dlopen(path, mode)) +GC_DL_DEF(void *, GC_dlsym(void *handle, const char *symbol), + dlsym(handle, symbol)) +GC_DL_DEF(int , GC_dlclose(void *handle), dlclose(handle)) +GC_DL_DEF(char *, GC_dlerror(), dlerror()) + +# define dlopen GC_dlopen +# define dlsym GC_dlsym +# define dlclose GC_dlclose +# define dlerror GC_dlerror +#endif +#endif /* PTHREADS */ + #else /* !DYNAMIC_LOADING */ @@ -564,6 +564,48 @@ GC_PTR GC_malloc_many(size_t lb); #endif /* SOLARIS_THREADS */ +#ifdef DEC_PTHREADS +# include <pthread.h> + int GC_pt_pthread_create(pthread_t *thread, pthread_attr_t attr, + pthread_startroutine_t start_routine, + pthread_addr_t arg); + int GC_pt_pthread_detach(pthread_t *thread); +# define pthread_create GC_pt_pthread_create +# define pthread_detach GC_pt_pthread_detach + void * GC_malloc_many(size_t lb); +# define GC_NEXT(p) (*(void **)(p)) +#endif /* DEC_PTHREADS */ + +#ifdef MIT_PTHREADS +# include <pthread.h> + int GC_pt_pthread_create(pthread_t *thread, pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); +# define pthread_create GC_pt_pthread_create + void * GC_malloc_many(size_t lb); +# define GC_NEXT(p) (*(void **)(p)) +#endif /* MIT_PTHREADS */ + +#if (defined(MIT_PTHREADS) || defined(DEC_PTHREADS)) +#ifdef __hp9000s700 +# include <dl.h> + shl_t GC_shl_load(const char *path, int flags, long address); + int GC_shl_findsym(shl_t *handle, const char *sym, short type, void *value); + int GC_shl_unload(shl_t handle); +# define shl_load GC_shl_load +# define shl_findsym GC_shl_findsym +# define shl_unload GC_shl_unload +#else + void *GC_dlopen(const char *pathname, int mode); + void *GC_dlsym(void *handle, const char *name); + int GC_dlclose(void *handle); + char *GC_dlerror(void); +# define dlopen GC_dlopen +# define dlsym GC_dlsym +# define dlclose GC_dlclose +# define dlerror GC_dlerror +#endif +#endif /* PTHREADS */ + /* * If you are planning on putting * the collector in a SunOS 5 dynamic library, you need to call GC_INIT() @@ -161,10 +161,12 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # define GATHERSTATS #endif -# if defined(SOLARIS_THREADS) && !defined(SUNOS5) +# if defined(SOLARIS_THREADS) && !defined(SUNOS5) \ + || (defined(DEC_PTHREADS) && !defined(ALPHA)) --> inconsistent configuration # endif -# if defined(PCR) || defined(SRC_M3) || defined(SOLARIS_THREADS) +# if defined(PCR) || defined(SRC_M3) || defined(SOLARIS_THREADS) \ + || defined(DEC_PTHREADS) || defined(MIT_PTHREADS) # define THREADS # endif @@ -420,6 +422,18 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # define LOCK() mutex_lock(&GC_allocate_ml); # define UNLOCK() mutex_unlock(&GC_allocate_ml); # endif +# ifdef DEC_PTHREADS + extern pthread_mutex_t GC_allocate_ml; + extern int GC_pt_init_ok; +# define LOCK() {if (!GC_pt_init_ok) GC_init(); pthread_mutex_lock(&GC_allocate_ml);} +# define UNLOCK() if (GC_pt_init_ok) pthread_mutex_unlock(&GC_allocate_ml) +# endif +# ifdef MIT_PTHREADS + extern pthread_mutex_t GC_allocate_ml; + extern int GC_pt_init_ok; +# define LOCK() if (GC_pt_init_ok) pthread_mutex_lock(&GC_allocate_ml) +# define UNLOCK() if (GC_pt_init_ok) pthread_mutex_unlock(&GC_allocate_ml) +# endif # else # define LOCK() # define UNLOCK() @@ -448,7 +462,8 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # else # if defined(SRC_M3) || defined(AMIGA) || defined(SOLARIS_THREADS) \ || defined(MSWIN32) || defined(MACOS) || defined(DJGPP) \ - || defined(NO_SIGNALS) + || defined(NO_SIGNALS) \ + || defined(DEC_PTHREADS) || defined(MIT_PTHREADS) /* Also useful for debugging. */ /* Should probably use thr_sigsetmask for SOLARIS_THREADS. */ # define DISABLE_SIGNALS() @@ -479,8 +494,18 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # define STOP_WORLD() GC_stop_world() # define START_WORLD() GC_start_world() # else -# define STOP_WORLD() -# define START_WORLD() +# ifdef DEC_PTHREADS +# define STOP_WORLD() GC_pt_stop_world() +# define START_WORLD() GC_pt_start_world() +# else +# ifdef MIT_PTHREADS +# define STOP_WORLD() GC_pt_stop_world() +# define START_WORLD() GC_pt_start_world() +# else +# define STOP_WORLD() +# define START_WORLD() +# endif +# endif # endif # endif @@ -39,7 +39,12 @@ # ifdef SOLARIS_THREADS mutex_t GC_allocate_ml; /* Implicitly initialized. */ # else - --> declare allocator lock here +# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS) + pthread_mutex_t GC_allocate_ml; + int GC_pt_init_ok= 0; +# else + --> declare allocator lock here +# endif # endif # endif # endif @@ -376,6 +381,17 @@ void GC_init() { DCL_LOCK_STATE; +# ifdef DEC_PTHREADS + /* initialise the DECthreads support */ + { + pthread_mutexattr_t attr; + pthread_mutexattr_create(&attr); + pthread_mutexattr_setkind_np(&attr, MUTEX_RECURSIVE_NP); + pthread_mutex_init(&GC_allocate_ml, attr); + GC_pt_init(); + } +# endif + DISABLE_SIGNALS(); LOCK(); GC_init_inner(); @@ -393,6 +409,19 @@ void GC_init_inner() word dummy; if (GC_is_initialized) return; +#ifdef MIT_PTHREADS + /* We need to beat the GC to initialisation, so that it doesn't + hang when it first tries to use the allocator lock. */ + { + pthread_mutexattr_t attr; + extern void *pthread_initial; + if (!pthread_initial) pthread_init(); + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEXTYPE_RECURSIVE); + pthread_mutex_init(&GC_allocate_ml, &attr); + if (!GC_pt_init_ok) GC_pt_init(); /* faut eviter cela... */ + } +#endif # ifdef MSWIN32 GC_init_win32(); # endif @@ -513,6 +542,8 @@ void GC_init_inner() void GC_enable_incremental GC_PROTO(()) { + /* this is broken under DECthreads */ +#ifndef DEC_PTHREADS DCL_LOCK_STATE; # ifndef FIND_LEAK @@ -551,6 +582,7 @@ out: UNLOCK(); ENABLE_SIGNALS(); # endif +#endif /* DEC_PTHREADS */ } diff --git a/mit_threads.c b/mit_threads.c new file mode 100644 index 00000000..c778eda9 --- /dev/null +++ b/mit_threads.c @@ -0,0 +1,467 @@ +/* mit_threads.c +/* +/* Support for MIT-pthreads in Boehm/Demer/Weiser garbage collector. +/* Depends on a gc.a compiled with -DMIT_PTHREADS, plus local modifications. +/* All files should be compiled with pgcc. +/* +/* GC_init() should be called before doing anything else (the collector tries +/* to do this early enough, but may not always succeed). +/* +/* Author: Ian.Piumarta@INRIA.fr +/* +/* Copyright (C) 1996 by INRIA and Ian Piumarta +/* +/* last edited: Sun Nov 17 22:49:05 1996 by piumarta (Ian Piumarta) on corto +/* +/***************************************************************************/ + +#ifdef MIT_PTHREADS + +#undef DEBUG + +#include "config.h" + +/* there seems to be no other (easy) way to get at these... */ + +#if defined(ALPHA) +# define THREAD_SP(thr) (thr)->machdep_data.machdep_state[34] /* JB_SP */ +#elif defined(SPARC) +# define THREAD_SP(thr) (thr)->machdep_data.machdep_state[2] /* sc_sp */ +#elif defined(LINUX) +# define THREAD_SP(thr) (thr)->machdep_data.machdep_state->__sp /* __sp */ +#elif defined(HP_PA) +# define THREAD_SP(thr) (((int*)((thr)->machdep_data.machdep_state))[1]) +#elif defined(MIPS) +# define THREAD_SP(thr) (thr)->machdep_data.machdep_state[JB_SP] +#else +/* ...define THREAD_SP for your architecture here... + */ +--> where is your stack pointer? +#endif + +#include <stdio.h> +#define PTHREAD_KERNEL +#include <pthread.h> +#undef RETURN + +extern void *GC_get_stack_base(); + +/* use malloc-safe printf from the GC, and GC-safe malloc from pthreads + */ +extern GC_err_printf(); +extern void *malloc(int); + +#ifdef DEBUG +# define _CR GC_err_printf("\n") +# define ENTER(X) static char*_me_=#X;GC_err_printf("ENTER: %s\n",_me_) +# define RETURN GC_err_printf("LEAVE: %s\n",_me_);return +# define PRINT(X) GC_err_printf("%s: %s\n",_me_,(X)) +# define PRINT1(X,P) GC_err_printf("%s: ",_me_);GC_err_printf((X),(P));_CR +# define PRINT2(X,P,Q) GC_err_printf("%s: ",_me_);GC_err_printf((X),(P),(Q));_CR +# define PRINT3(X,P,Q,R) GC_err_printf("%s: ",_me_);GC_err_printf((X),(P),(Q),(R));_CR +# define ERR(X) {GC_err_printf("%s: ",_me_);perror(X);abort();} +#else +# define ENTER(X) +# define RETURN return +# define PRINT(X) +# define PRINT1(X,P) +# define PRINT2(X,P,Q) +# define PRINT3(X,P,Q,R) +# define ERR(X) {perror(X);abort();} +#endif + +/* dequeues */ + +#define struct_base(PTR, FLD, TYP) ((TYP*)((char*)(PTR)-(char*)(&(((TYP*)0)->FLD)))) + +typedef struct QUEUE { + struct QUEUE *flink, *blink; +} queue; + +/* GC thread info structure */ + +typedef struct GC_PT_INFO { + queue q; /* the dequeue on which this structure exists */ + pthread_t thread; /* the corresponding thread structure */ + void *stack_top; /* the highest address in this thread's stack */ + void *(*launch)(void *); /* the thread's real start routine */ + void *arg; /* the thread's real start routine argument */ + int mark_state; /* this info structures current civil status */ +} gc_pt_info; + +#define GC_PT_UNMARKED 0 +#define GC_PT_MARKED 1 +#define GC_PT_NASCENT 2 + +static queue infos = { &infos, &infos }; /* the dequeue of info structures */ +extern int GC_pt_init_ok; /* true when init is done */ + +static pthread_key_t info_key; /* identity mechanism */ + + +/* stop the threads world, somewhat gracelessly + */ +void GC_pt_stop_world() +{ + pthread_kernel_lock++; +} + +/* restart the threads world; also lacks charm + */ +void GC_pt_start_world() +{ + pthread_kernel_lock--; +} + + +extern pthread_mutex_t GC_allocate_ml; + +/* initialise the threads-related GC stuff + */ +void GC_pt_init() +{ + ENTER(GC_pt_init); + if (!GC_pt_init_ok) + { + GC_pt_init_ok= 1; + if (pthread_key_create(&info_key, 0)) ERR("pthread_key_create"); + /* + * create an info structure for the initial thread and push it onto + * the info dequeue + */ + { + gc_pt_info *info= (gc_pt_info *)malloc(sizeof(gc_pt_info)); + if (!info) ERR("malloc"); + infos.flink= infos.blink= &info->q; + info->q.flink= info->q.blink= &infos; + info->thread= pthread_initial; + info->stack_top= GC_get_stack_base(); + PRINT3("initial thread %lx: info %lx, stack %lx", + pthread_initial, info, info->stack_top); + if (pthread_setspecific(info_key, info)) ERR("pthread_setspecific"); + /* GC_err_printf("THREAD INITIAL %lx: info set to %lx\n", pthread_run, info);*/ + } + } + RETURN; +} + + +static void GC_pt_print_threads() +{ + pthread_t thread; + + GC_err_printf("PLL:"); + + for(thread= pthread_link_list; thread; thread= thread->pll) + GC_err_printf(" %lx->%lx", thread, thread->pll); + GC_err_printf("\n"); +} + + +/* print the info list + */ +static void gc_pt_print_info() +{ + queue *ptr= infos.flink; + ENTER(GC_pt_print_info); + while (ptr != &infos) + { + gc_pt_info *info= struct_base(ptr, q, gc_pt_info); + GC_err_printf("INFO %lx:\n", info); + GC_err_printf(" queue: %lx\n", info->q); + GC_err_printf(" thread: %lx\n", info->thread); + GC_err_printf(" stack_top: %lx\n", info->stack_top); + GC_err_printf(" launch: %lx\n", info->launch); + GC_err_printf(" arg: %lx\n", info->arg); + GC_err_printf(" mark_state: %lx\n", info->mark_state); + ptr= ptr->flink; + } + RETURN; +} + + +#define FIND_INFO_BY_LOOKUP + +/* given some thread, find the corresponding info + */ +static gc_pt_info *GC_pt_find_info(pthread_t target) +{ +#ifdef FIND_INFO_BY_LOOKUP + queue *ptr= infos.flink; + ENTER(GC_pt_find_info); + PRINT1("looking for thread %lx", target); + while (ptr != &infos) + { + gc_pt_info *info= struct_base(ptr, q, gc_pt_info); + PRINT1("looking at %lx", info->thread); + if (info->thread == target) + { + RETURN(info); + } + ptr= ptr->flink; + } + GC_err_printf("I can't find the info -- giving up.\n"); + abort(); +#else + +# ifdef FIND_INFO_BY_GETSPECIFIC /* THIS APPEARS TO BE BROKEN */ + gc_pt_info *info= (gc_pt_info *)pthread_getspecific(info_key); + ENTER(GC_pt_find_info); + PRINT1("looking for thread %lx", target); + /* GC_err_printf("THREAD %lx: found info %lx\n", target, info);*/ + return info; +# else + +# ifdef FIND_INFO_BY_INTERNALS + ENTER(GC_pt_find_info); + PRINT1("looking for thread %lx", target); + if (!target->specific_data) + { + PRINT("GC_pt_find_info: no specific data\n"); + return 0; + } + if (!target->specific_data[info_key]) + { + PRINT("GC_pt_find_info: info is null\n"); + return 0; + } + return (gc_pt_info *)target->specific_data[info_key]; +# else + +--> so how do you do want me to find the info? + +# endif +# endif +#endif +} + + +/* unmark all info structures + */ +static void GC_pt_unmark_info() +{ + int size =0; + queue *ptr= infos.flink; + ENTER(GC_pt_unmark_info); + while (ptr != &infos) + { + gc_pt_info *info= struct_base(ptr, q, gc_pt_info); + if (info->mark_state != GC_PT_NASCENT) + info->mark_state= GC_PT_UNMARKED; + ptr= ptr->flink; + size++; + } + PRINT1("INFO LIST SIZE IS %d", size); +} + + +/* cleanup for info structure -- this used to be called as a destructor + * for the thread-specific data, but now it's only ever called explicitly + * from within this file + */ +static void GC_pt_delete_info(gc_pt_info *info) +{ + ENTER(GC_pt_delete_info); + PRINT1("pthread_run is %lx", pthread_run); + PRINT1("info is %lx", info); + /* GC_err_printf("THREAD %lx: DELETE INFO %lx\n", info->thread, info);*/ + pthread_mutex_lock(&GC_allocate_ml); + { + info->q.blink->flink= info->q.flink; + info->q.flink->blink= info->q.blink; + } + pthread_mutex_unlock(&GC_allocate_ml); + PRINT1("info is %lx", info); + free(info); + RETURN; +} + + +/* sweep up unused info structures + */ +static void GC_pt_sweep_info() +{ + queue *ptr= infos.flink; + ENTER(GC_pt_sweep_info); + while (ptr != &infos) + { + gc_pt_info *info= struct_base(ptr, q, gc_pt_info); + ptr= ptr->flink; + if (info->mark_state == GC_PT_UNMARKED) + GC_pt_delete_info(info); + } +} + + +/* traverse the pthread linked list (containing all threads) pushing + * the pthread structures and stack (if appropriate) for each thread. + */ +void GC_pt_push_all_stacks() +{ + pthread_t thread; + ENTER(GC_pt_push_all_stacks); + + /* GC_err_printf("MARKING STACKS...\n");*/ + + if ((infos.flink == &infos) && (infos.blink == &infos)) { RETURN; } + + /* unmark all info structures */ + GC_pt_unmark_info(); + + /* flush (register windows and other) state for running thread */ + machdep_save_state(); + + /* GC_err_printf("THREAD LOOP: START\n");*/ + + for(thread= pthread_link_list; thread; thread= thread->pll) + { + /* GC_err_printf("THREAD LOOP: THREAD %lx, PLL %lx\n", thread, thread->pll);*/ + + PRINT("thread structures"); + /* mark from the pthread internal structures */ + PRINT2("GC_push_all(%lx, %lx)", thread, (char *)thread + sizeof(*thread)); + GC_push_all(thread, (char *)thread + sizeof(*thread)); + PRINT2("GC_push_all(%lx, %lx)", thread->machdep_data.machdep_state, + (char *)thread->machdep_data.machdep_state + + sizeof(*thread->machdep_data.machdep_state)); + GC_push_all(thread->machdep_data.machdep_state, + (char *)thread->machdep_data.machdep_state + + sizeof(*thread->machdep_data.machdep_state)); + PRINT("specific data"); + /* mark the thread-specific data area */ + if (thread->specific_data_count) + { + PRINT2("GC_push_all(%lx, %lx)\n", (void *)thread->specific_data, + ((void **)thread->specific_data) + PTHREAD_DATAKEYS_MAX - 1); + GC_push_all((void *)thread->specific_data, + ((void **)thread->specific_data) + PTHREAD_DATAKEYS_MAX - 1); + } + PRINT("thread stacks"); + /* mark from the thread's stack */ +/* if (thread->state != PS_DEAD) + */ + { + gc_pt_info *info= GC_pt_find_info(thread); + /* GC_err_printf("LOOKING UP INFO FOR %lx\n", thread);*/ + if (info) + { + /* GC_err_printf("INFO FOR %lx AT %lx\n", thread, info);*/ + GC_push_all(info, (char *)info+sizeof(*info)); + /* GC_err_printf("THREAD %lx: MARK INFO %lx\n", info->thread, info);*/ + info->mark_state= GC_PT_MARKED; + if (thread == pthread_run) + { + PRINT2("(RUN) GC_push_all_stack(%lx, %lx)", &thread, info->stack_top); + if ((void*)&thread < info->stack_top) + GC_push_all_stack(&thread, info->stack_top); + else + GC_push_all_stack(info->stack_top, &thread); + } + else /* suspended */ + { + if ((void*)THREAD_SP(thread) < info->stack_top) + { + PRINT2("(SUS) GC_push_all_stack(%lx, %lx)", + THREAD_SP(thread), info->stack_top); + GC_push_all_stack(THREAD_SP(thread), info->stack_top); + } + else + { + /*PRINT2("(SUS) GC_push_all_stack(%lx, %lx) --- IGNORING BAD SP!!!", + THREAD_SP(thread), info->stack_top);*/ + GC_push_all_stack(info->stack_top, THREAD_SP(thread)); + } + + } + } + else + GC_err_printf("THREAD %lx: NO INFO!!!!!!!!!\n", thread); + /* else /* we're a dead thread */ + /* { + /* PRINT1("NO STACK (thread %lx is dead)", thread); + /* } + */ + } + } + + /* GC_err_printf("THREAD LOOP: END\n");*/ + + /* sweep up dead info */ + GC_pt_sweep_info(); + + RETURN; +} + + +/* start routine wrapper, which places the threads id into the info + * structure and registers it as thread-specific data; called with the + * info structure as argument. + */ +static void *GC_pt_starter(void *arg) +{ + gc_pt_info *info= (gc_pt_info *)arg; + void *result; + ENTER(GC_pt_starter); + if (pthread_setspecific(info_key, info)) ERR("pthread_setspecific"); + /* GC_err_printf("THREAD %lx: info set to %lx\n", pthread_run, info);*/ + if (!GC_pt_find_info(pthread_run)) + { + GC_err_printf("GC_pt_starter: the active thread does not exist!\n"); + abort(); + } + PRINT3("*** STARTING %lx: pthread_run is %lx, info %lx", + info->thread, pthread_run, info); + /* GC_err_printf("THREAD RUN: %lx\n", info->thread);*/ + info->mark_state= GC_PT_MARKED; + result= info->launch(info->arg); + /* GC_err_printf("THREAD EXIT: %lx\n", info->thread);*/ + PRINT3("*** EXITING %lx: pthread_run is %lx, info %lx", + info->thread, pthread_run, info); + RETURN(result); +} + + +/* pthread_create wrapper, which creates an info structure on the info + * dequeue and then has the thread start up in GC_pt_starter. + */ +int GC_pt_pthread_create(pthread_t *thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), + void *arg) +{ + int status; + gc_pt_info *info= (gc_pt_info *)malloc(sizeof(gc_pt_info)); + ENTER(GC_pt_pthread_create); + if (!info) ERR("malloc"); + + /* thread mustn't start until we've built the info struct */ + GC_pt_stop_world(); + + status= pthread_create(thread, attr, GC_pt_starter, (void *)info); + if (!attr) attr= &pthread_attr_default; /* no stack size? -- use the default */ + /* push the info onto the dequeue */ + info->q.flink= infos.flink; + info->q.blink= &infos; + infos.flink->blink= &info->q; + infos.flink= &info->q; + /* fill in the blanks */ + info->thread= *thread; + info->launch= start_routine; + info->arg= arg; + info->mark_state= GC_PT_NASCENT; /* thread not yet born */ + /* pthread_create filled in the initial SP -- profitons-en ! */ + info->stack_top= (void *)THREAD_SP(*thread); + + /* GC_err_printf("THREAD CREATE: %lx\n", *thread);*/ + + /* GC_pt_print_threads();*/ + + PRINT1("*** CREATED THREAD %lx", *thread); + + /* we're now ready for the thread to begin */ + GC_pt_start_world(); + RETURN(status); +} + + +#endif /* MIT_PTHREADS */ @@ -19,7 +19,11 @@ /* in some later Linux releases, asm/sigcontext.h may have to */ /* be included instead. */ # define __KERNEL__ -# include <asm/signal.h> +# ifndef MIT_PTHREADS +# include <asm/signal.h> +# else +# include <asm/sigcontext.h> +# endif # undef __KERNEL__ # endif # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) @@ -43,6 +47,23 @@ # define NEED_FIND_LIMIT # endif +# if defined(MIT_PTHREADS) +# define NEED_FIND_LIMIT +# if defined (ALPHA) + extern void (*signal(int sig, void (*function)(int)))(int); + /* Get a definition for struct sigcontext, which the pthreads */ + /* headers fail to define. Temporarily define _KERNEL to avoid */ + /* a redefinition of sig_atomic_t. */ +# define _KERNEL +# include <machine/signal.h> +# undef _KERNEL +# endif + /* Provide the usual definition for signal(), which the pthreads */ + /* headers again fail to define. (This may be an incorrect */ + /* assumption on some architectures!) */ + extern void (*signal(int sig, void (*function)(int)))(int); +# endif + #ifdef NEED_FIND_LIMIT # include <setjmp.h> #endif @@ -861,7 +882,9 @@ void GC_register_data_segments() # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \ && !defined(MSWIN32) && !defined(MACOS) +# if !defined(DEC_PTHREADS) extern caddr_t sbrk(); +# endif # ifdef __STDC__ # define SBRK_ARG_T size_t # else @@ -1086,6 +1109,13 @@ void GC_default_push_other_roots() # endif /* SOLARIS_THREADS */ +# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS) +void GC_default_push_other_roots() +{ + GC_pt_push_all_stacks(); +} +# endif + void (*GC_push_other_roots)() = GC_default_push_other_roots; #endif @@ -1255,6 +1285,10 @@ word addr; return(FALSE); } +#if defined(SUNOS4) && defined(MIT_PTHREADS) +# include <vm/faultcode.h> +#endif + #if defined(SUNOS4) || defined(FREEBSD) typedef void (* SIG_PF)(); #endif @@ -1539,8 +1573,12 @@ void GC_protect_heap() } # ifdef THREADS + /* HJB say that this bug manifests itself once in a lifetime, so let's + take the risk... */ +# if !defined(DEC_PTHREADS) && !defined(MIT_PTHREADS) --> The following is broken. We can lose dirty bits. We would need --> the signal handler to cooperate, as in PCR. +# endif # endif void GC_read_dirty() @@ -1565,9 +1603,13 @@ struct hblk * h; * the cord package issues a read while it already holds the allocation lock. */ -# ifdef THREADS +# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS) +void GC_begin_syscall() { LOCK(); } +void GC_end_syscall() { UNLOCK(); } +# else +# ifdef THREADS --> fix this -# endif +# endif void GC_begin_syscall() { } @@ -1575,6 +1617,7 @@ void GC_begin_syscall() void GC_end_syscall() { } +# endif void GC_unprotect_range(addr, len) ptr_t addr; @@ -1607,14 +1650,29 @@ word len; /* Replacement for UNIX system call. */ /* Other calls that write to the heap */ /* should be handled similarly. */ -# ifndef LINT +# if !defined(LINT) +# if defined(MIT_PTHREADS) + /* read() is defined in libpthread.a, so we can't redefine it here. */ + ssize_t GC_read(fd, buf, nbyte) +# else +# if defined(__GNUC__) + ssize_t read(fd, buf, nbyte) +# else int read(fd, buf, nbyte) +# endif +# endif # else int GC_read(fd, buf, nbyte) # endif +# if defined(MIT_PTHREADS) || defined(DEC_PTHREADS) +int fd; +void *buf; +size_t nbyte; +# else int fd; char *buf; int nbyte; +# endif { int result; @@ -40,7 +40,8 @@ # include <synch.h> # endif -# if defined(PCR) || defined(SOLARIS_THREADS) +# if defined(PCR) || defined(SOLARIS_THREADS) \ + || defined(MIT_PTHREADS) || defined(DEC_PTHREADS) # define THREADS # endif @@ -356,6 +357,10 @@ int finalizable_count = 0; int finalized_count = 0; VOLATILE int dropped_something = 0; +# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS) +static pthread_mutex_t incr_lock; +# endif + # ifdef __STDC__ void finalizer(void * obj, void * client_data) # else @@ -373,6 +378,9 @@ VOLATILE int dropped_something = 0; static mutex_t incr_lock; mutex_lock(&incr_lock); # endif +# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS) + pthread_mutex_lock(&incr_lock); +# endif if ((int)(GC_word)client_data != t -> level) { (void)GC_printf0("Wrong finalization data - collector is broken\n"); FAIL; @@ -384,6 +392,9 @@ VOLATILE int dropped_something = 0; # ifdef SOLARIS_THREADS mutex_unlock(&incr_lock); # endif +# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS) + pthread_mutex_unlock(&incr_lock); +# endif } size_t counter = 0; @@ -439,6 +450,9 @@ int n; static mutex_t incr_lock; mutex_lock(&incr_lock); # endif +# if defined (DEC_PTHREADS) || defined(MIT_PTHREADS) + pthread_mutex_lock(&incr_lock); +# endif /* Losing a count here causes erroneous report of failure. */ finalizable_count++; my_index = live_indicators_count++; @@ -448,6 +462,9 @@ int n; # ifdef SOLARIS_THREADS mutex_unlock(&incr_lock); # endif +# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS) + pthread_mutex_unlock(&incr_lock); +# endif } GC_REGISTER_FINALIZER((GC_PTR)result, finalizer, (GC_PTR)(GC_word)n, @@ -531,6 +548,51 @@ void * alloc8bytes() return(my_free_list); } +#elif defined(DEC_PTHREADS) || defined(MIT_PTHREADS) +pthread_key_t fl_key; + +void * alloc8bytes() +{ + void ** my_free_list_ptr; + void * my_free_list; + +# ifdef DEC_PTHREADS + if (pthread_getspecific(fl_key, (void **)(&my_free_list_ptr)) != 0) + { + (void)GC_printf0("pthread_getspecific failed\n"); + FAIL; + } +# endif +# ifdef MIT_PTHREADS + if (!(my_free_list_ptr= pthread_getspecific(fl_key))) + { + /* there's no way to tell if this is an error, so what's to do? */ + } +# endif + if (my_free_list_ptr == 0) + { + my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); + if (pthread_setspecific(fl_key, my_free_list_ptr) != 0) + { + (void)GC_printf0("pthread_setspecific failed\n"); + FAIL; + } + } + my_free_list = *my_free_list_ptr; + if (my_free_list == 0) + { + my_free_list = GC_malloc_many(8); + if (my_free_list == 0) + { + (void)GC_printf0("alloc8bytes out of memory\n"); + FAIL; + } + } + *my_free_list_ptr = GC_NEXT(my_free_list); + GC_NEXT(my_free_list) = 0; + return(my_free_list); +} + #else # define alloc8bytes() GC_MALLOC_ATOMIC(8) #endif @@ -844,7 +906,10 @@ void SetMinimumStack(long minSize) } -#if !defined(PCR) && !defined(SOLARIS_THREADS) || defined(LINT) +#if !defined(PCR) && !defined(SOLARIS_THREADS) \ + && !defined(DEC_PTHREADS) && !defined(MIT_PTHREADS) \ + || defined(LINT) + #ifdef MSWIN32 int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n) #else @@ -921,17 +986,21 @@ test() } #endif -#ifdef SOLARIS_THREADS +#if defined(SOLARIS_THREADS) || defined(DEC_PTHREADS) || defined(MIT_PTHREADS) void * thr_run_one_test(void * arg) { run_one_test(); return(0); } -#ifdef GC_DEBUG +# ifdef GC_DEBUG # define GC_free GC_debug_free +# endif + #endif +#ifdef SOLARIS_THREADS + main() { thread_t th1; @@ -968,3 +1037,66 @@ main() return(0); } #endif + +#if defined(DEC_PTHREADS) || defined(MIT_PTHREADS) + +main() +{ + pthread_t th1; + pthread_t th2; + int code; + pthread_attr_t thr_attr; + void *status; + +# ifdef DEC_PTHREADS + pthread_mutex_init(&incr_lock, pthread_mutexattr_default); + pthread_mutexattr_create(&thr_attr); +# else + pthread_mutex_init(&incr_lock, NULL); + pthread_attr_init(&thr_attr); +# endif + pthread_attr_setstacksize(&thr_attr, (long)1024*1024); + + n_tests = 0; + GC_init(); /* Only needed if gc is dynamic library. */ + /*GC_enable_incremental();*/ + (void) GC_set_warn_proc(warn_proc); +# ifdef DEC_PTHREADS + if (pthread_keycreate(&fl_key, GC_free) != 0) +# else + if (pthread_key_create(&fl_key, GC_free) != 0) +# endif + { + (void)GC_printf1("Key creation failed %lu\n", (long)code); + FAIL; + } +# ifdef DEC_PTHREADS + if ((code = pthread_create(&th1, thr_attr, thr_run_one_test, 0)) != 0) { +# else + if ((code = pthread_create(&th1, &thr_attr, thr_run_one_test, 0)) != 0) { +# endif + (void)GC_printf1("Thread 1 creation failed %lu\n", (unsigned long)code); + FAIL; + } +# ifdef DEC_PTHREADS + if ((code = pthread_create(&th2, thr_attr, thr_run_one_test, 0)) != 0) { +# else + if ((code = pthread_create(&th2, &thr_attr, thr_run_one_test, 0)) != 0) { +# endif + (void)GC_printf1("Thread 2 creation failed %lu\n", (unsigned long)code); + FAIL; + } + run_one_test(); + if ((code = pthread_join(th1, &status)) != 0) { + (void)GC_printf1("Thread 1 failed %lu\n", (unsigned long)code); + FAIL; + } + if ((code = pthread_join(th2, &status)) != 0) { + (void)GC_printf1("Thread 2 failed %lu\n", (unsigned long)code); + FAIL; + } + check_heap_stats(); + (void)fflush(stdout); + return(0); +} +#endif |