Skip to content

[BUG] iOS: Crash when GC collects SKMetalView resources #3178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
AlisiaNew opened this issue Feb 28, 2025 · 4 comments · May be fixed by #3258
Open
1 task done

[BUG] iOS: Crash when GC collects SKMetalView resources #3178

AlisiaNew opened this issue Feb 28, 2025 · 4 comments · May be fixed by #3258
Labels

Comments

@AlisiaNew
Copy link

Description

SkiaSharp v3.116.1
.Net 9
maui 9.0.0/9.0.100 SDK 9.0.100
maui-ios 9.0.0/9.0.100 SDK 9.0.100
IDE: JetBrains Rider

In our app we have several views, one of which contains an SKMetalView. Every time the GC runs to collect resources, the app crashes with the following stack:

at <unknown> <0xffffffff>
	  at SkiaSharp.SkiaApi:gr_direct_context_abandon_context <0x00007>
	  at SkiaSharp.GRContext:AbandonContext <0x0010f>
	  at SkiaSharp.GRContext:DisposeNative <0x00083>
	  at SkiaSharp.SKNativeObject:Dispose <0x001c3>
	  at SkiaSharp.SKObject:Dispose <0x0007b>
	  at SkiaSharp.GRContext:Dispose <0x0007b>
	  at SkiaSharp.SKNativeObject:Finalize <0x000a7>
	  at System.Object:runtime_invoke_virtual_void__this__ <0x00087>

Code

To reproduce the issue SkiaSharpSample.zip:

  1. Click Open Skia Screen, then go back
  2. Click Open Text Screen, then go back
  3. Repeat steps 1 and 2 and the app will crash

Expected Behavior

No response

Actual Behavior

No response

Version of SkiaSharp

3.116.0 (Current)

Last Known Good Version of SkiaSharp

2.88.9 (Previous)

IDE / Editor

Other (Please indicate in the description)

Platform / Operating System

iOS

Platform / Operating System Version

iOS 17.5
iOS 18.2

Devices

No response

Relevant Screenshots

No response

Relevant Log Output

at <unknown> <0xffffffff>
	  at SkiaSharp.SkiaApi:gr_direct_context_abandon_context <0x00007>
	  at SkiaSharp.GRContext:AbandonContext <0x0010f>
	  at SkiaSharp.GRContext:DisposeNative <0x00083>
	  at SkiaSharp.SKNativeObject:Dispose <0x001c3>
	  at SkiaSharp.SKObject:Dispose <0x0007b>
	  at SkiaSharp.GRContext:Dispose <0x0007b>
	  at SkiaSharp.SKNativeObject:Finalize <0x000a7>
	  at System.Object:runtime_invoke_virtual_void__this__ <0x00087>

Code of Conduct

  • I agree to follow this project's Code of Conduct
@jeremy-visionaid
Copy link
Contributor

Interesting, I've also experienced an occasional crash using SKMetalView too - might be the same as this one. I notice that the backend context is created twice with the default constructor. So, related in a sense, but probably won't fix this issue:

#3256

@jeremy-visionaid
Copy link
Contributor

jeremy-visionaid commented Apr 29, 2025

