#include <signal.h>
#include <string.h>
+#if defined(__OpenBSD__)
+#include <pthread.h>
+#include <pthread_np.h>
+#endif
+
#include <mono/metadata/object.h>
#include <mono/metadata/domain-internals.h>
#include <mono/metadata/profiler-private.h>
#include <mono/metadata/gc-internal.h>
+#ifdef PLATFORM_ANDROID
+#include <errno.h>
+
+extern int tkill (pid_t tid, int signal);
+#endif
+
/*#define THREAD_DEBUG(a) do { a; } while (0)*/
#define THREAD_DEBUG(a)
/*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
/*#define LIBGC_DEBUG(a) do { a; } while (0)*/
#define LIBGC_DEBUG(a)
+#define SPIN_TRYLOCK(i) (InterlockedCompareExchange (&(i), 1, 0) == 0)
+#define SPIN_LOCK(i) do { \
+ if (SPIN_TRYLOCK (i)) \
+ break; \
+ } while (1)
+
+#define SPIN_UNLOCK(i) i = 0
+
/* Provide this for systems with glib < 2.6 */
#ifndef G_GSIZE_FORMAT
# if GLIB_SIZEOF_LONG == 8
#define GET_CURRENT_OBJECT() tls_current_object
#else
#define SET_CURRENT_OBJECT(x) TlsSetValue (current_object_key, x)
-#define GET_CURRENT_OBJECT() (MonoThread*) TlsGetValue (current_object_key)
+#define GET_CURRENT_OBJECT() (MonoInternalThread*) TlsGetValue (current_object_key)
#endif
/* function called at thread start */
#define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
static void thread_adjust_static_data (MonoInternalThread *thread);
+static void mono_free_static_data (gpointer* static_data, gboolean threadlocal);
static void mono_init_static_data_info (StaticDataInfo *static_data);
static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
static gboolean mono_thread_resume (MonoInternalThread* thread);
static MonoInternalThread **small_id_table = NULL;
/* The hazard table */
+#if MONO_SMALL_CONFIG
+#define HAZARD_TABLE_MAX_SIZE 256
+#else
#define HAZARD_TABLE_MAX_SIZE 16384 /* There cannot be more threads than this number. */
+#endif
static volatile int hazard_table_size = 0;
static MonoThreadHazardPointers * volatile hazard_table = NULL;
}
if(threads==NULL) {
- MONO_GC_REGISTER_ROOT (threads);
+ MONO_GC_REGISTER_ROOT_FIXED (threads);
threads=mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
}
if (!small_id_table) {
small_id_table_size = 2;
+ /*
+ * Enabling this causes problems, because SGEN doesn't track/update the TLS slot holding
+ * the current thread.
+ */
+ //small_id_table = mono_gc_alloc_fixed (small_id_table_size * sizeof (MonoInternalThread*), mono_gc_make_root_descr_all_refs (small_id_table_size));
small_id_table = mono_gc_alloc_fixed (small_id_table_size * sizeof (MonoInternalThread*), NULL);
}
for (i = small_id_next; i < small_id_table_size; ++i) {
if (new_size >= (1 << 16))
g_assert_not_reached ();
id = small_id_table_size;
+ //new_table = mono_gc_alloc_fixed (new_size * sizeof (MonoInternalThread*), mono_gc_make_root_descr_all_refs (new_size));
new_table = mono_gc_alloc_fixed (new_size * sizeof (MonoInternalThread*), NULL);
memcpy (new_table, small_id_table, small_id_table_size * sizeof (void*));
mono_gc_free_fixed (small_id_table);
if (small_id_next > small_id_table_size)
small_id_next = 0;
+ g_assert (id < HAZARD_TABLE_MAX_SIZE);
if (id >= hazard_table_size) {
+#if MONO_SMALL_CONFIG
+ hazard_table = g_malloc0 (sizeof (MonoThreadHazardPointers) * HAZARD_TABLE_MAX_SIZE);
+ hazard_table_size = HAZARD_TABLE_MAX_SIZE;
+#else
gpointer page_addr;
int pagesize = mono_pagesize ();
int num_pages = (hazard_table_size * sizeof (MonoThreadHazardPointers) + pagesize - 1) / pagesize;
g_assert (hazard_table != NULL);
page_addr = (guint8*)hazard_table + num_pages * pagesize;
- g_assert (id < HAZARD_TABLE_MAX_SIZE);
-
mono_mprotect (page_addr, pagesize, MONO_MMAP_READ | MONO_MMAP_WRITE);
++num_pages;
hazard_table_size = num_pages * pagesize / sizeof (MonoThreadHazardPointers);
+#endif
g_assert (id < hazard_table_size);
-
hazard_table [id].hazard_pointers [0] = NULL;
hazard_table [id].hazard_pointers [1] = NULL;
}
}
/* if the thread is not in the hash it has been removed already */
- if (!handle_remove (thread))
+ if (!handle_remove (thread)) {
+ /* This needs to be called even if handle_remove () fails */
+ if (mono_thread_cleanup_fn)
+ mono_thread_cleanup_fn (thread);
return;
+ }
mono_release_type_locks (thread);
EnterCriticalSection (thread->synch_cs);
thread->cached_culture_info = NULL;
- mono_gc_free_fixed (thread->static_data);
+ mono_free_static_data (thread->static_data, TRUE);
thread->static_data = NULL;
if (mono_thread_cleanup_fn)
- mono_thread_cleanup_fn (thread->root_domain_thread);
+ mono_thread_cleanup_fn (thread);
small_id_free (thread->small_id);
thread->small_id = -2;
+
}
static gpointer
*current_thread_ptr = current;
}
+static MonoInternalThread*
+create_internal_thread_object (void)
+{
+ MonoVTable *vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class);
+ return (MonoInternalThread*)mono_gc_alloc_mature (vt);
+}
+
+static MonoThread*
+create_thread_object (MonoDomain *domain)
+{
+ MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class);
+ return (MonoThread*)mono_gc_alloc_mature (vt);
+}
+
static MonoThread*
new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal)
{
- MonoThread *thread = (MonoThread*) mono_object_new (domain, mono_defaults.thread_class);
+ MonoThread *thread = create_thread_object (domain);
MONO_OBJECT_SETREF (thread, internal_thread, internal);
return thread;
}
MONO_OBJECT_SETREF (thread, root_domain_thread, candidate);
}
-static guint32 WINAPI start_wrapper(void *data)
+static guint32 WINAPI start_wrapper_internal(void *data)
{
struct StartInfo *start_info=(struct StartInfo *)data;
guint32 (*start_func)(void *);
void *start_arg;
gsize tid;
- MonoThread *thread=start_info->obj;
- MonoInternalThread *internal = thread->internal_thread;
+ /*
+ * We don't create a local to hold start_info->obj, so hopefully it won't get pinned during a
+ * GC stack walk.
+ */
+ MonoInternalThread *internal = start_info->obj->internal_thread;
MonoObject *start_delegate = start_info->delegate;
+ MonoDomain *domain = start_info->obj->obj.vtable->domain;
THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, GetCurrentThreadId ()));
mono_monitor_init_tls ();
/* Every thread references the appdomain which created it */
- mono_thread_push_appdomain_ref (thread->obj.vtable->domain);
+ mono_thread_push_appdomain_ref (domain);
- if (!mono_domain_set (thread->obj.vtable->domain, FALSE)) {
+ if (!mono_domain_set (domain, FALSE)) {
/* No point in raising an appdomain_unloaded exception here */
/* FIXME: Cleanup here */
mono_thread_pop_appdomain_ref ();
/* We have to do this here because mono_thread_new_init()
requires that root_domain_thread is set up. */
thread_adjust_static_data (internal);
- init_root_domain_thread (internal, thread);
+ init_root_domain_thread (internal, start_info->obj);
/* This MUST be called before any managed code can be
* executed, as it calls the callback function that (for the
/* On 2.0 profile (and higher), set explicitly since state might have been
Unknown */
- if (mono_framework_version () != 1) {
- if (internal->apartment_state == ThreadApartmentState_Unknown)
- internal->apartment_state = ThreadApartmentState_MTA;
- }
+ if (internal->apartment_state == ThreadApartmentState_Unknown)
+ internal->apartment_state = ThreadApartmentState_MTA;
mono_thread_init_apartment_state ();
}
mono_threads_lock ();
- mono_g_hash_table_remove (thread_start_args, thread);
+ mono_g_hash_table_remove (thread_start_args, start_info->obj);
mono_threads_unlock ();
+ mono_thread_set_execution_context (start_info->obj->ec_to_set);
+ start_info->obj->ec_to_set = NULL;
+
g_free (start_info);
#ifdef DEBUG
g_message ("%s: start_wrapper for %"G_GSIZE_FORMAT, __func__,
thread->tid);
#endif
- mono_thread_set_execution_context (thread->ec_to_set);
- thread->ec_to_set = NULL;
-
/*
* Call this after calling start_notify, since the profiler callback might want
* to lock the thread, and the lock is held by thread_start () which waits for
return(0);
}
-void mono_thread_new_init (gsize tid, gpointer stack_start, gpointer func)
+static guint32 WINAPI start_wrapper(void *data)
+{
+#ifdef HAVE_SGEN_GC
+ volatile int dummy;
+
+ /* Avoid scanning the frames above this frame during a GC */
+ mono_gc_set_stack_end ((void*)&dummy);
+#endif
+
+ return start_wrapper_internal (data);
+}
+
+void mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
{
if (mono_thread_start_cb) {
mono_thread_start_cb (tid, stack_start, func);
register_thread_start_argument (MonoThread *thread, struct StartInfo *start_info)
{
if (thread_start_args == NULL) {
- MONO_GC_REGISTER_ROOT (thread_start_args);
+ MONO_GC_REGISTER_ROOT_FIXED (thread_start_args);
thread_start_args = mono_g_hash_table_new (NULL, NULL);
}
mono_g_hash_table_insert (thread_start_args, thread, start_info->start_arg);
struct StartInfo *start_info;
gsize tid;
- thread=(MonoThread *)mono_object_new (domain,
- mono_defaults.thread_class);
- internal = (MonoInternalThread*)mono_object_new (mono_get_root_domain (),
- mono_defaults.internal_thread_class);
+ thread = create_thread_object (domain);
+ internal = create_internal_thread_object ();
MONO_OBJECT_SETREF (thread, internal_thread, internal);
start_info=g_new0 (struct StartInfo, 1);
return NULL;
}
if (threads_starting_up == NULL) {
- MONO_GC_REGISTER_ROOT (threads_starting_up);
- threads_starting_up = mono_g_hash_table_new (NULL, NULL);
+ MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up);
+ threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC);
}
register_thread_start_argument (thread, start_info);
# elif defined(sun)
*staddr = NULL;
pthread_attr_getstacksize (&attr, &stsize);
+# elif defined(__OpenBSD__)
+ stack_t ss;
+ int rslt;
+
+ rslt = pthread_stackseg_np(pthread_self(), &ss);
+ g_assert (rslt == 0);
+
+ *staddr = (guint8*)((size_t)ss.ss_sp - ss.ss_size);
+ *stsize = ss.ss_size;
# else
*staddr = NULL;
*stsize = 0;
# endif
# endif
-# ifndef sun
+# if !defined(sun)
+# if !defined(__OpenBSD__)
pthread_attr_getstack (&attr, (void**)staddr, stsize);
+# endif
if (*staddr)
g_assert ((current > *staddr) && (current < *staddr + *stsize));
# endif
- pthread_attr_destroy (&attr);
+ pthread_attr_destroy (&attr);
+#else
+ *staddr = NULL;
+ *stsize = (size_t)-1;
#endif
/* When running under emacs, sometimes staddr is not aligned to a page size */
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 = (MonoInternalThread *)mono_object_new (domain, mono_defaults.internal_thread_class);
+ thread = create_internal_thread_object ();
thread_handle = GetCurrentThread ();
g_assert (thread_handle);
thread->handle=thread_handle;
thread->tid=tid;
+#ifdef PLATFORM_ANDROID
+ thread->android_tid = (gpointer) gettid ();
+#endif
thread->apartment_state=ThreadApartmentState_Unknown;
small_id_alloc (thread);
thread->stack_ptr = &tid;
void
ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this)
{
- MonoInternalThread *internal = (MonoInternalThread*)mono_object_new (mono_get_root_domain (), mono_defaults.internal_thread_class);
+ MonoInternalThread *internal = create_internal_thread_object ();
+
internal->state = ThreadState_Unstarted;
internal->apartment_state = ThreadApartmentState_Unknown;
mono_threads_lock ();
register_thread_start_argument (this, start_info);
if (threads_starting_up == NULL) {
- MONO_GC_REGISTER_ROOT (threads_starting_up);
- threads_starting_up = mono_g_hash_table_new (NULL, NULL);
+ MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up);
+ threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC);
}
mono_g_hash_table_insert (threads_starting_up, this, this);
mono_threads_unlock ();
return mono_domain_get()->domain_id;
}
+gboolean
+ves_icall_System_Threading_Thread_Yield (void)
+{
+#ifdef HOST_WIN32
+ return SwitchToThread ();
+#else
+ return sched_yield () == 0;
+#endif
+}
+
/*
* mono_thread_get_name:
*
this_obj->name = NULL;
LeaveCriticalSection (this_obj->synch_cs);
-}
-
-static MonoObject*
-lookup_cached_culture (MonoInternalThread *this, MonoDomain *domain, int start_idx)
-{
- MonoObject *res;
- int i;
-
- if (this->cached_culture_info) {
- domain = mono_domain_get ();
- for (i = start_idx; i < start_idx + NUM_CACHED_CULTURES; ++i) {
- res = mono_array_get (this->cached_culture_info, MonoObject*, i);
- if (res && res->vtable->domain == domain)
- return res;
- }
+ if (this_obj->name) {
+ char *tname = mono_string_to_utf8 (name);
+ mono_profiler_thread_name (this_obj->tid, tname);
+ mono_free (tname);
}
-
- return NULL;
}
/* If the array is already in the requested domain, we just return it,
return byte_array_to_domain (arr, mono_domain_get ());
}
-MonoObject*
-ves_icall_System_Threading_Thread_GetCachedCurrentCulture (MonoInternalThread *this)
-{
- return lookup_cached_culture (this, mono_domain_get (), CULTURES_START_IDX);
-}
-
-static void
-cache_culture (MonoInternalThread *this, MonoObject *culture, int start_idx)
-{
- int i;
- MonoDomain *domain = mono_domain_get ();
- MonoObject *obj;
- int free_slot = -1;
- int same_domain_slot = -1;
-
- ensure_synch_cs_set (this);
-
- EnterCriticalSection (this->synch_cs);
-
- if (!this->cached_culture_info)
- MONO_OBJECT_SETREF (this, cached_culture_info, mono_array_new_cached (mono_get_root_domain (), mono_defaults.object_class, NUM_CACHED_CULTURES * 2));
-
- for (i = start_idx; i < start_idx + NUM_CACHED_CULTURES; ++i) {
- obj = mono_array_get (this->cached_culture_info, MonoObject*, i);
- /* Free entry */
- if (!obj) {
- free_slot = i;
- /* we continue, because there may be a slot used with the same domain */
- continue;
- }
- /* Replace */
- if (obj->vtable->domain == domain) {
- same_domain_slot = i;
- break;
- }
- }
- if (same_domain_slot >= 0)
- mono_array_setref (this->cached_culture_info, same_domain_slot, culture);
- else if (free_slot >= 0)
- mono_array_setref (this->cached_culture_info, free_slot, culture);
- /* we may want to replace an existing entry here, even when no suitable slot is found */
-
- LeaveCriticalSection (this->synch_cs);
-}
-
-void
-ves_icall_System_Threading_Thread_SetCachedCurrentCulture (MonoThread *this, MonoObject *culture)
-{
- MonoDomain *domain = mono_object_get_domain (&this->obj);
- g_assert (domain == mono_domain_get ());
- cache_culture (this->internal_thread, culture, CULTURES_START_IDX);
-}
-
-MonoObject*
-ves_icall_System_Threading_Thread_GetCachedCurrentUICulture (MonoInternalThread *this)
-{
- return lookup_cached_culture (this, mono_domain_get (), UICULTURES_START_IDX);
-}
-
-void
-ves_icall_System_Threading_Thread_SetCachedCurrentUICulture (MonoThread *this, MonoObject *culture)
-{
- MonoDomain *domain = mono_object_get_domain (&this->obj);
- g_assert (domain == mono_domain_get ());
- cache_culture (this->internal_thread, culture, UICULTURES_START_IDX);
-}
-
MonoThread *
mono_thread_current (void)
{
/* FIXME: exitContext isnt documented */
gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
{
- HANDLE *handles;
+ HANDLE handles [MAXIMUM_WAIT_OBJECTS];
guint32 numhandles;
guint32 ret;
guint32 i;
MonoObject *waitHandle;
MonoInternalThread *thread = mono_thread_internal_current ();
+ guint32 start;
/* Do this WaitSleepJoin check before creating objects */
mono_thread_current_check_pending_interrupt ();
numhandles = mono_array_length(mono_handles);
- handles = g_new0(HANDLE, numhandles);
+ if (numhandles > MAXIMUM_WAIT_OBJECTS)
+ return WAIT_FAILED;
for(i = 0; i < numhandles; i++) {
waitHandle = mono_array_get(mono_handles, MonoObject*, i);
}
mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
-
- ret=WaitForMultipleObjectsEx(numhandles, handles, FALSE, ms, TRUE);
+
+ start = (ms == -1) ? 0 : mono_msec_ticks ();
+ do {
+ ret = WaitForMultipleObjectsEx (numhandles, handles, FALSE, ms, TRUE);
+ if (ret != WAIT_IO_COMPLETION)
+ break;
+ if (ms != -1) {
+ guint32 diff;
+
+ diff = mono_msec_ticks () - start;
+ ms -= diff;
+ if (ms <= 0)
+ break;
+ }
+ } while (ms == -1 || ms > 0);
mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
-
- g_free(handles);
THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, GetCurrentThreadId (), ret));
QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, NULL);
#else
/* fixme: store the state somewhere */
-#ifdef PTHREAD_POINTER_ID
- pthread_kill ((gpointer)(gsize)(thread->tid), mono_thread_get_abort_signal ());
-#else
- pthread_kill (thread->tid, mono_thread_get_abort_signal ());
-#endif
+ mono_thread_kill (thread, mono_thread_get_abort_signal ());
/*
* This will cause waits to be broken.
ves_icall_System_Threading_Thread_ResetAbort (void)
{
MonoInternalThread *thread = mono_thread_internal_current ();
+ gboolean was_aborting;
ensure_synch_cs_set (thread);
EnterCriticalSection (thread->synch_cs);
-
+ was_aborting = thread->state & ThreadState_AbortRequested;
thread->state &= ~ThreadState_AbortRequested;
-
- if (!thread->abort_exc) {
+ LeaveCriticalSection (thread->synch_cs);
+
+ if (!was_aborting) {
const char *msg = "Unable to reset abort because no abort was requested";
- LeaveCriticalSection (thread->synch_cs);
mono_raise_exception (mono_get_exception_thread_state (msg));
- } else {
+ }
+ thread->abort_exc = NULL;
+ if (thread->abort_state_handle) {
+ mono_gchandle_free (thread->abort_state_handle);
+ /* This is actually not necessary - the handle
+ only counts if the exception is set */
+ thread->abort_state_handle = 0;
+ }
+}
+
+void
+mono_thread_internal_reset_abort (MonoInternalThread *thread)
+{
+ ensure_synch_cs_set (thread);
+
+ EnterCriticalSection (thread->synch_cs);
+
+ thread->state &= ~ThreadState_AbortRequested;
+
+ if (thread->abort_exc) {
thread->abort_exc = NULL;
if (thread->abort_state_handle) {
mono_gchandle_free (thread->abort_state_handle);
thread->abort_state_handle = 0;
}
}
-
+
LeaveCriticalSection (thread->synch_cs);
}
void mono_thread_init (MonoThreadStartCB start_cb,
MonoThreadAttachCB attach_cb)
{
- MONO_GC_REGISTER_ROOT (small_id_table);
+ MONO_GC_REGISTER_ROOT_FIXED (small_id_table);
InitializeCriticalSection(&threads_mutex);
InitializeCriticalSection(&interlocked_mutex);
InitializeCriticalSection(&contexts_mutex);
THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
- ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, FALSE);
+ ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, TRUE);
if(ret==WAIT_FAILED) {
/* See the comment in build_wait_tids() */
count++;
}
- ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, FALSE);
+ ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, TRUE);
if(ret==WAIT_FAILED) {
/* See the comment in build_wait_tids() */
LeaveCriticalSection (current_thread->synch_cs);
}
+ /*since we're killing the thread, unset the current domain.*/
+ mono_domain_unset ();
+
/* Wake up other threads potentially waiting for us */
ExitThread (0);
} else {
void mono_thread_manage (void)
{
- struct wait_data *wait=g_new0 (struct wait_data, 1);
+ struct wait_data wait_data;
+ struct wait_data *wait = &wait_data;
+ memset (wait, 0, sizeof (struct wait_data));
/* join each thread that's still running */
THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
if(threads==NULL) {
THREAD_DEBUG (g_message("%s: No threads", __func__));
mono_threads_unlock ();
- g_free (wait);
return;
}
mono_threads_unlock ();
ResetEvent (background_change_event);
wait->num=0;
+ /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
+ memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
mono_g_hash_table_foreach (threads, build_wait_tids, wait);
mono_threads_unlock ();
if(wait->num>0) {
mono_threads_lock ();
wait->num = 0;
+ /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
+ memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait);
mono_threads_unlock ();
#ifndef HOST_WIN32
sched_yield ();
#endif
-
- g_free (wait);
}
static void terminate_thread (gpointer key, gpointer value, gpointer user)
*/
void mono_thread_suspend_all_other_threads (void)
{
- struct wait_data *wait = g_new0 (struct wait_data, 1);
+ struct wait_data wait_data;
+ struct wait_data *wait = &wait_data;
int i;
gsize self = GetCurrentThreadId ();
gpointer *events;
guint32 eventidx = 0;
gboolean starting, finished;
+ memset (wait, 0, sizeof (struct wait_data));
/*
* 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 while threads_mutex is held.
*/
wait->num = 0;
+ /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
+ memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
mono_threads_lock ();
mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
mono_threads_unlock ();
if (thread == NULL)
continue;
+
+ ensure_synch_cs_set (thread);
EnterCriticalSection (thread->synch_cs);
if ((thread->state & ThreadState_Suspended) != 0) {
g_free (events);
}
-
- g_free (wait);
}
static void
void
mono_threads_request_thread_dump (void)
{
- struct wait_data *wait = g_new0 (struct wait_data, 1);
+ struct wait_data wait_data;
+ struct wait_data *wait = &wait_data;
int i;
+ memset (wait, 0, sizeof (struct wait_data));
+
/*
* Make a copy of the hashtable since we can't do anything with
* threads while threads_mutex is held.
if (thread) {
/* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
- mono_threads_lock ();
+ SPIN_LOCK (thread->lock_thread_id);
thread->appdomain_refs = g_slist_prepend (thread->appdomain_refs, domain);
- mono_threads_unlock ();
+ SPIN_UNLOCK (thread->lock_thread_id);
}
}
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 ? */
+ SPIN_LOCK (thread->lock_thread_id);
if (thread->appdomain_refs)
thread->appdomain_refs = g_slist_remove (thread->appdomain_refs, thread->appdomain_refs->data);
- mono_threads_unlock ();
+ SPIN_UNLOCK (thread->lock_thread_id);
}
}
mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
{
gboolean res;
- mono_threads_lock ();
+ SPIN_LOCK (thread->lock_thread_id);
res = g_slist_find (thread->appdomain_refs, domain) != NULL;
- mono_threads_unlock ();
+ SPIN_UNLOCK (thread->lock_thread_id);
return res;
}
return NULL;
}
+#if MONO_SMALL_CONFIG
+#define NUM_STATIC_DATA_IDX 4
+static const int static_data_size [NUM_STATIC_DATA_IDX] = {
+ 64, 256, 1024, 4096
+};
+#else
#define NUM_STATIC_DATA_IDX 8
static const int static_data_size [NUM_STATIC_DATA_IDX] = {
1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
};
+#endif
+static uintptr_t* static_reference_bitmaps [NUM_STATIC_DATA_IDX];
+
+#ifdef HAVE_SGEN_GC
+static void
+mark_tls_slots (void *addr, MonoGCMarkFunc mark_func)
+{
+ int i;
+ gpointer *static_data = addr;
+ for (i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
+ int j, numwords;
+ void **ptr;
+ if (!static_data [i])
+ continue;
+ numwords = 1 + static_data_size [i] / sizeof (gpointer) / (sizeof(uintptr_t) * 8);
+ ptr = static_data [i];
+ for (j = 0; j < numwords; ++j, ptr += sizeof (uintptr_t) * 8) {
+ uintptr_t bmap = static_reference_bitmaps [i][j];
+ void ** p = ptr;
+ while (bmap) {
+ if ((bmap & 1) && *p) {
+ mark_func (p);
+ }
+ p++;
+ bmap >>= 1;
+ }
+ }
+ }
+}
+#endif
/*
* 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)
+mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal)
{
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 void* tls_desc = NULL;
+#ifdef HAVE_SGEN_GC
+ if (!tls_desc)
+ tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
+#endif
+ static_data = mono_gc_alloc_fixed (static_data_size [0], threadlocal?tls_desc:NULL);
*static_data_ptr = static_data;
static_data [0] = static_data;
}
for (i = 1; i <= idx; ++i) {
if (static_data [i])
continue;
+#ifdef HAVE_SGEN_GC
+ static_data [i] = threadlocal?g_malloc0 (static_data_size [i]):mono_gc_alloc_fixed (static_data_size [i], NULL);
+#else
static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL);
+#endif
+ }
+}
+
+static void
+mono_free_static_data (gpointer* static_data, gboolean threadlocal)
+{
+ int i;
+ for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
+ if (!static_data [i])
+ continue;
+#ifdef HAVE_SGEN_GC
+ if (threadlocal)
+ g_free (static_data [i]);
+ else
+ mono_gc_free_fixed (static_data [i]);
+#else
+ mono_gc_free_fixed (static_data [i]);
+#endif
}
+ mono_gc_free_fixed (static_data);
}
/*
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_alloc_static_data (&(thread->static_data), offset, TRUE);
}
mono_threads_unlock ();
}
MonoInternalThread *thread = value;
guint32 offset = GPOINTER_TO_UINT (user);
- mono_alloc_static_data (&(thread->static_data), offset);
+ mono_alloc_static_data (&(thread->static_data), offset, TRUE);
}
static MonoThreadDomainTls*
return NULL;
}
+static void
+update_tls_reference_bitmap (guint32 offset, uintptr_t *bitmap, int max_set)
+{
+ int i;
+ int idx = (offset >> 24) - 1;
+ uintptr_t *rb;
+ if (!static_reference_bitmaps [idx])
+ static_reference_bitmaps [idx] = g_new0 (uintptr_t, 1 + static_data_size [idx] / sizeof(gpointer) / (sizeof(uintptr_t) * 8));
+ rb = static_reference_bitmaps [idx];
+ offset &= 0xffffff;
+ offset /= sizeof (gpointer);
+ /* offset is now the bitmap offset */
+ for (i = 0; i < max_set; ++i) {
+ if (bitmap [i / sizeof (uintptr_t)] & (1L << (i & (sizeof (uintptr_t) * 8 -1))))
+ rb [(offset + i) / (sizeof (uintptr_t) * 8)] |= (1L << ((offset + i) & (sizeof (uintptr_t) * 8 -1)));
+ }
+}
+
+static void
+clear_reference_bitmap (guint32 offset, guint32 size)
+{
+ int idx = (offset >> 24) - 1;
+ uintptr_t *rb;
+ rb = static_reference_bitmaps [idx];
+ offset &= 0xffffff;
+ offset /= sizeof (gpointer);
+ size /= sizeof (gpointer);
+ size += offset;
+ /* offset is now the bitmap offset */
+ for (; offset < size; ++offset)
+ rb [offset / (sizeof (uintptr_t) * 8)] &= ~(1L << (offset & (sizeof (uintptr_t) * 8 -1)));
+}
+
/*
* 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),
*/
guint32
-mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align)
+mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int max_set)
{
guint32 offset;
- if (static_type == SPECIAL_STATIC_THREAD)
- {
+ if (static_type == SPECIAL_STATIC_THREAD) {
MonoThreadDomainTls *item;
mono_threads_lock ();
item = search_tls_slot_in_freelist (&thread_static_info, size, align);
} else {
offset = mono_alloc_static_data_slot (&thread_static_info, size, align);
}
+ update_tls_reference_bitmap (offset, bitmap, max_set);
/* 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
- {
+ } else {
g_assert (static_type == SPECIAL_STATIC_CONTEXT);
mono_contexts_lock ();
offset = mono_alloc_static_data_slot (&context_static_info, size, align);
}
gpointer
-mono_get_special_static_data (guint32 offset)
+mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
{
/* The high bit means either thread (0) or static (1) data. */
idx = (offset >> 24) - 1;
if (static_type == 0) {
- return get_thread_static_data (mono_thread_internal_current (), offset);
+ return get_thread_static_data (thread, offset);
} 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_alloc_static_data (&(context->static_data), offset, FALSE);
mono_contexts_unlock ();
}
return ((char*) context->static_data [idx]) + (offset & 0xffffff);
}
}
+gpointer
+mono_get_special_static_data (guint32 offset)
+{
+ return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
+}
+
typedef struct {
guint32 offset;
guint32 size;
MonoThreadDomainTls *item = g_new0 (MonoThreadDomainTls, 1);
data.offset = offset & 0x7fffffff;
data.size = size;
+ clear_reference_bitmap (data.offset, data.size);
if (threads != NULL)
mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
item->offset = offset;
item->size = size;
- item->next = thread_static_info.freelist;
- thread_static_info.freelist = item;
+
+ if (!mono_runtime_is_shutting_down ()) {
+ item->next = thread_static_info.freelist;
+ thread_static_info.freelist = item;
+ } else {
+ /* We could be called during shutdown after mono_thread_cleanup () is called */
+ g_free (item);
+ }
} else {
/* FIXME: free context static data as well */
}
}
}
+/*This function should be called by a thread after it has exited all of
+ * its handle blocks at interruption time.*/
+MonoException*
+mono_thread_resume_interruption (void)
+{
+ MonoInternalThread *thread = mono_thread_internal_current ();
+ gboolean still_aborting;
+
+ /* The thread may already be stopping */
+ if (thread == NULL)
+ return NULL;
+
+ ensure_synch_cs_set (thread);
+ EnterCriticalSection (thread->synch_cs);
+ still_aborting = (thread->state & ThreadState_AbortRequested) != 0;
+ LeaveCriticalSection (thread->synch_cs);
+
+ /*This can happen if the protected block called Thread::ResetAbort*/
+ if (!still_aborting)
+ return FALSE;
+
+ if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
+ return NULL;
+ InterlockedIncrement (&thread_interruption_requested);
+
+#ifndef HOST_WIN32
+ wapi_self_interrupt ();
+#endif
+ return mono_thread_execute_interruption (thread);
+}
+
gboolean mono_thread_interruption_requested ()
{
if (thread_interruption_requested) {
{
return has_tls_get;
}
+
+int
+mono_thread_kill (MonoInternalThread *thread, int signal)
+{
+#ifdef HOST_WIN32
+ /* Win32 uses QueueUserAPC and callers of this are guarded */
+ g_assert_not_reached ();
+#else
+# ifdef PTHREAD_POINTER_ID
+ return pthread_kill ((gpointer)(gsize)(thread->tid), mono_thread_get_abort_signal ());
+# else
+# ifdef PLATFORM_ANDROID
+ if (thread->android_tid != 0) {
+ int ret;
+ int old_errno = errno;
+
+ ret = tkill ((pid_t) thread->android_tid, signal);
+ if (ret < 0) {
+ ret = errno;
+ errno = old_errno;
+ }
+
+ return ret;
+ }
+ else
+ return pthread_kill (thread->tid, mono_thread_get_abort_signal ());
+# else
+ return pthread_kill (thread->tid, mono_thread_get_abort_signal ());
+# endif
+# endif
+#endif
+}