From 9097327dc1c667fc51b8e05cc7c0626fac96665d Mon Sep 17 00:00:00 2001 From: Guillaume Cottenceau Date: Mon, 14 May 2001 14:17:54 +0000 Subject: Initial revision --- mdk-stage1/dietlibc/libpthread/thread_internal.c | 344 +++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 mdk-stage1/dietlibc/libpthread/thread_internal.c (limited to 'mdk-stage1/dietlibc/libpthread/thread_internal.c') diff --git a/mdk-stage1/dietlibc/libpthread/thread_internal.c b/mdk-stage1/dietlibc/libpthread/thread_internal.c new file mode 100644 index 000000000..a93806203 --- /dev/null +++ b/mdk-stage1/dietlibc/libpthread/thread_internal.c @@ -0,0 +1,344 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "thread_internal.h" + +static struct _pthread_fastlock __thread_struct_lock = {0}; +static struct _pthread_descr_struct threads[PTHREAD_THREADS_MAX]; +static int _max_used_thread_id=1; +pthread_once_t __thread_inited; + +static struct _pthread_fastlock __manager_thread_signal_lock = {0}; +static struct _pthread_fastlock __manager_thread_data_lock = {1}; +static struct _pthread_fastlock __manager_thread_data_go_lock = {1}; + +//#define DEBUG + +/* find thread */ +int __find_thread_id(int pid) +{ + register int i; + for (i=0; i<_max_used_thread_id; i++) + if (threads[i].pid==pid) + return i; + return -1; +} + +/* get thread */ +_pthread_descr __get_thread_struct(int id) +{ + return threads+id; +} + +/* thread errno location */ +int *__errno_location(void) +{ + int id=0; + if (__thread_inited) id=__find_thread_id(getpid()); + if (id<0) + return 0; + else + return &threads[id].errno; +} + +/* thread self */ +_pthread_descr __thread_self() +{ + register int i=__find_thread_id(getpid()); + if (i<0) + return 0; + else + return threads+i; +} + +/* allocate a thread slot */ +_pthread_descr __thread_get_free() +{ + _pthread_descr ret=0; + int i; + + __NO_ASYNC_CANCEL_BEGIN; + __pthread_lock(&__thread_struct_lock); + + for (i=0; i=_max_used_thread_id) _max_used_thread_id=i+1; + break; + } + } + + __pthread_unlock(&__thread_struct_lock); + __NO_ASYNC_CANCEL_END; + return ret; +} + +/* sleep a little (reschedule for this time) */ +void __thread_wait_some_time() +{ + struct timespec reg; + reg.tv_sec=0; + reg.tv_nsec=SPIN_SLEEP_DURATION; + __libc_nanosleep(®,0); +} + +/* cleanup a thread struct */ +void __thread_cleanup(_pthread_descr th) +{ + /* lib provided stack should be freed */ + if (th->stack_begin) free(th->stack_begin); + + /* an other thread has joined this on */ + if (th->joined) { + th->joined->retval=th->retval; + th->joined->join=0; + th->joined=0; + } + th->pid=0; /* mark struct as free */ +} + +/* SIGHUP handler (thread cnacel) PTHREAD_CANCEL_ASYNCHRONOUS */ +static void __thread_cancel_handler(int sig) +{ + _pthread_descr this; + this = __thread_self(); + this->canceled=1; + if (this->canceltype==PTHREAD_CANCEL_ASYNCHRONOUS) + pthread_exit(PTHREAD_CANCELED); + signal( SIGHUP, __thread_cancel_handler ); +} + +/* kill ALL threads / other then prime task and manager thread */ +static void __kill_all_threads() +{ + int i; + + for (i=2; i<_max_used_thread_id; i++) { + if (threads[i].pid>1) { +#ifdef DEBUG + printf("CANCEL ! %d\n",threads[i].pid); +#endif + threads[i].canceled=1; + kill(threads[i].pid, SIGHUP); /* cancel thread */ + } + } + + __thread_wait_some_time(); + + for (i=2; i<_max_used_thread_id; i++) { + if (threads[i].pid>1) { +#ifdef DEBUG + printf("KILL ! %d\n",threads[i].pid); +#endif + kill(threads[i].pid, SIGTERM); /* KILL thread */ + } + } +} + +__attribute__((weak)) volatile void __thread_start__key(int id) { return; } +__attribute__((weak,alias("__thread_start__key"))) volatile void __thread_exit__key(int id); + +/* support for manager */ +static void *__mthread_starter(void *arg) +{ + _pthread_descr td = (_pthread_descr)arg; + int i = td->stack_size-4096; + + /* just to be sure */ + td->pid=getpid(); + + /* signal handling for a thread */ + signal(SIGTERM, _exit); + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, __thread_cancel_handler ); + + /* limit stack so that we NEVER have to worry */ + setrlimit(RLIMIT_STACK, (struct rlimit *)(&i)); + + /* set scheduler */ + if (td->policy!=SCHED_OTHER) { + struct sched_param sp; + sp.sched_priority=td->priority; + sched_setscheduler(td->pid,td->policy, &sp); + } + + /* thread_key glue */ + __thread_start__key(td-threads); + +#ifdef DEBUG + printf("in starter %d, parameter %8p\n", td->pid, td->func); +#endif + + if (!td->canceled) { + if (!(setjmp(td->jmp_exit))) { + td->retval=td->func(td->arg); +#ifdef DEBUG + } else { + printf("pthread_exit called in %d\n", td->pid); +#endif + } + } + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); + + /* thread_key glue */ + __thread_exit__key(td-threads); + +#ifdef DEBUG + printf("end starter %d, retval %8p\n", td->pid, td->retval); +#endif + + /* wake joined thread and put retval */ + if (td->joined) { + td->joined->retval=td->retval; + td->joined->join=0; + td->joined=0; + } + + /* execute all functions on the cleanup-stack */ + for (i=PTHREAD_MAX_CLEANUP;i;) { + if (td->cleanup_stack[--i].func) { + td->cleanup_stack[i].func(td->cleanup_stack[i].arg); + } + } + + return 0; +} + + +/* manager thread and signal handler */ +static char __manager_thread_stack[12*1024]; +static volatile _pthread_descr __manager_thread_data; +static void __manager_SIGCHLD(int sig) +{ + int pid, status, i; + + while(1) { + pid = __libc_waitpid (-1, &status, WNOHANG); + if (pid <= 0) break; + + for (i=0; i<_max_used_thread_id; i++) { + if (threads[i].pid==pid) { + __thread_cleanup(threads+i); + break; + } + } + } +} + +static void __manager_SIGTERM(int sig) +{ + __kill_all_threads(); + _exit(0); +} + +static void* __manager_thread(void *arg) +{ + struct sigaction sig_action_chld; + sig_action_chld.sa_handler = __manager_SIGCHLD; + sigemptyset(&sig_action_chld.sa_mask); + sig_action_chld.sa_flags = SA_RESTART; + + sigaction(SIGCHLD, &sig_action_chld, 0); + signal(SIGTERM, __manager_SIGTERM); + signal(SIGHUP, SIG_IGN); + + __pthread_unlock(&__manager_thread_data_go_lock); /* release init */ + while(1) { + do { + __thread_wait_some_time(); + if (getppid()<0) __manager_SIGTERM(0); + } while (__pthread_trylock(&__manager_thread_data_lock)); + + __manager_thread_data->pid = + __clone(__mthread_starter, + __manager_thread_data->stack_addr, + CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, + __manager_thread_data); + __thread_wait_some_time(); +#ifdef DEBUG + printf("manager new thread %d\n",__manager_thread_data->pid); +#endif + __pthread_unlock(&__manager_thread_data_go_lock); /* release sender */ + } + return 0; +} + +/* pthread_create bottom half */ +int signal_manager_thread(_pthread_descr td) +{ + __NO_ASYNC_CANCEL_BEGIN; + + __pthread_lock(&__manager_thread_signal_lock); /* lock */ + + __manager_thread_data = td; + __thread_wait_some_time(); + __pthread_unlock(&__manager_thread_data_lock); /* signal manager to start */ + __thread_wait_some_time(); + __pthread_lock(&__manager_thread_data_go_lock); /* wait for manager */ + + __pthread_unlock(&__manager_thread_signal_lock); /* unlock */ + + __NO_ASYNC_CANCEL_END; + + return td->pid; +} + + +/* thread stop */ +static void __thread_main_exit() +{ + if (getpid()!=threads[0].pid) { +#ifdef DEBUG + printf("A THREAD ? %d\n",getpid()); +#endif + kill(threads[0].pid, SIGTERM); + while(1) __thread_wait_some_time(); + } +#ifdef DEBUG + else + printf("EXIT ! %d\n",getpid()); +#endif + + /* stop ALL threads */ + kill(threads[1].pid, SIGTERM); + __thread_wait_some_time(); + __kill_all_threads(); +} + +/* thread intern init */ +void __thread_init() +{ + if (atexit(__thread_main_exit)==-1) + exit(42); + +#ifdef DEBUG + printf("INIT ! %d\n",getpid()); + memset(threads,0,sizeof(threads)); +#endif + + threads[0].pid = getpid(); + + ++_max_used_thread_id; + threads[1].stack_size=sizeof(__manager_thread_stack); + threads[1].stack_addr=&__manager_thread_stack[sizeof(__manager_thread_stack)]; + threads[1].stack_begin=0; + threads[1].func=__manager_thread; + + threads[1].pid = __clone(__mthread_starter, threads[1].stack_addr, + CLONE_VM | CLONE_FS | CLONE_FILES, threads+1); + +#ifdef DEBUG + printf("manager thread @ : %d\n",threads[1].pid); +#endif + __pthread_lock(&__manager_thread_data_go_lock); /* wait for manager to be ready */ +} + -- cgit v1.2.1