From 79deed4382ae05021182d18306d94c592db7d2c2 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 27 Feb 2023 06:56:25 -0800 Subject: [PATCH] Fix pthread_cond_wait race on macOS The native runtime event implementations for nativeaot and GC use pthread_cond_wait to wait for the event and pthread_cond_broadcast to signal that the event was set. While the usage of the pthread_cond_broadcast conforms with the documentation, it turns out that glibc before 2.25 had a race in the implementation that can cause the pthread_cond_broadcast to be unnoticed and the wait waiting forever. It turns out that macOS implementation has the same issue. The fix for the issue is to call pthread_cond_broadcast while the related mutex is taken. This change fixes intermittent crossgen2 hangs with nativeaot build of crossgen2 reported in #81570. I was able to repro the hang locally in tens of thousands of iterations of running crossgen2 without any arguments (the hang occurs when server GC creates threads). With this fix, it ran without problems over the weekend, passing 5.5 million iterations. --- src/coreclr/gc/unix/events.cpp | 3 +-- src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/gc/unix/events.cpp b/src/coreclr/gc/unix/events.cpp index 767b414f04c174..06dc1d9b172018 100644 --- a/src/coreclr/gc/unix/events.cpp +++ b/src/coreclr/gc/unix/events.cpp @@ -220,10 +220,9 @@ class GCEvent::Impl { pthread_mutex_lock(&m_mutex); m_state = true; - pthread_mutex_unlock(&m_mutex); - // Unblock all threads waiting for the condition variable pthread_cond_broadcast(&m_condition); + pthread_mutex_unlock(&m_mutex); } void Reset() diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index af0ba99e4380c3..0d19436d3b84a2 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -316,10 +316,9 @@ class UnixEvent { pthread_mutex_lock(&m_mutex); m_state = true; - pthread_mutex_unlock(&m_mutex); - // Unblock all threads waiting for the condition variable pthread_cond_broadcast(&m_condition); + pthread_mutex_unlock(&m_mutex); } void Reset()