2006-06-15 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / threads.c
index 323c85bc0a5cc0d2ab426b9b7bab1e42b114453b..773701d5f23cfd7ff06e765d1e24dab812885f2b 100644 (file)
 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
 #define LIBGC_DEBUG(a)
 
+/* Provide this for systems with glib < 2.6 */
+#ifndef G_GSIZE_FORMAT
+#   if GLIB_SIZEOF_LONG == 8
+#       define G_GSIZE_FORMAT "lu"
+#   else
+#       define G_GSIZE_FORMAT "u"
+#   endif
+#endif
+
 struct StartInfo 
 {
        guint32 (*func)(void *);
@@ -124,7 +133,7 @@ static MonoThreadStartCB mono_thread_start_cb = NULL;
 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
 
 /* function called at thread cleanup */
-static MonoThreadCleanupFunc mono_thread_cleanup = NULL;
+static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
 
 /* The default stack size for each thread */
 static guint32 default_stacksize = 0;
@@ -211,26 +220,6 @@ static void handle_remove(gsize tid)
         */
 }
 
-/*
- * Tell the Mono Debugger about a newly created thread.
- * mono_debugger_event() is a no-op if we're not running inside the debugger.
- */
-static void debugger_thread_created (MonoThread *thread)
-{
-       mono_debugger_event (MONO_DEBUGGER_EVENT_THREAD_CREATED,
-                            (guint64) (gsize) &thread->end_stack, thread->tid);
-}
-
-/*
- * Tell the Mono Debugger that a thrad is about to exit.
- * mono_debugger_event() is a no-op if we're not running inside the debugger.
- */
-static void debugger_thread_exited (MonoThread *thread)
-{
-       mono_debugger_event (MONO_DEBUGGER_EVENT_THREAD_EXITED,
-                            (guint64) (gsize) &thread->end_stack, thread->tid);
-}
-
 static void thread_cleanup (MonoThread *thread)
 {
        g_assert (thread != NULL);
@@ -253,10 +242,8 @@ static void thread_cleanup (MonoThread *thread)
 
        thread->cached_culture_info = NULL;
 
-       debugger_thread_exited (thread);
-
-       if (mono_thread_cleanup)
-               mono_thread_cleanup (thread);
+       if (mono_thread_cleanup_fn)
+               mono_thread_cleanup_fn (thread);
 }
 
 static guint32 WINAPI start_wrapper(void *data)
@@ -264,12 +251,12 @@ static guint32 WINAPI start_wrapper(void *data)
        struct StartInfo *start_info=(struct StartInfo *)data;
        guint32 (*start_func)(void *);
        void *start_arg;
-       guint32 tid;
+       gsize tid;
        MonoThread *thread=start_info->obj;
        MonoObject *start_delegate = start_info->delegate;
 
        THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, GetCurrentThreadId ()));
-       
+
        /* We can be sure start_info->obj->tid and
         * start_info->obj->handle have been set, because the thread
         * was created suspended, and these values were set before the
@@ -296,8 +283,6 @@ static guint32 WINAPI start_wrapper(void *data)
        mono_thread_new_init (tid, &tid, start_func);
        thread->stack_ptr = &tid;
 
-       debugger_thread_created (thread);
-
        LIBGC_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT",%d) Setting thread stack to %p", __func__, GetCurrentThreadId (), getpid (), thread->stack_ptr));
 
        THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), thread));
@@ -378,7 +363,7 @@ void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
        HANDLE thread_handle;
        struct StartInfo *start_info;
        gsize tid;
-       
+
        thread=(MonoThread *)mono_object_new (domain,
                                              mono_defaults.thread_class);
 
@@ -425,7 +410,7 @@ mono_thread_attach (MonoDomain *domain)
        }
 
        if (!mono_gc_register_thread (&domain)) {
-               g_error ("Thread %p calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", GetCurrentThread ());
+               g_error ("Thread %"G_GSIZE_FORMAT" calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", GetCurrentThreadId ());
        }
 
        thread = (MonoThread *)mono_object_new (domain,
@@ -436,14 +421,12 @@ mono_thread_attach (MonoDomain *domain)
 
        tid=GetCurrentThreadId ();
 
-#ifdef PLATFORM_WIN32
        /* 
         * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
         * refer to the thread from other threads for things like aborting.
         */
        DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle, 
                                         THREAD_ALL_ACCESS, TRUE, 0);
