Skip to content

Commit 0ae638d

Browse files
[Mono.Android] call new Java "GC Bridge" APIs
Context: dotnet/runtime#114184 Context: https://github.com/jonathanpeppers/BridgeSandbox Context: dotnet/runtime@main...BrzVlad:runtime:feature-clr-gcbridge So far, I: * Built dotnet/runtime, I built this branch: https://github.com/jonathanpeppers/runtime/tree/gcbridge_impl * Put the relevant arm64 packs in `packages/` folder and configured a `NuGet.config` to use them. * Setup `build-tools\scripts\custom-runtime.targets` to use the 10.0.0-dev dotnet/runtime packs. * In `ManagedValueManager.cs`... * Call a new native method on startup: var mark_cross_references_ftn = RuntimeNativeMethods.clr_initialize_gc_bridge (&BridgeProcessingFinished); * Pass the returned function pointer to: JavaMarshal.Initialize (mark_cross_references_ftn); * In `AddPeer(IJavaPeerable value)` call `JavaMarshal.CreateReferenceTrackingHandle()` * In `RemovePeer(IJavaPeerable value)` call: static unsafe void FreeHandle (GCHandle handle) { IntPtr context = JavaMarshal.GetContext (handle); NativeMemory.Free((void*)context); }
1 parent 199efa8 commit 0ae638d

File tree

13 files changed

+135
-26
lines changed

13 files changed

+135
-26
lines changed

