#include <mono/metadata/gc-internals.h>
#include <mono/metadata/verify-internals.h>
#include <mono/metadata/reflection-internals.h>
+#include <mono/metadata/w32event.h>
#include <mono/utils/strenc.h>
#include <mono/utils/mono-counters.h>
#include <mono/utils/mono-error-internals.h>
MonoNativeThreadId tid;
int do_initialization = 0;
MonoDomain *last_domain = NULL;
+ MonoException * pending_tae = NULL;
mono_error_init (error);
if (do_initialization) {
MonoException *exc = NULL;
+
+ mono_threads_begin_abort_protected_block ();
mono_runtime_try_invoke (method, NULL, NULL, (MonoObject**) &exc, error);
- if (exc != NULL && mono_error_ok (error)) {
- mono_error_set_exception_instance (error, exc);
- }
+ mono_threads_end_abort_protected_block ();
+
+ //exception extracted, error will be set to the right value later
+ if (exc == NULL && !mono_error_ok (error))//invoking failed but exc was not set
+ exc = mono_error_convert_to_exception (error);
+ else
+ mono_error_cleanup (error);
+
+ mono_error_init (error);
/* If the initialization failed, mark the class as unusable. */
/* Avoid infinite loops */
- if (!(mono_error_ok(error) ||
+ if (!(!exc ||
(klass->image == mono_defaults.corlib &&
!strcmp (klass->name_space, "System") &&
!strcmp (klass->name, "TypeInitializationException")))) {
MonoException *exc_to_throw = mono_get_exception_type_initialization_checked (full_name, exc, error);
g_free (full_name);
- return_val_if_nok (error, FALSE);
- mono_error_set_exception_instance (error, exc_to_throw);
+ mono_error_assert_ok (error); //We can't recover from this, no way to fail a type we can't alloc a failure.
- MonoException *exc_to_store = mono_error_convert_to_exception (error);
- /* What we really want to do here is clone the error object and store one copy in the
- * domain's exception hash and use the other one to error out here. */
- mono_error_init (error);
- mono_error_set_exception_instance (error, exc_to_store);
/*
* Store the exception object so it could be thrown on subsequent
* accesses.
mono_domain_lock (domain);
if (!domain->type_init_exception_hash)
domain->type_init_exception_hash = mono_g_hash_table_new_type (mono_aligned_addr_hash, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, "type initialization exceptions table");
- mono_g_hash_table_insert (domain->type_init_exception_hash, klass, exc_to_store);
+ mono_g_hash_table_insert (domain->type_init_exception_hash, klass, exc_to_throw);
mono_domain_unlock (domain);
}
mono_domain_set (last_domain, TRUE);
lock->done = TRUE;
mono_type_init_unlock (lock);
+ if (exc && mono_object_class (exc) == mono_defaults.threadabortexception_class)
+ pending_tae = exc;
+ //TAEs are blocked around .cctors, they must escape as soon as no cctor is left to run.
+ if (!pending_tae)
+ pending_tae = mono_thread_try_resume_interruption ();
} else {
/* this just blocks until the initializing thread is done */
mono_type_init_lock (lock);
vtable->initialized = 1;
mono_type_initialization_unlock ();
- if (vtable->init_failed) {
+ //TAE wins over TIE
+ if (pending_tae)
+ mono_error_set_exception_instance (error, pending_tae);
+ else if (vtable->init_failed) {
/* Either we were the initializing thread or we waited for the initialization */
mono_error_set_exception_instance (error, get_type_init_exception_for_vtable (vtable));
return FALSE;
}
static MonoDelegateTrampoline arch_create_delegate_trampoline = default_delegate_trampoline;
-static MonoImtThunkBuilder imt_thunk_builder;
-static gboolean always_build_imt_thunks;
+static MonoImtTrampolineBuilder imt_trampoline_builder;
+static gboolean always_build_imt_trampolines;
#if (MONO_IMT_SIZE > 32)
#error "MONO_IMT_SIZE cannot be larger than 32"
}
void
-mono_install_imt_thunk_builder (MonoImtThunkBuilder func) {
- imt_thunk_builder = func;
+mono_install_imt_trampoline_builder (MonoImtTrampolineBuilder func)
+{
+ imt_trampoline_builder = func;
}
void
-mono_set_always_build_imt_thunks (gboolean value)
+mono_set_always_build_imt_trampolines (gboolean value)
{
- always_build_imt_thunks = value;
+ always_build_imt_trampolines = value;
}
/**
break;
}
- free (hashes_start);
+ g_free (hashes_start);
/* Report the result */
return c % MONO_IMT_SIZE;
}
imt_emit_ir (sorted_array, 0, number_of_entries, result);
- free (sorted_array);
+ g_free (sorted_array);
return result;
}
MONO_REQ_GC_NEUTRAL_MODE;
if (imt_builder_entry != NULL) {
- if (imt_builder_entry->children == 0 && !fail_tramp && !always_build_imt_thunks) {
+ if (imt_builder_entry->children == 0 && !fail_tramp && !always_build_imt_trampolines) {
/* No collision, return the vtable slot contents */
return vtable->vtable [imt_builder_entry->value.vtable_slot];
} else {
- /* Collision, build the thunk */
+ /* Collision, build the trampoline */
GPtrArray *imt_ir = imt_sort_slot_entries (imt_builder_entry);
gpointer result;
int i;
- result = imt_thunk_builder (vtable, domain,
+ result = imt_trampoline_builder (vtable, domain,
(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));
if (has_generic_virtual || has_variant_iface) {
/*
- * There might be collisions later when the the thunk is expanded.
+ * There might be collisions later when the the trampoline is expanded.
*/
imt_collisions_bitmap |= (1 << i);
/*
- * The IMT thunk might be called with an instance of one of the
+ * The IMT trampoline 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], callbacks.get_imt_trampoline (vt, i));
entry = next;
}
}
- free (imt_builder);
+ g_free (imt_builder);
/* we OR the bitmap since we may build just a single imt slot at a time */
vt->imt_collisions_bitmap |= imt_collisions_bitmap;
}
* @imt_slot: slot in the IMT table
*
* Fill the given @imt_slot in the IMT table of @vtable with
- * a trampoline or a thunk for the case of collisions.
+ * a trampoline or a trampoline for the case of collisions.
* This is part of the internal mono API.
*
* LOCKING: Take the domain lock.
mono_loader_unlock ();
}
-
-/*
- * The first two free list entries both belong to the wait list: The
- * first entry is the pointer to the head of the list and the second
- * entry points to the last element. That way appending and removing
- * the first element are both O(1) operations.
- */
-#ifdef MONO_SMALL_CONFIG
-#define NUM_FREE_LISTS 6
-#else
-#define NUM_FREE_LISTS 12
-#endif
-#define FIRST_FREE_LIST_SIZE 64
-#define MAX_WAIT_LENGTH 50
#define THUNK_THRESHOLD 10
-/*
- * LOCKING: The domain lock must be held.
- */
-static void
-init_thunk_free_lists (MonoDomain *domain)
-{
- MONO_REQ_GC_NEUTRAL_MODE;
-
- if (domain->thunk_free_lists)
- return;
- domain->thunk_free_lists = (MonoThunkFreeList **)mono_domain_alloc0 (domain, sizeof (gpointer) * NUM_FREE_LISTS);
-}
-
-static int
-list_index_for_size (int item_size)
-{
- int i = 2;
- int size = FIRST_FREE_LIST_SIZE;
-
- while (item_size > size && i < NUM_FREE_LISTS - 1) {
- i++;
- size <<= 1;
- }
-
- return i;
-}
-
/**
- * mono_method_alloc_generic_virtual_thunk:
+ * mono_method_alloc_generic_virtual_trampoline:
* @domain: a domain
* @size: size in bytes
*
* Allocs size bytes to be used for the code of a generic virtual
- * thunk. It's either allocated from the domain's code manager or
+ * trampoline. It's either allocated from the domain's code manager or
* reused from a previously invalidated piece.
*
* LOCKING: The domain lock must be held.
*/
gpointer
-mono_method_alloc_generic_virtual_thunk (MonoDomain *domain, int size)
+mono_method_alloc_generic_virtual_trampoline (MonoDomain *domain, int size)
{
MONO_REQ_GC_NEUTRAL_MODE;
static gboolean inited = FALSE;
- static int generic_virtual_thunks_size = 0;
-
- guint32 *p;
- int i;
- MonoThunkFreeList **l;
+ static int generic_virtual_trampolines_size = 0;
- init_thunk_free_lists (domain);
-
- size += sizeof (guint32);
- if (size < sizeof (MonoThunkFreeList))
- size = sizeof (MonoThunkFreeList);
-
- i = list_index_for_size (size);
- for (l = &domain->thunk_free_lists [i]; *l; l = &(*l)->next) {
- if ((*l)->size >= size) {
- MonoThunkFreeList *item = *l;
- *l = item->next;
- return ((guint32*)item) + 1;
- }
- }
-
- /* no suitable item found - search lists of larger sizes */
- while (++i < NUM_FREE_LISTS) {
- MonoThunkFreeList *item = domain->thunk_free_lists [i];
- if (!item)
- continue;
- g_assert (item->size > size);
- domain->thunk_free_lists [i] = item->next;
- return ((guint32*)item) + 1;
- }
-
- /* still nothing found - allocate it */
if (!inited) {
- mono_counters_register ("Generic virtual thunk bytes",
- MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &generic_virtual_thunks_size);
+ mono_counters_register ("Generic virtual trampoline bytes",
+ MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &generic_virtual_trampolines_size);
inited = TRUE;
}
- generic_virtual_thunks_size += size;
-
- p = (guint32 *)mono_domain_code_reserve (domain, size);
- *p = size;
+ generic_virtual_trampolines_size += size;
- mono_domain_lock (domain);
- if (!domain->generic_virtual_thunks)
- domain->generic_virtual_thunks = g_hash_table_new (NULL, NULL);
- g_hash_table_insert (domain->generic_virtual_thunks, p, p);
- mono_domain_unlock (domain);
-
- return p + 1;
-}
-
-/*
- * LOCKING: The domain lock must be held.
- */
-static void
-invalidate_generic_virtual_thunk (MonoDomain *domain, gpointer code)
-{
- MONO_REQ_GC_NEUTRAL_MODE;
-
- guint32 *p = (guint32 *)code;
- MonoThunkFreeList *l = (MonoThunkFreeList*)(p - 1);
- gboolean found = FALSE;
-
- mono_domain_lock (domain);
- if (!domain->generic_virtual_thunks)
- domain->generic_virtual_thunks = g_hash_table_new (NULL, NULL);
- if (g_hash_table_lookup (domain->generic_virtual_thunks, l))
- found = TRUE;
- mono_domain_unlock (domain);
-
- if (!found)
- /* Not allocated by mono_method_alloc_generic_virtual_thunk (), i.e. AOT */
- return;
- init_thunk_free_lists (domain);
-
- while (domain->thunk_free_lists [0] && domain->thunk_free_lists [0]->length >= MAX_WAIT_LENGTH) {
- MonoThunkFreeList *item = domain->thunk_free_lists [0];
- int length = item->length;
- int i;
-
- /* unlink the first item from the wait list */
- domain->thunk_free_lists [0] = item->next;
- domain->thunk_free_lists [0]->length = length - 1;
-
- i = list_index_for_size (item->size);
-
- /* put it in the free list */
- item->next = domain->thunk_free_lists [i];
- domain->thunk_free_lists [i] = item;
- }
-
- l->next = NULL;
- if (domain->thunk_free_lists [1]) {
- domain->thunk_free_lists [1] = domain->thunk_free_lists [1]->next = l;
- domain->thunk_free_lists [0]->length++;
- } else {
- g_assert (!domain->thunk_free_lists [0]);
-
- domain->thunk_free_lists [0] = domain->thunk_free_lists [1] = l;
- domain->thunk_free_lists [0]->length = 1;
- }
+ return mono_domain_code_reserve (domain, size);
}
typedef struct _GenericVirtualCase {
* Registers a call via unmanaged code to a generic virtual method
* instantiation or variant interface method. If the number of calls reaches a threshold
* (THUNK_THRESHOLD), the method is added to the vtable slot's generic
- * virtual method thunk.
+ * virtual method trampoline.
*/
void
mono_method_add_generic_virtual_invocation (MonoDomain *domain, MonoVTable *vtable,
static gboolean inited = FALSE;
static int num_added = 0;
+ static int num_freed = 0;
GenericVirtualCase *gvc, *list;
MonoImtBuilderEntry *entries;
if (!domain->generic_virtual_cases)
domain->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL);
+ if (!inited) {
+ mono_counters_register ("Generic virtual cases", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_added);
+ mono_counters_register ("Freed IMT trampolines", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_freed);
+ inited = TRUE;
+ }
+
/* Check whether the case was already added */
list = (GenericVirtualCase *)g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
gvc = list;
g_hash_table_insert (domain->generic_virtual_cases, vtable_slot, gvc);
- if (!inited) {
- mono_counters_register ("Generic virtual cases", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_added);
- inited = TRUE;
- }
num_added++;
}
int displacement = (gpointer*)vtable_slot - (gpointer*)vtable;
int imt_slot = MONO_IMT_SIZE + displacement;
- /* Force the rebuild of the thunk at the next call */
+ /* Force the rebuild of the trampoline at the next call */
imt_trampoline = callbacks.get_imt_trampoline (vtable, imt_slot);
*vtable_slot = imt_trampoline;
} else {
sorted = imt_sort_slot_entries (entries);
- *vtable_slot = imt_thunk_builder (NULL, domain, (MonoIMTCheckItem**)sorted->pdata, sorted->len,
- vtable_trampoline);
+ *vtable_slot = imt_trampoline_builder (NULL, domain, (MonoIMTCheckItem**)sorted->pdata, sorted->len,
+ vtable_trampoline);
while (entries) {
MonoImtBuilderEntry *next = entries->next;
for (i = 0; i < sorted->len; ++i)
g_free (g_ptr_array_index (sorted, i));
g_ptr_array_free (sorted, TRUE);
- }
-#ifndef __native_client__
- /* We don't re-use any thunks as there is a lot of overhead */
- /* to deleting and re-using code in Native Client. */
- if (old_thunk != vtable_trampoline && old_thunk != imt_trampoline)
- invalidate_generic_virtual_thunk (domain, old_thunk);
-#endif
+ if (old_thunk != vtable_trampoline && old_thunk != imt_trampoline)
+ num_freed ++;
+ }
}
mono_domain_unlock (domain);
if (!current_appdomain_delegate && !root_appdomain_delegate) {
mono_print_unhandled_exception (exc);
} else {
+ /* unhandled exception callbacks must not be aborted */
+ mono_threads_begin_abort_protected_block ();
if (root_appdomain_delegate)
call_unhandled_exception_delegate (root_domain, root_appdomain_delegate, exc);
if (current_appdomain_delegate)
call_unhandled_exception_delegate (current_domain, current_appdomain_delegate, exc);
+ mono_threads_end_abort_protected_block ();
}
/* set exitcode only if we will abort the process */
gpointer wait_event = NULL;
ac->msg->exc = NULL;
+
res = mono_message_invoke (ares->async_delegate, ac->msg, &ac->msg->exc, &ac->out_args, &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
+
+ /* The exit side of the invoke must not be aborted as it would leave the runtime in an undefined state */
+ mono_threads_begin_abort_protected_block ();
+
+ if (!ac->msg->exc) {
+ MonoException *ex = mono_error_convert_to_exception (&error);
+ ac->msg->exc = (MonoObject *)ex;
+ } else {
+ mono_error_cleanup (&error);
+ }
+
MONO_OBJECT_SETREF (ac, res, res);
mono_monitor_enter ((MonoObject*) ares);
mono_monitor_exit ((MonoObject*) ares);
if (wait_event != NULL)
- SetEvent (wait_event);
+ mono_w32event_set (wait_event);
- if (ac->cb_method) {
+ mono_error_init (&error); //the else branch would leave it in an undefined state
+ if (ac->cb_method)
mono_runtime_invoke_checked (ac->cb_method, ac->cb_target, (gpointer*) &ares, &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
- }
+
+ mono_threads_end_abort_protected_block ();
+
+ if (mono_error_set_pending_exception (&error))
+ return NULL;
}
return res;