[runtime] Unify thread/context static field scanning and management.
authorAlex Rønne Petersen <alexrp@xamarin.com>
Wed, 29 Apr 2015 07:00:17 +0000 (09:00 +0200)
committerAlex Rønne Petersen <alexrp@xamarin.com>
Tue, 5 May 2015 23:30:51 +0000 (01:30 +0200)
Context static data is now scanned precisely in the same way as thread static
data. To facilitate this, the memory management of context static data has been
fixed to work similarly to thread static data. This further required that all
contexts be available for iteration, so contexts are now added to a global hash
table (by GC handle) when they are created.

mcs/class/corlib/System.Runtime.Remoting.Contexts/Context.cs
mono/metadata/appdomain.c
mono/metadata/icall-def.h
mono/metadata/threads-types.h
mono/metadata/threads.c

index c79e9a027aea4bca9102374efe497b0b4132c3f7..08a6316255f515cf465818c90c565267b220a2ba 100644 (file)
@@ -40,6 +40,7 @@ using System.Runtime.Remoting.Proxies;
 using System.Runtime.Remoting.Activation;
 using System.Runtime.Remoting.Messaging;
 using System.Runtime.Remoting.Lifetime;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
 
@@ -80,11 +81,16 @@ namespace System.Runtime.Remoting.Contexts {
                static DynamicPropertyCollection global_dynamic_properties;
                DynamicPropertyCollection context_dynamic_properties;
                ContextCallbackObject callback_object = null;
+
+               [MethodImpl (MethodImplOptions.InternalCall)]
+               extern static void RegisterContext (Context ctx);
                
                public Context ()
                {
                        domain_id = Thread.GetDomainID();
                        context_id = Interlocked.Increment (ref global_count);
+
+                       RegisterContext (this);
                }
 
                ~Context ()
index 93339cfa317a7e69c11bddccffb7449045e4f856..5fa6706c1907ac9cd3961e02cc4541bf8dbf2250 100644 (file)
@@ -340,6 +340,7 @@ mono_context_init (MonoDomain *domain)
        context = (MonoAppContext *) mono_object_new_pinned (domain, class);
        context->domain_id = domain->domain_id;
        context->context_id = 0;
+       ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (context);
        domain->default_context = context;
 }
 
index f3e35ac68094f1d079f3f6e188b9be4330f681f7..3cf75cbf7ef6634b774be9e71e105cbae8cfa916 100644 (file)
@@ -701,6 +701,9 @@ ICALL_TYPE(ACTS, "System.Runtime.Remoting.Activation.ActivationServices", ACTS_1
 ICALL(ACTS_1, "AllocateUninitializedClassInstance", ves_icall_System_Runtime_Activation_ActivationServices_AllocateUninitializedClassInstance)
 ICALL(ACTS_2, "EnableProxyActivation", ves_icall_System_Runtime_Activation_ActivationServices_EnableProxyActivation)
 
+ICALL_TYPE(CONTEXT, "System.Runtime.Remoting.Contexts.Context", CONTEXT_1)
+ICALL(CONTEXT_1, "RegisterContext", ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext)
+
 ICALL_TYPE(ARES, "System.Runtime.Remoting.Messaging.AsyncResult", ARES_1)
 ICALL(ARES_1, "Invoke", ves_icall_System_Runtime_Remoting_Messaging_AsyncResult_Invoke)
 
index 064a0335a774a50b2d4da33f35c1510b2307298f..ae7b15885611e205e9d83f8c66353afb24b38557 100644 (file)
@@ -184,6 +184,8 @@ void ves_icall_System_Threading_Thread_MemoryBarrier (void);
 void ves_icall_System_Threading_Thread_Interrupt_internal (MonoInternalThread *this_obj);
 void ves_icall_System_Threading_Thread_SpinWait_nop (void);
 
+void ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContext *ctx);
+
 MonoInternalThread *mono_thread_internal_current (void);
 
 void mono_thread_internal_stop (MonoInternalThread *thread);
@@ -193,7 +195,6 @@ gboolean mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, Mon
 void mono_thread_internal_reset_abort (MonoInternalThread *thread);
 
 void mono_alloc_special_static_data_free (GHashTable *special_static_fields);
