Do TLS using pthreads if __thread keyword not supported.
[mono.git] / mono / metadata / object.c
index a363fd7b1eb0b1fcf5e2d0e61e96eb21811215df..33f261b513a938faeb9cb7f9e59b1d85212243de 100644 (file)
@@ -141,6 +141,9 @@ static GHashTable *blocked_thread_hash;
 /* Main thread */
 static MonoThread *main_thread;
 
+/* Functions supplied by the runtime */
+static MonoRuntimeCallbacks callbacks;
+
 /**
  * mono_thread_set_main:
  * @thread: thread to set as the main thread
@@ -473,6 +476,12 @@ static MonoImtThunkBuilder imt_thunk_builder = NULL;
 #error "MONO_IMT_SIZE cannot be larger than 32"
 #endif
 
+void
+mono_install_callbacks (MonoRuntimeCallbacks *cbs)
+{
+       memcpy (&callbacks, cbs, sizeof (*cbs));
+}
+
 void
 mono_install_trampoline (MonoTrampoline func) 
 {
@@ -628,6 +637,10 @@ compute_class_bitmap (MonoClass *class, gsize *bitmap, int size, int offset, int
                        if (field->type->byref)
                                break;
 
+                       if (static_fields && field->offset == -1)
+                               /* special static */
+                               continue;
+
                        pos = field->offset / sizeof (gpointer);
                        pos += offset;
 
@@ -845,7 +858,7 @@ mono_string_alloc (int length)
        return mono_string_new_size (mono_domain_get (), length);
 }
 
-static void
+void
 mono_class_compute_gc_descriptor (MonoClass *class)
 {
        int max_set = 0;
@@ -957,6 +970,22 @@ field_is_special_static (MonoClass *fklass, MonoClassField *field)
        return SPECIAL_STATIC_NONE;
 }
 
+static gpointer imt_trampoline = NULL;
+
+void
+mono_install_imt_trampoline (gpointer tramp_code)
+{
+       imt_trampoline = tramp_code;
+}
+
+static gpointer vtable_trampoline = NULL;
+
+void
+mono_install_vtable_trampoline (gpointer tramp_code)
+{
+       vtable_trampoline = tramp_code;
+}
+
 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
 #define mix(a,b,c) { \
        a -= c;  a ^= rot(c, 4);  c += b; \
@@ -977,13 +1006,17 @@ field_is_special_static (MonoClass *fklass, MonoClassField *field)
 }
 
 guint32
-mono_method_get_imt_slot (MonoMethod *method) {
+mono_method_get_imt_slot (MonoMethod *method)
+{
        MonoMethodSignature *sig;
        int hashes_count;
        guint32 *hashes_start, *hashes;
        guint32 a, b, c;
        int i;
 
+       /* This can be used to stress tests the collision code */
+       //return 0;
+
        /*
         * We do this to simplify generic sharing.  It will hurt
         * performance in cases where a class implements two different
@@ -1056,7 +1089,7 @@ add_imt_builder_entry (MonoImtBuilderEntry **imt_builder, MonoMethod *method, gu
                return;
        }
 
-       entry = malloc (sizeof (MonoImtBuilderEntry));
+       entry = g_malloc0 (sizeof (MonoImtBuilderEntry));
        entry->key = method;
        entry->value.vtable_slot = vtable_slot;
        entry->next = imt_builder [imt_slot];
@@ -1114,6 +1147,7 @@ imt_emit_ir (MonoImtBuilderEntry **sorted_array, int start, int end, GPtrArray *
                        MonoIMTCheckItem *item = g_new0 (MonoIMTCheckItem, 1);
                        item->key = sorted_array [i]->key;
                        item->value = sorted_array [i]->value;
+                       item->has_target_code = sorted_array [i]->has_target_code;
                        item->is_equals = TRUE;
                        if (i < end - 1)
                                item->check_target_idx = out_array->len + 1;
@@ -1158,9 +1192,10 @@ imt_sort_slot_entries (MonoImtBuilderEntry *entries) {
 }
 
 static gpointer
-initialize_imt_slot (MonoVTable *vtable, MonoDomain *domain, MonoImtBuilderEntry *imt_builder_entry) {
+initialize_imt_slot (MonoVTable *vtable, MonoDomain *domain, MonoImtBuilderEntry *imt_builder_entry, gpointer fail_tramp)
+{
        if (imt_builder_entry != NULL) {
-               if (imt_builder_entry->children == 0) {
+               if (imt_builder_entry->children == 0 && !fail_tramp) {
                        /* No collision, return the vtable slot contents */
                        return vtable->vtable [imt_builder_entry->value.vtable_slot];
                } else {
@@ -1169,30 +1204,38 @@ initialize_imt_slot (MonoVTable *vtable, MonoDomain *domain, MonoImtBuilderEntry
                        gpointer result;
                        int i;
                        result = imt_thunk_builder (vtable, domain,
-                               (MonoIMTCheckItem**)imt_ir->pdata, imt_ir->len, NULL);
+                               (MonoIMTCheckItem**)imt_ir->pdata, imt_ir->len, fail_tramp);
                        for (i = 0; i < imt_ir->len; ++i)
                                g_free (g_ptr_array_index (imt_ir, i));
                        g_ptr_array_free (imt_ir, TRUE);
                        return result;
                }
        } else {
-               /* Empty slot */
-               return NULL;
+               if (fail_tramp)
+                       return fail_tramp;
+               else
+                       /* Empty slot */
+                       return NULL;
        }
 }
 
+static MonoImtBuilderEntry*
+get_generic_virtual_entries (MonoDomain *domain, gpointer *vtable_slot);
+
 /*
  * LOCKING: requires the loader and domain locks.
  *
 */
 static void
-build_imt_slots (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer* imt, GSList *extra_interfaces, int slot_num) {
+build_imt_slots (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer* imt, GSList *extra_interfaces, int slot_num)
+{
        int i;
        GSList *list_item;
        guint32 imt_collisions_bitmap = 0;
        MonoImtBuilderEntry **imt_builder = calloc (MONO_IMT_SIZE, sizeof (MonoImtBuilderEntry*));
        int method_count = 0;
        gboolean record_method_count_for_max_collisions = FALSE;
+       gboolean has_generic_virtual = FALSE;
 
 #if DEBUG_IMT
        printf ("Building IMT for class %s.%s\n", klass->name_space, klass->name);
@@ -1216,6 +1259,10 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer*
                                        continue;
                        }
                        method = mono_class_get_method_by_index (iface, method_slot_in_interface);
+                       if (method->is_generic) {
+                               has_generic_virtual = TRUE;
+                               continue;
+                       }
                        add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, interface_offset + method_slot_in_interface, slot_num);
                }
        }
@@ -1234,10 +1281,39 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer*
        }
        for (i = 0; i < MONO_IMT_SIZE; ++i) {
                /* overwrite the imt slot only if we're building all the entries or if 
-                * we're uilding this specific one
+                * we're building this specific one
                 */
-               if (slot_num < 0 || i == slot_num)
-                       imt [i] = initialize_imt_slot (vt, domain, imt_builder [i]);
+               if (slot_num < 0 || i == slot_num) {
+                       MonoImtBuilderEntry *entries = get_generic_virtual_entries (domain, &imt [i]);
+
+                       if (entries) {
+                               if (imt_builder [i]) {
+                                       MonoImtBuilderEntry *entry;
+
+                                       /* Link entries with imt_builder [i] */
+                                       for (entry = entries; entry->next; entry = entry->next)
+                                               ;                                               
+                                       entry->next = imt_builder [i];
+                                       entries->children += imt_builder [i]->children + 1;
+                               }
+                               imt_builder [i] = entries;
+                       }
+
+                       if (has_generic_virtual) {
+                               /*
+                                * There might be collisions later when the the thunk is expanded.
+                                */
+                               imt_collisions_bitmap |= (1 << i);
+
+                               /* 
+                                * The IMT thunk might be called with an instance of one of the 
+                                * generic virtual methods, so has to fallback to the IMT trampoline.
+                                */
+                               imt [i] = initialize_imt_slot (vt, domain, imt_builder [i], imt_trampoline);
+                       } else {
+                               imt [i] = initialize_imt_slot (vt, domain, imt_builder [i], NULL);
+                       }
+               }
 #if DEBUG_IMT
                printf ("initialize_imt_slot[%d]: %p\n", i, imt [i]);
 #endif
@@ -1260,7 +1336,7 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer*
                MonoImtBuilderEntry* entry = imt_builder [i];
                while (entry != NULL) {
                        MonoImtBuilderEntry* next = entry->next;
-                       free (entry);
+                       g_free (entry);
                        entry = next;
                }
        }
@@ -1274,22 +1350,6 @@ build_imt (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer* imt,
        build_imt_slots (klass, vt, domain, imt, extra_interfaces, -1);
 }
 
