diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index 34cc3ccc252..3d8cf0942dd 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -1,3 +1,7 @@ +#include +#include +#include + #include #include #include @@ -7,6 +11,41 @@ using namespace xamarin::android; +void GCBridge::initialize_shared_args_semaphore () noexcept +{ + int ret = sem_init (&shared_args_semaphore, 0, 0); + abort_unless (ret == 0, "Failed to initialize GC bridge semaphore"); +} + +void GCBridge::start_bridge_processing_thread () noexcept +{ + pthread_t thread {}; + int ret = pthread_create (&thread, nullptr, bridge_processing_thread_entry, nullptr); + abort_unless (ret == 0, "Failed to create GC bridge processing thread"); + + ret = pthread_detach (thread); + abort_unless (ret == 0, "Failed to detach GC bridge processing thread"); +} + +void GCBridge::publish_shared_args (MarkCrossReferencesArgs *args) noexcept +{ + __atomic_store_n (&shared_args, args, __ATOMIC_RELEASE); + + int ret = sem_post (&shared_args_semaphore); + abort_unless (ret == 0, "Failed to release GC bridge semaphore"); +} + +auto GCBridge::wait_for_shared_args () noexcept -> MarkCrossReferencesArgs* +{ + int ret; + do { + ret = sem_wait (&shared_args_semaphore); + } while (ret == -1 && errno == EINTR); + abort_unless (ret == 0, "Failed to acquire GC bridge semaphore"); + + return __atomic_load_n (&shared_args, __ATOMIC_ACQUIRE); +} + void GCBridge::initialize_on_onload (JNIEnv *env) noexcept { abort_if_invalid_pointer_argument (env, "env"); @@ -55,8 +94,7 @@ void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept abort_unless (args->CrossReferences != nullptr || args->CrossReferenceCount == 0, "CrossReferences must not be null if CrossReferenceCount is greater than 0"); log_mark_cross_references_args_if_enabled (args); - shared_args.store (args); - shared_args_semaphore.release (); + publish_shared_args (args); } void GCBridge::bridge_processing () noexcept @@ -66,8 +104,7 @@ void GCBridge::bridge_processing () noexcept while (true) { // wait until mark cross references args are set by the GC callback - shared_args_semaphore.acquire (); - MarkCrossReferencesArgs *args = shared_args.load (); + MarkCrossReferencesArgs *args = wait_for_shared_args (); bridge_processing_started_callback (args); @@ -78,6 +115,12 @@ void GCBridge::bridge_processing () noexcept } } +auto GCBridge::bridge_processing_thread_entry ([[maybe_unused]] void *arg) noexcept -> void* +{ + bridge_processing (); + return nullptr; +} + [[gnu::always_inline]] void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArgs *args) noexcept { diff --git a/src/native/clr/include/host/gc-bridge.hh b/src/native/clr/include/host/gc-bridge.hh index 80c0ac68132..2ec7bade985 100644 --- a/src/native/clr/include/host/gc-bridge.hh +++ b/src/native/clr/include/host/gc-bridge.hh @@ -1,11 +1,8 @@ #pragma once -#include +#include + #include -#include -#include -#include -#include #include @@ -72,8 +69,8 @@ namespace xamarin::android { GCBridge::bridge_processing_started_callback = bridge_processing_started; GCBridge::bridge_processing_finished_callback = bridge_processing_finished; - bridge_processing_thread = std::thread { GCBridge::bridge_processing }; - bridge_processing_thread.detach (); + initialize_shared_args_semaphore (); + start_bridge_processing_thread (); return mark_cross_references; } @@ -81,10 +78,11 @@ namespace xamarin::android { static void trigger_java_gc (JNIEnv *env) noexcept; private: - static inline std::thread bridge_processing_thread {}; - - static inline std::binary_semaphore shared_args_semaphore{0}; - static inline std::atomic shared_args; + static inline sem_t shared_args_semaphore {}; + // JavaMarshal serializes bridge rounds: it does not publish another argument block until + // bridge_processing_finished_callback has completed. This is therefore a single-slot + // handoff; the semaphore signals availability but does not queue distinct argument blocks. + static inline MarkCrossReferencesArgs *shared_args = nullptr; static inline jobject Runtime_instance = nullptr; static inline jmethodID Runtime_gc = nullptr; @@ -92,7 +90,13 @@ namespace xamarin::android { static inline BridgeProcessingStartedFtn bridge_processing_started_callback = nullptr; static inline BridgeProcessingFinishedFtn bridge_processing_finished_callback = nullptr; + static void initialize_shared_args_semaphore () noexcept; + static void start_bridge_processing_thread () noexcept; + static void publish_shared_args (MarkCrossReferencesArgs *args) noexcept; + static auto wait_for_shared_args () noexcept -> MarkCrossReferencesArgs*; + static void bridge_processing () noexcept; + static auto bridge_processing_thread_entry (void *arg) noexcept -> void*; static void mark_cross_references (MarkCrossReferencesArgs *args) noexcept; static void log_mark_cross_references_args_if_enabled (MarkCrossReferencesArgs *args) noexcept;