summaryrefslogtreecommitdiff
path: root/mit_threads.c
diff options
context:
space:
mode:
Diffstat (limited to 'mit_threads.c')
-rw-r--r--mit_threads.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/mit_threads.c b/mit_threads.c
new file mode 100644
index 0000000..c778eda
--- /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 */