-static gpointer imt_trampoline = NULL;
-
-void
-mono_install_imt_trampoline (gpointer tramp_code)
-{
-       imt_trampoline = tramp_code;
-}
-
-static gpointer vtable_trampoline = NULL;
-
-void
-mono_install_vtable_trampoline (gpointer tramp_code)
-{
-       vtable_trampoline = tramp_code;
-}
-
 /**
  * mono_vtable_build_imt_slot:
  * @vtable: virtual object table struct
@@ -1459,17 +1519,58 @@ invalidate_generic_virtual_thunk (MonoDomain *domain, gpointer code)
 }
 
 typedef struct _GenericVirtualCase {
-       MonoGenericInst *inst;
+       MonoMethod *method;
        gpointer code;
        int count;
        struct _GenericVirtualCase *next;
 } GenericVirtualCase;
 
+/*
+ * get_generic_virtual_entries:
+ *
+ *   Return IMT entries for the generic virtual method instances for vtable slot
+ * VTABLE_SLOT.
+ */ 
+static MonoImtBuilderEntry*
+get_generic_virtual_entries (MonoDomain *domain, gpointer *vtable_slot)
+{
+       GenericVirtualCase *list;
+       MonoImtBuilderEntry *entries;
+  
+       mono_domain_lock (domain);
+       if (!domain->generic_virtual_cases)
+               domain->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL);
+       list = g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
+       entries = NULL;
+       for (; list; list = list->next) {
+               MonoImtBuilderEntry *entry;
+               if (list->count < THUNK_THRESHOLD)
+                       continue;
+               entry = g_new0 (MonoImtBuilderEntry, 1);
+               entry->key = list->method;
+               entry->value.target_code = mono_get_addr_from_ftnptr (list->code);
+               entry->has_target_code = 1;
+               if (entries)
+                       entry->children = entries->children + 1;
+               entry->next = entries;
+               entries = entry;
+       }
+       mono_domain_unlock (domain);
+       /* FIXME: Leaking memory ? */
+       return entries;
+}
+
 /**
  * mono_method_add_generic_virtual_invocation:
  * @domain: a domain
  * @vtable_slot: pointer to the vtable slot
- * @method_inst: the method's method_inst
+ * @method: the inflated generic virtual method
  * @code: the method's code
  *
  * Registers a call via unmanaged code to a generic virtual method
@@ -1478,8 +1579,9 @@ typedef struct _GenericVirtualCase {
  * virtual method thunk.
  */
 void
-mono_method_add_generic_virtual_invocation (MonoDomain *domain, gpointer *vtable_slot,
-       MonoGenericInst *method_inst, gpointer code)
+mono_method_add_generic_virtual_invocation (MonoDomain *domain, MonoVTable *vtable,
+                                                                                       gpointer *vtable_slot,
+                                                                                       MonoMethod *method, gpointer code)
 {
        static gboolean inited = FALSE;
        static int num_added = 0;
@@ -1494,9 +1596,10 @@ mono_method_add_generic_virtual_invocation (MonoDomain *domain, gpointer *vtable
                domain->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL);
 
        /* Check whether the case was already added */
-       gvc = g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
+       list = g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
+       gvc = list;
        while (gvc) {
-               if (gvc->inst == method_inst)
+               if (gvc->method == method)
                        break;
                gvc = gvc->next;
        }
@@ -1504,7 +1607,7 @@ mono_method_add_generic_virtual_invocation (MonoDomain *domain, gpointer *vtable
        /* If not found, make a new one */
        if (!gvc) {
                gvc = mono_domain_alloc (domain, sizeof (GenericVirtualCase));
-               gvc->inst = method_inst;
+               gvc->method = method;
                gvc->code = code;
                gvc->count = 0;
                gvc->next = g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
@@ -1518,46 +1621,36 @@ mono_method_add_generic_virtual_invocation (MonoDomain *domain, gpointer *vtable
                num_added++;
        }
 
-       if (++gvc->count < THUNK_THRESHOLD) {
-               mono_domain_unlock (domain);
-               return;
-       }
-
-       entries = NULL;
-       for (list = gvc; list; list = list->next) {
-               MonoImtBuilderEntry *entry;
-
-               if (list->count < THUNK_THRESHOLD)
-                       continue;
+       if (++gvc->count == THUNK_THRESHOLD) {
+               gpointer *old_thunk = *vtable_slot;
 
-               entry = g_new0 (MonoImtBuilderEntry, 1);
-               entry->key = list->inst;
-               entry->value.target_code = mono_get_addr_from_ftnptr (list->code);
-               if (entries)
-                       entry->children = entries->children + 1;
-               entry->next = entries;
-               entries = entry;
-       }
+               if ((gpointer)vtable_slot < (gpointer)vtable)
+                       /* Force the rebuild of the thunk at the next call */
+                       *vtable_slot = imt_trampoline;
+               else {
+                       entries = get_generic_virtual_entries (domain, vtable_slot);
 
-       sorted = imt_sort_slot_entries (entries);
+                       sorted = imt_sort_slot_entries (entries);
 
-       if (*vtable_slot != vtable_trampoline)
-               invalidate_generic_virtual_thunk (domain, *vtable_slot);
+                       *vtable_slot = imt_thunk_builder (NULL, domain, (MonoIMTCheckItem**)sorted->pdata, sorted->len,
+                                                                                         vtable_trampoline);
 
-       *vtable_slot = imt_thunk_builder (NULL, domain, (MonoIMTCheckItem**)sorted->pdata, sorted->len,
-               vtable_trampoline);
+                       while (entries) {
+                               MonoImtBuilderEntry *next = entries->next;
+                               g_free (entries);
+                               entries = next;
+                       }
 
-       mono_domain_unlock (domain);
+                       for (i = 0; i < sorted->len; ++i)
+                               g_free (g_ptr_array_index (sorted, i));
+                       g_ptr_array_free (sorted, TRUE);
+               }
 
-       while (entries) {
-               MonoImtBuilderEntry *next = entries->next;
-               g_free (entries);
-               entries = next;
+               if (old_thunk != vtable_trampoline && old_thunk != imt_trampoline)
+                       invalidate_generic_virtual_thunk (domain, old_thunk);
        }
 
-       for (i = 0; i < sorted->len; ++i)
-               g_free (g_ptr_array_index (sorted, i));
-       g_ptr_array_free (sorted, TRUE);
+       mono_domain_unlock (domain);
 }
 
 static MonoVTable *mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *class);
@@ -1652,6 +1745,7 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *class)
 
        if (class->exception_type) {
                mono_domain_unlock (domain);
+               mono_loader_unlock ();
                return NULL;
        }
 
@@ -1709,7 +1803,7 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *class)
 
                        bitmap = compute_class_bitmap (class, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, TRUE);
                        /*g_print ("bitmap 0x%x for %s.%s (size: %d)\n", bitmap [0], class->name_space, class->name, class_size);*/
