public static IntPtr StringToHeap (string s, Encoding encoding)
{
+ if (s == null)
+ return IntPtr.Zero;
+
return StringToHeap (s, 0, s.Length, encoding);
}
}
#endif
+ [Test]
+ public void BXC10074 ()
+ {
+ var result = UnixMarshal.StringToHeap (null, Encoding.ASCII);
+ Assert.AreEqual (IntPtr.Zero, result, "This used to crash due to a NullReferenceException");
+ }
+
[Test]
public void TestStringToHeap ()
{
File.Delete (path2);
try {
- symlink (path1, path2);
- symlink (path2, path1);
+ if (symlink (path1, path2) != 0)
+ Assert.Fail ("symlink #1 failed with errno={0}", Marshal.GetLastWin32Error ());
+ if (symlink (path2, path1) != 0)
+ Assert.Fail ("symlink #2 failed with errno={0}", Marshal.GetLastWin32Error ());
Assert.IsTrue (File.Exists (path1), "File.Exists must return true for path1 symlink loop");
Assert.IsTrue (File.Exists (path2), "File.Exists must return true for path2 symlink loop");
{
}
+ private void GenericMethod2<A, B, C, D> ()
+ where C : Duper
+ where A : B, IFace
+ where B : C
+ where D : Baz<object>
+ {
+ }
+
public class Nested
{
// Tests for parameters with generic constraints
mi = typeof (TypeTest).GetMethod ("GenericMethod", BindingFlags.Instance|BindingFlags.NonPublic);
Assert.IsTrue (typeof (IFace).IsAssignableFrom (mi.GetParameters ()[1].ParameterType));
+
+ // Transitivity of IsAssignableFrom for type parameters
+ mi = typeof (TypeTest).GetMethod ("GenericMethod2", BindingFlags.Instance|BindingFlags.NonPublic);
+ var gparams = mi.GetGenericArguments ();
+ // B : Duper since B : C and C : Duper
+ Assert.IsTrue (typeof (Duper).IsAssignableFrom (gparams[1]), "#36");
+ // A : Duper since A : B and B : Duper
+ Assert.IsTrue (typeof (Duper).IsAssignableFrom (gparams[0]), "#37a");
+ // A : IFace since A : IFace
+ Assert.IsTrue (typeof (IFace).IsAssignableFrom (gparams[0]), "#37b");
+ // B : Super since B : Duper and Duper : Super
+ Assert.IsTrue (typeof (Super).IsAssignableFrom (gparams[1]), "#38");
+ // A : Super since A : B and B : Super
+ Assert.IsTrue (typeof (Super).IsAssignableFrom (gparams[0]), "#39");
+ // D : IBar<object> since D : Baz<object> and Baz<object> : IBar<object>
+ Assert.IsTrue (typeof (IBar<object>).IsAssignableFrom (gparams [3]), "#40");
+ // A not assignable from B since A : B
+ Assert.IsFalse (gparams[0].IsAssignableFrom (gparams [1]), "#41");
+ Assert.IsFalse (gparams[0].IsAssignableFrom (gparams [2]), "#42");
+
+ // A is not assignable from Array and Delegate and vice versa
+ Assert.IsFalse (gparams[0].IsAssignableFrom (typeof (Array)), "#43");
+ Assert.IsFalse (gparams[0].IsAssignableFrom (typeof (Delegate)), "#44");
+ Assert.IsFalse (typeof (Array).IsAssignableFrom (gparams[0]), "#45");
+ Assert.IsFalse (typeof (Delegate).IsAssignableFrom (gparams[0]), "#46");
+
+ }
+
+ [Test]
+ public void GenericParameterBaseType ()
+ {
+ var mi = typeof (TypeTest).GetMethod ("GenericMethod2", BindingFlags.Instance|BindingFlags.NonPublic);
+ var gparams = mi.GetGenericArguments ();
+
+ // From the .NET documentation: BaseType property of a
+ // gparam is "object" if its only constraints are other
+ // gparams or interfaces, otherwise if it has a class
+ // constraint that class is the BaseType.
+
+ // A : B where B is a gparam, and A : IFace which is an
+ // interface, so A.BaseType is object
+ Assert.AreEqual (typeof (object), gparams[0].BaseType, "#1");
+ // B : C where C is a gparam, so B.BaseType is object
+ Assert.AreEqual (typeof (object), gparams[1].BaseType, "#2");
+ // C : Duper where Duper is a class, so A.BaseType is Duper
+ Assert.AreEqual (typeof (Duper), gparams[2].BaseType, "#3");
+ // D : Baz<object>
+ Assert.AreEqual (typeof (Baz<object>), gparams[3].BaseType, "#4");
}
[Test]
public int field;
}
+ [Test]
+ public void IsAssignableFromGenericArgumentsWithConstraints ()
+ {
+ // Regression test for #58809
+
+ // Generic Parameters of a gtd should have their
+ // constraints respected even when those constraints
+ // are other generic parameters themselves.
+
+ var ps = typeof (GenericWithParamConstraints<,,>).GetGenericArguments ();
+
+ var a = ps[0];
+ var b = ps[1];
+ var c = ps[2];
+
+ // Foo<C>
+ var fooOfC = typeof (Foo<>).MakeGenericType (c);
+
+ // constraint B : Foo <C>
+ Assert.IsTrue (fooOfC.IsAssignableFrom (b), "#1");
+
+ // constraint A : B
+ Assert.IsTrue (b.IsAssignableFrom (a), "#2");
+
+ // A : Foo<C> since A : B and B : Foo<C>
+ Assert.IsTrue (fooOfC.IsAssignableFrom (a), "#3");
+ }
+
+ class GenericWithParamConstraints<A, B, C> where B : Foo<C> where A : B
+ {
+ }
+
[Test] // Bug #612780
public void CannotMakeDerivedTypesFromTypedByRef ()
{
else
{
string zlib = (compress ? "-lz" : "");
+ string objc = (style == "osx" ? "-framework CoreFoundation -lobjc" : "");
string debugging = "-g";
string cc = GetEnv("CC", "cc");
string cmd = null;
smonolib = "`pkg-config --variable=libdir mono-2`/libmono-2.0.a ";
else
smonolib = "-Wl,-Bstatic -lmono-2.0 -Wl,-Bdynamic ";
- cmd = String.Format("{4} -o '{2}' -Wall `pkg-config --cflags mono-2` {0} {3} " +
+ cmd = String.Format("{4} -o '{2}' -Wall {5} `pkg-config --cflags mono-2` {0} {3} " +
"`pkg-config --libs-only-L mono-2` " + smonolib +
"`pkg-config --libs-only-l mono-2 | sed -e \"s/\\-lmono-2.0 //\"` {1}",
- temp_c, temp_o, output, zlib, cc);
+ temp_c, temp_o, output, zlib, cc, objc);
}
else
{
- cmd = String.Format("{4} " + debugging + " -o '{2}' -Wall {0} `pkg-config --cflags --libs mono-2` {3} {1}",
- temp_c, temp_o, output, zlib, cc);
+ cmd = String.Format("{4} " + debugging + " -o '{2}' -Wall {5} {0} `pkg-config --cflags --libs mono-2` {3} {1}",
+ temp_c, temp_o, output, zlib, cc, objc);
}
Execute (cmd);
}
*ifaces = g_hash_table_new (NULL, NULL);
if (g_hash_table_lookup (*ifaces, ic))
continue;
+ /* A gparam is not an implemented interface for the purposes of
+ * mono_class_get_implemented_interfaces */
+ if (mono_class_is_gparam (ic))
+ continue;
g_ptr_array_add (*res, ic);
g_hash_table_insert (*ifaces, ic, ic);
mono_class_init (ic);
for (i = 0; i < k->interface_count; i++) {
ic = k->interfaces [i];
- mono_class_init (ic);
+ /* A gparam does not have any interface_id set. */
+ if (! mono_class_is_gparam (ic))
+ mono_class_init (ic);
if (max_iid < ic->interface_id)
max_iid = ic->interface_id;
return mono_gparam_is_assignable_from (klass, oklass);
}
- if (MONO_CLASS_IS_INTERFACE (klass)) {
- if ((oklass->byval_arg.type == MONO_TYPE_VAR) || (oklass->byval_arg.type == MONO_TYPE_MVAR)) {
- MonoGenericParam *gparam = oklass->byval_arg.data.generic_param;
- MonoClass **constraints = mono_generic_container_get_param_info (gparam->owner, gparam->num)->constraints;
- int i;
+ /* This can happen if oklass is a tyvar that has a constraint which is another tyvar which in turn
+ * has a constraint which is a class type:
+ *
+ * class Foo { }
+ * class G<T1, T2> where T1 : T2 where T2 : Foo { }
+ *
+ * In this case, Foo is assignable from T1.
+ */
+ if ((oklass->byval_arg.type == MONO_TYPE_VAR) || (oklass->byval_arg.type == MONO_TYPE_MVAR)) {
+ MonoGenericParam *gparam = oklass->byval_arg.data.generic_param;
+ MonoClass **constraints = mono_generic_container_get_param_info (gparam->owner, gparam->num)->constraints;
+ int i;
- if (constraints) {
- for (i = 0; constraints [i]; ++i) {
- if (mono_class_is_assignable_from (klass, constraints [i]))
- return TRUE;
- }
+ if (constraints) {
+ for (i = 0; constraints [i]; ++i) {
+ if (mono_class_is_assignable_from (klass, constraints [i]))
+ return TRUE;
}
-
- return FALSE;
}
+ return mono_class_has_parent (oklass, klass);
+ }
+
+ if (MONO_CLASS_IS_INTERFACE (klass)) {
+
/* interface_offsets might not be set for dynamic classes */
if (mono_class_get_ref_info_handle (oklass) && !oklass->interface_bitmap) {
/*
MONO_API MonoClass *
mono_class_from_typeref_checked (MonoImage *image, uint32_t type_token, MonoError *error);
+MONO_RT_EXTERNAL_ONLY
MONO_API MonoClass *
mono_class_from_generic_parameter (MonoGenericParam *param, MonoImage *image, mono_bool is_mvar);
#include <mono/utils/hazard-pointer.h>
#include <mono/utils/w32api.h>
#include <mono/utils/unlocked.h>
+#include <mono/utils/mono-os-wait.h>
#ifndef HOST_WIN32
#include <pthread.h>
mono_gc_finalize_notify ();
/* g_print ("Waiting for pending finalizers....\n"); */
MONO_ENTER_GC_SAFE;
- WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
+ mono_win32_wait_for_single_object_ex (pending_done_event, INFINITE, TRUE);
MONO_EXIT_GC_SAFE;
/* g_print ("Done pending....\n"); */
#else
ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE);
g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
- mono_threads_add_joinable_thread (GUINT_TO_POINTER (gc_thread->tid));
+ mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid)));
break;
}
g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
- mono_threads_add_joinable_thread (GUINT_TO_POINTER (gc_thread->tid));
+ mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid)));
break;
}
#include <mono/utils/mono-time.h>
#include <mono/utils/atomic.h>
#include <mono/utils/w32api.h>
+#include <mono/utils/mono-os-wait.h>
/*
* Pull the list of opcodes
*/
MONO_ENTER_GC_SAFE;
#ifdef HOST_WIN32
- ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (event, ms, TRUE), 1);
+ ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (event, ms, TRUE), 1);
#else
ret = mono_w32handle_wait_one (event, ms, TRUE);
#endif /* HOST_WIN32 */
*/
MONO_ENTER_GC_SAFE;
#ifdef HOST_WIN32
- ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (event, 0, FALSE), 1);
+ ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (event, 0, FALSE), 1);
#else
ret = mono_w32handle_wait_one (event, 0, FALSE);
#endif /* HOST_WIN32 */
DECL_OFFSET(MonoLMF, gregs)
DECL_OFFSET(DynCallArgs, regs)
DECL_OFFSET(DynCallArgs, fpregs)
+DECL_OFFSET(DynCallArgs, n_stackargs)
DECL_OFFSET(DynCallArgs, n_fpargs)
DECL_OFFSET(DynCallArgs, n_fpret)
#endif
tid = mono_thread_info_get_tid (p);
- if (p->client_info.info.runtime_thread)
- mono_threads_add_joinable_thread ((gpointer)tid);
+ mono_threads_add_joinable_runtime_thread (&p->client_info.info);
if (mono_gc_get_gc_callbacks ()->thread_detach_func) {
mono_gc_get_gc_callbacks ()->thread_detach_func (p->client_info.runtime_data);
#include <mono/utils/mono-threads.h>
#include <mono/utils/mono-time.h>
#include <mono/utils/refcount.h>
+#include <mono/utils/mono-os-wait.h>
typedef struct {
MonoDomain *domain;
mono_monitor_exit ((MonoObject*) ares);
MONO_ENTER_GC_SAFE;
#ifdef HOST_WIN32
- WaitForSingleObjectEx (wait_event, INFINITE, TRUE);
+ mono_win32_wait_for_single_object_ex (wait_event, INFINITE, TRUE);
#else
mono_w32handle_wait_one (wait_event, MONO_INFINITE_WAIT, TRUE);
#endif
mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error);
/* Can't include utils/mono-threads.h because of the THREAD_INFO_TYPE wizardry */
+void mono_threads_add_joinable_runtime_thread (gpointer thread_info);
void mono_threads_add_joinable_thread (gpointer tid);
void mono_threads_join_threads (void);
void mono_thread_join (gpointer tid);
#include <mono/metadata/abi-details.h>
#include <mono/metadata/w32error.h>
#include <mono/utils/w32api.h>
+#include <mono/utils/mono-os-wait.h>
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#if defined(HOST_WIN32)
#include <objbase.h>
+
+extern gboolean
+mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
#endif
#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
thread->abort_exc = NULL;
thread->current_appcontext = NULL;
+ /*
+ * Prevent race condition between execution of this method and runtime shutdown.
+ * Adding runtime thread to the joinable threads list will make sure runtime shutdown
+ * won't complete until added runtime thread have exited. Owner of threads attached to the
+ * runtime but not identified as runtime threads needs to make sure thread detach calls won't
+ * race with runtime shutdown.
+ */
+ mono_threads_add_joinable_runtime_thread (thread->thread_info);
+
/*
* thread->synch_cs can be NULL if this was called after
* ves_icall_System_Threading_InternalThread_Thread_free_internal.
mono_thread_detach_internal (internal);
- return(0);
+ return 0;
}
static gsize WINAPI
else
stack_set_size = 0;
- if (!mono_thread_platform_create_thread (start_wrapper, start_info, &stack_set_size, &tid)) {
+ if (!mono_thread_platform_create_thread ((MonoThreadStart)start_wrapper, start_info, &stack_set_size, &tid)) {
/* The thread couldn't be created, so set an exception */
mono_threads_lock ();
mono_g_hash_table_remove (threads_starting_up, thread);
void
mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
{
+ MonoNativeThreadId tid = 0;
+
LOCK_THREAD (this_obj);
error_init (error);
else
this_obj->name = NULL;
-
+ if (!(this_obj->state & ThreadState_Stopped))
+ tid = thread_get_tid (this_obj);
+
UNLOCK_THREAD (this_obj);
- if (this_obj->name && this_obj->tid) {
+ if (this_obj->name && tid) {
char *tname = mono_string_to_utf8_checked (name, error);
return_if_nok (error);
- MONO_PROFILER_RAISE (thread_name, (this_obj->tid, tname));
- mono_native_thread_set_name (thread_get_tid (this_obj), tname);
+ MONO_PROFILER_RAISE (thread_name, ((uintptr_t)tid, tname));
+ mono_native_thread_set_name (tid, tname);
mono_free (tname);
}
}
return FALSE;
}
- MonoNativeThreadId tid = thread_get_tid (thread);
-
UNLOCK_THREAD (thread);
if (ms == -1)
if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
THREAD_DEBUG (g_message ("%s: join successful", __func__));
-#ifdef HOST_WIN32
- /* TODO: Do this on Unix platforms as well. See PR #5454 for context. */
/* Wait for the thread to really exit */
- MONO_ENTER_GC_SAFE;
- /* This shouldn't block */
- mono_threads_join_lock ();
- mono_native_thread_join (tid);
- mono_threads_join_unlock ();
- MONO_EXIT_GC_SAFE;
-#endif
+ MonoNativeThreadId tid = thread_get_tid (thread);
+ mono_thread_join (tid);
return TRUE;
}
MONO_ENTER_GC_SAFE;
#ifdef HOST_WIN32
if (numhandles != 1)
- ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
+ ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_multiple_objects_ex(numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
else
- ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], timeoutLeft, TRUE), 1);
+ ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (handles [0], timeoutLeft, TRUE), 1);
#else
/* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE);
MONO_ENTER_GC_SAFE;
#ifdef HOST_WIN32
- ret = mono_w32handle_convert_wait_ret (SignalObjectAndWait (toSignal, toWait, ms, TRUE), 1);
+ ret = mono_w32handle_convert_wait_ret (mono_win32_signal_object_and_wait (toSignal, toWait, ms, TRUE), 1);
#else
ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
#endif
static void
thread_detach_with_lock (MonoThreadInfo *info)
{
- return mono_gc_thread_detach_with_lock (info);
+ mono_gc_thread_detach_with_lock (info);
}
static gboolean
/* this will consume pending APC calls */
#ifdef HOST_WIN32
- WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
+ mono_win32_wait_for_single_object_ex (GetCurrentThread (), 0, TRUE);
#endif
+
/* Clear the interrupted flag of the thread so it can wait again */
mono_thread_info_clear_self_interrupt ();
/* this will awake the thread if it is in WaitForSingleObject
or similar */
- /* Our implementation of this function ignores the func argument */
#ifdef HOST_WIN32
- QueueUserAPC ((PAPCFUNC)dummy_apc, thread->native_handle, (ULONG_PTR)NULL);
+ mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid);
#else
mono_thread_info_self_interrupt ();
#endif
return info->runtime_thread == FALSE;
}
+#ifndef HOST_WIN32
+static void
+threads_native_thread_join_lock (gpointer tid, gpointer value)
+{
+ pthread_t thread = (pthread_t)tid;
+ if (thread != pthread_self ()) {
+ MONO_ENTER_GC_SAFE;
+ /* This shouldn't block */
+ mono_threads_join_lock ();
+ mono_native_thread_join (thread);
+ mono_threads_join_unlock ();
+ MONO_EXIT_GC_SAFE;
+ }
+}
+static void
+threads_native_thread_join_nolock (gpointer tid, gpointer value)
+{
+ pthread_t thread = (pthread_t)tid;
+ MONO_ENTER_GC_SAFE;
+ mono_native_thread_join (thread);
+ MONO_EXIT_GC_SAFE;
+}
+
+static void
+threads_add_joinable_thread_nolock (gpointer tid)
+{
+ g_hash_table_insert (joinable_threads, tid, tid);
+}
+#else
+static void
+threads_native_thread_join_lock (gpointer tid, gpointer value)
+{
+ MonoNativeThreadId thread_id = (MonoNativeThreadId)(guint64)tid;
+ HANDLE thread_handle = (HANDLE)value;
+ if (thread_id != GetCurrentThreadId () && thread_handle != NULL && thread_handle != INVALID_HANDLE_VALUE) {
+ MONO_ENTER_GC_SAFE;
+ /* This shouldn't block */
+ mono_threads_join_lock ();
+ mono_native_thread_join_handle (thread_handle, TRUE);
+ mono_threads_join_unlock ();
+ MONO_EXIT_GC_SAFE;
+ }
+}
+
+static void
+threads_native_thread_join_nolock (gpointer tid, gpointer value)
+{
+ HANDLE thread_handle = (HANDLE)value;
+ MONO_ENTER_GC_SAFE;
+ mono_native_thread_join_handle (thread_handle, TRUE);
+ MONO_EXIT_GC_SAFE;
+}
+
+static void
+threads_add_joinable_thread_nolock (gpointer tid)
+{
+ g_hash_table_insert (joinable_threads, tid, (gpointer)OpenThread (SYNCHRONIZE, TRUE, (MonoNativeThreadId)(guint64)tid));
+}
+#endif
+
+void
+mono_threads_add_joinable_runtime_thread (gpointer thread_info)
+{
+ g_assert (thread_info);
+ MonoThreadInfo *mono_thread_info = (MonoThreadInfo*)thread_info;
+
+ if (mono_thread_info->runtime_thread) {
+ if (InterlockedCompareExchange (&mono_thread_info->thread_pending_native_join, TRUE, FALSE) == FALSE)
+ mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info))));
+ }
+}
+
/*
* mono_add_joinable_thread:
*
void
mono_threads_add_joinable_thread (gpointer tid)
{
-#ifndef HOST_WIN32
/*
* We cannot detach from threads because it causes problems like
* 2fd16f60/r114307. So we collect them and join them when
- * we have time (in he finalizer thread).
+ * we have time (in the finalizer thread).
*/
joinable_threads_lock ();
if (!joinable_threads)
joinable_threads = g_hash_table_new (NULL, NULL);
- g_hash_table_insert (joinable_threads, tid, tid);
- UnlockedIncrement (&joinable_thread_count);
+
+ gpointer orig_key;
+ gpointer value;
+ if (!g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
+ threads_add_joinable_thread_nolock (tid);
+ UnlockedIncrement (&joinable_thread_count);
+ }
joinable_threads_unlock ();
mono_gc_finalize_notify ();
-#endif
}
/*
void
mono_threads_join_threads (void)
{
-#ifndef HOST_WIN32
GHashTableIter iter;
gpointer key;
- gpointer tid;
- pthread_t thread;
+ gpointer value;
gboolean found;
/* Fastpath */
found = FALSE;
if (g_hash_table_size (joinable_threads)) {
g_hash_table_iter_init (&iter, joinable_threads);
- g_hash_table_iter_next (&iter, &key, (void**)&tid);
- thread = (pthread_t)tid;
+ g_hash_table_iter_next (&iter, &key, (void**)&value);
g_hash_table_remove (joinable_threads, key);
UnlockedDecrement (&joinable_thread_count);
found = TRUE;
}
joinable_threads_unlock ();
- if (found) {
- if (thread != pthread_self ()) {
- MONO_ENTER_GC_SAFE;
- /* This shouldn't block */
- mono_threads_join_lock ();
- mono_native_thread_join (thread);
- mono_threads_join_unlock ();
- MONO_EXIT_GC_SAFE;
- }
- } else {
+ if (found)
+ threads_native_thread_join_lock (key, value);
+ else
break;
- }
}
-#endif
}
/*
void
mono_thread_join (gpointer tid)
{
-#ifndef HOST_WIN32
- pthread_t thread;
gboolean found = FALSE;
+ gpointer orig_key;
+ gpointer value;
joinable_threads_lock ();
if (!joinable_threads)
joinable_threads = g_hash_table_new (NULL, NULL);
- if (g_hash_table_lookup (joinable_threads, tid)) {
+
+ if (g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
g_hash_table_remove (joinable_threads, tid);
UnlockedDecrement (&joinable_thread_count);
found = TRUE;
}
joinable_threads_unlock ();
+
if (!found)
return;
- thread = (pthread_t)tid;
- MONO_ENTER_GC_SAFE;
- mono_native_thread_join (thread);
- MONO_EXIT_GC_SAFE;
-#endif
+
+ threads_native_thread_join_nolock (tid, value);
}
void
signalled = (waitall && count == nhandles) || (!waitall && count > 0);
if (signalled) {
- for (i = 0; i < nhandles; i++)
- own_if_signalled (handles [i], &abandoned [i]);
+ for (i = 0; i < nhandles; i++) {
+ if (own_if_signalled (handles [i], &abandoned [i]) && !waitall) {
+ /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
+ * throw AbandonedMutexException in case we owned it but didn't release it */
+ break;
+ }
+ }
}
mono_w32handle_unlock_handles (handles, nhandles);
&start_ticks, &user_ticks, &kernel_ticks);
ticks_to_processtime (start_ticks, creation_processtime);
- ticks_to_processtime (user_ticks, kernel_processtime);
- ticks_to_processtime (kernel_ticks, user_processtime);
+ ticks_to_processtime (kernel_ticks, kernel_processtime);
+ ticks_to_processtime (user_ticks, user_processtime);
return TRUE;
}
#include "w32socket-internals.h"
#include "utils/w32api.h"
+#include "utils/mono-os-wait.h"
#define LOGDEBUG(...)
WSAEVENT event = WSACreateEvent ();
if (event != WSA_INVALID_EVENT) {
if (WSAEventSelect (sock, event, (1 << event_bit) | FD_CLOSE) != SOCKET_ERROR) {
- LOGDEBUG (g_message ("%06d - Calling WSAWaitForMultipleEvents () on socket %d", GetCurrentThreadId (), sock));
- DWORD ret = WSAWaitForMultipleEvents (1, &event, TRUE, timeout, TRUE);
+ LOGDEBUG (g_message ("%06d - Calling mono_win32_wsa_wait_for_multiple_events () on socket %d", GetCurrentThreadId (), sock));
+ DWORD ret = mono_win32_wsa_wait_for_multiple_events (1, &event, TRUE, timeout, TRUE);
if (ret == WSA_WAIT_IO_COMPLETION) {
- LOGDEBUG (g_message ("%06d - WSAWaitForMultipleEvents () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), sock));
+ LOGDEBUG (g_message ("%06d - mono_win32_wsa_wait_for_multiple_events () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), sock));
error = WSAEINTR;
} else if (ret == WSA_WAIT_TIMEOUT) {
error = WSAETIMEDOUT;
if (error == WSA_IO_PENDING) {
error = 0;
// NOTE: .NET's Socket.SendFile() doesn't honor the Socket's SendTimeout so we shouldn't either
- DWORD ret = WaitForSingleObjectEx (overlapped.hEvent, INFINITE, TRUE);
+ DWORD ret = mono_win32_wait_for_single_object_ex (overlapped.hEvent, INFINITE, TRUE);
if (ret == WAIT_IO_COMPLETION) {
- LOGDEBUG (g_message ("%06d - WaitForSingleObjectEx () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), hSocket));
+ LOGDEBUG (g_message ("%06d - mono_win32_wait_for_single_object_ex () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), hSocket));
error = WSAEINTR;
} else if (ret == WAIT_TIMEOUT) {
error = WSAETIMEDOUT;
if (par->gshared_constraint) {
MonoGSharedGenericParam *gpar = (MonoGSharedGenericParam*)par;
encode_type (acfg, par->gshared_constraint, p, &p);
- encode_klass_ref (acfg, mono_class_from_generic_parameter (gpar->parent, NULL, klass->byval_arg.type == MONO_TYPE_MVAR), p, &p);
+ encode_klass_ref (acfg, mono_class_from_generic_parameter_internal (gpar->parent), p, &p);
} else {
encode_value (klass->byval_arg.type, p, &p);
encode_value (mono_type_get_generic_param_num (&klass->byval_arg), p, &p);
vcall2: len:40 clob:c
vcall2_reg: src1:i len:40 clob:c
vcall2_membase: src1:b len:40 clob:c
-dyn_call: src1:i src2:i len:192 clob:c
+dyn_call: src1:i src2:i len:216 clob:c
# This is different from the original JIT opcodes
float_beq: len:32
cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
}
- /* Has to use a call inst since it local regalloc expects it */
+ /* Has to use a call inst since local regalloc expects it */
MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
ins = (MonoInst*)call;
sp -= 2;
MONO_ADD_INS (cfg->cbb, ins);
cfg->param_area = MAX (cfg->param_area, cfg->backend->dyn_call_param_area);
+ /* OP_DYN_CALL might need to allocate a dynamically sized param area */
+ cfg->flags |= MONO_CFG_HAS_ALLOCA;
ip += 2;
inline_costs += 10 * num_calls++;
typedef struct {
MonoMethodSignature *sig;
CallInfo *cinfo;
+ int nstack_args;
} ArchDynCallInfo;
static gboolean
case ArgInFloatSSEReg:
case ArgInDoubleSSEReg:
case ArgValuetypeInReg:
- break;
case ArgOnStack:
- if (!(ainfo->offset + (ainfo->arg_size / 8) <= DYN_CALL_STACK_ARGS))
- return FALSE;
break;
default:
return FALSE;
{
ArchDynCallInfo *info;
CallInfo *cinfo;
+ int i;
cinfo = get_call_info (NULL, sig);
// FIXME: Preprocess the info to speed up get_dyn_call_args ().
info->sig = sig;
info->cinfo = cinfo;
+ info->nstack_args = 0;
+
+ for (i = 0; i < cinfo->nargs; ++i) {
+ ArgInfo *ainfo = &cinfo->args [i];
+ switch (ainfo->storage) {
+ case ArgOnStack:
+ info->nstack_args = MAX (info->nstack_args, ainfo->offset + (ainfo->arg_size / 8));
+ break;
+ default:
+ break;
+ }
+ }
+ /* Align to 16 bytes */
+ if (info->nstack_args & 1)
+ info->nstack_args ++;
return (MonoDynCallInfo*)info;
}
g_free (ainfo);
}
+int
+mono_arch_dyn_call_get_buf_size (MonoDynCallInfo *info)
+{
+ ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
+
+ /* Extend the 'regs' field dynamically */
+ return sizeof (DynCallArgs) + (ainfo->nstack_args * sizeof (mgreg_t));
+}
+
#define PTR_TO_GREG(ptr) (mgreg_t)(ptr)
#define GREG_TO_PTR(greg) (gpointer)(greg)
* libffi.
*/
void
-mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len)
+mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf)
{
ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
DynCallArgs *p = (DynCallArgs*)buf;
param_reg_to_index_inited = 1;
}
- g_assert (buf_len >= sizeof (DynCallArgs));
-
p->res = 0;
p->ret = ret;
+ p->nstack_args = dinfo->nstack_args;
arg_index = 0;
greg = 0;
code = emit_move_return_value (cfg, ins, code);
break;
case OP_DYN_CALL: {
- int i;
+ int i, limit_reg, index_reg, src_reg, dst_reg;
MonoInst *var = cfg->dyn_call_var;
guint8 *label;
+ guint8 *buf [16];
g_assert (var->opcode == OP_REGOFFSET);
amd64_sse_movsd_reg_membase (code, i, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, fregs) + (i * sizeof (double)));
amd64_patch (label, code);
+ /* Allocate param area */
+ /* This doesn't need to be freed since OP_DYN_CALL is never called in a loop */
+ amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, nstack_args), 8);
+ amd64_shift_reg_imm (code, X86_SHL, AMD64_RAX, 3);
+ amd64_alu_reg_reg (code, X86_SUB, AMD64_RSP, AMD64_RAX);
/* Set stack args */
- for (i = 0; i < DYN_CALL_STACK_ARGS; ++i) {
- amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS + i) * sizeof(mgreg_t)), sizeof(mgreg_t));
- amd64_mov_membase_reg (code, AMD64_RSP, i * sizeof (mgreg_t), AMD64_RAX, sizeof (mgreg_t));
- }
+ /* rax/rcx/rdx/r8/r9 is scratch */
+ limit_reg = AMD64_RAX;
+ index_reg = AMD64_RCX;
+ src_reg = AMD64_R8;
+ dst_reg = AMD64_R9;
+ amd64_mov_reg_membase (code, limit_reg, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, nstack_args), 8);
+ amd64_mov_reg_imm (code, index_reg, 0);
+ amd64_lea_membase (code, src_reg, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS) * sizeof(mgreg_t)));
+ amd64_mov_reg_reg (code, dst_reg, AMD64_RSP, 8);
+ buf [0] = code;
+ x86_jump8 (code, 0);
+ buf [1] = code;
+ amd64_mov_reg_membase (code, AMD64_RDX, src_reg, 0, 8);
+ amd64_mov_membase_reg (code, dst_reg, 0, AMD64_RDX, 8);
+ amd64_alu_reg_imm (code, X86_ADD, index_reg, 1);
+ amd64_alu_reg_imm (code, X86_ADD, src_reg, 8);
+ amd64_alu_reg_imm (code, X86_ADD, dst_reg, 8);
+ amd64_patch (buf [0], code);
+ amd64_alu_reg_reg (code, X86_CMP, index_reg, limit_reg);
+ buf [2] = code;
+ x86_branch8 (code, X86_CC_LT, 0, FALSE);
+ amd64_patch (buf [2], buf [1]);
/* Set argument registers */
for (i = 0; i < PARAM_REGS; ++i)
- amd64_mov_reg_membase (code, param_regs [i], AMD64_R11, i * sizeof(mgreg_t), sizeof(mgreg_t));
+ amd64_mov_reg_membase (code, param_regs [i], AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, regs) + (i * sizeof(mgreg_t)), sizeof(mgreg_t));
/* Make the call */
amd64_call_reg (code, AMD64_R10);
gpointer bp_addrs [MONO_ZERO_LEN_ARRAY];
} SeqPointInfo;
-#define DYN_CALL_STACK_ARGS 6
-
typedef struct {
- mgreg_t regs [PARAM_REGS + DYN_CALL_STACK_ARGS];
mgreg_t res;
guint8 *ret;
double fregs [8];
mgreg_t has_fp;
+ mgreg_t nstack_args;
guint8 buffer [256];
+ /* This should come last as the structure is dynamically extended */
+ mgreg_t regs [PARAM_REGS];
} DynCallArgs;
typedef enum {
#define MONO_ARCH_GSHARED_SUPPORTED 1
#define MONO_ARCH_DYN_CALL_SUPPORTED 1
-#define MONO_ARCH_DYN_CALL_PARAM_AREA (DYN_CALL_STACK_ARGS * 8)
+#define MONO_ARCH_DYN_CALL_PARAM_AREA 0
#define MONO_ARCH_LLVM_SUPPORTED 1
#define MONO_ARCH_HAVE_CARD_TABLE_WBARRIER 1
g_free (ainfo);
}
+int
+mono_arch_dyn_call_get_buf_size (MonoDynCallInfo *info)
+{
+ return sizeof (DynCallArgs);
+}
+
void
-mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len)
+mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf)
{
ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
DynCallArgs *p = (DynCallArgs*)buf;
int arg_index, greg, i, j, pindex;
MonoMethodSignature *sig = dinfo->sig;
- g_assert (buf_len >= sizeof (DynCallArgs));
-
p->res = 0;
p->ret = ret;
p->has_fpregs = 0;
{
int i;
- if (sig->hasthis + sig->param_count > PARAM_REGS + DYN_CALL_STACK_ARGS)
- return FALSE;
-
// FIXME: Add more cases
switch (cinfo->ret.storage) {
case ArgNone:
case ArgInFRegR4:
case ArgHFA:
case ArgVtypeByRef:
- break;
case ArgOnStack:
- if (ainfo->offset >= DYN_CALL_STACK_ARGS * sizeof (mgreg_t))
- return FALSE;
break;
default:
return FALSE;
g_free (ainfo);
}
+int
+mono_arch_dyn_call_get_buf_size (MonoDynCallInfo *info)
+{
+ ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
+
+ g_assert (ainfo->cinfo->stack_usage % MONO_ARCH_FRAME_ALIGNMENT == 0);
+ return sizeof (DynCallArgs) + ainfo->cinfo->stack_usage;
+}
+
static double
bitcast_r4_to_r8 (float f)
{
}
void
-mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len)
+mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf)
{
ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
DynCallArgs *p = (DynCallArgs*)buf;
CallInfo *cinfo = dinfo->cinfo;
int buffer_offset = 0;
- g_assert (buf_len >= sizeof (DynCallArgs));
-
p->res = 0;
p->ret = ret;
p->n_fpargs = dinfo->n_fpargs;
p->n_fpret = dinfo->n_fpret;
+ p->n_stackargs = cinfo->stack_usage / sizeof (mgreg_t);
arg_index = 0;
greg = 0;
code = emit_ldrfpx (code, ARMREG_D0 + i, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, fpregs) + (i * 8));
arm_patch_rel (labels [0], code, MONO_R_ARM64_BCC);
+ /* Allocate callee area */
+ code = emit_ldrx (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_stackargs));
+ arm_lslw (code, ARMREG_R0, ARMREG_R0, 3);
+ arm_movspx (code, ARMREG_R1, ARMREG_SP);
+ arm_subx (code, ARMREG_R1, ARMREG_R1, ARMREG_R0);
+ arm_movspx (code, ARMREG_SP, ARMREG_R1);
+
/* Set stack args */
- for (i = 0; i < DYN_CALL_STACK_ARGS; ++i) {
- code = emit_ldrx (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS + 1 + i) * sizeof (mgreg_t)));
- code = emit_strx (code, ARMREG_R0, ARMREG_SP, i * sizeof (mgreg_t));
- }
+ /* R1 = limit */
+ code = emit_ldrx (code, ARMREG_R1, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_stackargs));
+ /* R2 = pointer into 'regs' */
+ code = emit_imm (code, ARMREG_R2, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS + 1) * sizeof (mgreg_t)));
+ arm_addx (code, ARMREG_R2, ARMREG_LR, ARMREG_R2);
+ /* R3 = pointer to stack */
+ arm_movspx (code, ARMREG_R3, ARMREG_SP);
+ labels [0] = code;
+ arm_b (code, code);
+ labels [1] = code;
+ code = emit_ldrx (code, ARMREG_R5, ARMREG_R2, 0);
+ code = emit_strx (code, ARMREG_R5, ARMREG_R3, 0);
+ code = emit_addx_imm (code, ARMREG_R2, ARMREG_R2, sizeof (mgreg_t));
+ code = emit_addx_imm (code, ARMREG_R3, ARMREG_R3, sizeof (mgreg_t));
+ code = emit_subx_imm (code, ARMREG_R1, ARMREG_R1, 1);
+ arm_patch_rel (labels [0], code, MONO_R_ARM64_B);
+ arm_cmpw (code, ARMREG_R1, ARMREG_RZR);
+ arm_bcc (code, ARMCOND_GT, labels [1]);
/* Set argument registers + r8 */
- code = mono_arm_emit_load_regarray (code, 0x1ff, ARMREG_LR, 0);
+ code = mono_arm_emit_load_regarray (code, 0x1ff, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, regs));
/* Make the call */
arm_blrx (code, ARMREG_IP1);
#define PARAM_REGS 8
#define FP_PARAM_REGS 8
-#define DYN_CALL_STACK_ARGS 6
-
typedef struct {
- /* The +1 is for r8 */
- mgreg_t regs [PARAM_REGS + 1 + DYN_CALL_STACK_ARGS];
mgreg_t res, res2;
guint8 *ret;
double fpregs [FP_PARAM_REGS];
- int n_fpargs, n_fpret;
+ int n_fpargs, n_fpret, n_stackargs;
guint8 buffer [256];
+ /* This should come last as the structure is dynamically extended */
+ /* The +1 is for r8 */
+ mgreg_t regs [PARAM_REGS + 1];
} DynCallArgs;
typedef struct {
#define MONO_ARCH_HAVE_EXCEPTIONS_INIT 1
#define MONO_ARCH_HAVE_GET_TRAMPOLINES 1
#define MONO_ARCH_DYN_CALL_SUPPORTED 1
-#define MONO_ARCH_DYN_CALL_PARAM_AREA (DYN_CALL_STACK_ARGS * 8)
+#define MONO_ARCH_DYN_CALL_PARAM_AREA 0
#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
#define MONO_ARCH_GSHAREDVT_SUPPORTED 1
#define MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX 1
if (mono_class_is_contextbound (method->klass) || !info->compiled_method)
supported = FALSE;
- if (supported)
+ if (supported) {
info->dyn_call_info = mono_arch_dyn_call_prepare (sig);
+ if (debug_options.dyn_runtime_invoke)
+ g_assert (info->dyn_call_info);
+ }
}
#endif
MonoMethodSignature *sig = mono_method_signature (method);
gpointer *args;
static RuntimeInvokeDynamicFunction dyn_runtime_invoke;
- int i, pindex;
- guint8 buf [512];
+ int i, pindex, buf_size;
+ guint8 *buf;
guint8 retval [256];
if (!dyn_runtime_invoke) {
//printf ("M: %s\n", mono_method_full_name (method, TRUE));
- mono_arch_start_dyn_call (info->dyn_call_info, (gpointer**)args, retval, buf, sizeof (buf));
+ buf_size = mono_arch_dyn_call_get_buf_size (info->dyn_call_info);
+ buf = g_alloca (buf_size);
+ g_assert (buf);
+
+ mono_arch_start_dyn_call (info->dyn_call_info, (gpointer**)args, retval, buf);
dyn_runtime_invoke (buf, exc, info->compiled_method);
mono_arch_finish_dyn_call (info->dyn_call_info, buf);
void mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val);
MonoDynCallInfo *mono_arch_dyn_call_prepare (MonoMethodSignature *sig);
void mono_arch_dyn_call_free (MonoDynCallInfo *info);
-void mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len);
+int mono_arch_dyn_call_get_buf_size (MonoDynCallInfo *info);
+void mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf);
void mono_arch_finish_dyn_call (MonoDynCallInfo *info, guint8 *buf);
MonoInst *mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args);
void mono_arch_decompose_opts (MonoCompile *cfg, MonoInst *ins);
ENTER_LOG (&sync_points_ctr, logbuffer,
EVENT_SIZE /* event */ +
- LEB128_SIZE /* type */
+ BYTE_SIZE /* type */
);
emit_event (logbuffer, TYPE_META | TYPE_SYNC_POINT);
EVENT_SIZE /* event */ +
BYTE_SIZE /* clause type */ +
LEB128_SIZE /* clause num */ +
- LEB128_SIZE /* method */
+ LEB128_SIZE /* method */ +
+ LEB128_SIZE /* exc */
);
emit_event (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
LEB128_SIZE /* load address */ +
LEB128_SIZE /* offset */ +
LEB128_SIZE /* size */ +
- nlen /* file name */
+ len /* file name */
);
emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_UBIN);
;
for (agent = log_profiler.counters; agent; agent = agent->next) {
+ /*
+ * FIXME: This calculation is incorrect for string counters since
+ * mono_counter_get_size () just returns 0 in that case. We should
+ * address this if we ever actually add any string counters to Mono.
+ */
+
size +=
LEB128_SIZE /* index */ +
BYTE_SIZE /* type */ +
imt_big_iface_test.cs \
bug-58782-plain-throw.cs \
bug-58782-capture-and-throw.cs \
- recursive-struct-arrays.cs
+ recursive-struct-arrays.cs \
+ bug-59281.cs
if AMD64
TESTS_CS_SRC += async-exc-compilation.cs finally_guard.cs finally_block_ending_in_dead_bb.cs
--- /dev/null
+using System;
+using System.Threading;
+
+class Driver
+{
+
+ static readonly Mutex[] mutexes = new Mutex[2];
+
+ public static void Main(string[] args)
+ {
+ for (int i = 0; i < mutexes.Length; i++) {
+ mutexes [i] = new Mutex();
+ }
+
+ Thread thread1 = new Thread(() => {
+ for (int i = 0; i < 1; i++) {
+ int idx = -1;
+ try {
+ idx = WaitHandle.WaitAny (mutexes);
+ Console.WriteLine($"Thread 1 iter: {i} with mutex: {idx}");
+ } finally {
+ if (idx != -1)
+ mutexes [idx].ReleaseMutex();
+ }
+ }
+
+ Console.WriteLine("Thread 1 ended");
+ });
+
+ thread1.Start();
+ thread1.Join();
+
+ Thread thread2 = new Thread(() => {
+ for (int i = 0; i < 1000; i++) {
+ int idx = -1;
+ try {
+ idx = WaitHandle.WaitAny (mutexes);
+ Console.WriteLine($"Thread 2 iter: {i} with mutex: {idx}");
+ } finally {
+ if (idx != -1)
+ mutexes [idx].ReleaseMutex();
+ }
+ }
+
+ Console.WriteLine("Thread 2 ended");
+ });
+
+ thread2.Start();
+ thread2.Join();
+ }
+}
\ No newline at end of file
D
}
+struct AStruct {
+ public int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
+ public int a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+}
+
class Tests
{
public static Enum1 return_enum1 () {
return UInt64.MaxValue - 5;
}
+ public static object return_t<T> (T t) {
+ return (object)t;
+ }
+
static int Main (string[] args)
{
return TestDriver.RunTests (typeof (Tests), args);
else
return 1;
}
+
+ public static int test_0_large_arg ()
+ {
+ var arg = new AStruct ();
+ arg.a1 = 1;
+ arg.a2 = 2;
+ arg.a3 = 3;
+ arg.a20 = 20;
+ var res = typeof (Tests).GetMethod ("return_t").MakeGenericMethod (new Type [] { typeof (AStruct) }).Invoke (null, new object [] { arg });
+ var arg2 = (AStruct)res;
+ if (arg2.a20 == 20)
+ return 0;
+ else
+ return 1;
+ }
}
if HOST_WIN32
win32_sources = \
- os-event-win32.c
+ os-event-win32.c \
+ mono-os-wait-win32.c
platform_sources = $(win32_sources)
else
mono-mmap-windows-internals.h \
mono-os-mutex.h \
mono-os-mutex.c \
+ mono-os-wait.h \
mono-coop-mutex.h \
mono-once.h \
mono-lazy-init.h \
#elif !defined(HOST_WIN32) && defined(HAVE_SEMAPHORE_H)
#include <semaphore.h>
#else
-#include <winsock2.h>
-#include <windows.h>
+#include <mono/utils/mono-os-wait.h>
#endif
#define MONO_HAS_SEMAPHORES 1
BOOL res;
retry:
- res = WaitForSingleObjectEx (*sem, timeout_ms, flags & MONO_SEM_FLAGS_ALERTABLE);
+ res = mono_win32_wait_for_single_object_ex (*sem, timeout_ms, flags & MONO_SEM_FLAGS_ALERTABLE);
if (G_UNLIKELY (res != WAIT_OBJECT_0 && res != WAIT_IO_COMPLETION && res != WAIT_TIMEOUT))
- g_error ("%s: WaitForSingleObjectEx failed with error %d", __func__, GetLastError ());
+ g_error ("%s: mono_win32_wait_for_single_object_ex failed with error %d", __func__, GetLastError ());
if (res == WAIT_IO_COMPLETION && !(flags & MONO_SEM_FLAGS_ALERTABLE))
goto retry;
--- /dev/null
+/**
+* \file
+* Win32 OS wait wrappers and interrupt/abort APC handling.
+*
+* Author:
+* Johan Lorensson (lateralusx.github@gmail.com)
+*
+* Licensed under the MIT license. See LICENSE file in the project root for full license information.
+*/
+
+#include <mono/utils/mono-os-wait.h>
+#include <mono/utils/mono-threads.h>
+#include <mono/utils/mono-threads-debug.h>
+
+enum ThreadWaitInfo {
+ THREAD_WAIT_INFO_CLEARED = 0,
+ THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT = 1 << 0,
+ THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT = 1 << 1,
+ THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT = 1 << 2
+};
+
+static inline void
+request_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid)
+{
+ /*
+ * On Windows platforms, an async interrupt/abort request queues an APC
+ * that needs to be processed by target thread before it can return from an
+ * alertable OS wait call and complete the mono interrupt/abort request.
+ * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread
+ * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete
+ * This check makes sure that only one APC per type gets queued, preventing potential flooding
+ * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in
+ * an alertable wait or not. This is done to prevent races between interrupt/abort requests and
+ * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION
+ * return scenarios and restart the alertable wait operation if needed or take other actions
+ * (like service the interrupt/abort request).
+ */
+ MonoThreadInfo *info = (MonoThreadInfo *)thread_info;
+ gint32 old_wait_info, new_wait_info;
+
+ do {
+ old_wait_info = InterlockedRead (&info->thread_wait_info);
+ if (old_wait_info & pending_apc_slot)
+ return;
+
+ new_wait_info = old_wait_info | pending_apc_slot;
+ } while (InterlockedCompareExchange (&info->thread_wait_info, new_wait_info, old_wait_info) != old_wait_info);
+
+ THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid);
+ QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL);
+}
+
+static void CALLBACK
+interrupt_apc (ULONG_PTR param)
+{
+ THREADS_INTERRUPT_DEBUG ("%06d - interrupt_apc () called", GetCurrentThreadId ());
+}
+
+void
+mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
+{
+ request_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT, interrupt_apc, tid);
+}
+
+static void CALLBACK
+abort_apc (ULONG_PTR param)
+{
+ THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
+}
+
+void
+mono_win32_abort_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
+{
+ request_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT, abort_apc, tid);
+}
+
+static inline void
+enter_alertable_wait (MonoThreadInfo *info)
+{
+ // Clear any previous flags. Set alertable wait flag.
+ InterlockedExchange (&info->thread_wait_info, THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT);
+}
+
+static inline void
+leave_alertable_wait (MonoThreadInfo *info)
+{
+ // Clear any previous flags. Thread is exiting alertable wait state, and info around pending interrupt/abort APC's
+ // can now be discarded as well, thread is out of wait operation and can proceed it's execution.
+ InterlockedExchange (&info->thread_wait_info, THREAD_WAIT_INFO_CLEARED);
+}
+
+DWORD
+mono_win32_sleep_ex (DWORD timeout, BOOL alertable)
+{
+ DWORD result = WAIT_FAILED;
+ MonoThreadInfo *info = mono_thread_info_current_unchecked ();
+
+ if (alertable && info) {
+ enter_alertable_wait (info);
+ }
+
+ result = SleepEx (timeout, alertable);
+
+ // NOTE, leave_alertable_wait should not affect GetLastError but
+ // if changed, GetLastError needs to be preserved and reset before returning.
+ if (alertable && info) {
+ leave_alertable_wait (info);
+ }
+
+ return result;
+}
+
+DWORD
+mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable)
+{
+ DWORD result = WAIT_FAILED;
+ MonoThreadInfo *info = mono_thread_info_current_unchecked ();
+
+ if (alertable && info) {
+ enter_alertable_wait (info);
+ }
+
+ result = WaitForSingleObjectEx (handle, timeout, alertable);
+
+ // NOTE, leave_alertable_wait should not affect GetLastError but
+ // if changed, GetLastError needs to be preserved and reset before returning.
+ if (alertable && info) {
+ leave_alertable_wait (info);
+ }
+
+ return result;
+}
+
+DWORD
+mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
+{
+ DWORD result = WAIT_FAILED;
+ MonoThreadInfo *info = mono_thread_info_current_unchecked ();
+
+ if (alertable && info) {
+ enter_alertable_wait (info);
+ }
+
+ result = WaitForMultipleObjectsEx (count, handles, waitAll, timeout, alertable);
+
+ // NOTE, leave_alertable_wait should not affect GetLastError but
+ // if changed, GetLastError needs to be preserved and reset before returning.
+ if (alertable && info) {
+ leave_alertable_wait (info);
+ }
+
+ return result;
+}
+
+DWORD
+mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable)
+{
+ DWORD result = WAIT_FAILED;
+ MonoThreadInfo *info = mono_thread_info_current_unchecked ();
+
+ if (alertable && info) {
+ enter_alertable_wait (info);
+ }
+
+ result = SignalObjectAndWait (toSignal, toWait, timeout, alertable);
+
+ // NOTE, leave_alertable_wait should not affect GetLastError but
+ // if changed, GetLastError needs to be preserved and reset before returning.
+ if (alertable && info) {
+ leave_alertable_wait (info);
+ }
+
+ return result;
+}
+
+DWORD
+mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags)
+{
+ DWORD result = WAIT_FAILED;
+ MonoThreadInfo *info = mono_thread_info_current_unchecked ();
+ BOOL alertable = flags & MWMO_ALERTABLE;
+
+ if (alertable && info) {
+ enter_alertable_wait (info);
+ }
+
+ result = MsgWaitForMultipleObjectsEx (count, handles, timeout, wakeMask, flags);
+
+ // NOTE, leave_alertable_wait should not affect GetLastError but
+ // if changed, GetLastError needs to be preserved and reset before returning.
+ if (alertable && info) {
+ leave_alertable_wait (info);
+ }
+
+ return result;
+}
+
+DWORD
+mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
+{
+ DWORD result = WAIT_FAILED;
+ MonoThreadInfo *info = mono_thread_info_current_unchecked ();
+
+ if (alertable && info) {
+ enter_alertable_wait (info);
+ }
+
+ result = WSAWaitForMultipleEvents (count, handles, waitAll, timeout, alertable);
+
+ // NOTE, leave_alertable_wait should not affect GetLastError but
+ // if changed, GetLastError needs to be preserved and reset before returning.
+ if (alertable && info) {
+ leave_alertable_wait (info);
+ }
+
+ return result;
+}
--- /dev/null
+/**
+* \file
+*/
+
+#ifndef _MONO_UTILS_OS_WAIT_H_
+#define _MONO_UTILS_OS_WAIT_H_
+
+#include <config.h>
+#ifdef HOST_WIN32
+
+#include <winsock2.h>
+#include <windows.h>
+
+DWORD
+mono_win32_sleep_ex (DWORD timeout, BOOL alertable);
+
+DWORD
+mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable);
+
+DWORD
+mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable);
+
+DWORD
+mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable);
+
+DWORD
+mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags);
+
+DWORD
+mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable);
+
+void
+mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid);
+
+void
+mono_win32_abort_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid);
+
+#endif
+
+#endif /* _MONO_UTILS_OS_WAIT_H_ */
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-threads-debug.h>
+#include <mono/utils/mono-os-wait.h>
#include <limits.h>
-
void
mono_threads_suspend_init (void)
{
}
-static void CALLBACK
-interrupt_apc (ULONG_PTR param)
-{
-}
-
gboolean
mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
{
info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
if (info->suspend_can_continue) {
- //FIXME do we need to QueueUserAPC on this case?
if (interrupt_kernel)
- QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
+ mono_win32_interrupt_wait (info, handle, id);
} else {
THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
}
return info->suspend_can_continue;
}
-static void CALLBACK
-abort_apc (ULONG_PTR param)
-{
- THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
-}
+
void
mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
g_assert (handle);
- THREADS_INTERRUPT_DEBUG ("%06d - Aborting syscall in thread %06d", GetCurrentThreadId (), id);
- QueueUserAPC ((PAPCFUNC)abort_apc, handle, (ULONG_PTR)NULL);
+ mono_win32_abort_wait (info, handle, id);
CloseHandle (handle);
}
return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
}
+gboolean
+mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle)
+{
+ DWORD res = WaitForSingleObject (thread_handle, INFINITE);
+
+ if (close_handle)
+ CloseHandle (thread_handle);
+
+ return res != WAIT_FAILED;
+}
+
gboolean
mono_native_thread_join (MonoNativeThreadId tid)
{
HANDLE handle;
- if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
+ if (!(handle = OpenThread (SYNCHRONIZE, TRUE, tid)))
return FALSE;
- DWORD res = WaitForSingleObject (handle, INFINITE);
-
- CloseHandle (handle);
-
- return res != WAIT_FAILED;
+ return mono_native_thread_join_handle (handle, TRUE);
}
#if HAVE_DECL___READFSDWORD==0
* handled a sampling signal before sending another one.
*/
gint32 profiler_signal_ack;
+
+ gint32 thread_pending_native_join;
+
+#ifdef USE_WINDOWS_BACKEND
+ gint32 thread_wait_info;
+#endif
+
} MonoThreadInfo;
typedef struct {
#include <winbase.h>
#include "atomic.h"
+#include "mono-os-wait.h"
void
mono_os_event_init (MonoOSEvent *event, gboolean initial)
g_assert (event);
g_assert (event->handle);
- res = WaitForSingleObjectEx (event->handle, timeout, alertable);
+ res = mono_win32_wait_for_single_object_ex (event->handle, timeout, alertable);
if (res == WAIT_OBJECT_0)
return MONO_OS_EVENT_WAIT_RET_SUCCESS_0;
else if (res == WAIT_IO_COMPLETION)
else if (res == WAIT_TIMEOUT)
return MONO_OS_EVENT_WAIT_RET_TIMEOUT;
else if (res == WAIT_FAILED)
- g_error ("%s: WaitForSingleObjectEx failed with error %d", __func__, GetLastError ());
+ g_error ("%s: mono_thread_win32_wait_one_handle failed with error %d", __func__, GetLastError ());
else
g_error ("%s: unknown res value %d", __func__, res);
}
handles [i] = events [i]->handle;
}
- res = WaitForMultipleObjectsEx (nevents, handles, waitall, timeout, alertable);
+ res = mono_win32_wait_for_multiple_objects_ex ((DWORD)nevents, handles, waitall, timeout, alertable);
if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_0 + MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS)
return MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + (res - WAIT_OBJECT_0);
else if (res == WAIT_IO_COMPLETION)
else if (res == WAIT_TIMEOUT)
return MONO_OS_EVENT_WAIT_RET_TIMEOUT;
else if (res == WAIT_FAILED)
- g_error ("%s: WaitForSingleObjectEx failed with error %d", __func__, GetLastError ());
+ g_error ("%s: mono_thread_win32_wait_multiple_handle failed with error %d", __func__, GetLastError ());
else
g_error ("%s: unknown res value %d", __func__, res);
}
#if MONO_HAS_CLANG_THREAD_SANITIZER
#define MONO_UNLOCKED_ATTRS MONO_NO_SANITIZE_THREAD MONO_NEVER_INLINE static
+#elif defined(_MSC_VER)
+#define MONO_UNLOCKED_ATTRS MONO_ALWAYS_INLINE static
#else
#define MONO_UNLOCKED_ATTRS MONO_ALWAYS_INLINE static inline
#endif
-<?xml version="1.0" encoding="utf-8"?>\r
+<?xml version="1.0" encoding="utf-8"?>\r
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
<ItemGroup Label="ProjectConfigurations">\r
<ProjectConfiguration Include="Debug|Win32">\r
<ClCompile Include="..\mono\utils\mono-mmap-windows.c" />\r
<ClCompile Include="..\mono\utils\mono-mmap.c" />\r
<ClCompile Include="..\mono\utils\mono-networkinterfaces.c" />\r
+ <ClCompile Include="..\mono\utils\mono-os-wait-win32.c" />\r
<ClCompile Include="..\mono\utils\mono-proclib-windows.c" />\r
<ClCompile Include="..\mono\utils\mono-rand-windows.c" />\r
<ClCompile Include="..\mono\utils\mono-rand.c" />\r
<ClInclude Include="..\mono\utils\mono-once.h" />\r
<ClInclude Include="..\mono\utils\mono-os-mutex.h" />\r
<ClInclude Include="..\mono\utils\mono-os-semaphore.h" />\r
+ <ClInclude Include="..\mono\utils\mono-os-wait.h" />\r
<ClInclude Include="..\mono\utils\mono-path.h" />\r
<ClInclude Include="..\mono\utils\mono-poll.h" />\r
<ClInclude Include="..\mono\utils\mono-proclib-windows-internals.h" />\r
<ClInclude Include="..\mono\utils\strenc.h" />\r
<ClInclude Include="..\mono\utils\valgrind.h" />\r
<ClInclude Include="..\mono\utils\atomic.h" />\r
- <ClInclude Include="..\mono\utils\unlocked.h" />
+ <ClInclude Include="..\mono\utils\unlocked.h" />\r
<ClInclude Include="..\mono\utils\mono-hwcap.h" />\r
<ClInclude Include="..\mono\utils\mono-hwcap-x86.h" />\r
<ClInclude Include="..\mono\utils\bsearch.h" />\r
<ClCompile Include="..\mono\utils\mono-os-mutex.c">\r
<Filter>Source Files</Filter>\r
</ClCompile>\r
+ <ClCompile Include="..\mono\utils\mono-os-wait-win32.c">\r
+ <Filter>Source Files</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="..\mono\utils\atomic.h">\r
<ClInclude Include="..\mono\utils\os-event.h">\r
<Filter>Header Files</Filter>\r
</ClInclude>\r
- <ClInclude Include="..\mono\utils\unlocked.h">
- <Filter>Header Files</Filter>
- </ClInclude>
+ <ClInclude Include="..\mono\utils\unlocked.h">\r
+ <Filter>Header Files</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\mono\utils\mono-os-wait.h">\r
+ <Filter>Header Files</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<Filter Include="Header Files">\r