X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fio-layer%2Ftimed-thread.c;h=6599e0660f307150c3d0359bd34fce0f7687a4b6;hb=275ea834beec38b00a0c131d968f89b2a206daca;hp=2464beb5fcd8aab9ba8024cdb46274abcffe4831;hpb=aa1e79f21030d52a577e36c5b441bf90d673c56b;p=mono.git diff --git a/mono/io-layer/timed-thread.c b/mono/io-layer/timed-thread.c index 2464beb5fcd..6599e0660f3 100644 --- a/mono/io-layer/timed-thread.c +++ b/mono/io-layer/timed-thread.c @@ -1,9 +1,22 @@ +/* + * timed-thread.c: Implementation of timed thread joining + * + * Author: + * Dick Porter (dick@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + #include #include -#if HAVE_BOEHM_GC -#include -#endif +#include #include +#ifdef HAVE_SEMAPHORE_H +#include +#endif +#include + +#include #include "timed-thread.h" @@ -17,17 +30,26 @@ */ static pthread_key_t timed_thread_key; -static pthread_once_t timed_thread_once = PTHREAD_ONCE_INIT; +static mono_once_t timed_thread_once = MONO_ONCE_INIT; +static mono_mutex_t apc_mutex; + static void timed_thread_init(void) { - pthread_key_create(&timed_thread_key, NULL); + int thr_ret; + + thr_ret = pthread_key_create(&timed_thread_key, NULL); + g_assert (thr_ret == 0); + + thr_ret = mono_mutex_init(&apc_mutex, NULL); + g_assert (thr_ret == 0); } void _wapi_timed_thread_exit(guint32 exitstatus) { TimedThread *thread; void *specific; + int thr_ret; if((specific = pthread_getspecific(timed_thread_key)) == NULL) { /* Handle cases which won't happen with correct usage. @@ -36,8 +58,15 @@ void _wapi_timed_thread_exit(guint32 exitstatus) } thread=(TimedThread *)specific; + + if(thread->exit_routine!=NULL) { + thread->exit_routine(exitstatus, thread->exit_userdata); + } - mono_mutex_lock(&thread->join_mutex); + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&thread->join_mutex); + thr_ret = mono_mutex_lock(&thread->join_mutex); + g_assert (thr_ret == 0); /* Tell a joiner that we're exiting. */ @@ -49,13 +78,13 @@ void _wapi_timed_thread_exit(guint32 exitstatus) thread->exitstatus=exitstatus; thread->exiting=TRUE; - - if(thread->exit_routine!=NULL) { - thread->exit_routine(exitstatus, thread->exit_userdata); - } - pthread_cond_signal(&thread->exit_cond); - mono_mutex_unlock(&thread->join_mutex); + thr_ret = pthread_cond_signal(&thread->exit_cond); + g_assert (thr_ret == 0); + + thr_ret = mono_mutex_unlock(&thread->join_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); /* Call pthread_exit() to call destructors and really exit the * thread. @@ -70,10 +99,39 @@ static void *timed_thread_start_routine(gpointer args) G_GNUC_NORETURN; static void *timed_thread_start_routine(gpointer args) { TimedThread *thread = (TimedThread *)args; + int thr_ret; + + mono_once(&timed_thread_once, timed_thread_init); + thr_ret = pthread_setspecific(timed_thread_key, (void *)thread); + g_assert (thr_ret == 0); + + /* This used to be pthread_detach(thread->id); + * + * thread->id is set in _wapi_timed_thread_create: + * + * if((result = pthread_create(&thread->id, attr, + * timed_thread_start_routine, + * (void *)thread)) != 0) { + * + * Strangeness happened: if _wapi_timed_thread_create was + * called directly, then thread->id was always set here. + * However, if _wapi_timed_thread_create was called via + * another function that did nothing but call + * _wapi_timed_thread_create, thread->id was not ever set, + * leading to the thread's 2M stack being wasted as it was not + * detached. + * + * This was 100% reproducible on Debian Woody with gcc 2.95.4, + * and on Red Hat 9 with gcc 3.2.2. + */ + thr_ret = pthread_detach(pthread_self ()); + g_assert (thr_ret == 0); + + if(thread->create_flags & CREATE_SUSPENDED) { + thread->suspend_count = 1; + _wapi_timed_thread_suspend (thread); + } - pthread_once(&timed_thread_once, timed_thread_init); - pthread_setspecific(timed_thread_key, (void *)thread); - pthread_detach(thread->id); _wapi_timed_thread_exit(thread->start_routine(thread->arg)); } @@ -81,23 +139,33 @@ static void *timed_thread_start_routine(gpointer args) */ int _wapi_timed_thread_create(TimedThread **threadp, const pthread_attr_t *attr, + guint32 create_flags, guint32 (*start_routine)(gpointer), void (*exit_routine)(guint32, gpointer), gpointer arg, gpointer exit_userdata) { TimedThread *thread; int result; + int thr_ret; thread=(TimedThread *)g_new0(TimedThread, 1); - mono_mutex_init(&thread->join_mutex, NULL); - pthread_cond_init(&thread->exit_cond, NULL); + thr_ret = mono_mutex_init(&thread->join_mutex, NULL); + g_assert (thr_ret == 0); + + thr_ret = pthread_cond_init(&thread->exit_cond, NULL); + g_assert (thr_ret == 0); + + thread->create_flags = create_flags; + MONO_SEM_INIT (&thread->suspend_sem, 0); + MONO_SEM_INIT (&thread->suspended_sem, 0); thread->start_routine = start_routine; thread->exit_routine = exit_routine; thread->arg = arg; thread->exit_userdata = exit_userdata; thread->exitstatus = 0; thread->exiting = FALSE; + thread->apc_queue = NULL; *threadp = thread; @@ -111,12 +179,57 @@ int _wapi_timed_thread_create(TimedThread **threadp, return(0); } +int _wapi_timed_thread_attach(TimedThread **threadp, + void (*exit_routine)(guint32, gpointer), + gpointer exit_userdata) +{ + TimedThread *thread; + int thr_ret; + + thread=(TimedThread *)g_new0(TimedThread, 1); + + thr_ret = mono_mutex_init(&thread->join_mutex, NULL); + g_assert (thr_ret == 0); + + thr_ret = pthread_cond_init(&thread->exit_cond, NULL); + g_assert (thr_ret == 0); + + thr_ret = MONO_SEM_INIT (&thread->suspend_sem, 0); + g_assert (thr_ret != -1); + + thr_ret = MONO_SEM_INIT (&thread->suspended_sem, 0); + g_assert (thr_ret != -1); + + thread->exit_routine = exit_routine; + thread->exit_userdata = exit_userdata; + thread->exitstatus = 0; + thread->exiting = FALSE; + thread->id = pthread_self(); + + /* Make sure the timed-thread initialisation that the start + * routing does happens here too (we might be first to be + * called) + */ + mono_once(&timed_thread_once, timed_thread_init); + thr_ret = pthread_setspecific(timed_thread_key, (void *)thread); + g_assert (thr_ret == 0); + + *threadp = thread; + + return(0); +} + int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout, guint32 *exitstatus) { int result; + int thr_ret; + + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&thread->join_mutex); + thr_ret = mono_mutex_lock(&thread->join_mutex); + g_assert (thr_ret == 0); - mono_mutex_lock(&thread->join_mutex); result=0; /* Wait until the thread announces that it's exiting, or until @@ -133,12 +246,116 @@ int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout, } } - mono_mutex_unlock(&thread->join_mutex); + thr_ret = mono_mutex_unlock(&thread->join_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); + if(result == 0 && thread->exiting) { if(exitstatus!=NULL) { *exitstatus = thread->exitstatus; } + + _wapi_timed_thread_destroy (thread); } return(result); } +void _wapi_timed_thread_destroy (TimedThread *thread) +{ + mono_mutex_destroy (&thread->join_mutex); + pthread_cond_destroy (&thread->exit_cond); + MONO_SEM_DESTROY (&thread->suspend_sem); + MONO_SEM_DESTROY (&thread->suspended_sem); + + g_free(thread); +} + +/* I was going to base thread suspending on the algorithm presented at + * http://home.earthlink.net/~anneart/family/Threads/code/susp.c + * + * Unfortunately the Boehm GC library also wants to use this technique + * to stop the world, and will deadlock if a thread has already been + * suspended when it tries. + * + * While Mono is still using libgc this will just have to be a kludge + * to implement suspended creation of threads, rather than the general + * purpose thread suspension. + */ +void _wapi_timed_thread_suspend (TimedThread *thread) +{ + TimedThread *self; + void *specific; + + if((specific = pthread_getspecific (timed_thread_key))==NULL) { + g_warning (G_GNUC_PRETTY_FUNCTION ": thread lookup failed"); + return; + } + self=(TimedThread *)specific; + + if(thread != self) { + g_error (G_GNUC_PRETTY_FUNCTION + ": attempt to suspend a different thread!"); + exit (-1); + } + + while (MONO_SEM_WAIT (&thread->suspend_sem) != 0 && errno == EINTR); +} + +void _wapi_timed_thread_resume (TimedThread *thread) +{ + MONO_SEM_POST (&thread->suspend_sem); +} + +void _wapi_timed_thread_queue_apc (TimedThread *thread, + guint32 (*apc_callback)(gpointer), gpointer param) +{ + ApcInfo *apc; + int thr_ret; + + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&apc_mutex); + thr_ret = mono_mutex_lock(&apc_mutex); + g_assert (thr_ret == 0); + + apc = (ApcInfo *)g_new(ApcInfo, 1); + apc->callback = apc_callback; + apc->param = param; + thread->apc_queue = g_slist_append (thread->apc_queue, apc); + + thr_ret = mono_mutex_unlock(&apc_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); +} + +gboolean _wapi_timed_thread_apc_pending (TimedThread *thread) +{ + return thread->apc_queue != NULL; +} + +void _wapi_timed_thread_dispatch_apc_queue (TimedThread *thread) +{ + ApcInfo* apc; + GSList *list; + int thr_ret; + + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&apc_mutex); + thr_ret = mono_mutex_lock(&apc_mutex); + g_assert (thr_ret == 0); + + list = thread->apc_queue; + thread->apc_queue = NULL; + + thr_ret = mono_mutex_unlock(&apc_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); + + while (list != NULL) { + apc = (ApcInfo*)list->data; + apc->callback (apc->param); + g_free (apc); + list = g_slist_next (list); + } + g_slist_free (list); +} +