/*#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 *);
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;
*/
}
-/*
- * 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);
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)
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
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));
HANDLE thread_handle;
struct StartInfo *start_info;
gsize tid;
-
+
thread=(MonoThread *)mono_object_new (domain,
mono_defaults.thread_class);
}
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,
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;
+ thread->stack_ptr = &tid;
MONO_OBJECT_SETREF (thread, synch_lock, mono_object_new (domain, mono_defaults.object_class));
THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle));
*location = orig + value;
mono_interlocked_unlock ();
- return orig;
+ return orig + value;
#endif
}
*location = orig + value;
mono_interlocked_unlock ();
- return orig;
+ return orig + value;
#endif
}
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
/* 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
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
{
return &thread_interruption_requested;
}
-
-static void debugger_create_all_threads (gpointer key, gpointer value, gpointer user)
-{
- debugger_thread_created ((MonoThread *) value);
-}
-
-void
-mono_debugger_create_all_threads (void)
-{
- mono_threads_lock ();
- mono_g_hash_table_foreach (threads, debugger_create_all_threads, NULL);
- mono_threads_unlock ();
-}
-
-#if MONO_DEBUGGER_SUPPORTED
-
-static guint64 debugger_main_thread_id = -1;
-static gpointer debugger_main_thread_stack_ptr = NULL;
-static gpointer debugger_main_thread_end_stack = NULL;
-
-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)
- 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)
-{
- gpointer end_stack = (debugger_main_thread_id == GetCurrentThreadId ()) ?
- &end_stack : debugger_main_thread_end_stack;
-
- GC_push_all_stack (end_stack, debugger_main_thread_stack_ptr);
-
- 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
-};
-
-/**
- * mono_debugger_init_threads:
- *
- * This is used when running inside the Mono Debugger.
- */
-void
-mono_debugger_init_threads (gpointer main_thread_stack)
-{
- gc_thread_vtable = &debugger_thread_vtable;
-
- debugger_main_thread_id = GetCurrentThreadId ();
- debugger_main_thread_stack_ptr = main_thread_stack;
-
- mono_debugger_event (MONO_DEBUGGER_EVENT_THREAD_CREATED,
- (guint64) (gsize) &debugger_main_thread_end_stack,
- debugger_main_thread_id);
-}
-
-#else /* WITH_INCLUDED_LIBGC */
-
-void
-mono_debugger_init_threads (gpointer main_thread_stack)
-{
- g_assert_not_reached ();
-}
-
-#endif