OK, what I'm seeing is definitely the same problem as described here. It occurs on both iOS and MacCatalyst. As the original stack trace shows, there's a bug disposing/finalizing GRContext with the Metal backend. SKMetalView isn't even required to repro - just creating two GRContexts and disposing them is enough:

        CreateAndDisposeMetalContext();
        CreateAndDisposeMetalContext();

        private static void CreateAndDisposeMetalContext()
        {
            var device = MTLDevice.SystemDefault!;
            using (GRMtlBackendContext backendContext = new()
            {
                Device = device,
                Queue = device.CreateCommandQueue(),
            })
            {
                using GRContext context = GRContext.CreateMetal(backendContext);
            }

            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

I'll have a look into fixing it, or at least adding the above as a unit test.

jeremy-visionaid added a commit to jeremy-visionaid/SkiaSharp that referenced this issue Apr 30, 2025
AbandonContext appears to destroy the command buffer, causing the
finalizer to crash from a double free.

Fixes: mono#3178
@jeremy-visionaid jeremy-visionaid linked a pull request Apr 30, 2025 that will close this issue
5 tasks
@jeremy-visionaid
Copy link
Contributor

So, the above code was based on what SKMetalView was doing... The actual crash is coming from here

Thread 35 Crashed:: .NET Long Running Task
0   libdispatch.dylib             	       0x18f9c6c7c _dispatch_semaphore_dispose.cold.1 + 40
1   libdispatch.dylib             	       0x18f993e0c _dispatch_semaphore_dispose + 72
2   libdispatch.dylib             	       0x18f9924e4 _dispatch_dispose + 208
3   libdispatch.dylib             	       0x18f99232c dispatch_release + 152
4   Metal                         	       0x19ac8526c -[_MTLCommandQueue dealloc] + 172
5   IOGPU                         	       0x1b145fa94 -[IOGPUMetalCommandQueue dealloc] + 112
6   AGXMetalG14X                  	       0x11a34a230 -[AGXG14XFamilyCommandQueue dealloc] + 84
7   SkiaSharp.Tests.Devices       	       0x102c85988 xamarin_dyn_objc_msgSend + 160 (trampolines-arm64-objc_msgSend.inc.s:99)
8   SkiaSharp.Tests.Devices       	       0x1030278e4 do_icall + 160 (interp.c:2268)
9   SkiaSharp.Tests.Devices       	       0x103025f20 do_icall_wrapper + 356 (interp.c:2350)
10  SkiaSharp.Tests.Devices       	       0x10301b500 mono_interp_exec_method + 2832
11  SkiaSharp.Tests.Devices       	       0x103019114 interp_runtime_invoke + 244 (interp.c:2109)
12  SkiaSharp.Tests.Devices       	       0x102f67420 mono_jit_runtime_invoke + 1320 (mini-runtime.c:3683)
13  SkiaSharp.Tests.Devices       	       0x102f07de8 do_runtime_invoke + 60 (object.c:2576) [inlined]
14  SkiaSharp.Tests.Devices       	       0x102f07de8 mono_runtime_invoke_checked + 148 (object.c:2792)
15  SkiaSharp.Tests.Devices       	       0x102f0f3ec mono_runtime_try_invoke_byrefs + 548 (object.c:5176)
16  SkiaSharp.Tests.Devices       	       0x102ecc070 ves_icall_InternalInvoke + 236 (icall.c:3615)
17  SkiaSharp.Tests.Devices       	       0x102ed5e88 ves_icall_InternalInvoke_raw + 100 (icall-def.h:371)
18  SkiaSharp.Tests.Devices       	       0x10302794c do_icall + 264 (interp.c:2298)
19  SkiaSharp.Tests.Devices       	       0x103025f58 do_icall_wrapper + 412 (interp.c:2354)
20  SkiaSharp.Tests.Devices       	       0x10301b500 mono_interp_exec_method + 2832
21  SkiaSharp.Tests.Devices       	       0x103019114 interp_runtime_invoke + 244 (interp.c:2109)
22  SkiaSharp.Tests.Devices       	       0x102f67420 mono_jit_runtime_invoke + 1320 (mini-runtime.c:3683)
23  SkiaSharp.Tests.Devices       	       0x102f07de8 do_runtime_invoke + 60 (object.c:2576) [inlined]
24  SkiaSharp.Tests.Devices       	       0x102f07de8 mono_runtime_invoke_checked + 148 (object.c:2792)
25  SkiaSharp.Tests.Devices       	       0x102f1c560 start_wrapper_internal + 524 (threads.c:1213) [inlined]
26  SkiaSharp.Tests.Devices       	       0x102f1c560 start_wrapper + 592 (threads.c:1271)
27  libsystem_pthread.dylib       	       0x18fb49c0c _pthread_start + 136
28  libsystem_pthread.dylib       	       0x18fb44b80 thread_start + 8

I can't say I'm familiar with Apple APIs or Objective C (and especially not Apple interop). But it looks/feels like a double free situation (i.e. abandonContext freeing the command queue followed by the finalizer). If I manually call retain on the command queue then it at least doesn't crash, but I'm not sure if this is just masks the problem or causes a leak in its place:

NSObject o = NSObject.FromObject(commandQueue);
o.DangerousRetain();

I've put in a draft pull request with a unit test and a proof of concept for the retain. Hopefully @mattleibow can take a look/take over!

jeremy-visionaid added a commit to jeremy-visionaid/SkiaSharp that referenced this issue Apr 30, 2025
AbandonContext appears to destroy the command buffer, causing the
finalizer to crash from a double free.

Fixes: mono#3178
jeremy-visionaid added a commit to jeremy-visionaid/SkiaSharp that referenced this issue Apr 30, 2025
AbandonContext appears to destroy the command buffer, causing the
finalizer to crash from a double free.

Fixes: mono#3178
jeremy-visionaid added a commit to jeremy-visionaid/SkiaSharp that referenced this issue Apr 30, 2025
AbandonContext appears to destroy the command buffer, causing the
finalizer to crash from a double free.

Fixes: mono#3178
@jeremy-visionaid
Copy link
Contributor

Some bits in Skia showing use of retain for the backend context. Looks like it calls through to CFRetain on the command buffer when creating a test context:

https://skia.googlesource.com/skia/+/7755e6ab0bc0/tools/gpu/mtl/MtlTestContext.mm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: New
Development

Successfully merging a pull request may close this issue.

2 participants