Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 30, 2026

  • Analyze current GC bridge architecture
  • Implement managed callback approach with native background thread
  • Trigger Java GC directly through JNI from C#
  • Remove C++ structs and logging - use void* and do validation/logging in C#
  • Address code review feedback:
    • Rename clr_initialize_gc_bridge (keep original name)
    • Rename JavaGCTrigger to BridgeProcessingJniHelper
    • Add missing files to Mono.Android.csproj
    • Remove CS0436 from NoWarn (address underlying cause)
    • Move runtime checks to static ctor (avoid repeated checks)
    • Add BridgeProcessingLogger for detailed logging
    • Regenerate pinvoke-tables.include
  • Verify build compiles

Summary

Architecture:

  • Native mark_cross_references just signals semaphore
  • Native background thread waits on semaphore, then calls managed callback
  • Managed callback does all processing in BridgeProcessing.Process()
  • Java GC triggered directly through JNI from C# (BridgeProcessingJniHelper class)

Code Organization:

  • BridgeProcessing.cs - Main processing logic
  • BridgeProcessingLogger.cs - Detailed logging based on Logger.LogGC flags
  • BridgeProcessingJniHelper.cs - Java GC trigger + GCUserPeerable managed reference helpers
Original prompt

This section details on the original issue you should resolve

<issue_title>[NativeAOT] Try to move GC bridge code from C++ to C#</issue_title>
<issue_description>### Android framework version

net11.0-android (Preview)

Affected platform version

.NET 11

Description

