#include <mono/metadata/object.h>
#include <mono/metadata/gc-internal.h>
#include <mono/metadata/exception.h>
-#include <mono/metadata/appdomain.h>
+#include <mono/metadata/domain-internals.h>
+#include "mono/metadata/metadata-internals.h"
+#include "mono/metadata/class-internals.h"
#include <mono/metadata/assembly.h>
#include <mono/metadata/threadpool.h>
#include <mono/metadata/marshal.h>
#include "mono/metadata/debug-helpers.h"
#include "mono/metadata/marshal.h"
#include <mono/metadata/threads.h>
+#include <mono/metadata/threads-types.h>
#include <mono/metadata/environment.h>
#include "mono/metadata/profiler-private.h"
#include <mono/os/gc_wrapper.h>
g_assert (method);
+ if (method->klass->valuetype)
+ this = mono_object_unbox (this);
mono_runtime_invoke (method, this, NULL, NULL);
}
+/* The pseudo algorithm for type initialization from the spec
+Note it doesn't say anything about domains - only threads.
+
+2. If the type is initialized you are done.
+2.1. If the type is not yet initialized, try to take an
+ initialization lock.
+2.2. If successful, record this thread as responsible for
+ initializing the type and proceed to step 2.3.
+2.2.1. If not, see whether this thread or any thread
+ waiting for this thread to complete already holds the lock.
+2.2.2. If so, return since blocking would create a deadlock. This thread
+ will now see an incompletely initialized state for the type,
+ but no deadlock will arise.
+2.2.3 If not, block until the type is initialized then return.
+2.3 Initialize the parent type and then all interfaces implemented
+ by this type.
+2.4 Execute the type initialization code for this type.
+2.5 Mark the type as initialized, release the initialization lock,
+ awaken any threads waiting for this type to be initialized,
+ and return.
+
+*/
+
+typedef struct
+{
+ guint32 initializing_tid;
+ guint32 waiting_count;
+ CRITICAL_SECTION initialization_section;
+} TypeInitializationLock;
+
+/* for locking access to type_initialization_hash and blocked_thread_hash */
+static CRITICAL_SECTION type_initialization_section;
+
+/* from vtable to lock */
+static GHashTable *type_initialization_hash;
+
+/* from thread id to thread id being waited on */
+static GHashTable *blocked_thread_hash;
+
+/* Main thread */
+static MonoThread *main_thread;
+
+void
+mono_type_initialization_init (void)
+{
+ InitializeCriticalSection (&type_initialization_section);
+ type_initialization_hash = g_hash_table_new (NULL, NULL);
+ blocked_thread_hash = g_hash_table_new (NULL, NULL);
+}
+
/*
* mono_runtime_class_init:
* @vtable: vtable that needs to be initialized
MonoClass *klass;
gchar *full_name;
gboolean found;
- MonoDomain *last_domain = NULL;
MONO_ARCH_SAVE_REGS;
- if (vtable->initialized || vtable->initializing)
+ if (vtable->initialized)
return;
exc = NULL;
}
if (found) {
- mono_domain_lock (vtable->domain);
+ MonoDomain *domain = vtable->domain;
+ TypeInitializationLock *lock;
+ guint32 tid = GetCurrentThreadId();
+ int do_initialization = 0;
+ MonoDomain *last_domain = NULL;
+
+ EnterCriticalSection (&type_initialization_section);
/* double check... */
- if (vtable->initialized || vtable->initializing) {
- mono_domain_unlock (vtable->domain);
+ if (vtable->initialized) {
+ LeaveCriticalSection (&type_initialization_section);
return;
}
- vtable->initializing = 1;
- if (mono_domain_get () != vtable->domain) {
- /* Transfer into the target domain */
- last_domain = mono_domain_get ();
- if (!mono_domain_set (vtable->domain, FALSE)) {
- vtable->initialized = 1;
- vtable->initializing = 0;
- mono_domain_unlock (vtable->domain);
- mono_raise_exception (mono_get_exception_appdomain_unloaded ());
+ lock = g_hash_table_lookup (type_initialization_hash, vtable);
+ if (lock == NULL) {
+ /* This thread will get to do the initialization */
+ if (mono_domain_get () != domain) {
+ /* Transfer into the target domain */
+ last_domain = mono_domain_get ();
+ if (!mono_domain_set (domain, FALSE)) {
+ vtable->initialized = 1;
+ LeaveCriticalSection (&type_initialization_section);
+ mono_raise_exception (mono_get_exception_appdomain_unloaded ());
+ }
}
+ lock = g_malloc (sizeof(TypeInitializationLock));
+ InitializeCriticalSection (&lock->initialization_section);
+ lock->initializing_tid = tid;
+ lock->waiting_count = 1;
+ /* grab the vtable lock while this thread still owns type_initialization_section */
+ EnterCriticalSection (&lock->initialization_section);
+ g_hash_table_insert (type_initialization_hash, vtable, lock);
+ do_initialization = 1;
+ } else {
+ gpointer blocked;
+
+ if (lock->initializing_tid == tid) {
+ LeaveCriticalSection (&type_initialization_section);
+ return;
+ }
+ /* see if the thread doing the initialization is already blocked on this thread */
+ blocked = GUINT_TO_POINTER (lock->initializing_tid);
+ while ((blocked = g_hash_table_lookup (blocked_thread_hash, blocked))) {
+ if (blocked == GUINT_TO_POINTER (tid)) {
+ LeaveCriticalSection (&type_initialization_section);
+ return;
+ }
+ }
+ ++lock->waiting_count;
+ /* record the fact that we are waiting on the initializing thread */
+ g_hash_table_insert (blocked_thread_hash, GUINT_TO_POINTER (tid), GUINT_TO_POINTER (lock->initializing_tid));
+ }
+ LeaveCriticalSection (&type_initialization_section);
+
+ if (do_initialization) {
+ mono_runtime_invoke (method, NULL, NULL, (MonoObject **) &exc);
+ if (last_domain)
+ mono_domain_set (last_domain, TRUE);
+ LeaveCriticalSection (&lock->initialization_section);
+ } else {
+ /* this just blocks until the initializing thread is done */
+ EnterCriticalSection (&lock->initialization_section);
+ LeaveCriticalSection (&lock->initialization_section);
+ }
+
+ EnterCriticalSection (&type_initialization_section);
+ if (lock->initializing_tid != tid)
+ g_hash_table_remove (blocked_thread_hash, GUINT_TO_POINTER (tid));
+ --lock->waiting_count;
+ if (lock->waiting_count == 0) {
+ DeleteCriticalSection (&lock->initialization_section);
+ g_hash_table_remove (type_initialization_hash, vtable);
+ g_free (lock);
}
- mono_runtime_invoke (method, NULL, NULL, (MonoObject **) &exc);
- if (last_domain)
- mono_domain_set (last_domain, TRUE);
vtable->initialized = 1;
- vtable->initializing = 0;
/* FIXME: if the cctor fails, the type must be marked as unusable */
- mono_domain_unlock (vtable->domain);
+ LeaveCriticalSection (&type_initialization_section);
} else {
vtable->initialized = 1;
return;
/* GC 6.1 has trouble handling 64 bit descriptors... */
if ((class->instance_size / sizeof (gpointer)) > 30) {
-// printf ("TOO LARGE: %s %d.\n", class->name, class->instance_size / sizeof (gpointer));
+/* printf ("TOO LARGE: %s %d.\n", class->name, class->instance_size / sizeof (gpointer)); */
return;
}
count ++;
-// if (count > 442)
-// return;
+/* if (count > 442) */
+/* return; */
-// printf("KLASS: %s.\n", class->name);
+/* printf("KLASS: %s.\n", class->name); */
for (p = class; p != NULL; p = p->parent) {
for (i = 0; i < p->field.count; ++i) {
case MONO_TYPE_U8:
case MONO_TYPE_R4:
case MONO_TYPE_R8:
-// printf ("F: %s %s %d %lld %llx.\n", class->name, field->name, field->offset, ((guint64)1) << pos, bitmap);
+/* printf ("F: %s %s %d %lld %llx.\n", class->name, field->name, field->offset, ((guint64)1) << pos, bitmap); */
break;
case MONO_TYPE_I:
case MONO_TYPE_STRING:
g_assert ((field->offset % sizeof(gpointer)) == 0);
bitmap |= ((guint64)1) << pos;
-// printf ("F: %s %s %d %d %lld %llx.\n", class->name, field->name, field->offset, pos, ((guint64)(1)) << pos, bitmap);
+/* printf ("F: %s %s %d %d %lld %llx.\n", class->name, field->name, field->offset, pos, ((guint64)(1)) << pos, bitmap); */
break;
case MONO_TYPE_VALUETYPE: {
MonoClass *fclass = field->type->data.klass;
}
}
-// printf("CLASS: %s.%s -> %d %llx.\n", class->name_space, class->name, class->instance_size / sizeof (gpointer), bitmap);
+/* printf("CLASS: %s.%s -> %d %llx.\n", class->name_space, class->name, class->instance_size / sizeof (gpointer), bitmap); */
class->gc_bitmap = bitmap;
/* Convert to the format expected by GC_make_descriptor */
bm [0] = (guint32)bitmap;
bm [1] = (guint32)(bitmap >> 32);
- class->gc_descr = (gpointer)GC_make_descriptor (&bm, class->instance_size / sizeof (gpointer));
+ class->gc_descr = (gpointer)GC_make_descriptor ((GC_bitmap)&bm, class->instance_size / sizeof (gpointer));
}
}
#endif /* CREATION_SPEEDUP */
#if CREATION_SPEEDUP
mono_class_compute_gc_descriptor (class);
- if (domain != mono_root_domain)
+ if (domain != mono_get_root_domain ())
/*
* We can't use typed allocation in the non-root domains, since the
* collector needs the GC descriptor stored in the vtable even after
mono_domain_unlock (domain);
}
-/*
- * Retrieve the MonoMethod that would to be called on obj if obj is passed as
+/**
+ * mono_object_get_virtual_method:
+ *
+ * Retrieve the MonoMethod that would be called on obj if obj is passed as
* the instance of a callvirt of method.
*/
MonoMethod*
if (!is_proxy)
res = vtable [klass->interface_offsets [method->klass->interface_id] + method->slot];
} else {
- res = vtable [method->slot];
+ if (method->slot != -1)
+ res = vtable [method->slot];
}
if (is_proxy) {
static MonoInvokeFunc default_mono_runtime_invoke = dummy_mono_runtime_invoke;
+/**
+ * mono_runtime_invoke:
+ *
+ * Invokes the method represented by `method' on the object `obj'.
+ *
+ * obj is the 'this' pointer, it should be NULL for static
+ * methods, a MonoObject* for object instances and a pointer to
+ * the value type for value types.
+ *
+ * The params array contains the arguments to the method with the
+ * same convention: MonoObject* pointers for object instances and
+ * pointers to the value type otherwise.
+ *
+ * From unmanaged code you'll usually use the
+ * mono_runtime_invoke() variant.
+ *
+ * Note that this function doesn't handle virtual methods for
+ * you, it will exec the exact method you pass: we still need to
+ * expose a function to lookup the derived class implementation
+ * of a virtual method (there are examples of this in the code,
+ * though).
+ *
+ * You can pass NULL as the exc argument if you don't want to
+ * catch exceptions, otherwise, *exc will be set to the exception
+ * thrown, if any. if an exception is thrown, you can't use the
+ * MonoObject* result from the function.
+ *
+ * If the method returns a value type, it is boxed in an object
+ * reference.
+ */
MonoObject*
mono_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
{
field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "ProcessExit");
g_assert (field);
- if (domain != mono_root_domain)
+ if (domain != mono_get_root_domain ())
return;
delegate = *(MonoObject **)(((char *)domain->domain) + field->offset);
MonoDomain *domain = mono_domain_get ();
gchar *utf8_fullpath;
int result;
-
+
+ main_thread = mono_thread_current ();
+
main_args = (MonoArray*)mono_array_new (domain, mono_defaults.string_class, argc);
if (!g_path_is_absolute (argv [0])) {
if (exc->vtable->klass != mono_defaults.threadabortexception_class) {
delegate = *(MonoObject **)(((char *)domain->domain) + field->offset);
- /* set exitcode only in the main thread? */
- mono_environment_exitcode_set (1);
- if (domain != mono_root_domain || !delegate) {
+ /* set exitcode only in the main thread */
+ if (mono_thread_current () == main_thread)
+ mono_environment_exitcode_set (1);
+ if (domain != mono_get_root_domain () || !delegate) {
mono_print_unhandled_exception (exc);
} else {
MonoObject *e = NULL;
domain = mono_object_domain (args);
if (!domain->entry_assembly) {
- domain->entry_assembly = method->klass->image->assembly;
- ves_icall_System_AppDomainSetup_InitAppDomainSetup (domain->setup);
+ gchar *str;
+ gchar *config_suffix;
+ MonoAssembly *assembly;
+
+ assembly = method->klass->image->assembly;
+ domain->entry_assembly = assembly;
+ domain->setup->application_base = mono_string_new (domain, assembly->basedir);
+
+ config_suffix = g_strconcat (assembly->aname.name, ".exe.config", NULL);
+ str = g_build_filename (assembly->basedir, config_suffix, NULL);
+ g_free (config_suffix);
+ domain->setup->configuration_file = mono_string_new (domain, str);
+ g_free (str);
}
/* FIXME: check signature of method */
default_mono_runtime_invoke = func ? func: dummy_mono_runtime_invoke;
}
+/**
+ * mono_runtime_invoke:
+ *
+ * Invokes the method represented by `method' on the object `obj'.
+ *
+ * obj is the 'this' pointer, it should be NULL for static
+ * methods, a MonoObject* for object instances and a pointer to
+ * the value type for value types.
+ *
+ * The params array contains the arguments to the method with the
+ * same convention: MonoObject* pointers for object instances and
+ * pointers to the value type otherwise. The _invoke_array
+ * variant takes a C# object[] as the params argument (MonoArray
+ * *params): in this case the value types are boxed inside the
+ * respective reference representation.
+ *
+ * From unmanaged code you'll usually use the
+ * mono_runtime_invoke() variant.
+ *
+ * Note that this function doesn't handle virtual methods for
+ * you, it will exec the exact method you pass: we still need to
+ * expose a function to lookup the derived class implementation
+ * of a virtual method (there are examples of this in the code,
+ * though).
+ *
+ * You can pass NULL as the exc argument if you don't want to
+ * catch exceptions, otherwise, *exc will be set to the exception
+ * thrown, if any. if an exception is thrown, you can't use the
+ * MonoObject* result from the function.
+ *
+ * If the method returns a value type, it is boxed in an object
+ * reference.
+ */
MonoObject*
mono_runtime_invoke_array (MonoMethod *method, void *obj, MonoArray *params,
MonoObject **exc)
case MONO_TYPE_R4:
case MONO_TYPE_R8:
case MONO_TYPE_VALUETYPE:
+ if (sig->params [i]->byref) {
+ /* MS seems to create the objects if a null is passed in */
+ if (! ((gpointer *)params->vector)[i])
+ ((gpointer*)params->vector)[i] = mono_object_new (mono_domain_get (), mono_class_from_mono_type (sig->params [i]));
+ }
+ else
+ g_assert (((gpointer*)params->vector) [i]);
pa [i] = (char *)(((gpointer *)params->vector)[i]) + sizeof (MonoObject);
break;
case MONO_TYPE_STRING:
}
if (!strcmp (method->name, ".ctor") && method->klass != mono_defaults.string_class) {
+ void *o = obj;
if (!obj) {
obj = mono_object_new (mono_domain_get (), method->klass);
if (mono_object_class(obj) == mono_defaults.transparent_proxy_class) {
- method = mono_marshal_get_remoting_invoke (method->klass->vtable [method->slot]);
+ method = mono_marshal_get_remoting_invoke (method->slot == -1 ? method : method->klass->vtable [method->slot]);
}
+ if (method->klass->valuetype)
+ o = mono_object_unbox (obj);
+ else
+ o = obj;
}
- mono_runtime_invoke (method, obj, pa, exc);
+ mono_runtime_invoke (method, o, pa, exc);
return obj;
- } else
+ } else {
+ /* obj must be already unboxed if needed */
return mono_runtime_invoke (method, obj, pa, exc);
+ }
}
static void
}
#endif
-/**
- * mono_object_free:
- *
- * Frees the memory used by the object. Debugging purposes
- * only, as we will have our GC system.
- */
-void
-mono_object_free (MonoObject *o)
-{
-#if HAVE_BOEHM_GC
- g_error ("mono_object_free called with boehm gc.");
-#else
- MonoClass *c = o->vtable->klass;
-
- memset (o, 0, c->instance_size);
- free (o);
-#endif
-}
-
/**
* mono_object_new:
* @klass: the class of the object that we want to create
*
- * Returns: A newly created object whose definition is
- * looked up using @klass
+ * Returns a newly created object whose definition is
+ * looked up using @klass. This will not invoke any constructors,
+ * so the consumer of this routine has to invoke any constructors on
+ * its own to initialize the object.
*/
MonoObject *
mono_object_new (MonoDomain *domain, MonoClass *klass)
if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
o = mono_object_allocate_spec (vtable->klass->instance_size, vtable);
} else {
-// printf("OBJECT: %s.%s.\n", vtable->klass->name_space, vtable->klass->name);
+/* printf("OBJECT: %s.%s.\n", vtable->klass->name_space, vtable->klass->name); */
o = mono_object_allocate (vtable->klass->instance_size);
o->vtable = vtable;
}
if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
o = mono_object_allocate_spec (vtable->klass->instance_size, vtable);
} else {
-// printf("OBJECT: %s.%s.\n", vtable->klass->name_space, vtable->klass->name);
+/* printf("OBJECT: %s.%s.\n", vtable->klass->name_space, vtable->klass->name); */
o = mono_object_allocate (vtable->klass->instance_size);
o->vtable = vtable;
}
if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
o = mono_object_allocate_spec (byte_len, vtable);
} else {
-// printf("ARRAY: %s.%s.\n", vtable->klass->name_space, vtable->klass->name);
+/* printf("ARRAY: %s.%s.\n", vtable->klass->name_space, vtable->klass->name); */
o = mono_object_allocate (byte_len);
o->vtable = vtable;
}
{
MonoString *s;
MonoVTable *vtable;
+ size_t size = (sizeof (MonoString) + ((len + 1) * 2));
+
+ /* overflow ? can't fit it, can't allocate it! */
+ if (len > size)
+ out_of_memory (-1);
vtable = mono_class_vtable (domain, mono_defaults.string_class);
#if CREATION_SPEEDUP
if (vtable->gc_descr != GC_NO_DESCRIPTOR)
- s = mono_object_allocate_spec (sizeof (MonoString) + ((len + 1) * 2), vtable);
+ s = mono_object_allocate_spec (size, vtable);
else {
- s = (MonoString*)mono_object_allocate (sizeof (MonoString) + ((len + 1) * 2));
+ s = (MonoString*)mono_object_allocate (size);
s->object.vtable = vtable;
}
#else
- s = (MonoString*)mono_object_allocate (sizeof (MonoString) + ((len + 1) * 2));
+ s = (MonoString*)mono_object_allocate (size);
s->object.vtable = vtable;
#endif
return res;
}
+MonoDomain*
+mono_object_get_domain (MonoObject *obj)
+{
+ return mono_object_domain (obj);
+}
+
+MonoClass*
+mono_object_get_class (MonoObject *obj)
+{
+ return mono_object_class (obj);
+}
+
gpointer
mono_object_unbox (MonoObject *obj)
{
/* add assert for valuetypes? */
+ g_assert (obj->vtable->klass->valuetype);
return ((char*)obj) + sizeof (MonoObject);
}
}
}
- return mono_runtime_invoke_array (method, target, msg->args, exc);
+ return mono_runtime_invoke_array (method, method->klass->valuetype? mono_object_unbox (target): target, msg->args, exc);
}
void
case MONO_TYPE_CLASS:
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_OBJECT:
**((MonoObject ***)params [i]) = (MonoObject *)arg;
break;
default: