*
* Author:
* Dick Porter (dick@ximian.com)
+ * Patrik Torstensson (patrik.torstensson@labs2.com)
*
* (C) 2001 Ximian, Inc.
*/
#include <glib.h>
#include <mono/metadata/object.h>
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/profiler-private.h>
#include <mono/metadata/threads.h>
#include <mono/metadata/threads-types.h>
#include <mono/io-layer/io-layer.h>
+#if HAVE_BOEHM_GC
+#include <gc/gc.h>
+#endif
+
#undef THREAD_DEBUG
#undef THREAD_LOCK_DEBUG
#undef THREAD_WAIT_DEBUG
{
guint32 (*func)(void *);
MonoObject *obj;
+ void *this;
+ MonoDomain *domain;
};
+typedef union {
+ gint32 ival;
+ gfloat fval;
+} IntFloatUnion;
+
/* Controls access to the 'threads' array */
static CRITICAL_SECTION threads_mutex;
/* The TLS key that holds the LocalDataStoreSlot hash in each thread */
static guint32 slothash_key;
+/* Spin lock for InterlockedXXX 64 bit functions */
+static CRITICAL_SECTION interlocked_mutex;
+
+/* handle_store() and handle_remove() manage the array of threads that
+ * still need to be waited for when the main thread exits.
+ */
+static void handle_store(HANDLE thread)
+{
+#ifdef THREAD_DEBUG
+ g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
+#endif
+
+ EnterCriticalSection(&threads_mutex);
+ if(threads==NULL) {
+ threads=g_ptr_array_new();
+ }
+ g_ptr_array_add(threads, thread);
+ LeaveCriticalSection(&threads_mutex);
+}
+
+static void handle_remove(HANDLE thread)
+{
+#ifdef THREAD_DEBUG
+ g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
+#endif
+
+ EnterCriticalSection(&threads_mutex);
+ g_ptr_array_remove_fast(threads, thread);
+ LeaveCriticalSection(&threads_mutex);
+
+ /* Don't close the handle here, wait for the object finalizer
+ * to do it. Otherwise, the following race condition applies:
+ *
+ * 1) Thread exits (and handle_remove() closes the handle)
+ *
+ * 2) Some other handle is reassigned the same slot
+ *
+ * 3) Another thread tries to join the first thread, and
+ * blocks waiting for the reassigned handle to be signalled
+ * (which might never happen). This is possible, because the
+ * thread calling Join() still has a reference to the first
+ * thread's object.
+ */
+}
+
static guint32 start_wrapper(void *data)
{
struct StartInfo *start_info=(struct StartInfo *)data;
guint32 (*start_func)(void *);
+ void *this;
+ HANDLE thread;
#ifdef THREAD_DEBUG
g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
* This is recorded so CurrentThread can return the
* Thread object.
*/
- TlsSetValue(current_object_key, start_info->obj);
- start_func=start_info->func;
-
- g_free(start_info);
+ TlsSetValue (current_object_key, start_info->obj);
+ start_func = start_info->func;
+ mono_domain_set (start_info->domain);
+ this = start_info->this;
+ g_free (start_info);
+
+ thread=GetCurrentThread ();
- start_func(NULL);
+ handle_store(thread);
+ mono_profiler_thread_start (thread);
+
+ start_func (this);
#ifdef THREAD_DEBUG
g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
#endif
+
+ mono_profiler_thread_end (thread);
+ handle_remove (thread);
return(0);
}
-
-static void handle_store(HANDLE thread)
+
+MonoObject *
+mono_thread_create (MonoDomain *domain, gpointer func)
{
-#ifdef THREAD_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
-#endif
+ MonoClassField *field;
+ MonoObject *thread;
+ HANDLE thread_handle;
+ struct StartInfo *start_info;
+ guint32 tid;
+
+ thread = mono_object_new (domain, mono_defaults.thread_class);
- EnterCriticalSection(&threads_mutex);
- if(threads==NULL) {
- threads=g_ptr_array_new();
- }
- g_ptr_array_add(threads, thread);
- LeaveCriticalSection(&threads_mutex);
-}
+ field=mono_class_get_field_from_name(mono_defaults.thread_class, "system_thread_handle");
+ g_assert (field);
-static void handle_remove(HANDLE thread)
-{
-#ifdef THREAD_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
-#endif
+ start_info=g_new0 (struct StartInfo, 1);
+ start_info->func = func;
+ start_info->obj = thread;
+ start_info->domain = domain;
+ /* start_info->this needs to be set? */
+
+ thread_handle = CreateThread(NULL, 0, start_wrapper, start_info, 0, &tid);
+ g_assert (thread_handle);
- EnterCriticalSection(&threads_mutex);
- g_ptr_array_remove_fast(threads, thread);
- LeaveCriticalSection(&threads_mutex);
- CloseHandle(thread);
-}
+ *(gpointer *)(((char *)thread) + field->offset) = thread_handle;
+ return thread;
+}
HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoObject *this,
MonoObject *start)
{
- MonoClassField *field;
+ MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
guint32 (*start_func)(void *);
struct StartInfo *start_info;
HANDLE thread;
this, start);
#endif
- field=mono_class_get_field_from_name(mono_defaults.delegate_class, "method_ptr");
- start_func= *(gpointer *)(((char *)start) + field->offset);
+ start_func = delegate->delegate.method_ptr;
if(start_func==NULL) {
g_warning(G_GNUC_PRETTY_FUNCTION
return(NULL);
} else {
/* This is freed in start_wrapper */
- start_info=g_new0(struct StartInfo, 1);
- start_info->func=start_func;
- start_info->obj=this;
+ start_info = g_new0 (struct StartInfo, 1);
+ start_info->func = start_func;
+ start_info->this = delegate->delegate.target;
+ start_info->obj = this;
+ start_info->domain = mono_domain_get ();
thread=CreateThread(NULL, 0, start_wrapper, start_info,
CREATE_SUSPENDED, &tid);
}
#ifdef THREAD_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d",
- tid);
+ g_message(G_GNUC_PRETTY_FUNCTION
+ ": Started thread ID %d (handle %p)", tid, thread);
#endif
-
- /* Store handle for cleanup later */
- handle_store(thread);
return(thread);
}
}
+void ves_icall_System_Threading_Thread_Thread_free_internal (MonoObject *this,
+ HANDLE thread)
+{
+#ifdef THREAD_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
+ this, thread);
+#endif
+
+ CloseHandle (thread);
+}
+
void ves_icall_System_Threading_Thread_Start_internal(MonoObject *this,
HANDLE thread)
{
Sleep(ms);
}
+MonoAppDomain *ves_icall_System_Threading_Thread_CurrentThreadDomain_internal(void)
+{
+ /* return the current app */
+ return mono_domain_get()->domain;
+}
+
MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void)
{
MonoObject *thread;
/* Find the current thread object */
thread=TlsGetValue(current_object_key);
+
+#ifdef THREAD_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
+#endif
+
return(thread);
}
if(ms== -1) {
ms=INFINITE;
}
+#ifdef THREAD_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
+ thread, ms);
+#endif
ret=WaitForSingleObject(thread, ms);
if(ret==WAIT_OBJECT_0) {
- /* Clean up the handle */
- handle_remove(thread);
+#ifdef THREAD_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
+#endif
+
return(TRUE);
}
+#ifdef THREAD_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
+#endif
+
return(FALSE);
}
return(data);
}
+static void mon_finalize (void *o, void *unused)
+{
+ MonoThreadsSync *mon=(MonoThreadsSync *)o;
+
+#ifdef THREAD_LOCK_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync");
+#endif
+
+ CloseHandle (mon->monitor);
+ CloseHandle (mon->sema);
+ CloseHandle (mon->waiters_done);
+}
+
static MonoThreadsSync *mon_new(void)
{
MonoThreadsSync *new;
+#if HAVE_BOEHM_GC
+ new=(MonoThreadsSync *)GC_debug_malloc (sizeof(MonoThreadsSync), "sync", 1);
+ GC_register_finalizer (new, mon_finalize, NULL, NULL, NULL);
+#else
/* This should be freed when the object that owns it is
* deleted
*/
-
- new=(MonoThreadsSync *)g_new0(MonoThreadsSync, 1);
- new->monitor=CreateMutex(NULL, FALSE, NULL);
-#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": ThreadsSync mutex created: %p",
- new->monitor);
+ new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1);
#endif
+
+ new->monitor=CreateMutex(NULL, FALSE, "sync");
+ if(new->monitor==NULL) {
+ /* Throw some sort of system exception? (ditto for the
+ * sem and event handles below)
+ */
+ }
new->waiters_count=0;
new->was_broadcast=FALSE;
new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
+#ifdef THREAD_LOCK_DEBUG
+ g_message(G_GNUC_PRETTY_FUNCTION
+ ": ThreadsSync %p mutex created: %p, sem: %p, event: %p",
+ new, new->monitor, new->sema, new->waiters_done);
+#endif
+
return(new);
}
#ifdef THREAD_LOCK_DEBUG
g_message(G_GNUC_PRETTY_FUNCTION
- ": Trying to lock %p in thread %d", obj,
+ ": Trying to lock object %p in thread %d", obj,
GetCurrentThreadId());
#endif
mon->tid=GetCurrentThreadId();
#ifdef THREAD_LOCK_DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
- mon->count);
+ g_message(G_GNUC_PRETTY_FUNCTION
+ ": object %p now locked %d times", obj, mon->count);
#endif
return(TRUE);
}
if(mon->tid!=GetCurrentThreadId()) {
+#ifdef THREAD_LOCK_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION
+ ": object %p is owned by thread %d", obj, mon->tid);
+#endif
+
goto finished;
}
void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
ReleaseMutex(handle);
}
-\r
-HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,\r
- MonoBoolean initial,\r
- char *name) {\r
- return (CreateEvent(NULL,manual,initial,name));\r
-}\r
-\r
-gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {\r
- return (SetEvent(handle));\r
-}\r
-
-gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {\r
- return (ResetEvent(handle));\r
-}\r
+
+HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
+ MonoBoolean initial,
+ char *name) {
+ return (CreateEvent(NULL,manual,initial,name));
+}
+
+gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
+ return (SetEvent(handle));
+}
+
+gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
+ return (ResetEvent(handle));
+}
void mono_thread_init(MonoDomain *domain)
{
* object? In theory, I guess the whole program should act as
* though exit() were called :-)
*/
+#ifdef THREAD_DEBUG
+ g_message(G_GNUC_PRETTY_FUNCTION
+ ": Starting to build main Thread object");
+#endif
main_thread = mono_object_new (domain, thread_class);
+#ifdef THREAD_DEBUG
+ g_message(G_GNUC_PRETTY_FUNCTION
+ ": Finished building main Thread object: %p", main_thread);
+#endif
InitializeCriticalSection(&threads_mutex);
InitializeCriticalSection(&monitor_mutex);
+ InitializeCriticalSection(&interlocked_mutex);
current_object_key=TlsAlloc();
+#ifdef THREAD_DEBUG
+ g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
+ current_object_key);
+#endif
+
TlsSetValue(current_object_key, main_thread);
slothash_key=TlsAlloc();
*
* The first method call should be started in its own thread,
* and then the main thread should poll an event and wait for
- * any terminated threads, until there are none left.
+ * any terminated threads, until there are none left. (This
+ * loop will break if a subthread creates new threads after
+ * the main thread ends.)
*/
#ifdef THREAD_DEBUG
g_message("There are %d threads to join", threads->len);
+ for(i=0; i<threads->len; i++) {
+ g_message("Waiting for: %p", g_ptr_array_index(threads, i));
+ }
#endif
for(i=0; i<threads->len; i+=MAXIMUM_WAIT_OBJECTS) {
g_ptr_array_free(threads, FALSE);
threads=NULL;
}
+
+gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
+{
+ return InterlockedIncrement (location);
+}
+
+gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
+{
+ gint32 lowret;
+ gint32 highret;
+
+ EnterCriticalSection(&interlocked_mutex);
+
+ lowret = InterlockedIncrement((gint32 *) location);
+ if (0 == lowret)
+ highret = InterlockedIncrement((gint32 *) location + 1);
+ else
+ highret = *((gint32 *) location + 1);
+
+ LeaveCriticalSection(&interlocked_mutex);
+
+ return (gint64) highret << 32 | (gint64) lowret;
+}
+
+gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
+{
+ return InterlockedDecrement(location);
+}
+
+gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
+{
+ gint32 lowret;
+ gint32 highret;
+
+ EnterCriticalSection(&interlocked_mutex);
+
+ lowret = InterlockedDecrement((gint32 *) location);
+ if (-1 == lowret)
+ highret = InterlockedDecrement((gint32 *) location + 1);
+ else
+ highret = *((gint32 *) location + 1);
+
+ LeaveCriticalSection(&interlocked_mutex);
+
+ return (gint64) highret << 32 | (gint64) lowret;
+}
+
+gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
+{
+ return InterlockedExchange(location1, value);
+}
+
+MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
+{
+ return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
+}
+
+gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
+{
+ IntFloatUnion val, ret;
+
+ val.fval = value;
+ ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
+
+ return ret.fval;
+}
+
+gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
+{
+ return InterlockedCompareExchange(location1, value, comparand);
+}
+
+MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
+{
+ return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
+}
+
+gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
+{
+ IntFloatUnion val, ret, cmp;
+
+ val.fval = value;
+ cmp.fval = comparand;
+ ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
+
+ return ret.fval;
+}