+ if(thread->tid != (gsize)user) {
+ /*TerminateThread (thread->handle, -1);*/
+ }
+}
+
+void mono_thread_abort_all_other_threads (void)
+{
+ gsize self = GetCurrentThreadId ();
+
+ mono_threads_lock ();
+ THREAD_DEBUG (g_message ("%s: There are %d threads to abort", __func__,
+ mono_g_hash_table_size (threads));
+ mono_g_hash_table_foreach (threads, print_tids, NULL));
+
+ mono_g_hash_table_foreach (threads, terminate_thread, (gpointer)self);
+
+ mono_threads_unlock ();
+}
+
+static void
+collect_threads (gpointer key, gpointer value, gpointer user_data)
+{
+ MonoThread *thread = (MonoThread*)value;
+ struct wait_data *wait = (struct wait_data*)user_data;
+ HANDLE handle;
+
+ if (wait->num<MAXIMUM_WAIT_OBJECTS) {
+ handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+ if (handle == NULL)
+ return;
+
+ wait->handles [wait->num] = handle;
+ wait->threads [wait->num] = thread;
+ wait->num++;
+ }
+}
+
+/*
+ * mono_thread_suspend_all_other_threads:
+ *
+ * Suspend all managed threads except the finalizer thread and this thread.
+ */
+void mono_thread_suspend_all_other_threads (void)
+{
+ struct wait_data *wait = g_new0 (struct wait_data, 1);
+ int i, waitnum;
+ gsize self = GetCurrentThreadId ();
+ gpointer *events;
+ guint32 eventidx = 0;
+
+ /*
+ * Make a copy of the hashtable since we can't do anything with
+ * threads while threads_mutex is held.
+ */
+ mono_threads_lock ();
+ mono_g_hash_table_foreach (threads, collect_threads, wait);
+ mono_threads_unlock ();
+
+ events = g_new0 (gpointer, wait->num);
+ waitnum = 0;
+ /* Get the suspended events that we'll be waiting for */
+ for (i = 0; i < wait->num; ++i) {
+ MonoThread *thread = wait->threads [i];
+
+ if ((thread->tid == self) || mono_gc_is_finalizer_thread (thread)) {
+ //CloseHandle (wait->handles [i]);
+ wait->threads [i] = NULL; /* ignore this thread in next loop */
+ continue;
+ }
+
+ mono_monitor_enter (thread->synch_lock);
+
+ if ((thread->state & ThreadState_Suspended) != 0 ||
+ (thread->state & ThreadState_SuspendRequested) != 0 ||
+ (thread->state & ThreadState_StopRequested) != 0 ||
+ (thread->state & ThreadState_Stopped) != 0) {
+ mono_monitor_exit (thread->synch_lock);
+ CloseHandle (wait->handles [i]);
+ wait->threads [i] = NULL; /* ignore this thread in next loop */
+ continue;
+ }
+
+ /* Convert abort requests into suspend requests */
+ if ((thread->state & ThreadState_AbortRequested) != 0)
+ thread->state &= ~ThreadState_AbortRequested;
+
+ thread->state |= ThreadState_SuspendRequested;
+
+ if (thread->suspended_event == NULL) {
+ thread->suspended_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (thread->suspended_event == NULL) {
+ /* Forget this one and go on to the next */
+ mono_monitor_exit (thread->synch_lock);
+ continue;
+ }
+ }
+
+ events [eventidx++] = thread->suspended_event;
+ mono_monitor_exit (thread->synch_lock);
+
+ /* Signal the thread to suspend */
+ signal_thread_state_change (thread);
+ }
+
+ WaitForMultipleObjectsEx (eventidx, events, TRUE, INFINITE, FALSE);
+ for (i = 0; i < wait->num; ++i) {
+ MonoThread *thread = wait->threads [i];
+
+ if (thread == NULL)
+ continue;
+
+ mono_monitor_enter (thread->synch_lock);
+ CloseHandle (thread->suspended_event);
+ thread->suspended_event = NULL;
+ mono_monitor_exit (thread->synch_lock);
+ }
+
+ g_free (events);
+ g_free (wait);
+}
+
+/**
+ * mono_threads_request_thread_dump:
+ *
+ * Ask all threads except the current to print their stacktrace to stdout.
+ */
+void
+mono_threads_request_thread_dump (void)
+{
+ struct wait_data *wait = g_new0 (struct wait_data, 1);
+ int i;
+
+ /*
+ * Make a copy of the hashtable since we can't do anything with
+ * threads while threads_mutex is held.
+ */
+ mono_threads_lock ();
+ mono_g_hash_table_foreach (threads, collect_threads, wait);
+ mono_threads_unlock ();
+
+ for (i = 0; i < wait->num; ++i) {
+ MonoThread *thread = wait->threads [i];
+
+ if (!mono_gc_is_finalizer_thread (thread) && (thread != mono_thread_current ()) && !thread->thread_dump_requested) {
+ thread->thread_dump_requested = TRUE;
+
+ signal_thread_state_change (thread);
+ }
+
+ CloseHandle (wait->handles [i]);
+ }
+}
+
+/*
+ * mono_thread_push_appdomain_ref:
+ *
+ * Register that the current thread may have references to objects in domain
+ * @domain on its stack. Each call to this function should be paired with a
+ * call to pop_appdomain_ref.
+ */
+void
+mono_thread_push_appdomain_ref (MonoDomain *domain)
+{
+ MonoThread *thread = mono_thread_current ();
+
+ if (thread) {
+ /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
+ mono_threads_lock ();
+ thread->appdomain_refs = g_slist_prepend (thread->appdomain_refs, domain);
+ mono_threads_unlock ();
+ }
+}
+
+void
+mono_thread_pop_appdomain_ref (void)
+{
+ MonoThread *thread = mono_thread_current ();
+
+ if (thread) {
+ /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
+ mono_threads_lock ();
+ /* FIXME: How can the list be empty ? */
+ if (thread->appdomain_refs)
+ thread->appdomain_refs = g_slist_remove (thread->appdomain_refs, thread->appdomain_refs->data);
+ mono_threads_unlock ();
+ }
+}
+
+gboolean
+mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
+{
+ gboolean res;
+ mono_threads_lock ();
+ res = g_slist_find (thread->appdomain_refs, domain) != NULL;
+ mono_threads_unlock ();
+ return res;
+}
+
+typedef struct abort_appdomain_data {
+ struct wait_data wait;
+ MonoDomain *domain;
+} abort_appdomain_data;
+
+static void
+abort_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
+{
+ MonoThread *thread = (MonoThread*)value;
+ abort_appdomain_data *data = (abort_appdomain_data*)user_data;
+ MonoDomain *domain = data->domain;
+
+ if (mono_thread_has_appdomain_ref (thread, domain)) {
+ HANDLE handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+ if (handle == NULL)
+ return;
+
+ /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
+
+ ves_icall_System_Threading_Thread_Abort (thread, NULL);
+
+ if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
+ data->wait.handles [data->wait.num] = handle;
+ data->wait.threads [data->wait.num] = thread;
+ data->wait.num++;
+ } else {
+ /* Just ignore the rest, we can't do anything with
+ * them yet
+ */
+ }
+ }
+}
+
+/*
+ * mono_threads_abort_appdomain_threads:
+ *
+ * Abort threads which has references to the given appdomain.
+ */
+gboolean
+mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
+{
+ abort_appdomain_data user_data;
+ guint32 start_time;
+
+ /* printf ("ABORT BEGIN.\n"); */
+
+ start_time = GetTickCount ();
+ do {
+ mono_threads_lock ();
+
+ user_data.domain = domain;
+ user_data.wait.num = 0;
+ mono_g_hash_table_foreach (threads, abort_appdomain_thread, &user_data);
+ mono_threads_unlock ();
+
+ if (user_data.wait.num > 0)
+ /*
+ * We should wait for the threads either to abort, or to leave the
+ * domain. We can't do the latter, so we wait with a timeout.
+ */
+ wait_for_tids (&user_data.wait, 100);
+
+ /* Update remaining time */
+ timeout -= GetTickCount () - start_time;
+ start_time = GetTickCount ();
+
+ if (timeout < 0)
+ return FALSE;
+ }
+ while (user_data.wait.num > 0);
+
+ /* printf ("ABORT DONE.\n"); */
+
+ return TRUE;
+}
+
+static void
+clear_cached_culture (gpointer key, gpointer value, gpointer user_data)
+{
+ MonoThread *thread = (MonoThread*)value;
+ MonoDomain *domain = (MonoDomain*)user_data;
+ int i;
+
+ /* No locking needed here */
+ /* FIXME: why no locking? writes to the cache are protected with synch_lock above */
+
+ if (thread->cached_culture_info) {
+ for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i) {
+ MonoObject *obj = mono_array_get (thread->cached_culture_info, MonoObject*, i);
+ if (obj && obj->vtable->domain == domain)
+ mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
+ }
+ }
+}
+
+/*
+ * mono_threads_clear_cached_culture:
+ *
+ * Clear the cached_current_culture from all threads if it is in the
+ * given appdomain.
+ */
+void
+mono_threads_clear_cached_culture (MonoDomain *domain)
+{
+ mono_threads_lock ();
+ mono_g_hash_table_foreach (threads, clear_cached_culture, domain);
+ mono_threads_unlock ();
+}
+
+/*
+ * mono_thread_get_pending_exception:
+ *
+ * Return an exception which needs to be raised when leaving a catch clause.
+ * This is used for undeniable exception propagation.
+ */
+MonoException*
+mono_thread_get_pending_exception (void)
+{
+ MonoThread *thread = mono_thread_current ();
+
+ MONO_ARCH_SAVE_REGS;
+
+ if (thread && thread->abort_exc && !is_running_protected_wrapper ()) {
+ /*
+ * FIXME: Clear the abort exception and return an AppDomainUnloaded
+ * exception if the thread no longer references a dying appdomain.
+ */
+ thread->abort_exc->trace_ips = NULL;
+ thread->abort_exc->stack_trace = NULL;
+ return thread->abort_exc;
+ }
+
+ return NULL;
+}
+
+#define NUM_STATIC_DATA_IDX 8
+static const int static_data_size [NUM_STATIC_DATA_IDX] = {
+ 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
+};
+
+
+/*
+ * mono_alloc_static_data
+ *
+ * Allocate memory blocks for storing threads or context static data
+ */
+static void
+mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset)
+{
+ guint idx = (offset >> 24) - 1;
+ int i;
+
+ gpointer* static_data = *static_data_ptr;
+ if (!static_data) {
+ static_data = mono_gc_alloc_fixed (static_data_size [0], NULL);
+ *static_data_ptr = static_data;
+ static_data [0] = static_data;
+ }
+
+ for (i = 1; i <= idx; ++i) {
+ if (static_data [i])
+ continue;
+ static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL);
+ }
+}
+
+/*
+ * mono_init_static_data_info
+ *
+ * Initializes static data counters
+ */
+static void mono_init_static_data_info (StaticDataInfo *static_data)
+{
+ static_data->idx = 0;
+ static_data->offset = 0;
+}
+
+/*
+ * mono_alloc_static_data_slot
+ *
+ * Generates an offset for static data. static_data contains the counters
+ * used to generate it.
+ */
+static guint32
+mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
+{
+ guint32 offset;
+
+ if (!static_data->idx && !static_data->offset) {
+ /*
+ * we use the first chunk of the first allocation also as
+ * an array for the rest of the data
+ */
+ static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
+ }
+ static_data->offset += align - 1;
+ static_data->offset &= ~(align - 1);
+ if (static_data->offset + size >= static_data_size [static_data->idx]) {
+ static_data->idx ++;
+ g_assert (size <= static_data_size [static_data->idx]);
+ /*
+ * massive unloading and reloading of domains with thread-static
+ * data may eventually exceed the allocated storage...
+ * Need to check what the MS runtime does in that case.
+ * Note that for each appdomain, we need to allocate a separate
+ * thread data slot for security reasons. We could keep track
+ * of the slots per-domain and when the domain is unloaded
+ * out the slots on a sort of free list.
+ */
+ g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
+ static_data->offset = 0;
+ }
+ offset = static_data->offset | ((static_data->idx + 1) << 24);
+ static_data->offset += size;
+ return offset;
+}
+
+/*
+ * ensure thread static fields already allocated are valid for thread
+ * This function is called when a thread is created or on thread attach.
+ */
+static void
+thread_adjust_static_data (MonoThread *thread)
+{
+ guint32 offset;
+
+ mono_threads_lock ();
+ if (thread_static_info.offset || thread_static_info.idx > 0) {
+ /* get the current allocated size */
+ offset = thread_static_info.offset | ((thread_static_info.idx + 1) << 24);
+ mono_alloc_static_data (&(thread->static_data), offset);
+ }
+ mono_threads_unlock ();
+}
+
+static void
+alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
+{
+ MonoThread *thread = value;
+ guint32 offset = GPOINTER_TO_UINT (user);
+
+ mono_alloc_static_data (&(thread->static_data), offset);
+}
+
+/*
+ * The offset for a special static variable is composed of three parts:
+ * a bit that indicates the type of static data (0:thread, 1:context),
+ * an index in the array of chunks of memory for the thread (thread->static_data)
+ * and an offset in that chunk of mem. This allows allocating less memory in the
+ * common case.
+ */
+
+guint32
+mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align)
+{
+ guint32 offset;
+ if (static_type == SPECIAL_STATIC_THREAD)
+ {
+ mono_threads_lock ();
+ offset = mono_alloc_static_data_slot (&thread_static_info, size, align);
+ /* This can be called during startup */
+ if (threads != NULL)
+ mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
+ mono_threads_unlock ();
+ }
+ else
+ {
+ g_assert (static_type == SPECIAL_STATIC_CONTEXT);
+ mono_contexts_lock ();
+ offset = mono_alloc_static_data_slot (&context_static_info, size, align);
+ mono_contexts_unlock ();
+ offset |= 0x80000000; /* Set the high bit to indicate context static data */
+ }
+ return offset;
+}
+
+gpointer
+mono_get_special_static_data (guint32 offset)
+{
+ /* The high bit means either thread (0) or static (1) data. */
+
+ guint32 static_type = (offset & 0x80000000);
+ int idx;
+
+ offset &= 0x7fffffff;
+ idx = (offset >> 24) - 1;
+
+ if (static_type == 0)
+ {
+ MonoThread *thread = mono_thread_current ();
+ return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
+ }
+ else
+ {
+ /* Allocate static data block under demand, since we don't have a list
+ // of contexts
+ */
+ MonoAppContext *context = mono_context_get ();
+ if (!context->static_data || !context->static_data [idx]) {
+ mono_contexts_lock ();
+ mono_alloc_static_data (&(context->static_data), offset);
+ mono_contexts_unlock ();
+ }
+ return ((char*) context->static_data [idx]) + (offset & 0xffffff);
+ }
+}
+
+static MonoClassField *local_slots = NULL;
+
+typedef struct {
+ /* local tls data to get locals_slot from a thread */
+ guint32 offset;
+ int idx;
+ /* index in the locals_slot array */
+ int slot;
+} LocalSlotID;
+
+static void
+clear_local_slot (gpointer key, gpointer value, gpointer user_data)
+{
+ LocalSlotID *sid = user_data;
+ MonoThread *thread = (MonoThread*)value;
+ MonoArray *slots_array;
+ /*
+ * the static field is stored at: ((char*) thread->static_data [idx]) + (offset & 0xffffff);
+ * it is for the right domain, so we need to check if it is allocated an initialized
+ * for the current thread.
+ */
+ /*g_print ("handling thread %p\n", thread);*/
+ if (!thread->static_data || !thread->static_data [sid->idx])
+ return;
+ slots_array = *(MonoArray **)(((char*) thread->static_data [sid->idx]) + (sid->offset & 0xffffff));
+ if (!slots_array || sid->slot >= mono_array_length (slots_array))
+ return;
+ mono_array_set (slots_array, MonoObject*, sid->slot, NULL);
+}
+
+void
+mono_thread_free_local_slot_values (int slot, MonoBoolean thread_local)
+{
+ MonoDomain *domain;
+ LocalSlotID sid;
+ sid.slot = slot;
+ if (thread_local) {
+ void *addr = NULL;
+ if (!local_slots) {
+ local_slots = mono_class_get_field_from_name (mono_defaults.thread_class, "local_slots");
+ if (!local_slots) {
+ g_warning ("local_slots field not found in Thread class");
+ return;
+ }
+ }
+ domain = mono_domain_get ();
+ mono_domain_lock (domain);
+ if (domain->special_static_fields)
+ addr = g_hash_table_lookup (domain->special_static_fields, local_slots);
+ mono_domain_unlock (domain);
+ if (!addr)
+ return;
+ /*g_print ("freeing slot %d at %p\n", slot, addr);*/
+ sid.offset = GPOINTER_TO_UINT (addr);
+ sid.offset &= 0x7fffffff;
+ sid.idx = (sid.offset >> 24) - 1;
+ mono_threads_lock ();
+ mono_g_hash_table_foreach (threads, clear_local_slot, &sid);
+ mono_threads_unlock ();
+ } else {
+ /* FIXME: clear the slot for MonoAppContexts, too */
+ }
+}
+
+#ifdef __MINGW32__
+static CALLBACK void dummy_apc (ULONG_PTR param)
+{
+}
+#else
+static guint32 dummy_apc (gpointer param)
+{
+ return 0;
+}
+#endif
+
+/*
+ * mono_thread_execute_interruption
+ *
+ * Performs the operation that the requested thread state requires (abort,
+ * suspend or stop)
+ */
+static MonoException* mono_thread_execute_interruption (MonoThread *thread)
+{
+ mono_monitor_enter (thread->synch_lock);
+
+ if (thread->interruption_requested) {
+ /* this will consume pending APC calls */
+ WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
+ InterlockedDecrement (&thread_interruption_requested);
+ thread->interruption_requested = FALSE;
+ }
+
+ if ((thread->state & ThreadState_AbortRequested) != 0) {
+ if (thread->abort_exc == NULL)
+ MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
+ mono_monitor_exit (thread->synch_lock);
+ return thread->abort_exc;
+ }
+ else if ((thread->state & ThreadState_SuspendRequested) != 0) {
+ thread->state &= ~ThreadState_SuspendRequested;
+ thread->state |= ThreadState_Suspended;
+ thread->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (thread->suspend_event == NULL) {
+ mono_monitor_exit (thread->synch_lock);
+ return(NULL);
+ }
+ if (thread->suspended_event)
+ SetEvent (thread->suspended_event);
+ mono_monitor_exit (thread->synch_lock);
+
+ WaitForSingleObject (thread->suspend_event, INFINITE);
+
+ mono_monitor_enter (thread->synch_lock);
+ CloseHandle (thread->suspend_event);
+ thread->suspend_event = NULL;
+ thread->state &= ~ThreadState_Suspended;
+
+ /* The thread that requested the resume will have replaced this event
+ * and will be waiting for it
+ */
+ SetEvent (thread->resume_event);
+ mono_monitor_exit (thread->synch_lock);
+ return NULL;
+ }
+ else if ((thread->state & ThreadState_StopRequested) != 0) {
+ /* FIXME: do this through the JIT? */
+ mono_monitor_exit (thread->synch_lock);
+ mono_thread_exit ();
+ return NULL;
+ } else if (thread->thread_interrupt_requested) {
+ mono_monitor_exit (thread->synch_lock);
+ return(mono_get_exception_thread_interrupted ());
+ }
+
+ mono_monitor_exit (thread->synch_lock);
+ return NULL;
+}
+
+/*
+ * mono_thread_request_interruption
+ *
+ * A signal handler can call this method to request the interruption of a
+ * thread. The result of the interruption will depend on the current state of
+ * the thread. If the result is an exception that needs to be throw, it is
+ * provided as return value.
+ */
+MonoException* mono_thread_request_interruption (gboolean running_managed)
+{
+ MonoThread *thread = mono_thread_current ();
+
+ /* The thread may already be stopping */
+ if (thread == NULL)
+ return NULL;
+
+ mono_monitor_enter (thread->synch_lock);
+
+ if (thread->interruption_requested) {
+ mono_monitor_exit (thread->synch_lock);
+ return NULL;
+ }
+
+ if (!running_managed || is_running_protected_wrapper ()) {
+ /* Can't stop while in unmanaged code. Increase the global interruption
+ request count. When exiting the unmanaged method the count will be
+ checked and the thread will be interrupted. */
+
+ InterlockedIncrement (&thread_interruption_requested);
+ thread->interruption_requested = TRUE;
+ mono_monitor_exit (thread->synch_lock);
+
+ /* this will awake the thread if it is in WaitForSingleObject
+ or similar */
+ QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, NULL);
+ return NULL;
+ }
+ else {
+ mono_monitor_exit (thread->synch_lock);
+ return mono_thread_execute_interruption (thread);
+ }
+}
+
+gboolean mono_thread_interruption_requested ()
+{
+ if (thread_interruption_requested) {
+ MonoThread *thread = mono_thread_current ();
+ /* The thread may already be stopping */
+ if (thread != NULL)
+ return (thread->interruption_requested);
+ }
+ return FALSE;
+}
+
+static void mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
+{
+ MonoThread *thread = mono_thread_current ();
+
+ /* The thread may already be stopping */
+ if (thread == NULL)
+ return;
+
+ if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
+ MonoException* exc = mono_thread_execute_interruption (thread);
+ if (exc) mono_raise_exception (exc);
+ }
+}
+
+/*
+ * Performs the interruption of the current thread, if one has been requested,
+ * and the thread is not running a protected wrapper.
+ */
+void mono_thread_interruption_checkpoint ()
+{
+ mono_thread_interruption_checkpoint_request (FALSE);
+}
+
+/*
+ * Performs the interruption of the current thread, if one has been requested.
+ */
+void mono_thread_force_interruption_checkpoint ()
+{
+ mono_thread_interruption_checkpoint_request (TRUE);
+}
+
+/**
+ * mono_thread_interruption_request_flag:
+ *
+ * Returns the address of a flag that will be non-zero if an interruption has
+ * been requested for a thread. The thread to interrupt may not be the current
+ * thread, so an additional call to mono_thread_interruption_requested() or
+ * mono_thread_interruption_checkpoint() is allways needed if the flag is not
+ * zero.
+ */
+gint32* mono_thread_interruption_request_flag ()
+{
+ return &thread_interruption_requested;