Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/native/clr/host/bridge-processing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ bool BridgeProcessingShared::add_reference (jobject from, jobject to) noexcept

env->DeleteLocalRef (java_class);
env->CallVoidMethod (from, add_method_id, to);
abort_on_pending_java_exception ("A Java exception was thrown by monodroidAddReference during GC bridge processing"sv);
return true;
}

Expand Down Expand Up @@ -217,6 +218,18 @@ void BridgeProcessingShared::clear_references (jobject handle) noexcept

env->DeleteLocalRef (java_class);
env->CallVoidMethod (handle, clear_method_id);
abort_on_pending_java_exception ("A Java exception was thrown by monodroidClearReferences during GC bridge processing"sv);
}

void BridgeProcessingShared::abort_on_pending_java_exception (std::string_view message) noexcept
{
if (!env->ExceptionCheck ()) [[likely]] {
return;
}

env->ExceptionDescribe ();
env->ExceptionClear ();
Helpers::abort_application (LOG_GC, message);
}

void BridgeProcessingShared::take_global_ref (HandleContext &context) noexcept
Expand All @@ -230,6 +243,11 @@ void BridgeProcessingShared::take_global_ref (HandleContext &context) noexcept
log_weak_to_gref (weak, handle);

if (handle == nullptr) {
// A null result normally means the weak reference's target was collected by the Java GC.
// However, NewGlobalRef can also fail (returning null with a pending exception) when the VM
// is out of memory or the global reference table is full. Treating that as "collected" would
// tear down a live peer, so distinguish the two and fail fast on a genuine failure.
abort_on_pending_java_exception ("Failed to promote a weak global reference to a global reference during GC bridge processing"sv);
log_weak_ref_collected (weak);
}

Expand All @@ -249,6 +267,15 @@ void BridgeProcessingShared::take_weak_global_ref (const HandleContext &context)
log_take_weak_global_ref (handle);

jobject weak = env->NewWeakGlobalRef (handle);
if (weak == nullptr) [[unlikely]] {
// `handle` is a valid strong global reference, so NewWeakGlobalRef only returns null when the
// VM is out of memory. Continuing would delete the strong reference below and lose the object
// (a later NewGlobalRef of a null weak reference would look like a collected peer), so fail fast.
// The OOM failure raises a pending exception; abort unconditionally in case it somehow did not.
constexpr std::string_view failure = "Failed to create a weak global reference during GC bridge processing"sv;
abort_on_pending_java_exception (failure);
Helpers::abort_application (LOG_GC, failure);
}
log_weak_gref_new (handle, weak);

context.control_block->handle = weak;
Expand Down
6 changes: 6 additions & 0 deletions src/native/clr/include/host/bridge-processing-shared.hh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <jni.h>
#include <string_view>
#include <unordered_map>

#include <host/gc-bridge.hh>
Expand Down Expand Up @@ -51,6 +52,11 @@ private:
void clear_references_if_needed (const HandleContext &context) noexcept;
void clear_references (jobject handle) noexcept;

// If a Java exception is pending on `env`, describe it, clear it, and abort. Bridge
// processing has no safe way to recover from an exception thrown by a peer's reference
// callbacks, and leaving an exception pending would make subsequent JNI calls undefined.
void abort_on_pending_java_exception (std::string_view message) noexcept;

void log_missing_add_references_method (jclass java_class) noexcept;
void log_missing_clear_references_method (jclass java_class) noexcept;
void log_weak_to_gref (jobject weak, jobject handle) noexcept;
Expand Down
Loading