-void mono_special_static_data_free_slot (guint32 offset, guint32 size);
 uint32_t mono_thread_alloc_tls   (MonoReflectionType *type);
 void     mono_thread_destroy_tls (uint32_t tls_offset);
 void     mono_thread_destroy_domain_tls (MonoDomain *domain);
index c52256d654aaf3a8855a03eb29cbe591f88fcf36..e6ccd444562fdd9e66b916bdd7cb091d2452109f 100644 (file)
@@ -105,9 +105,9 @@ typedef union {
        gdouble fval;
 } LongDoubleUnion;
  
-typedef struct _MonoThreadDomainTls MonoThreadDomainTls;
-struct _MonoThreadDomainTls {
-       MonoThreadDomainTls *next;
+typedef struct _StaticDataFreeList StaticDataFreeList;
+struct _StaticDataFreeList {
+       StaticDataFreeList *next;
        guint32 offset;
        guint32 size;
 };
@@ -115,7 +115,7 @@ struct _MonoThreadDomainTls {
 typedef struct {
        int idx;
        int offset;
-       MonoThreadDomainTls *freelist;
+       StaticDataFreeList *freelist;
 } StaticDataInfo;
 
 /* Number of cached culture objects in the MonoThread->cached_culture_info array
@@ -131,11 +131,6 @@ static void mono_threads_lock (void);
 static void mono_threads_unlock (void);
 static mono_mutex_t threads_mutex;
 
-/* Controls access to context static data */
-#define mono_contexts_lock() mono_mutex_lock (&contexts_mutex)
-#define mono_contexts_unlock() mono_mutex_unlock (&contexts_mutex)
-static mono_mutex_t contexts_mutex;
-
 /* Controls access to the 'joinable_threads' hash table */
 #define joinable_threads_lock() mono_mutex_lock (&joinable_threads_mutex)
 #define joinable_threads_unlock() mono_mutex_unlock (&joinable_threads_mutex)
@@ -150,6 +145,11 @@ static StaticDataInfo context_static_info;
  */
 static MonoGHashTable *threads=NULL;
 
+/* List of app context GC handles.
+ * Added to from ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext ().
+ */
+static GHashTable *contexts = 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.
@@ -201,7 +201,8 @@ static guint32 default_stacksize = 0;
 #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 context_adjust_static_data (MonoAppContext *ctx);
+static void mono_free_static_data (gpointer* static_data);
 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);
@@ -474,7 +475,7 @@ static void thread_cleanup (MonoInternalThread *thread)
 
        thread->cached_culture_info = NULL;
 
-       mono_free_static_data (thread->static_data, TRUE);
+       mono_free_static_data (thread->static_data);
        thread->static_data = NULL;
        ref_stack_destroy (thread->appdomain_refs);
        thread->appdomain_refs = NULL;
@@ -498,6 +499,17 @@ get_thread_static_data (MonoInternalThread *thread, guint32 offset)
        return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
 }
 
+static gpointer
+get_context_static_data (MonoAppContext *ctx, guint32 offset)
+{
+       g_assert ((offset & 0x80000000) != 0);
+
+       offset &= 0x7fffffff;
+       int idx = (offset >> 24) - 1;
+
+       return ((char *) ctx->static_data [idx]) + (offset & 0xffffff);
+}
+
 static MonoThread**
 get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread)
 {
@@ -2527,6 +2539,23 @@ ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value)
        mono_gc_wbarrier_generic_store_atomic (ptr, value);
 }
 