.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@ Makefile eol=lf
3535
*.wixproj eol=crlf
3636
*.wxs eol=crlf
3737
*.rtf eol=crlf
38+
39+
packages/* filter=lfs diff=lfs merge=lfs -text

Documentation/workflow/DevelopmentTips.md

+19-6
Original file line numberDiff line numberDiff line change
@@ -450,13 +450,26 @@ A second (better) way is to add this MSBuild target to your Android
450450
`.csproj` file:
451451

452452
```xml
453-
<Target Name="UpdateMonoRuntimePacks" BeforeTargets="ProcessFrameworkReferences">
453+
<Target Name="UpdateRuntimeVersion" BeforeTargets="ProcessFrameworkReferences">
454+
<PropertyGroup>
455+
<!-- This could be a version I built myself -->
456+
<_Version>10.0.0-dev</_Version>
457+
</PropertyGroup>
454458
<ItemGroup>
455-
<KnownRuntimePack
456-
Update="Microsoft.NETCore.App"
457-
Condition=" '%(KnownRuntimePack.TargetFramework)' == 'net6.0' "
458-
LatestRuntimeFrameworkVersion="6.0.0-preview.7.21364.3"
459-
/>
459+
<!-- For runtime packs only -->
460+
<KnownRuntimePack
461+
Update="Microsoft.NETCore.App"
462+
Condition=" '%(KnownRuntimePack.TargetFramework)' == 'net10.0' "
463+
LatestRuntimeFrameworkVersion="$(_Version)"
464+
/>
465+
<!-- For new .NET APIs -->
466+
<KnownFrameworkReference
467+
Update="Microsoft.NETCore.App"
468+
Condition=" '%(KnownFrameworkReference.TargetFramework)' == 'net10.0' "
469+
DefaultRuntimeFrameworkVersion="$(_Version)"
470+
LatestRuntimeFrameworkVersion="$(_Version)"
471+
TargetingPackVersion="$(_Version)"
472+
/>
460473
</ItemGroup>
461474
</Target>
462475
```

NuGet.config

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<!-- End: Package sources from dotnet-android -->
88
<!--End: Package sources managed by Dependency Flow automation. Do not edit the sources above.-->
99
<!-- ensure only the sources defined below are used -->
10+
<add key="local-packages" value="packages" />
1011
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" protocolVersion="3" />
1112
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" protocolVersion="3" />
1213
<!-- This is for packages needed by debugger-libs -->
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project>
2+
<!-- Use version in local packages folder -->
3+
<Target Name="UpdateRuntimeVersion" BeforeTargets="ProcessFrameworkReferences">
4+
<PropertyGroup>
5+
<_Version>10.0.0-dev</_Version>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<KnownFrameworkReference Update="Microsoft.NETCore.App"
9+
Condition=" '%(KnownFrameworkReference.TargetFramework)' == 'net10.0' "
10+
DefaultRuntimeFrameworkVersion="$(_Version)"
11+
LatestRuntimeFrameworkVersion="$(_Version)"
12+
TargetingPackVersion="$(_Version)"
13+
/>
14+
<KnownRuntimePack
15+
Update="Microsoft.NETCore.App"
16+
Condition=" '%(KnownRuntimePack.TargetFramework)' == 'net10.0' "
17+
LatestRuntimeFrameworkVersion="$(_Version)"
18+
/>
19+
</ItemGroup>
20+
</Target>
21+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:d23f78cb9dec82f9ed64e0b9b803fe533d2628d63e1af54685ff8827b1d799bb
3+
size 5313525
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:7c6e9746aaf2b91c7009d940eeda588129f4be8fc864ca6259dc6e5845abe48f
3+
size 215347082

src/Mono.Android.Runtime/Mono.Android.Runtime.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,6 @@
6464
<ProjectReference Include="..\..\external\Java.Interop\src\Java.Interop\Java.Interop.csproj" />
6565
</ItemGroup>
6666

67+
<Import Project="..\..\build-tools\scripts\custom-runtime.targets" />
6768
<Import Project="Mono.Android.Runtime.targets" />
6869
</Project>

src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System;
33
using System.Runtime.CompilerServices;
44
using System.Runtime.InteropServices;
5+
using System.Runtime.InteropServices.Java;
56
using System.Text;
67

78
namespace Android.Runtime
@@ -18,7 +19,7 @@ enum TraceKind : uint
1819
All = Java | Managed | Native | Signals,
1920
}
2021

21-
internal static class RuntimeNativeMethods
22+
internal unsafe static class RuntimeNativeMethods
2223
{
2324
[DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
2425
internal extern static void monodroid_log (LogLevel level, LogCategories category, string message);
@@ -92,6 +93,17 @@ internal static class RuntimeNativeMethods
9293
[DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
9394
internal static extern bool clr_typemap_java_to_managed (string java_type_name, out IntPtr managed_assembly_name, out uint managed_type_token_id);
9495

96+
/// <summary>
97+
/// TODO: implement this in the native side.
98+
/// Initializes the "GC Bridge" implementation for the CoreCLR runtime.
99+
/// </summary>
100+
/// <param name="bridge_processing_finished_callback">A function pointer to a C# callback that will be invoked when bridge processing has completed.</param>
101+
/// <returns>A function pointer that should be passed to JavaMarshal.Initialize() on startup.</returns>
102+
[DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
103+
internal static extern delegate* unmanaged<nint, StronglyConnectedComponent*, nint, ComponentCrossReference*, void> clr_initialize_gc_bridge (
104+
delegate* unmanaged<nint, StronglyConnectedComponent*, nint, ComponentCrossReference*, void> bridge_processing_finished_callback
105+
);
106+
95107
[MethodImplAttribute(MethodImplOptions.InternalCall)]
96108
internal static extern void monodroid_unhandled_exception (Exception javaException);
97109

src/Mono.Android/Microsoft.Android.Runtime/ManagedValueManager.cs

+45-19
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Reflection;
1111
using System.Runtime.CompilerServices;
1212
using System.Runtime.InteropServices;
13+
using System.Runtime.InteropServices.Java;
1314
using System.Threading;
1415
using Android.Runtime;
1516
using Java.Interop;
@@ -20,10 +21,12 @@ class ManagedValueManager : JniRuntime.JniValueManager
2021
{
2122
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
2223

23-
Dictionary<int, List<IJavaPeerable>>? RegisteredInstances = new Dictionary<int, List<IJavaPeerable>>();
24+
Dictionary<int, List<GCHandle>>? RegisteredInstances = new ();
2425

25-
internal ManagedValueManager ()
26+
internal unsafe ManagedValueManager ()
2627
{
28+
var mark_cross_references_ftn = RuntimeNativeMethods.clr_initialize_gc_bridge (&BridgeProcessingFinished);
29+
JavaMarshal.Initialize (mark_cross_references_ftn);
2730
}
2831

2932
public override void WaitForGCBridgeProcessing ()
@@ -35,7 +38,7 @@ public override void CollectPeers ()
3538
if (RegisteredInstances == null)
3639
throw new ObjectDisposedException (nameof (ManagedValueManager));
3740

38-
var peers = new List<IJavaPeerable> ();
41+
var peers = new List<GCHandle> ();
3942

4043
lock (RegisteredInstances) {
4144
foreach (var ps in RegisteredInstances.Values) {
@@ -48,7 +51,8 @@ public override void CollectPeers ()
4851
List<Exception>? exceptions = null;
4952
foreach (var peer in peers) {
5053
try {
51-
peer.Dispose ();
54+
if (peer.Target is IDisposable disposable)
55+
disposable.Dispose ();
5256
}
5357
catch (Exception e) {
5458
exceptions = exceptions ?? new List<Exception> ();
@@ -74,33 +78,35 @@ public override void AddPeer (IJavaPeerable value)
7478
}
7579
int key = value.JniIdentityHashCode;
7680
lock (RegisteredInstances) {
77-
List<IJavaPeerable>? peers;
81+
List<GCHandle>? peers;
7882
if (!RegisteredInstances.TryGetValue (key, out peers)) {
79-
peers = new List<IJavaPeerable> () {
80-
value,
83+
peers = new List<GCHandle> () {
84+
CreateReferenceTrackingHandle (value)
8185
};
8286
RegisteredInstances.Add (key, peers);
8387
return;
8488
}
8589

8690
for (int i = peers.Count - 1; i >= 0; i--) {
8791
var p = peers [i];
88-
if (!JniEnvironment.Types.IsSameObject (p.PeerReference, value.PeerReference))
92+
if (p.Target is not IJavaPeerable peer)
93+
continue;
94+
if (!JniEnvironment.Types.IsSameObject (peer.PeerReference, value.PeerReference))
8995
continue;
9096
if (Replaceable (p)) {
91-
peers [i] = value;
97+
peers [i] = CreateReferenceTrackingHandle (value);
9298
} else {
93-
WarnNotReplacing (key, value, p);
99+
WarnNotReplacing (key, value, peer);
94100
}
95101
return;
96102
}
97-
peers.Add (value);
103+
peers.Add (CreateReferenceTrackingHandle (value));
98104
}
99105
}
100106

101-
static bool Replaceable (IJavaPeerable peer)
107+
static bool Replaceable (GCHandle handle)
102108
{
103-
if (peer == null)
109+
if (handle.Target is not IJavaPeerable peer)
104110
return true;
105111
return peer.JniManagedPeerState.HasFlag (JniManagedPeerStates.Replaceable);
106112
}
@@ -132,14 +138,14 @@ void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepVal
132138
int key = GetJniIdentityHashCode (reference);
133139

134140
lock (RegisteredInstances) {
135-
List<IJavaPeerable>? peers;
141+
List<GCHandle>? peers;
136142
if (!RegisteredInstances.TryGetValue (key, out peers))
137143
return null;
138144

139145
for (int i = peers.Count - 1; i >= 0; i--) {
140146
var p = peers [i];
141-
if (JniEnvironment.Types.IsSameObject (reference, p.PeerReference))
142-
return p;
147+
if (p.Target is IJavaPeerable peer && JniEnvironment.Types.IsSameObject (reference, peer.PeerReference))
148+
return peer;
143149
}
144150
if (peers.Count == 0)
145151
RegisteredInstances.Remove (key);
@@ -157,14 +163,15 @@ public override void RemovePeer (IJavaPeerable value)
157163

158164
int key = value.JniIdentityHashCode;
159165
lock (RegisteredInstances) {
160-
List<IJavaPeerable>? peers;
166+
List<GCHandle>? peers;
161167
if (!RegisteredInstances.TryGetValue (key, out peers))
162168
return;
163169

164170
for (int i = peers.Count - 1; i >= 0; i--) {
165171
var p = peers [i];
166-
if (object.ReferenceEquals (value, p)) {
172+
if (object.ReferenceEquals (value, p.Target)) {
167173
peers.RemoveAt (i);
174+
FreeHandle (p);
168175
}
169176
}
170177
if (peers.Count == 0)
@@ -251,13 +258,32 @@ public override List<JniSurfacedPeerInfo> GetSurfacedPeers ()
251258
var peers = new List<JniSurfacedPeerInfo> (RegisteredInstances.Count);
252259
foreach (var e in RegisteredInstances) {
253260
foreach (var p in e.Value) {
254-
peers.Add (new JniSurfacedPeerInfo (e.Key, new WeakReference<IJavaPeerable> (p)));
261+
if (p.Target is not IJavaPeerable peer)
262+
continue;
263+
peers.Add (new JniSurfacedPeerInfo (e.Key, new WeakReference<IJavaPeerable> (peer)));
255264
}
256265
}
257266
return peers;
258267
}
259268
}
260269

270+
static GCHandle CreateReferenceTrackingHandle (IJavaPeerable value) =>
271+
JavaMarshal.CreateReferenceTrackingHandle (value, value.PeerReference.Handle);
272+
273+
static unsafe void FreeHandle (GCHandle handle)
274+
{
275+
IntPtr context = JavaMarshal.GetContext (handle);
276+
NativeMemory.Free ((void*) context);
277+
}
278+
279+
[UnmanagedCallersOnly]
280+
internal static unsafe void BridgeProcessingFinished (nint sccsLen, StronglyConnectedComponent* sccs, nint ccrsLen, ComponentCrossReference* ccrs)
281+
{
282+
JavaMarshal.ReleaseMarkCrossReferenceResources (
283+
new Span<StronglyConnectedComponent> (sccs, (int) sccsLen),
284+
new Span<ComponentCrossReference> (ccrs, (int) ccrsLen));
285+
}
286+
261287
const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
262288

263289
static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) };

src/Mono.Android/Mono.Android.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
<Import Project="..\Xamarin.Android.NamingCustomAttributes\Xamarin.Android.NamingCustomAttributes.projitems" Label="Shared" Condition="Exists('..\Xamarin.Android.NamingCustomAttributes\Xamarin.Android.NamingCustomAttributes.projitems')" />
114114
<Import Project="Mono.Android.targets" />
115115
<Import Project="..\..\build-tools\scripts\JavaCallableWrappers.targets" />
116+
<Import Project="..\..\build-tools\scripts\custom-runtime.targets" />
116117
<Import Project="$(IntermediateOutputPath)mcw\Mono.Android.projitems" Condition="Exists('$(IntermediateOutputPath)mcw\Mono.Android.projitems')" />
117118

118119
<ItemGroup>

src/native/clr/host/generate-pinvoke-tables.cc

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const std::vector<std::string> internal_pinvoke_names = {
7272
"monodroid_TypeManager_get_java_class_name",
7373
"clr_typemap_managed_to_java",
7474
"clr_typemap_java_to_managed",
75+
"clr_initialize_gc_bridge",
7576
"_monodroid_weak_gref_delete",
7677
"_monodroid_weak_gref_get",
7778
"_monodroid_weak_gref_new",

src/native/clr/host/internal-pinvokes.cc

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ bool clr_typemap_java_to_managed (const char *java_type_name, char const** assem
3636
return TypeMapper::typemap_java_to_managed (java_type_name, assembly_name, managed_type_token_id);
3737
}
3838

39+
static void clr_mark_cross_references (size_t sccsLen, StronglyConnectedComponent* sccs, size_t ccrsLen, ComponentCrossReference* ccrs)
40+
{
41+
// TODO: implement this
42+
}
43+
44+
MarkCrossReferencesFtn clr_initialize_gc_bridge (MarkCrossReferencesFtn callback) noexcept
45+
{
46+
return clr_mark_cross_references;
47+
}
48+
3949
void monodroid_log (LogLevel level, LogCategories category, const char *message) noexcept
4050
{
4151
switch (level) {

src/native/clr/include/runtime-base/internal-pinvokes.hh

+15
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,28 @@
77
#include "logger.hh"
88
#include <runtime-base/timing.hh>
99

10+
struct StronglyConnectedComponent
11+
{
12+
size_t Count;
13+
void** ContextMemory;
14+
};
15+
16+
struct ComponentCrossReference
17+
{
18+
size_t SourceGroupIndex;
19+
size_t DestinationGroupIndex;
20+
};
21+
22+
typedef void (*MarkCrossReferencesFtn)(size_t, StronglyConnectedComponent*, size_t, ComponentCrossReference*);
23+
1024
extern "C" {
1125
int _monodroid_gref_get () noexcept;
1226
void _monodroid_gref_log (const char *message) noexcept;
1327
int _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from, int from_writable) noexcept;
1428
void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from, int from_writable) noexcept;
1529
const char* clr_typemap_managed_to_java (const char *typeName, const uint8_t *mvid) noexcept;
1630
bool clr_typemap_java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept;
31+
MarkCrossReferencesFtn clr_initialize_gc_bridge (MarkCrossReferencesFtn callback) noexcept;
1732
void monodroid_log (xamarin::android::LogLevel level, LogCategories category, const char *message) noexcept;
1833
char* monodroid_TypeManager_get_java_class_name (jclass klass) noexcept;
1934
void monodroid_free (void *ptr) noexcept;

0 commit comments

Comments
 (0)