From e2fd20d8412e3da4a1081f9f1dcd3e60f63d3bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Fri, 12 Jan 2024 17:37:06 -0500 Subject: [PATCH] [mono] Fix class initialization spurious wakeups (#96903) * mono_runtime_class_init_full: handle spurious wakeups the condition variable may be signaled even if the initialization by the other thread is not done yet. Handle spurious wakeups the same way as timeouts: go around once more from the beginning. Fixes https://github.com/dotnet/runtime/issues/96872 and https://github.com/dotnet/runtime/issues/96804 * fix unbalanced handle frames if we goto retry_top, don't set up a new handle frame that lacks a matching HANDLE_FUNCTION_RETURN_VAL. Instead setup the handle frame once upfront --- src/mono/mono/metadata/object.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index 7740bc142a3220..e39bece1f9e508 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -457,21 +457,24 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error) * on this cond var. */ + HANDLE_FUNCTION_ENTER (); + retry_top: + (void)0; // appease C compiler; label must preceed a statement not a var declaration + + gboolean ret = FALSE; + mono_type_initialization_lock (); /* double check... */ if (vtable->initialized) { mono_type_initialization_unlock (); - return TRUE; + goto return_true; } gboolean do_initialization = FALSE; TypeInitializationLock *lock = NULL; gboolean pending_tae = FALSE; - gboolean ret = FALSE; - - HANDLE_FUNCTION_ENTER (); if (vtable->init_failed) { /* The type initialization already failed once, rethrow the same exception */ @@ -614,8 +617,8 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error) if (!lock->done) { int timeout_ms = 500; int wait_result = mono_coop_cond_timedwait (&lock->cond, &lock->mutex, timeout_ms); - if (wait_result == -1) { - /* timed out - go around again from the beginning. If we got here + if (wait_result == -1 || (wait_result == 0 && !lock->done)) { + /* timed out or spurious wakeup - go around again from the beginning. If we got here * from the "is_blocked = FALSE" case, above (another thread was * blocked on the current thread, but on a lock that was already * done but it didn't get to wake up yet), then it might still be @@ -646,7 +649,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error) g_hash_table_remove (type_initialization_hash, vtable); mono_type_initialization_unlock (); goto retry_top; - } else if (wait_result == 0) { + } else if (wait_result == 0 && lock->done) { /* Success: we were signaled that the other thread is done. Proceed */ } else { g_assert_not_reached ();