-#endif
 
        thread->handle=thread_handle;
        thread->tid=tid;
@@ -459,8 +442,6 @@ mono_thread_attach (MonoDomain *domain)
        SET_CURRENT_OBJECT (thread);
        mono_domain_set (domain, TRUE);
 
-       debugger_thread_created (thread);
-
        thread_adjust_static_data (thread);
 
        if (mono_thread_attach_cb) {
@@ -1387,7 +1368,7 @@ ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
        *location = orig + value;
        mono_interlocked_unlock ();
 
-       return orig;
+       return orig + value;
 #endif
 }
 
@@ -1406,7 +1387,7 @@ ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
        *location = orig + value;
        mono_interlocked_unlock ();
 
-       return orig;
+       return orig + value;
 #endif
 }
 
@@ -1807,10 +1788,38 @@ void mono_thread_init (MonoThreadStartCB start_cb,
        GetCurrentProcess ();
 }
 
+void mono_thread_cleanup (void)
+{
+#if !defined(PLATFORM_WIN32) && !defined(RUN_IN_SUBTHREAD)
+       /* The main thread must abandon any held mutexes (particularly
+        * important for named mutexes as they are shared across
+        * processes, see bug 74680.)  This will happen when the
+        * thread exits, but if it's not running in a subthread it
+        * won't exit in time.
+        */
+       /* Using non-w32 API is a nasty kludge, but I couldn't find
+        * anything in the documentation that would let me do this
+        * here yet still be safe to call on windows.
+        */
+       _wapi_thread_signal_self (mono_environment_exitcode_get ());
+#endif
+
+#if 0
+       /* This stuff needs more testing, it seems one of these
+        * critical sections can be locked when mono_thread_cleanup is
+        * called.
+        */
+       DeleteCriticalSection (&threads_mutex);
+       DeleteCriticalSection (&interlocked_mutex);
+       DeleteCriticalSection (&contexts_mutex);
+       CloseHandle (background_change_event);
+#endif
+}
+
 void
 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
 {
-       mono_thread_cleanup = func;
+       mono_thread_cleanup_fn = func;
 }
 
 G_GNUC_UNUSED
@@ -1926,27 +1935,38 @@ static void build_wait_tids (gpointer key, gpointer value, gpointer user)
                /* Ignore background threads, we abort them later */
                mono_monitor_enter (thread->synch_lock);
                if (thread->state & ThreadState_Background) {
+                       THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
                        mono_monitor_exit (thread->synch_lock);
                        return; /* just leave, ignore */
                }
                mono_monitor_exit (thread->synch_lock);
                
-               if (mono_gc_is_finalizer_thread (thread))
+               if (mono_gc_is_finalizer_thread (thread)) {
+                       THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
                        return;
+               }
 
-               if (thread == mono_thread_current ())
+               if (thread == mono_thread_current ()) {
+                       THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
                        return;
+               }
 
-               if (thread == mono_thread_get_main ())
+               if (thread == mono_thread_get_main ()) {
+                       THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
                        return;
+               }
 
                handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
