#include <mono/metadata/gc-internal.h>
#include <mono/metadata/marshal.h>
#include <mono/io-layer/io-layer.h>
+#ifndef PLATFORM_WIN32
+#include <mono/io-layer/threads.h>
+#endif
#include <mono/metadata/object-internals.h>
#include <mono/metadata/mono-debug-debugger.h>
#include <mono/utils/mono-compiler.h>
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;
}
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) {
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
MonoThread *
mono_thread_current (void)
{
- THREAD_DEBUG (g_message ("%s: returning %p", __func__, GET_CURRENT_OBJECT ()));
- return GET_CURRENT_OBJECT ();
+ MonoThread *res = GET_CURRENT_OBJECT ()
+ THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
+ return res;
}
gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
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);
#else
pthread_kill (thread->tid, mono_thread_get_abort_signal ());
#endif
+
+ /*
+ * This will cause waits to be broken.
+ * It will also prevent the thread from entering a wait, so if the thread returns
+ * from the wait before it receives the abort signal, it will just spin in the wait
+ * functions in the io-layer until the signal handler calls QueueUserAPC which will
+ * make it return.
+ */
+ wapi_interrupt_thread (thread->handle);
#endif /* PLATFORM_WIN32 */
}
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);
}
{
abort_appdomain_data user_data;
guint32 start_time;
+ int orig_timeout = timeout;
THREAD_DEBUG (g_message ("%s: starting abort", __func__));
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);
WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
InterlockedDecrement (&thread_interruption_requested);
thread->interruption_requested = FALSE;
+#ifndef PLATFORM_WIN32
+ /* Clear the interrupted flag of the thread so it can wait again */
+ wapi_clear_interruption ();
+#endif
}
if ((thread->state & ThreadState_AbortRequested) != 0) {
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);
-
- /* FIXME: This is NOT signal safe */
- 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 */
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);
+ }
}
/**