*/
#include <config.h>
-#ifdef PLATFORM_WIN32
-#define _WIN32_WINNT 0x0500
-#endif
#include <glib.h>
#include <signal.h>
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-mmap.h>
#include <mono/utils/mono-membar.h>
+#include <mono/utils/mono-time.h>
#include <mono/metadata/gc-internal.h>
*/
static MonoGHashTable *threads=NULL;
+/*
+ * Threads which are starting up and they are not in the 'threads' hash yet.
+ * When handle_store is called for a thread, it will be removed from this hash table.
+ * Protected by mono_threads_lock ().
+ */
+static MonoGHashTable *threads_starting_up = NULL;
+
/* The TLS key that holds the MonoObject assigned to each thread */
static guint32 current_object_key = -1;
THREAD_DEBUG (g_message ("%s: thread %p ID %"G_GSIZE_FORMAT, __func__, thread, (gsize)thread->tid));
+ if (threads_starting_up)
+ mono_g_hash_table_remove (threads_starting_up, thread);
+
if (shutting_down) {
mono_threads_unlock ();
return FALSE;
return &hazard_table [current_thread->small_id];
}
+static void
+try_free_delayed_free_item (int index)
+{
+ if (delayed_free_table->len > index) {
+ DelayedFreeItem item = { NULL, NULL };
+
+ EnterCriticalSection (&delayed_free_table_mutex);
+ /* We have to check the length again because another
+ thread might have freed an item before we acquired
+ the lock. */
+ if (delayed_free_table->len > index) {
+ item = g_array_index (delayed_free_table, DelayedFreeItem, index);
+
+ if (!is_pointer_hazardous (item.p))
+ g_array_remove_index_fast (delayed_free_table, index);
+ else
+ item.p = NULL;
+ }
+ LeaveCriticalSection (&delayed_free_table_mutex);
+
+ if (item.p != NULL)
+ item.free_func (item.p);
+ }
+}
+
void
mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func)
{
/* First try to free a few entries in the delayed free
table. */
- for (i = 2; i >= 0; --i) {
- if (delayed_free_table->len > i) {
- DelayedFreeItem item;
-
- item.p = NULL;
- EnterCriticalSection (&delayed_free_table_mutex);
- /* We have to check the length again because another
- thread might have freed an item before we acquired
- the lock. */
- if (delayed_free_table->len > i) {
- item = g_array_index (delayed_free_table, DelayedFreeItem, i);
-
- if (!is_pointer_hazardous (item.p))
- g_array_remove_index_fast (delayed_free_table, i);
- else
- item.p = NULL;
- }
- LeaveCriticalSection (&delayed_free_table_mutex);
-
- if (item.p != NULL)
- item.free_func (item.p);
- }
- }
+ for (i = 2; i >= 0; --i)
+ try_free_delayed_free_item (i);
/* Now see if the pointer we're freeing is hazardous. If it
isn't, free it. Otherwise put it in the delay list. */
free_func (p);
}
+void
+mono_thread_hazardous_try_free_all (void)
+{
+ int len;
+ int i;
+
+ if (!delayed_free_table)
+ return;
+
+ len = delayed_free_table->len;
+
+ for (i = len - 1; i >= 0; --i)
+ try_free_delayed_free_item (i);
+}
+
static void ensure_synch_cs_set (MonoThread *thread)
{
CRITICAL_SECTION *synch_cs;
if (thread->serialized_culture_info)
g_free (thread->serialized_culture_info);
+ g_free (thread->name);
+
thread->cached_culture_info = NULL;
mono_gc_free_fixed (thread->static_data);
SET_CURRENT_OBJECT (thread);
+ mono_monitor_init_tls ();
+
/* Every thread references the appdomain which created it */
mono_thread_push_appdomain_ref (start_info->domain);
/* On 2.0 profile (and higher), set explicitly since state might have been
Unknown */
- if (mono_get_runtime_info ()->framework_version [0] != '1') {
+ if (mono_framework_version () != 1) {
if (thread->apartment_state == ThreadApartmentState_Unknown)
thread->apartment_state = ThreadApartmentState_MTA;
}
* when the new thread is started but not yet registered with the collector.
*/
MONO_GC_REGISTER_ROOT (start_info->start_arg);
-
+
+ mono_threads_lock ();
+ if (shutting_down) {
+ mono_threads_unlock ();
+ return;
+ }
+ if (threads_starting_up == NULL) {
+ MONO_GC_REGISTER_ROOT (threads_starting_up);
+ threads_starting_up = mono_g_hash_table_new (NULL, NULL);
+ }
+ mono_g_hash_table_insert (threads_starting_up, thread, thread);
+ mono_threads_unlock ();
+
/* Create suspended, so we can do some housekeeping before the thread
* starts
*/
if (thread_handle == NULL) {
/* The thread couldn't be created, so throw an exception */
MONO_GC_UNREGISTER_ROOT (start_info->start_arg);
+ mono_threads_lock ();
+ mono_g_hash_table_remove (threads_starting_up, thread);
+ mono_threads_unlock ();
g_free (start_info);
mono_raise_exception (mono_get_exception_execution_engine ("Couldn't create thread"));
return;
InitializeCriticalSection (thread->synch_cs);
thread->threadpool_thread = threadpool_thread;
+ if (threadpool_thread)
+ mono_thread_set_state (thread, ThreadState_Background);
if (handle_store (thread))
ResumeThread (thread_handle);
/*
* mono_thread_get_stack_bounds:
*
- * Return the address and size of the current threads stack. Return NULL as the stack
- * address if the stack address cannot be determined.
+ * Return the address and size of the current threads stack. Return NULL as the
+ * stack address if the stack address cannot be determined.
*/
void
mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize)
#if defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
*staddr = (guint8*)pthread_get_stackaddr_np (pthread_self ());
*stsize = pthread_get_stacksize_np (pthread_self ());
+ *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
return;
/* FIXME: simplify the mess below */
#elif !defined(PLATFORM_WIN32)
pthread_attr_init (&attr);
#ifdef HAVE_PTHREAD_GETATTR_NP
- pthread_getattr_np (pthread_self(), &attr);
+ pthread_getattr_np (pthread_self(), &attr);
#else
#ifdef HAVE_PTHREAD_ATTR_GET_NP
- pthread_attr_get_np (pthread_self(), &attr);
+ pthread_attr_get_np (pthread_self(), &attr);
#elif defined(sun)
- *staddr = NULL;
- pthread_attr_getstacksize (&attr, &stsize);
+ *staddr = NULL;
+ pthread_attr_getstacksize (&attr, &stsize);
#else
- *staddr = NULL;
- *stsize = 0;
- return;
+ *staddr = NULL;
+ *stsize = 0;
+ return;
#endif
#endif
#ifndef sun
- pthread_attr_getstack (&attr, (void**)staddr, stsize);
- if (*staddr)
- g_assert ((current > *staddr) && (current < *staddr + *stsize));
+ pthread_attr_getstack (&attr, (void**)staddr, stsize);
+ if (*staddr)
+ g_assert ((current > *staddr) && (current < *staddr + *stsize));
#endif
- pthread_attr_destroy (&attr);
+ pthread_attr_destroy (&attr);
#endif
+
+ /* When running under emacs, sometimes staddr is not aligned to a page size */
+ *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
}
MonoThread *
SET_CURRENT_OBJECT (thread);
mono_domain_set (domain, TRUE);
+ mono_monitor_init_tls ();
+
thread_adjust_static_data (thread);
if (mono_thread_attach_cb) {
return(NULL);
}
+ mono_threads_lock ();
+ if (threads_starting_up == NULL) {
+ MONO_GC_REGISTER_ROOT (threads_starting_up);
+ threads_starting_up = mono_g_hash_table_new (NULL, NULL);
+ }
+ mono_g_hash_table_insert (threads_starting_up, this, this);
+ mono_threads_unlock ();
+
thread=CreateThread(NULL, default_stacksize_for_thread (this), (LPTHREAD_START_ROUTINE)start_wrapper, start_info,
CREATE_SUSPENDED, &tid);
if(thread==NULL) {
LeaveCriticalSection (this->synch_cs);
+ mono_threads_lock ();
+ mono_g_hash_table_remove (threads_starting_up, this);
+ mono_threads_unlock ();
g_warning("%s: CreateThread error 0x%x", __func__, GetLastError());
return(NULL);
}
mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
}
-void ves_icall_System_Threading_Thread_SpinWait_internal (gint32 iterations)
+void ves_icall_System_Threading_Thread_SpinWait_nop (void)
{
- gint32 i;
-
- for(i = 0; i < iterations; i++) {
- /* We're busy waiting, but at least we can tell the
- * scheduler to let someone else have a go...
- */
- Sleep (0);
- }
}
gint32
gboolean throw = FALSE;
ensure_synch_cs_set (this);
+
+ if (this == mono_thread_current ())
+ return;
EnterCriticalSection (this->synch_cs);
MonoThread *thread = mono_thread_current ();
gboolean throw = FALSE;
+ mono_debugger_check_interruption ();
+
ensure_synch_cs_set (thread);
EnterCriticalSection (thread->synch_cs);
LeaveCriticalSection (thread->synch_cs);
THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Abort requested for %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid));
-
- /* Make sure the thread is awake */
- mono_thread_resume (thread);
+
+ /* During shutdown, we can't wait for other threads */
+ if (!shutting_down)
+ /* Make sure the thread is awake */
+ mono_thread_resume (thread);
signal_thread_state_change (thread);
}
void mono_thread_cleanup (void)
{
+ mono_thread_hazardous_try_free_all ();
+
#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
#endif
g_array_free (delayed_free_table, TRUE);
+ delayed_free_table = NULL;
TlsFree (current_object_key);
}
mono_thread_cleanup_fn = func;
}
+void
+mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
+{
+ thread->manage_callback = func;
+}
+
void mono_threads_install_notify_pending_exc (MonoThreadNotifyPendingExcFunc func)
{
mono_thread_notify_pending_exc_fn = func;
return;
}
- wait->handles[wait->num]=handle;
- wait->threads[wait->num]=thread;
- wait->num++;
+ THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
+ if ((thread->manage_callback == NULL) || (thread->manage_callback (thread) == TRUE)) {
+ 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));
+ THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
+ } else {
+ THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
+ }
+
+
} else {
/* Just ignore the rest, we can't do anything with
* them yet
handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
if (handle == NULL)
return FALSE;
-
- if(thread->state & ThreadState_AbortRequested ||
- thread->state & ThreadState_Aborted) {
- THREAD_DEBUG (g_message ("%s: Thread id %"G_GSIZE_FORMAT" already aborting", __func__, (gsize)thread->tid));
- return(TRUE);
- }
/* printf ("A: %d\n", wait->num); */
wait->handles[wait->num]=thread->handle;
/**
* mono_threads_set_shutting_down:
- * @may_abort: Whether the function is allowed to abort the current
- * thread if it cannot shut down Mono.
*
- * Is called by a thread that wants to shut down Mono. Returs whether
- * the thread is allowed to do that. The reason for not allowing it
- * is because another thread has already commenced shutdown.
+ * Is called by a thread that wants to shut down Mono. If the runtime is already
+ * shutting down, the calling thread is suspended/stopped, and this function never
+ * returns.
*/
-gboolean
-mono_threads_set_shutting_down (gboolean may_abort)
+void
+mono_threads_set_shutting_down (void)
{
MonoThread *current_thread = mono_thread_current ();
mono_threads_lock ();
if (shutting_down) {
- if (may_abort) {
- ves_icall_System_Threading_Thread_Abort (current_thread, NULL);
-
- return FALSE;
- } else {
- mono_threads_unlock ();
-
- /* Make sure we're properly suspended/stopped */
-
- EnterCriticalSection (current_thread->synch_cs);
-
- if ((current_thread->state & ThreadState_SuspendRequested) ||
- (current_thread->state & ThreadState_AbortRequested) ||
- (current_thread->state & ThreadState_StopRequested)) {
- LeaveCriticalSection (current_thread->synch_cs);
- mono_thread_execute_interruption (current_thread);
- } else {
- current_thread->state |= ThreadState_Stopped;
- LeaveCriticalSection (current_thread->synch_cs);
- }
-
- /* Wake up other threads potentially waiting for us */
+ mono_threads_unlock ();
- /* FIXME: We have to do this on Win32 in some way, too */
-#if !defined(PLATFORM_WIN32)
- _wapi_thread_signal_self (0);
-#endif
+ /* Make sure we're properly suspended/stopped */
- /* Wait for the end of the world */
+ EnterCriticalSection (current_thread->synch_cs);
- for (;;)
- Sleep (10000);
+ if ((current_thread->state & ThreadState_SuspendRequested) ||
+ (current_thread->state & ThreadState_AbortRequested) ||
+ (current_thread->state & ThreadState_StopRequested)) {
+ LeaveCriticalSection (current_thread->synch_cs);
+ mono_thread_execute_interruption (current_thread);
+ } else {
+ current_thread->state |= ThreadState_Stopped;
+ LeaveCriticalSection (current_thread->synch_cs);
}
+
+ /* Wake up other threads potentially waiting for us */
+ ExitThread (0);
} else {
shutting_down = TRUE;
+ /* Not really a background state change, but this will
+ * interrupt the main thread if it is waiting for all
+ * the other threads.
+ */
+ SetEvent (background_change_event);
+
mono_threads_unlock ();
-
- /* Even though our state hasn't changed we still wake
- up other threads. Actually we only care about the
- main thread, which might be waiting for us to
- finish. */
-
- /* FIXME: We have to do this on Win32 in some way, too */
-#if !defined(PLATFORM_WIN32)
- _wapi_thread_signal_self (0);
-#endif
-
- return TRUE;
}
}
THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
} while(wait->num>0);
- mono_threads_set_shutting_down (FALSE);
+ mono_threads_set_shutting_down ();
/* No new threads will be created after this point */
}
static void
-collect_threads (gpointer key, gpointer value, gpointer user_data)
+collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
{
MonoThread *thread = (MonoThread*)value;
struct wait_data *wait = (struct wait_data*)user_data;
HANDLE handle;
+ /*
+ * We try to exclude threads early, to avoid running into the MAXIMUM_WAIT_OBJECTS
+ * limitation.
+ * This needs no locking.
+ */
+ if ((thread->state & ThreadState_Suspended) != 0 ||
+ (thread->state & ThreadState_Stopped) != 0)
+ return;
+
if (wait->num<MAXIMUM_WAIT_OBJECTS) {
handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
if (handle == NULL)
/*
* mono_thread_suspend_all_other_threads:
*
- * Suspend all managed threads except the finalizer thread and this thread.
+ * Suspend all managed threads except the finalizer thread and this thread. It is
+ * not possible to resume them later.
*/
void mono_thread_suspend_all_other_threads (void)
{
struct wait_data *wait = g_new0 (struct wait_data, 1);
- int i, waitnum;
+ int i;
gsize self = GetCurrentThreadId ();
gpointer *events;
guint32 eventidx = 0;
+ gboolean starting, finished;
+
+ /*
+ * The other threads could be in an arbitrary state at this point, i.e.
+ * they could be starting up, shutting down etc. This means that there could be
+ * threads which are not even in the threads hash table yet.
+ */
/*
- * Make a copy of the hashtable since we can't do anything with
- * threads while threads_mutex is held.
+ * First we set a barrier which will be checked by all threads before they
+ * are added to the threads hash table, and they will exit if the flag is set.
+ * This ensures that no threads could be added to the hash later.
+ * We will use shutting_down as the barrier for now.
*/
- mono_threads_lock ();
- mono_g_hash_table_foreach (threads, collect_threads, wait);
- mono_threads_unlock ();
+ g_assert (shutting_down);
- 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];
+ /*
+ * We make multiple calls to WaitForMultipleObjects since:
+ * - we can only wait for MAXIMUM_WAIT_OBJECTS threads
+ * - some threads could exit without becoming suspended
+ */
+ finished = FALSE;
+ while (!finished) {
+ /*
+ * Make a copy of the hashtable since we can't do anything with
+ * threads while threads_mutex is held.
+ */
+ wait->num = 0;
+ mono_threads_lock ();
+ mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
+ mono_threads_unlock ();
- 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;
- }
+ events = g_new0 (gpointer, wait->num);
+ eventidx = 0;
+ /* Get the suspended events that we'll be waiting for */
+ for (i = 0; i < wait->num; ++i) {
+ MonoThread *thread = wait->threads [i];
- ensure_synch_cs_set (thread);
+ 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;
+ }
+
+ ensure_synch_cs_set (thread);
- EnterCriticalSection (thread->synch_cs);
+ EnterCriticalSection (thread->synch_cs);
- if ((thread->state & ThreadState_Suspended) != 0 ||
- (thread->state & ThreadState_SuspendRequested) != 0 ||
- (thread->state & ThreadState_StopRequested) != 0 ||
- (thread->state & ThreadState_Stopped) != 0) {
- LeaveCriticalSection (thread->synch_cs);
- CloseHandle (wait->handles [i]);
- wait->threads [i] = NULL; /* ignore this thread in next loop */
- continue;
- }
+ if ((thread->state & ThreadState_Suspended) != 0 ||
+ (thread->state & ThreadState_SuspendRequested) != 0 ||
+ (thread->state & ThreadState_StopRequested) != 0 ||
+ (thread->state & ThreadState_Stopped) != 0) {
+ LeaveCriticalSection (thread->synch_cs);
+ 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;
+ /* Convert abort requests into suspend requests */
+ if ((thread->state & ThreadState_AbortRequested) != 0)
+ thread->state &= ~ThreadState_AbortRequested;
- thread->state |= ThreadState_SuspendRequested;
+ 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 */
- LeaveCriticalSection (thread->synch_cs);
- continue;
+ thread->suspended_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (thread->suspended_event == NULL) {
+ /* Forget this one and go on to the next */
+ LeaveCriticalSection (thread->synch_cs);
+ continue;
+ }
}
- }
- events [eventidx++] = thread->suspended_event;
- LeaveCriticalSection (thread->synch_cs);
+ events [eventidx++] = thread->suspended_event;
+ LeaveCriticalSection (thread->synch_cs);
- /* Signal the thread to suspend */
- signal_thread_state_change (thread);
- }
+ /* 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 (eventidx > 0) {
+ WaitForMultipleObjectsEx (eventidx, events, TRUE, 100, FALSE);
+ for (i = 0; i < wait->num; ++i) {
+ MonoThread *thread = wait->threads [i];
- if (thread == NULL)
- continue;
+ if (thread == NULL)
+ continue;
+
+ EnterCriticalSection (thread->synch_cs);
+ if ((thread->state & ThreadState_Suspended) != 0) {
+ CloseHandle (thread->suspended_event);
+ thread->suspended_event = NULL;
+ }
+ LeaveCriticalSection (thread->synch_cs);
+ }
+ } else {
+ /*
+ * If there are threads which are starting up, we wait until they
+ * are suspended when they try to register in the threads hash.
+ * This is guaranteed to finish, since the threads which can create new
+ * threads get suspended after a while.
+ * FIXME: The finalizer thread can still create new threads.
+ */
+ mono_threads_lock ();
+ starting = mono_g_hash_table_size (threads_starting_up) > 0;
+ mono_threads_unlock ();
+ if (starting)
+ Sleep (100);
+ else
+ finished = TRUE;
+ }
- EnterCriticalSection (thread->synch_cs);
- CloseHandle (thread->suspended_event);
- thread->suspended_event = NULL;
- LeaveCriticalSection (thread->synch_cs);
+ g_free (events);
}
- g_free (events);
g_free (wait);
}
+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_threads_request_thread_dump:
*
{
abort_appdomain_data user_data;
guint32 start_time;
+ int orig_timeout = timeout;
THREAD_DEBUG (g_message ("%s: starting abort", __func__));
- start_time = GetTickCount ();
+ start_time = mono_msec_ticks ();
do {
mono_threads_lock ();
wait_for_tids (&user_data.wait, 100);
/* Update remaining time */
- timeout -= GetTickCount () - start_time;
- start_time = GetTickCount ();
+ timeout -= mono_msec_ticks () - start_time;
+ start_time = mono_msec_ticks ();
- if (timeout < 0)
+ if (orig_timeout != -1 && timeout < 0)
return FALSE;
}
while (user_data.wait.num > 0);
SetEvent (thread->suspended_event);
LeaveCriticalSection (thread->synch_cs);
+
+ if (shutting_down) {
+ /* After we left the lock, the runtime might shut down so everything becomes invalid */
+ for (;;)
+ Sleep (1000);
+ }
WaitForSingleObject (thread->suspend_event, INFINITE);
return NULL;
} else if (thread->thread_interrupt_requested) {
+ thread->thread_interrupt_requested = FALSE;
LeaveCriticalSection (thread->synch_cs);
return(mono_get_exception_thread_interrupted ());
* 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)
+MonoException*
+mono_thread_request_interruption (gboolean running_managed)
{
MonoThread *thread = mono_thread_current ();
if (thread == NULL)
return NULL;
- ensure_synch_cs_set (thread);
-
- EnterCriticalSection (thread->synch_cs);
-
- if (thread->interruption_requested) {
- LeaveCriticalSection (thread->synch_cs);
-
+ if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
return NULL;
- }
if (!running_managed || is_running_protected_wrapper ()) {
/* Can't stop while in unmanaged code. Increase the global interruption
checked and the thread will be interrupted. */
InterlockedIncrement (&thread_interruption_requested);
- thread->interruption_requested = TRUE;
-
- LeaveCriticalSection (thread->synch_cs);
if (mono_thread_notify_pending_exc_fn && !running_managed)
/* The JIT will notify the thread about the interruption */
/* this will awake the thread if it is in WaitForSingleObject
or similar */
+ /* Our implementation of this function ignores the func argument */
QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, NULL);
return NULL;
}
else {
- LeaveCriticalSection (thread->synch_cs);
-
return mono_thread_execute_interruption (thread);
}
}
if (thread == NULL)
return;
+ mono_debugger_check_interruption ();
+
if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
MonoException* exc = mono_thread_execute_interruption (thread);
if (exc) mono_raise_exception (exc);
if (thread->interruption_requested && !is_running_protected_wrapper ()) {
return mono_thread_execute_interruption (thread);
}
- else
- return NULL;
+
+ if (thread->pending_exception) {
+ MonoException *exc = thread->pending_exception;
+
+ thread->pending_exception = NULL;
+ return exc;
+ }
+
+ return NULL;
+}
+
+/*
+ * mono_set_pending_exception:
+ *
+ * Set the pending exception of the current thread to EXC. On platforms which
+ * support it, the exception will be thrown when execution returns to managed code.
+ * On other platforms, this function is equivalent to mono_raise_exception ().
+ * Internal calls which report exceptions using this function instead of
+ * raise_exception () might be called by JITted code using a more efficient calling
+ * convention.
+ */
+void
+mono_set_pending_exception (MonoException *exc)
+{
+ MonoThread *thread = mono_thread_current ();
+
+ /* The thread may already be stopping */
+ if (thread == NULL)
+ return;
+
+ if (mono_thread_notify_pending_exc_fn) {
+ MONO_OBJECT_SETREF (thread, pending_exception, exc);
+
+ mono_thread_notify_pending_exc_fn ();
+ } else {
+ /* No way to notify the JIT about the exception, have to throw it now */
+ mono_raise_exception (exc);
+ }
}
/**