Tue May 13 16:41:49 CEST 2003 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / metadata / threads.c
index c2977ec8e31d87a03ad59c4bb8c6f1fb9d9983e2..faa768f34e046894af69b80f5c4d9952c0444252 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Author:
  *     Dick Porter (dick@ximian.com)
+ *     Paolo Molaro (lupus@ximian.com)
  *     Patrik Torstensson (patrik.torstensson@labs2.com)
  *
  * (C) 2001 Ximian, Inc.
@@ -68,6 +69,8 @@ static MonoThreadCallbacks *mono_thread_callbacks = NULL;
 /* 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;
 
@@ -162,6 +165,13 @@ static guint32 start_wrapper(void *data)
         * 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
@@ -182,6 +192,8 @@ static guint32 start_wrapper(void *data)
        
        g_free (start_info);
 
+       thread_adjust_static_data (thread);
+
        start_func (this);
 
        /* If the thread calls ExitThread at all, this remaining code
@@ -204,6 +216,9 @@ void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
        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)
@@ -283,6 +298,8 @@ mono_thread_attach (MonoDomain *domain)
        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);
        }
@@ -1023,3 +1040,216 @@ void mono_thread_abort_all_other_threads (void)
        
        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 */