-               if (handle == NULL)
+               if (handle == NULL) {
+                       THREAD_DEBUG (g_message ("%s: ignoring unopenable thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
                        return;
+               }
                
                wait->handles[wait->num]=handle;
                wait->threads[wait->num]=thread;
                wait->num++;
+
+               THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
        } else {
                /* Just ignore the rest, we can't do anything with
                 * them yet
@@ -2045,20 +2065,6 @@ void mono_thread_manage (void)
                        wait_for_tids (wait, INFINITE);
                }
        } while (wait->num > 0);
-
-#if !defined(PLATFORM_WIN32) && !defined(RUN_IN_SUBTHREAD)
-       /* The main thread must abandon any held mutexes (particularly
-        * important for named mutexes as they are shared across
-        * processes, see bug 74680.)  This will happen when the
-        * thread exits, but if it's not running in a subthread it
-        * won't exit in time.
-        */
-       /* Using non-w32 API is a nasty kludge, but I couldn't find
-        * anything in the documentation that would let me do this
-        * here yet still be safe to call on windows.
-        */
-       _wapi_thread_abandon_mutexes (GetCurrentThread ());
-#endif
        
        /* 
         * give the subthreads a chance to really quit (this is mainly needed
@@ -2804,116 +2810,3 @@ gint32* mono_thread_interruption_request_flag ()
 {
        return &thread_interruption_requested;
 }
-
-#if MONO_DEBUGGER_SUPPORTED
-
-extern void GC_push_all_stack (gpointer b, gpointer t);
-
-static void
-debugger_gc_push_stack (gpointer key, gpointer value, gpointer user)
-{
-       MonoThread *thread = (MonoThread*)value;
-       gpointer end_stack;
-
-       /*
-        * The debugger stops all other threads for us in debugger_gc_stop_world() and
-        * then sets `thread->end_stack' for each of them.
-        */
-
-       end_stack = (thread->tid == GetCurrentThreadId ()) ? &key : thread->end_stack;
-
-       if (!end_stack || !thread->stack_ptr) {
-               g_warning (G_STRLOC ": Cannot push stack of thread %Lx", thread->tid);
-               return;
-       }
-
-       GC_push_all_stack (end_stack, thread->stack_ptr);
-}
-
-/*
- * We're called with the thread lock.
- */
-static void
-debugger_gc_push_all_stacks (void)
-{
-       if (threads != NULL)
-               mono_g_hash_table_foreach (threads, debugger_gc_push_stack, NULL);
-}
-
-static void
-debugger_gc_stop_world (void)
-{
-       /*
-        * Acquire the thread lock and tell the debugger to stop all other threads.
-        */
-       mono_threads_lock ();
-       mono_debugger_event (
-               MONO_DEBUGGER_EVENT_ACQUIRE_GLOBAL_THREAD_LOCK, 0, 0);
-}
-
-static void
-debugger_gc_start_world (void)
-{
-       /*
-        * Tell the debugger to resume all other threads and release the lock.
-        */
-       mono_debugger_event (
-               MONO_DEBUGGER_EVENT_RELEASE_GLOBAL_THREAD_LOCK, 0, 0);
-       mono_threads_unlock ();
-}
-
-static void
-debugger_gc_init (void)
-{ }
-
-static GCThreadFunctions debugger_thread_vtable = {
-       debugger_gc_init,
-
-       debugger_gc_stop_world,
-       debugger_gc_push_all_stacks,
-       debugger_gc_start_world
-};
-
-static GCThreadFunctions *old_gc_thread_vtable = NULL;
-
-/**
- * mono_debugger_init_threads:
- *
- * This is used when running inside the Mono Debugger.
- */
-void
-mono_debugger_init_threads (void)
-{
-       old_gc_thread_vtable = gc_thread_vtable;
-       gc_thread_vtable = &debugger_thread_vtable;
-       debugger_thread_created (mono_thread_current ());
-}
-
-/**
- * mono_debugger_finalize_threads:
- *
- * This is used when running inside the Mono Debugger.
- * Undo the effects of mono_debugger_init_threads(); this is called
- * prior to detaching from a process.
- */
-void
-mono_debugger_finalize_threads (void)
-{
-       gc_thread_vtable = old_gc_thread_vtable;
-}
-
-#else /* WITH_INCLUDED_LIBGC */
-
-void
-mono_debugger_init_threads (void)
-{
-       g_assert_not_reached ();
-}
-
-void
-mono_debugger_finalize_threads (void)
-{
-       g_assert_not_reached ();
-}
-
-#endif