The GC bridge for Native AOT and CoreCLR is currently implemented in C++ (#10198). I would like to explore the posibility of moving as much code from native to managed code. All the processing in the background thread could be in managed code (what is now void GCBridge::bridge_processing ()). The only thing that needs to be a native entry point (can be C, doesn't have to be C++) is now void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArgs *args) which runs when the managed world is stopped.

Steps to Reproduce

Did you find any workaround?

No response

Relevant log output

```</issue_description>

<agent_instructions>Most relevant code is in these files:
- src/Mono.Android/Microsoft.Android.Runtime/ManagedValueManager.cs
- src/native/clr/host/gc-bridge.cc
- src/native/clr/host/bridge-processing.cc

The goal is to reduce the amount of C++ code and use C# code which can be better shared between Native AOT and CoreCLR builds of the .NET for Android runtime.</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

- Add BridgeProcessing.cs with managed implementation of bridge processing
- Add new native methods for managed processing mode
- Update ManagedValueManager to use managed processing loop
- Update gc-bridge.hh with new managed processing APIs

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copilot AI and others added 3 commits January 30, 2026 22:35
Removed files:
- src/native/clr/host/bridge-processing.cc
- src/native/clr/include/host/bridge-processing.hh
- src/native/clr/include/host/bridge-processing-shared.hh
- src/native/nativeaot/host/bridge-processing.cc
- src/native/nativeaot/include/host/bridge-processing.hh

Updated files to remove references to deleted files:
- src/native/clr/host/CMakeLists.txt
- src/native/nativeaot/host/CMakeLists.txt
- src/native/clr/host/gc-bridge.cc
- src/native/nativeaot/host/host.cc

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copilot AI changed the title [WIP] Move GC bridge code from C++ to C# [NativeAOT] Move GC bridge processing from C++ to C# Jan 30, 2026
Copilot AI requested a review from simonrozsival January 30, 2026 22:42
Copilot AI and others added 2 commits January 31, 2026 04:13
- Remove clr_initialize_gc_bridge P/Invoke (old callback mode never used)
- Remove initialize_callback() function from gc-bridge.hh
- Remove bridge_processing() function (native background thread loop)
- Remove bridge_processing_started_callback and bridge_processing_finished_callback
- Remove bridge_processing_thread and managed_processing_mode flag
- Remove unused includes (thread, shared_mutex, unordered_map)
- Remove BridgeProcessingStartedFtn and BridgeProcessingFinishedFtn typedefs

This eliminates 2 reverse p/invokes per GC cycle that were never used.

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
…idge

The autogenerated file was out of sync with the source after
removing clr_initialize_gc_bridge and adding the new managed
processing mode functions.

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Pass UCO callback to native during initialization
- Native mark_cross_references calls the UCO callback directly
- UCO stores args in ConcurrentQueue and signals SemaphoreSlim
- C# background thread waits on semaphore and processes from queue
- Remove clr_gc_bridge_wait_for_processing P/Invoke
- Remove semaphore and atomic from C++ code
- Regenerate pinvoke-tables.include

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
…omment

- Call Runtime.gc() directly from C# via JNI instead of calling native code
- Remove clr_gc_bridge_trigger_java_gc P/Invoke and native implementation
- Remove unnecessary comment from host.cc line 66
- Simplify native GCBridge by removing Runtime instance caching
- Regenerate pinvoke-tables.include (now 24 internal p/invokes)

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Remove explicit Cdecl calling convention from UnmanagedCallersOnly and
  function pointers (Cdecl is default on Linux/Android)
- Restore bridgeProcessingSemaphore for WaitForGCBridgeProcessing
- Convert BridgeProcessing.Initialize to static constructor using RuntimeFeature
- Replace CrossReferenceTarget struct with ValueTuple
- Rename peer variable to peerToDispose for clarity
- Simplify AddReference/ClearReferences by removing outer try-catch blocks

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Member

@simonrozsival simonrozsival left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot one thing we're missing and which can be useful is the detailed logging -- if enabled!

I suggest adding static BridgeProcessingLogger? s_logger which will be initialized based on the Android.Runtime.Logger.LogGC and other flags. Migrate the same tags and formatting from bridge-processing.cc.

- Add BridgeProcessingLogger class for detailed GC bridge logging
- Migrate logging from C++ bridge-processing.cc (same tags and formatting)
- Enable logging based on Logger.LogGC and Logger.LogGlobalRef flags
- Replace s_isNativeAOT field with !RuntimeFeature.IsCoreClrRuntime checks
- Log: missing method errors, weak/gref operations, GC summary

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 4, 2026

@copilot one thing we're missing and which can be useful is the detailed logging -- if enabled!

I suggest adding static BridgeProcessingLogger? s_logger which will be initialized based on the `Andr...

Added BridgeProcessingLogger in bba1d17. It's initialized based on Logger.LogGC and Logger.LogGlobalRef flags. Migrated the same tags and formatting from C++ bridge-processing.cc: missing method errors, weak/gref operations, and GC cleanup summary.

Copy link
Member

@simonrozsival simonrozsival left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot address review comments. try to clean up the code and remove unnecessary or verbose comments.

- Remove unused System.Threading.Tasks using from ManagedValueManager.cs
- Remove unused System.Threading using from BridgeProcessing.cs
- Revert symbol name to shorter clr_gc_bridge_init
- Move Java GC triggering to separate JavaGCTrigger.cs
- Move BridgeProcessingLogger to separate file with only unsafe method marked
- Simplify peerToDispose variable

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Member

@simonrozsival simonrozsival left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot we're getting there! address this next round of review comments.

Copilot AI and others added 2 commits February 4, 2026 12:27
Add CS0436 to NoWarn in Mono.Android.csproj. This warning occurs because
RuntimeFeature.cs is compiled into both Mono.Android and Mono.Android.Runtime
assemblies via linked file. The warning is benign - using the local
definition is the intended behavior.

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
…rable helpers

- Rename clr_gc_bridge_init back to clr_initialize_gc_bridge
- Remove unnecessary comment from internal-pinvokes.hh
- Move GCUserPeerable helpers to JavaGCTrigger class
- Remove runtime check from JavaGCTrigger.Trigger (static ctor guarantees init)
- Suppress CS0436 warning for RuntimeFeature type conflict
- Regenerate pinvoke-tables.include with new hash values

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Member

@simonrozsival simonrozsival left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot address these comments

…te tables

- Add BridgeProcessingLogger.cs and BridgeProcessingJniHelper.cs to Mono.Android.csproj
- Rename JavaGCTrigger to BridgeProcessingJniHelper
- Update BridgeProcessing.cs to use the renamed class
- Regenerate pinvoke-tables.include (was out of sync)
- Remove CS0436 from NoWarn (use the warning to catch issues)

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Member

@simonrozsival simonrozsival left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot address this review

{
// Count == 0 case: Some SCCs might have no IGCUserPeers associated with them, so we must create one
if (scc.Count == 0) {
var newObject = s_GCUserPeerClass!.NewObject (s_GCUserPeerCtor!, null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect this to be also moved to the JNI helper class.

Comment on lines +192 to +206
// Fall back to reflection-based approach
var fromClassRef = JniEnvironment.Types.GetObjectClass (from);
using var fromClass = new JniType (ref fromClassRef, JniObjectReferenceOptions.CopyAndDispose);

JniMethodInfo addMethod;
try {
addMethod = fromClass.GetInstanceMethod ("monodroidAddReference", "(Ljava/lang/Object;)V");
} catch (Java.Lang.NoSuchMethodError) {
s_logger?.LogMissingAddReferencesMethod (fromClass);
return false;
}

JniArgumentValue* args = stackalloc JniArgumentValue[1];
args[0] = new JniArgumentValue (to);
JniEnvironment.InstanceMethods.CallVoidMethod (from, addMethod, args);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect this to be moved to the JNI helper class as well

}
}

public static void Trigger ()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename this to TriggerJavaGC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NativeAOT] Try to move GC bridge code from C++ to C#

2 participants