*
* Author:
* Dick Porter (dick@ximian.com)
+ * Paolo Molaro (lupus@ximian.com)
* Patrik Torstensson (patrik.torstensson@labs2.com)
*
* (C) 2001 Ximian, Inc.
#include <mono/metadata/threads-types.h>
#include <mono/metadata/exception.h>
#include <mono/metadata/environment.h>
+#include <mono/metadata/monitor.h>
#include <mono/io-layer/io-layer.h>
#include <mono/os/gc_wrapper.h>
#undef THREAD_DEBUG
-#undef THREAD_LOCK_DEBUG
#undef THREAD_WAIT_DEBUG
struct StartInfo
/* Controls access to the 'threads' hash table */
static CRITICAL_SECTION threads_mutex;
-/* Controls access to the sync field in MonoObjects, to avoid race
- * conditions when adding sync data to an object for the first time.
- */
-static CRITICAL_SECTION monitor_mutex;
-
/* The hash of existing threads (key is thread ID) that need joining
* before exit
*/
/* The TLS key that holds the LocalDataStoreSlot hash in each thread */
static guint32 slothash_key;
+static void thread_adjust_static_data (MonoThread *thread);
+
/* Spin lock for InterlockedXXX 64 bit functions */
static CRITICAL_SECTION interlocked_mutex;
threads=mono_g_hash_table_new(g_direct_hash, g_direct_equal);
}
- /* GHashTable will remove a previous entry if a duplicate key
- * is stored, which is exactly what we want: we store the
- * thread both in the start_wrapper (in the subthread), and as
- * soon as possible in the parent thread. This is to minimise
- * the window in which the thread exists but we haven't
- * recorded it.
- */
- mono_g_hash_table_remove (threads, GUINT_TO_POINTER(thread->tid));
-
/* We don't need to duplicate thread->handle, because it is
* only closed when the thread object is finalized by the GC.
*/
*/
}
-static void thread_cleanup (guint32 tid)
+static void thread_cleanup (MonoThread *thread)
{
- mono_profiler_thread_end (tid);
- handle_remove (tid);
+ mono_monitor_try_enter ((MonoObject *)thread, INFINITE);
+ thread->state |= ThreadState_Stopped;
+ mono_monitor_exit ((MonoObject *)thread);
+
+ mono_profiler_thread_end (thread->tid);
+ handle_remove (thread->tid);
}
static guint32 start_wrapper(void *data)
guint32 (*start_func)(void *);
void *this;
guint32 tid;
+ MonoThread *thread=start_info->obj;
#ifdef THREAD_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
+ g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper",
+ GetCurrentThreadId ());
#endif
/* We can be sure start_info->obj->tid and
* thread resumed
*/
- tid=start_info->obj->tid;
+ tid=thread->tid;
mono_domain_set (start_info->domain);
* jit) sets the lmf marker.
*/
mono_thread_new_init (tid, &tid, start_func);
+ thread->stack_ptr = &tid;
+
+#ifdef LIBGC_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION
+ ": (%d,%d) Setting thread stack to %p",
+ GetCurrentThreadId (), getpid (), thread->stack_ptr);
+#endif
+
+#ifdef THREAD_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION
+ ": (%d) Setting current_object_key to %p",
+ GetCurrentThreadId (), thread);
+#endif
- TlsSetValue (current_object_key, start_info->obj);
- handle_store(start_info->obj);
+ TlsSetValue (current_object_key, thread);
mono_profiler_thread_start (tid);
+ if(thread->start_notify!=NULL) {
+ /* Let the thread that called Start() know we're
+ * ready
+ */
+ ReleaseSemaphore (thread->start_notify, 1, NULL);
+ }
+
g_free (start_info);
+ thread_adjust_static_data (thread);
+
start_func (this);
/* If the thread calls ExitThread at all, this remaining code
*/
#ifdef THREAD_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
+ g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating",
+ GetCurrentThreadId ());
#endif
- thread_cleanup (tid);
+ thread_cleanup (thread);
return(0);
}
if (mono_thread_start_cb) {
mono_thread_start_cb (tid, stack_start, func);
}
+
+ if (mono_thread_callbacks)
+ (* mono_thread_callbacks->thread_created) (tid, stack_start, func);
}
void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
handle_store(thread);
+#ifdef THREAD_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION
+ ": (%d) Setting current_object_key to %p",
+ GetCurrentThreadId (), thread);
+#endif
+
TlsSetValue (current_object_key, thread);
mono_domain_set (domain);
+ thread_adjust_static_data (thread);
+
if (mono_thread_attach_cb) {
mono_thread_attach_cb (tid, &tid);
}
start_info->obj = this;
start_info->domain = mono_domain_get ();
+ this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
+ if(this->start_notify==NULL) {
+ g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
+ return(NULL);
+ }
+
thread=CreateThread(NULL, 0, start_wrapper, start_info,
CREATE_SUSPENDED, &tid);
if(thread==NULL) {
": CreateThread error 0x%x", GetLastError());
return(NULL);
}
-
+
this->handle=thread;
this->tid=tid;
MONO_ARCH_SAVE_REGS;
#ifdef THREAD_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
+ g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
+ GetCurrentThreadId (), this, this->tid);
#endif
/* Only store the handle when the thread is about to be
if (mono_thread_callbacks)
(* mono_thread_callbacks->end_resume) (this->tid);
+
+ if(this->start_notify!=NULL) {
+ /* Wait for the thread to set up its TLS data etc, so
+ * theres no potential race condition if someone tries
+ * to look up the data believing the thread has
+ * started
+ */
+
+#ifdef THREAD_DEBUG
+ g_message(G_GNUC_PRETTY_FUNCTION
+ ": (%d) waiting for thread %p (%d) to start",
+ GetCurrentThreadId (), this, this->tid);
+#endif
+
+ WaitForSingleObject (this->start_notify, INFINITE);
+ CloseHandle (this->start_notify);
+ this->start_notify=NULL;
+ }
+
+#ifdef THREAD_DEBUG
+ g_message(G_GNUC_PRETTY_FUNCTION
+ ": (%d) Done launching thread %p (%d)",
+ GetCurrentThreadId (), this, this->tid);
+#endif
}
void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
return(data);
}
-static void mon_finalize (void *o, void *unused)
-{
- MonoThreadsSync *mon=(MonoThreadsSync *)o;
-
-#ifdef THREAD_LOCK_DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync");
-#endif
-
- CloseHandle (mon->monitor);
- CloseHandle (mon->sema);
- CloseHandle (mon->waiters_done);
- DeleteCriticalSection (&mon->waiters_count_lock);
-}
-
-static MonoThreadsSync *mon_new(void)
-{
- MonoThreadsSync *new;
-
-#if HAVE_BOEHM_GC
- new=(MonoThreadsSync *)GC_MALLOC (sizeof(MonoThreadsSync));
- GC_REGISTER_FINALIZER (new, mon_finalize, NULL, NULL, NULL);
-#else
- /* This should be freed when the object that owns it is
- * deleted
- */
- new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1);
-#endif
-
- new->monitor=CreateMutex(NULL, FALSE, NULL);
- if(new->monitor==NULL) {
- /* Throw some sort of system exception? (ditto for the
- * sem and event handles below)
- */
- }
-
- new->waiters_count=0;
- new->was_broadcast=FALSE;
- InitializeCriticalSection(&new->waiters_count_lock);
- new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
- new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) ThreadsSync %p mutex created: %p, sem: %p, event: %p", GetCurrentThreadId (), new, new->monitor, new->sema, new->waiters_done);
-#endif
-
- return(new);
-}
-
-gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
- int ms)
-{
- MonoThreadsSync *mon;
- guint32 ret;
-
- MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION
- ": (%d) Trying to lock object %p", GetCurrentThreadId(),
- obj);
-#endif
-
- EnterCriticalSection(&monitor_mutex);
-
- mon=obj->synchronisation;
- if(mon==NULL) {
- mon=mon_new();
- obj->synchronisation=mon;
- }
-
- /* Don't hold the monitor lock while waiting to acquire the
- * object lock
- */
- LeaveCriticalSection(&monitor_mutex);
-
- /* Acquire the mutex */
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Acquiring monitor mutex %p",
- GetCurrentThreadId (), mon->monitor);
-#endif
- ret=WaitForSingleObject(mon->monitor, ms);
- if(ret==WAIT_OBJECT_0) {
- mon->count++;
- mon->tid=GetCurrentThreadId();
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION
- ": (%d) object %p now locked %d times",
- GetCurrentThreadId (), obj, mon->count);
-#endif
-
- return(TRUE);
- }
-
- return(FALSE);
-}
-
-void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
-{
- MonoThreadsSync *mon;
-
- MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p",
- GetCurrentThreadId (), obj);
-#endif
-
- /* No need to lock monitor_mutex here because we only adjust
- * the monitor state if this thread already owns the lock
- */
- mon=obj->synchronisation;
-
- if(mon==NULL) {
- return;
- }
-
- if(mon->tid!=GetCurrentThreadId()) {
- return;
- }
-
- mon->count--;
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p now locked %d times",
- GetCurrentThreadId (), obj, mon->count);
-#endif
-
- if(mon->count==0) {
- mon->tid=0; /* FIXME: check that 0 isnt a valid id */
- }
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
- GetCurrentThreadId (), mon->monitor);
-#endif
-
- ReleaseMutex(mon->monitor);
-}
-
-gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
-{
- MonoThreadsSync *mon;
- gboolean ret=FALSE;
-
- MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION
- ": Testing if %p is owned by thread %d", obj,
- GetCurrentThreadId());
-#endif
-
- EnterCriticalSection(&monitor_mutex);
-
- mon=obj->synchronisation;
- if(mon==NULL) {
- goto finished;
- }
-
- if(mon->tid!=GetCurrentThreadId()) {
-#ifdef THREAD_LOCK_DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- ": (%d) object %p is owned by thread %d",
- GetCurrentThreadId (), obj, mon->tid);
-#endif
-
- goto finished;
- }
-
- ret=TRUE;
-
-finished:
- LeaveCriticalSection(&monitor_mutex);
-
- return(ret);
-}
-
-gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
-{
- MonoThreadsSync *mon;
- gboolean ret=FALSE;
-
- MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION
- ": (%d) Testing if %p is owned by any thread",
- GetCurrentThreadId (), obj);
-#endif
-
- EnterCriticalSection(&monitor_mutex);
-
- mon=obj->synchronisation;
- if(mon==NULL) {
- goto finished;
- }
-
- if(mon->tid==0) {
- goto finished;
- }
-
- g_assert(mon->count);
-
- ret=TRUE;
-
-finished:
- LeaveCriticalSection(&monitor_mutex);
-
- return(ret);
-}
-
-
-void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
-{
- gboolean have_waiters;
- MonoThreadsSync *mon;
-
- MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION "(%d) Pulsing %p",
- GetCurrentThreadId (), obj);
-#endif
-
- EnterCriticalSection(&monitor_mutex);
-
- mon=obj->synchronisation;
- if(mon==NULL) {
-#ifdef THREAD_LOCK_DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- "(%d) object %p not locked", GetCurrentThreadId (),
- obj);
-#endif
-
- LeaveCriticalSection(&monitor_mutex);
- return;
- }
-
- if(mon->tid!=GetCurrentThreadId()) {
-#ifdef THREAD_LOCK_DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- "(%d) doesn't own lock (owned by %d)",
- GetCurrentThreadId (), mon->tid);
-#endif
-
- LeaveCriticalSection(&monitor_mutex);
- return;
- }
- LeaveCriticalSection(&monitor_mutex);
-
- EnterCriticalSection(&mon->waiters_count_lock);
- have_waiters=(mon->waiters_count>0);
- LeaveCriticalSection(&mon->waiters_count_lock);
-
- if(have_waiters==TRUE) {
- ReleaseSemaphore(mon->sema, 1, 0);
- }
-}
-
-void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
-{
- gboolean have_waiters=FALSE;
- MonoThreadsSync *mon;
-
- MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
- g_message("(%d) Pulsing all %p", GetCurrentThreadId (), obj);
-#endif
-
- EnterCriticalSection(&monitor_mutex);
-
- mon=obj->synchronisation;
- if(mon==NULL) {
- LeaveCriticalSection(&monitor_mutex);
- return;
- }
-
- if(mon->tid!=GetCurrentThreadId()) {
- LeaveCriticalSection(&monitor_mutex);
- return;
- }
- LeaveCriticalSection(&monitor_mutex);
-
- EnterCriticalSection(&mon->waiters_count_lock);
- if(mon->waiters_count>0) {
- mon->was_broadcast=TRUE;
- have_waiters=TRUE;
- }
-
- if(have_waiters==TRUE) {
- ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
-
- LeaveCriticalSection(&mon->waiters_count_lock);
-
- WaitForSingleObject(mon->waiters_done, INFINITE);
- mon->was_broadcast=FALSE;
- } else {
- LeaveCriticalSection(&mon->waiters_count_lock);
- }
-}
-
-gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
- int ms)
-{
- gboolean last_waiter;
- MonoThreadsSync *mon;
- guint32 save_count;
-
- MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION
- "(%d) Trying to wait for %p with timeout %dms",
- GetCurrentThreadId (), obj, ms);
-#endif
-
- EnterCriticalSection(&monitor_mutex);
-
- mon=obj->synchronisation;
- if(mon==NULL) {
- LeaveCriticalSection(&monitor_mutex);
- return(FALSE);
- }
-
- if(mon->tid!=GetCurrentThreadId()) {
- LeaveCriticalSection(&monitor_mutex);
- return(FALSE);
- }
- LeaveCriticalSection(&monitor_mutex);
-
- EnterCriticalSection(&mon->waiters_count_lock);
- mon->waiters_count++;
- LeaveCriticalSection(&mon->waiters_count_lock);
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p locked %d times",
- GetCurrentThreadId (), obj, mon->count);
-#endif
-
- /* We need to put the lock count back afterwards */
- save_count=mon->count;
-
- while(mon->count>1) {
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
- GetCurrentThreadId (), mon->monitor);
-#endif
-
- ReleaseMutex(mon->monitor);
- mon->count--;
- }
-
- /* We're releasing this mutex */
- mon->count=0;
- mon->tid=0;
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Signalling monitor mutex %p",
- GetCurrentThreadId (), mon->monitor);
-#endif
-
- SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
-
- EnterCriticalSection(&mon->waiters_count_lock);
- mon->waiters_count++;
- last_waiter=mon->was_broadcast && mon->waiters_count==0;
- LeaveCriticalSection(&mon->waiters_count_lock);
-
- if(last_waiter) {
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
- GetCurrentThreadId (), mon->monitor);
-#endif
- SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
- } else {
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
- GetCurrentThreadId (), mon->monitor);
-#endif
- WaitForSingleObject(mon->monitor, INFINITE);
- }
-
- /* We've reclaimed this mutex */
- mon->count=save_count;
- mon->tid=GetCurrentThreadId();
-
- /* Lock the mutex the required number of times */
- while(save_count>1) {
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION
- ": (%d) Waiting for monitor mutex %p",
- GetCurrentThreadId (), mon->monitor);
-#endif
- WaitForSingleObject(mon->monitor, INFINITE);
- save_count--;
- }
-
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p still locked %d times",
- GetCurrentThreadId (), obj, mon->count);
-#endif
-
- return(TRUE);
-}
-
/* FIXME: exitContext isnt documented */
gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
{
thread->abort_state = state;
thread->abort_exc = mono_get_exception_thread_abort ();
+#ifdef THREAD_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION
+ ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
+ thread, thread->tid);
+#endif
+
#ifdef __MINGW32__
g_assert_not_reached ();
#else
MonoThreadAttachCB attach_cb)
{
InitializeCriticalSection(&threads_mutex);
- InitializeCriticalSection(&monitor_mutex);
InitializeCriticalSection(&interlocked_mutex);
current_object_key=TlsAlloc();
g_message (G_GNUC_PRETTY_FUNCTION
": cleaning up after thread %d", tid);
#endif
- thread_cleanup (tid);
+ thread_cleanup (wait->threads[i]);
}
}
}
if(wait->num<MAXIMUM_WAIT_OBJECTS) {
MonoThread *thread=(MonoThread *)value;
+
+ /* BUG: For now we just ignore background threads, we should abort them
+ */
+ if (thread->state & ThreadState_Background)
+ return; /* just leave, ignore */
- /* Theres a theoretical chance that thread->handle
- * might be NULL if the child thread has called
- * handle_store() but the parent thread hasn't set the
- * handle pointer yet. WaitForMultipleObjects will
- * fail, and we'll just go round the loop again. By
- * that time the handle should be stored properly.
- */
wait->handles[wait->num]=thread->handle;
wait->threads[wait->num]=thread;
wait->num++;
LeaveCriticalSection (&threads_mutex);
}
+
+static int static_data_idx = 0;
+static int static_data_offset = 0;
+#define NUM_STATIC_DATA_IDX 8
+static const int static_data_size [NUM_STATIC_DATA_IDX] = {
+ 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
+};
+
+static void
+thread_alloc_static_data (MonoThread *thread, guint32 offset)
+{
+ guint idx = (offset >> 24) - 1;
+ int i;
+
+ if (!thread->static_data) {
+ thread->static_data = GC_MALLOC (static_data_size [0]);
+ thread->static_data [0] = thread->static_data;
+ }
+ for (i = 1; i < idx; ++i) {
+ if (thread->static_data [i])
+ continue;
+ thread->static_data [i] = GC_MALLOC (static_data_size [i]);
+ }
+
+}
+
+/*
+ * 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;
+
+ EnterCriticalSection (&threads_mutex);
+ if (static_data_offset || static_data_idx > 0) {
+ /* get the current allocated size */
+ offset = static_data_offset | ((static_data_idx + 1) << 24);
+ thread_alloc_static_data (thread, offset);
+ }
+ LeaveCriticalSection (&threads_mutex);
+}
+
+static void
+alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
+{
+ MonoThread *thread = value;
+ guint32 offset = GPOINTER_TO_UINT (user);
+
+ thread_alloc_static_data (thread, offset);
+}
+
+/*
+ * The offset for a thread static variable is composed of two parts:
+ * 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_threads_alloc_static_data (guint32 size, guint32 align)
+{
+ guint32 offset;
+
+ EnterCriticalSection (&threads_mutex);
+
+ 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;
+
+ mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
+
+ LeaveCriticalSection (&threads_mutex);
+ return offset;
+}
+
+gpointer
+mono_threads_get_static_data (guint32 offset)
+{
+ MonoThread *thread = mono_thread_current ();
+ int idx = offset >> 24;
+
+ return ((char*) thread->static_data [idx - 1]) + (offset & 0xffffff);
+}
+
+#ifdef WITH_INCLUDED_LIBGC
+
+static void gc_stop_world (gpointer key, gpointer value, gpointer user)
+{
+ MonoThread *thread=(MonoThread *)value;
+ guint32 self=GPOINTER_TO_UINT (user);
+
+#ifdef LIBGC_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
+#endif
+
+ if(thread->tid==self)
+ return;
+
+ SuspendThread (thread->handle);
+}
+
+void mono_gc_stop_world (void)
+{
+ guint32 self=GetCurrentThreadId ();
+
+#ifdef LIBGC_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
+#endif
+
+ EnterCriticalSection (&threads_mutex);
+
+ if (threads != NULL)
+ mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self));
+
+ LeaveCriticalSection (&threads_mutex);
+}
+
+static void gc_start_world (gpointer key, gpointer value, gpointer user)
+{
+ MonoThread *thread=(MonoThread *)value;
+ guint32 self=GPOINTER_TO_UINT (user);
+
+#ifdef LIBGC_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
+#endif
+
+ if(thread->tid==self)
+ return;
+
+ ResumeThread (thread->handle);
+}
+
+void mono_gc_start_world (void)
+{
+ guint32 self=GetCurrentThreadId ();
+
+#ifdef LIBGC_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
+#endif
+
+ EnterCriticalSection (&threads_mutex);
+
+ if (threads != NULL)
+ mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self));
+
+ LeaveCriticalSection (&threads_mutex);
+}
+
+static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user)
+{
+ MonoThread *thread=(MonoThread *)value;
+ guint32 *selfp=(guint32 *)user, self = *selfp;
+
+#ifdef LIBGC_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d - %p", self, thread->tid, thread->stack_ptr);
+#endif
+
+ if(thread->tid==self) {
+#ifdef LIBGC_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr);
+#endif
+ GC_push_all_stack (selfp, thread->stack_ptr);
+ return;
+ }
+
+#ifdef PLATFORM_WIN32
+ GC_win32_push_thread_stack (thread->handle, thread->stack_ptr);
+#else
+ mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr);
+#endif
+}
+
+void mono_gc_push_all_stacks (void)
+{
+ guint32 self=GetCurrentThreadId ();
+
+#ifdef LIBGC_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
+#endif
+
+ EnterCriticalSection (&threads_mutex);
+
+ if (threads != NULL)
+ mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self);
+
+ LeaveCriticalSection (&threads_mutex);
+}
+
+#endif /* WITH_INCLUDED_LIBGC */