-                       statics_gc_descr = mono_gc_make_descr_from_bitmap (bitmap, max_set? max_set + 1: 0);
+                       statics_gc_descr = mono_gc_make_descr_from_bitmap (bitmap, max_set + 1);
                        vt->data = mono_gc_alloc_fixed (class_size, statics_gc_descr);
                        mono_domain_add_class_static_data (domain, class, vt->data, NULL);
                        if (bitmap != default_bitmap)
@@ -1737,6 +1831,11 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *class)
                                if (!domain->special_static_fields)
                                        domain->special_static_fields = g_hash_table_new (NULL, NULL);
                                g_hash_table_insert (domain->special_static_fields, field, GUINT_TO_POINTER (offset));
+                               /* 
+                                * This marks the field as special static to speed up the
+                                * checks in mono_field_static_get/set_value ().
+                                */
+                               field->offset = -1;
                                continue;
                        }
                }
@@ -1826,13 +1925,8 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *class)
                for (i = 0; i < class->vtable_size; ++i) {
                        MonoMethod *cm;
 
-                       if ((cm = class->vtable [i])) {
-                               if (mono_method_signature (cm)->generic_param_count)
-                                       /* FIXME: Why is this needed ? */
-                                       vt->vtable [i] = cm;
-                               else
-                                       vt->vtable [i] = vtable_trampoline? vtable_trampoline: arch_create_jit_trampoline (cm);
-                       }
+                       if ((cm = class->vtable [i]))
+                               vt->vtable [i] = vtable_trampoline? vtable_trampoline: arch_create_jit_trampoline (cm);
                }
        }
 
@@ -2625,8 +2719,14 @@ mono_field_static_set_value (MonoVTable *vt, MonoClassField *field, void *value)
        g_return_if_fail (field->type->attrs & FIELD_ATTRIBUTE_STATIC);
        /* you cant set a constant! */
        g_return_if_fail (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL));
-       
-       dest = (char*)vt->data + field->offset;
+
+       if (field->offset == -1) {
+               /* Special static */
+               gpointer addr = g_hash_table_lookup (vt->domain->special_static_fields, field);
+               dest = mono_get_special_static_data (GPOINTER_TO_UINT (addr));
+       } else {
+               dest = (char*)vt->data + field->offset;
+       }
        set_value (field->type, dest, value, FALSE);
 }
 
@@ -2831,7 +2931,13 @@ mono_field_static_get_value (MonoVTable *vt, MonoClassField *field, void *value)
                return;
        }
 
-       src = (char*)vt->data + field->offset;
+       if (field->offset == -1) {
+               /* Special static */
+               gpointer addr = g_hash_table_lookup (vt->domain->special_static_fields, field);
+               src = mono_get_special_static_data (GPOINTER_TO_UINT (addr));
+       } else {
+               src = (char*)vt->data + field->offset;
+       }
        set_value (field->type, value, src, TRUE);
 }
 
@@ -4845,8 +4951,11 @@ mono_raise_exception (MonoException *ex)
         * will point into the next function in the executable, not this one.
         */
 
-       if (((MonoObject*)ex)->vtable->klass == mono_defaults.threadabortexception_class)
-               MONO_OBJECT_SETREF (mono_thread_current (), abort_exc, ex);
+       if (((MonoObject*)ex)->vtable->klass == mono_defaults.threadabortexception_class) {
+               MonoThread *thread = mono_thread_current ();
+               g_assert (ex->object.vtable->domain == mono_domain_get ());
+               MONO_OBJECT_SETREF (thread, abort_exc, ex);
+       }
        
        ex_handler (ex);
 }
@@ -4899,6 +5008,26 @@ mono_wait_handle_get_handle (MonoWaitHandle *handle)
        }
 }
 
+
+static MonoObject*
+mono_runtime_capture_context (MonoDomain *domain)
+{
+       RuntimeInvokeFunction runtime_invoke;
+
+       if (!domain->capture_context_runtime_invoke || !domain->capture_context_method) {
+               MonoMethod *method = mono_get_context_capture_method ();
+               MonoMethod *wrapper;
+               if (!method)
+                       return NULL;
+               wrapper = mono_marshal_get_runtime_invoke (method, FALSE);
+               domain->capture_context_runtime_invoke = mono_compile_method (wrapper);
+               domain->capture_context_method = mono_compile_method (method);
+       }
+
+       runtime_invoke = domain->capture_context_runtime_invoke;
+
+       return runtime_invoke (NULL, NULL, NULL, domain->capture_context_method);
+}
 /**
  * mono_async_result_new:
  * @domain:domain where the object will be created.
@@ -4914,11 +5043,10 @@ MonoAsyncResult *
 mono_async_result_new (MonoDomain *domain, HANDLE handle, MonoObject *state, gpointer data, MonoObject *object_data)
 {
        MonoAsyncResult *res = (MonoAsyncResult *)mono_object_new (domain, mono_defaults.asyncresult_class);
-       MonoMethod *method = mono_get_context_capture_method ();
-
+       MonoObject *context = mono_runtime_capture_context (domain);
        /* we must capture the execution context from the original thread */
-       if (method) {
-               MONO_OBJECT_SETREF (res, execution_context, mono_runtime_invoke (method, NULL, NULL, NULL));
+       if (context) {
+               MONO_OBJECT_SETREF (res, execution_context, context);
                /* note: result may be null if the flow is suppressed */
        }
 
@@ -5585,49 +5713,24 @@ mono_store_remote_field_new (MonoObject *this, MonoClass *klass, MonoClassField
  * mono_create_ftnptr:
  *
  *   Given a function address, create a function descriptor for it.
- * This is only needed on IA64 and PPC64.
+ * This is only needed on some platforms.
  */
 gpointer
 mono_create_ftnptr (MonoDomain *domain, gpointer addr)
 {
-#ifdef __ia64__
-       gpointer *desc;
-
-       desc = mono_domain_code_reserve (domain, 2 * sizeof (gpointer));
-
-       desc [0] = addr;
-       desc [1] = NULL;
-
-       return desc;
-#elif defined(__ppc64__) || defined(__powerpc64__)
-       gpointer *desc;
-
-       desc = mono_domain_code_reserve (domain, 3 * sizeof (gpointer));
-
-       desc [0] = addr;
-       desc [1] = NULL;
-       desc [2] = NULL;
-
-       return desc;
-#else
-       return addr;
-#endif
+       return callbacks.create_ftnptr (domain, addr);
 }
 
 /*
  * mono_get_addr_from_ftnptr:
  *
  *   Given a pointer to a function descriptor, return the function address.
- * This is only needed on IA64 and PPC64.
+ * This is only needed on some platforms.
  */
 gpointer
 mono_get_addr_from_ftnptr (gpointer descr)
 {
-#if defined(__ia64__) || defined(__ppc64__) || defined(__powerpc64__)
-       return *(gpointer*)descr;
-#else
-       return descr;
-#endif
+       return callbacks.get_addr_from_ftnptr (descr);
 }      
 
 #if 0