+void
+ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContext *ctx)
+{
+       mono_threads_lock ();
+
+       //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
+
+       if (!contexts)
+               contexts = g_hash_table_new (NULL, NULL);
+
+       context_adjust_static_data (ctx);
+       gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref (&ctx->obj, FALSE));
+       g_hash_table_insert (contexts, gch, gch);
+
+       mono_threads_unlock ();
+}
+
 void
 mono_thread_init_tls (void)
 {
@@ -2539,7 +2568,6 @@ void mono_thread_init (MonoThreadStartCB start_cb,
 {
        mono_mutex_init_recursive(&threads_mutex);
        mono_mutex_init_recursive(&interlocked_mutex);
-       mono_mutex_init_recursive(&contexts_mutex);
        mono_mutex_init_recursive(&joinable_threads_mutex);
        
        background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL);
@@ -2583,7 +2611,6 @@ void mono_thread_cleanup (void)
         */
        mono_mutex_destroy (&threads_mutex);
        mono_mutex_destroy (&interlocked_mutex);
-       mono_mutex_destroy (&contexts_mutex);
        mono_mutex_destroy (&delayed_free_table_mutex);
        mono_mutex_destroy (&small_id_mutex);
        CloseHandle (background_change_event);
@@ -3488,10 +3515,11 @@ static const int static_data_size [NUM_STATIC_DATA_IDX] = {
 };
 #endif
 
-static MonoBitSet* static_reference_bitmaps [NUM_STATIC_DATA_IDX];
+static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX];
+static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX];
 
 static void
-mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
+mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data)
 {
        gpointer *static_data = addr;
 
@@ -3501,7 +3529,7 @@ mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
                if (!ptr)
                        continue;
 
-               MONO_BITSET_FOREACH (static_reference_bitmaps [i], idx, {
+               MONO_BITSET_FOREACH (bitmaps [i], idx, {
                        void **p = ptr + idx;
 
                        if (*p)
@@ -3510,6 +3538,18 @@ mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
        }
 }
 
+static void
+mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
+{
+       mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data);
+}
+
+static void
+mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
+{
+       mark_slots (addr, context_reference_bitmaps, mark_func, gc_data);
+}
+
 /*
  *  mono_alloc_static_data
  *
@@ -3523,10 +3563,18 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean thr
 
        gpointer* static_data = *static_data_ptr;
        if (!static_data) {
-               static void* tls_desc = NULL;
-               if (mono_gc_user_markers_supported () && !tls_desc)
-                       tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
-               static_data = mono_gc_alloc_fixed (static_data_size [0], threadlocal?tls_desc:NULL);
+               static void *tls_desc = NULL;
+               static void *ctx_desc = NULL;
+
+               if (mono_gc_user_markers_supported ()) {
+                       if (!tls_desc)
+                               tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
+
+                       if (!ctx_desc)
+                               ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
+               }
+
+               static_data = mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc);
                *static_data_ptr = static_data;
                static_data [0] = static_data;
        }
@@ -3534,7 +3582,8 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean thr
        for (i = 1; i <= idx; ++i) {
                if (static_data [i])
                        continue;
-               if (mono_gc_user_markers_supported () && threadlocal)
+
+               if (mono_gc_user_markers_supported ())
                        static_data [i] = g_malloc0 (static_data_size [i]);
                else
                        static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL);
@@ -3542,7 +3591,7 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean thr
 }
 
 static void 
-mono_free_static_data (gpointer* static_data, gboolean threadlocal)
+mono_free_static_data (gpointer* static_data)
 {
        int i;
        for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
@@ -3553,12 +3602,12 @@ mono_free_static_data (gpointer* static_data, gboolean threadlocal)
                 * At this point, the static data pointer array is still registered with the
                 * GC, so must ensure that mark_tls_slots() will not encounter any invalid
                 * data.  Freeing the individual arrays without first nulling their slots
-                * would make it possible for mark_tls_slots() to encounter a pointer to
+                * would make it possible for mark_tls/ctx_slots() to encounter a pointer to
                 * such an already freed array.  See bug #13813.
                 */
                static_data [i] = NULL;
                mono_memory_write_barrier ();
-               if (mono_gc_user_markers_supported () && threadlocal)
+               if (mono_gc_user_markers_supported ())
                        g_free (p);
                else
                        mono_gc_free_fixed (p);
@@ -3627,6 +3676,24 @@ thread_adjust_static_data (MonoInternalThread *thread)
        mono_threads_unlock ();
 }
 
+static void
+context_adjust_static_data (MonoAppContext *ctx)
+{
+       guint32 offset;
+
+       mono_threads_lock ();
+
+       if (context_static_info.offset || context_static_info.idx > 0) {
+               offset = context_static_info.offset | ((context_static_info.idx + 1) << 24);
+               mono_alloc_static_data (&ctx->static_data, offset, FALSE);
+       }
+
+       mono_threads_unlock ();
+}
+
+/*
+ * LOCKING: requires that threads_mutex is held
+ */
 static void 
 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
 {
@@ -3636,11 +3703,30 @@ alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
        mono_alloc_static_data (&(thread->static_data), offset, TRUE);
 }
 
-static MonoThreadDomainTls*
-search_tls_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
+/*
+ * LOCKING: requires that threads_mutex is held
+ */
+static void
+alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
 {
-       MonoThreadDomainTls* prev = NULL;
-       MonoThreadDomainTls* tmp = static_data->freelist;
+       uint32_t gch = GPOINTER_TO_INT (key);
+       MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (gch);
+
+       if (!ctx) {
+               g_hash_table_remove (contexts, key);
+               mono_gchandle_free (gch);
+               return;
+       }
+
+       guint32 offset = GPOINTER_TO_UINT (user);
+       mono_alloc_static_data (&ctx->static_data, offset, FALSE);
+}
+
+static StaticDataFreeList*
+search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
+{
+       StaticDataFreeList* prev = NULL;
+       StaticDataFreeList* tmp = static_data->freelist;
        while (tmp) {
                if (tmp->size == size) {
                        if (prev)
@@ -3662,34 +3748,31 @@ search_tls_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32
 #endif
 
 static void
-update_tls_reference_bitmap (guint32 offset, uintptr_t *bitmap, int numbits)
+update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits)
 {
-       int i;
        int idx = (offset >> 24) - 1;
-       if (!static_reference_bitmaps [idx])
-               static_reference_bitmaps [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
-       MonoBitSet *rb = static_reference_bitmaps [idx];
+       if (!sets [idx])
+               sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
+       MonoBitSet *rb = sets [idx];
        offset &= 0xffffff;
-       offset /= sizeof (gpointer);
+       offset /= sizeof (uintptr_t);
        /* offset is now the bitmap offset */
-       for (i = 0; i < numbits; ++i) {
+       for (int i = 0; i < numbits; ++i) {
                if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
                        mono_bitset_set_fast (rb, offset + i);
        }
 }
 
 static void
-clear_reference_bitmap (guint32 offset, guint32 size)
+clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size)
 {
        int idx = (offset >> 24) - 1;
-       MonoBitSet *rb = static_reference_bitmaps [idx];
+       MonoBitSet *rb = sets [idx];
        offset &= 0xffffff;
-       offset /= sizeof (gpointer);
-       size /= sizeof (gpointer);
-       size += offset;
+       offset /= sizeof (uintptr_t);
        /* offset is now the bitmap offset */
-       for (; offset < size; ++offset)
-               mono_bitset_clear_fast (rb, offset);
+       for (int i = 0; i < size / sizeof (uintptr_t); i++)
+               mono_bitset_clear_fast (rb, offset + i);
 }
 
 /*
@@ -3703,30 +3786,46 @@ clear_reference_bitmap (guint32 offset, guint32 size)
 guint32
 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits)
 {
+       g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT);
+
+       StaticDataInfo *info;
+       MonoBitSet **sets;
+
+       if (static_type == SPECIAL_STATIC_THREAD) {
+               info = &thread_static_info;
+               sets = thread_reference_bitmaps;
+       } else {
+               info = &context_static_info;
+               sets = context_reference_bitmaps;
+       }
+
+       mono_threads_lock ();
+
+       StaticDataFreeList *item = search_slot_in_freelist (info, size, align);
        guint32 offset;
+
+       if (item) {
+               offset = item->offset;
+               g_free (item);
+       } else {
+               offset = mono_alloc_static_data_slot (info, size, align);
+       }
+
+       update_reference_bitmap (sets, offset, bitmap, numbits);
+
        if (static_type == SPECIAL_STATIC_THREAD) {
-               MonoThreadDomainTls *item;
-               mono_threads_lock ();
-               item = search_tls_slot_in_freelist (&thread_static_info, size, align);
-               /*g_print ("TLS alloc: %d in domain %p (total: %d), cached: %p\n", size, mono_domain_get (), thread_static_info.offset, item);*/
-               if (item) {
-                       offset = item->offset;
-                       g_free (item);
-               } else {
-                       offset = mono_alloc_static_data_slot (&thread_static_info, size, align);
-               }
-               update_tls_reference_bitmap (offset, bitmap, numbits);
                /* 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 {
-               g_assert (static_type == SPECIAL_STATIC_CONTEXT);
-               mono_contexts_lock ();
-               offset = mono_alloc_static_data_slot (&context_static_info, size, align);
-               mono_contexts_unlock ();
-               offset |= 0x80000000;   /* Set the high bit to indicate context static data */
+               if (contexts != NULL)
+                       g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
+
+               offset |= 0x80000000; /* Set the high bit to indicate context static data */
        }
+
+       mono_threads_unlock ();
+
        return offset;
 }
 
@@ -3736,24 +3835,11 @@ mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 off
        /* The high bit means either thread (0) or static (1) data. */
 
        guint32 static_type = (offset & 0x80000000);
-       int idx;
-
-       offset &= 0x7fffffff;
-       idx = (offset >> 24) - 1;
 
        if (static_type == 0) {
                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, FALSE);
-                       mono_contexts_unlock ();
-               }
-               return ((char*) context->static_data [idx]) + (offset & 0xffffff);      
+               return get_context_static_data (thread->current_appcontext, offset);
        }
 }
 
