2004-11-04 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / io-layer / timed-thread.c
index e4cbc54b2fc28baec60c12611f1d5e53ecf3ed2f..6599e0660f307150c3d0359bd34fce0f7687a4b6 100644 (file)
@@ -14,6 +14,7 @@
 #ifdef HAVE_SEMAPHORE_H
 #include <semaphore.h>
 #endif
+#include <errno.h>
 
 #include <mono/io-layer/processes.h>
 
 
 static pthread_key_t timed_thread_key;
 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.
@@ -48,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.
         */
@@ -61,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.
@@ -82,9 +99,11 @@ 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);
-       pthread_setspecific(timed_thread_key, (void *)thread);
+       thr_ret = pthread_setspecific(timed_thread_key, (void *)thread);
+       g_assert (thr_ret == 0);
 
        /* This used to be pthread_detach(thread->id);
         *
@@ -105,7 +124,8 @@ static void *timed_thread_start_routine(gpointer args)
         * This was 100% reproducible on Debian Woody with gcc 2.95.4,
         * and on Red Hat 9 with gcc 3.2.2.
         */
-       pthread_detach(pthread_self ());
+       thr_ret = pthread_detach(pthread_self ());
+       g_assert (thr_ret == 0);
 
        if(thread->create_flags & CREATE_SUSPENDED) {
                thread->suspend_count = 1;
@@ -126,20 +146,26 @@ int _wapi_timed_thread_create(TimedThread **threadp,
 {
        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;
-       sem_init (&thread->suspend_sem, 0, 0);
-       sem_init (&thread->suspended_sem, 0, 0);
+       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;
 
@@ -158,13 +184,22 @@ int _wapi_timed_thread_attach(TimedThread **threadp,
                              gpointer exit_userdata)
 {
        TimedThread *thread;
-
+       int thr_ret;
+       
        thread=(TimedThread *)g_new0(TimedThread, 1);
 
-       mono_mutex_init(&thread->join_mutex, NULL);
-       pthread_cond_init(&thread->exit_cond, NULL);
-       sem_init (&thread->suspend_sem, 0, 0);
-       sem_init (&thread->suspended_sem, 0, 0);
+       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;
@@ -176,7 +211,8 @@ int _wapi_timed_thread_attach(TimedThread **threadp,
         * called)
         */
        mono_once(&timed_thread_once, timed_thread_init);
-       pthread_setspecific(timed_thread_key, (void *)thread);
+       thr_ret = pthread_setspecific(timed_thread_key, (void *)thread);
+       g_assert (thr_ret == 0);
 
        *threadp = thread;
 
@@ -187,8 +223,13 @@ 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
@@ -205,11 +246,16 @@ 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);
 }
@@ -218,8 +264,8 @@ void _wapi_timed_thread_destroy (TimedThread *thread)
 {
        mono_mutex_destroy (&thread->join_mutex);
        pthread_cond_destroy (&thread->exit_cond);
-       sem_destroy (&thread->suspend_sem);
-       sem_destroy (&thread->suspended_sem);
+       MONO_SEM_DESTROY (&thread->suspend_sem);
+       MONO_SEM_DESTROY (&thread->suspended_sem);
        
        g_free(thread);
 }
@@ -252,10 +298,64 @@ void _wapi_timed_thread_suspend (TimedThread *thread)
                exit (-1);
        }
 
-       sem_wait (&thread->suspend_sem);
+       while (MONO_SEM_WAIT (&thread->suspend_sem) != 0 && errno == EINTR);
 }
 
 void _wapi_timed_thread_resume (TimedThread *thread)
 {
-       sem_post (&thread->suspend_sem);
+       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);
 }
+