@@ -3766,13 +3852,16 @@ mono_get_special_static_data (guint32 offset)
 typedef struct {
        guint32 offset;
        guint32 size;
-} TlsOffsetSize;
+} OffsetSize;
 
+/*
+ * LOCKING: requires that threads_mutex is held
+ */
 static void 
 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
 {
        MonoInternalThread *thread = value;
-       TlsOffsetSize *data = user;
+       OffsetSize *data = user;
        int idx = (data->offset >> 24) - 1;
        char *ptr;
 
@@ -3782,31 +3871,66 @@ free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
        mono_gc_bzero_atomic (ptr, data->size);
 }
 
+/*
+ * LOCKING: requires that threads_mutex is held
+ */
+static void
+free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
+{
+       uint32_t gch = GPOINTER_TO_INT (key);
+       MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (gch);
+
+       if (!ctx) {
+               g_hash_table_remove (contexts, key);
+               mono_gchandle_free (gch);
+               return;
+       }
+
+       OffsetSize *data = user;
+       int idx = (data->offset >> 24) - 1;
+       char *ptr;
+
+       if (!ctx->static_data || !ctx->static_data [idx])
+               return;
+
+       ptr = ((char*) ctx->static_data [idx]) + (data->offset & 0xffffff);
+       mono_gc_bzero_atomic (ptr, data->size);
+}
+
 static void
 do_free_special_slot (guint32 offset, guint32 size)
 {
        guint32 static_type = (offset & 0x80000000);
-       /*g_print ("free %s , size: %d, offset: %x\n", field->name, size, offset);*/
+       MonoBitSet **sets;
+       StaticDataInfo *info;
+
+       if (static_type == 0) {
+               info = &thread_static_info;
+               sets = thread_reference_bitmaps;
+       } else {
+               info = &context_static_info;
+               sets = context_reference_bitmaps;
+       }
+
+       OffsetSize data = { offset & 0x7fffffff, size };
+       clear_reference_bitmap (sets, data.offset, data.size);
+
        if (static_type == 0) {
-               TlsOffsetSize data;
-               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);
+       } else {
+               if (contexts != NULL)
+                       g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
+       }
+
+       if (!mono_runtime_is_shutting_down ()) {
+               StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1);
+
                item->offset = offset;
                item->size = size;
 
-               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 */
+               item->next = info->freelist;
+               info->freelist = item;
        }
 }
 
@@ -3825,13 +3949,18 @@ void
 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
 {
        mono_threads_lock ();
+
        g_hash_table_foreach (special_static_fields, do_free_special, NULL);
+
        mono_threads_unlock ();
 }
 
-void
+static void
 mono_special_static_data_free_slot (guint32 offset, guint32 size)
 {
+       /* Only ever called for ThreadLocal instances */
+       g_assert ((offset & 0x80000000) == 0);
+
        mono_threads_lock ();
        do_free_special_slot (offset, size);
        mono_threads_unlock ();