Hi all,
I have created a repository to replicate this native crash I am seeing on Mac OS X Catalina when using the basic avalonia mvvm template. This happens when exiting the application... according to the stacktrace looks to be related to finalizer activities / deleting references in Skia. Is this the right place to post...?
=================================================================
Native Crash Reporting
=================================================================
Got a segv while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
=================================================================
Native stacktrace:
=================================================================
0x108272ad9 - /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono : mono_dump_native_crash_info
0x10820afb5 - /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono : mono_handle_native_crash
0x10826cc56 - /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono : altstack_handle_and_restore
0x7fff3fc708ac - /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib : glDeleteBuffers
0x10fc5f36c - /private/tmp/macosx-avalonia-crash/MyApp/bin/Debug/net48/libSkiaSharp.dylib : gr_backendrendertarget_get_gl_framebufferinfo
0x10fb03e9f - /private/tmp/macosx-avalonia-crash/MyApp/bin/Debug/net48/libSkiaSharp.dylib : gr_backendrendertarget_get_gl_framebufferinfo
0x10fb2a9cd - /private/tmp/macosx-avalonia-crash/MyApp/bin/Debug/net48/libSkiaSharp.dylib : gr_backendrendertarget_get_gl_framebufferinfo
0x10faf104c - /private/tmp/macosx-avalonia-crash/MyApp/bin/Debug/net48/libSkiaSharp.dylib : gr_backendrendertarget_get_gl_framebufferinfo
0x10fb05615 - /private/tmp/macosx-avalonia-crash/MyApp/bin/Debug/net48/libSkiaSharp.dylib : gr_backendrendertarget_get_gl_framebufferinfo
0x1102fa5b4 - Unknown
0x11430d95c - Unknown
0x1083f3119 - /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono : mono_gc_run_finalize
0x10841201c - /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono : sgen_gc_invoke_finalizers
0x1083f5009 - /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono : finalizer_thread
0x1083a16ed - /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono : start_wrapper
0x7fff6ffc4109 - /usr/lib/system/libsystem_pthread.dylib : _pthread_start
0x7fff6ffbfb8b - /usr/lib/system/libsystem_pthread.dylib : thread_start
=================================================================
Telemetry Dumper:
=================================================================
Pkilling 0x11697cdc0 from 0x70000865e000
Entering thread summarizer pause from 0x70000865e000
Finished thread summarizer pause from 0x70000865e000.
Failed to create breadcrumb file (null)/crash_hash_0x8d658a08a
Waiting for dumping threads to resume
=================================================================
External Debugger Dump:
=================================================================
=================================================================
Basic Fault Address Reporting
=================================================================
Memory around native instruction pointer (0x7fff3fc708ac):
0x7fff3fc7089c 89 e5 48 89 f2 89 fe 65 48 8b 04 25 f0 00 00 00 ..H....eH..%....
0x7fff3fc708ac 48 8b 88 20 14 00 00 48 8b 38 5d ff e1 55 48 89 H.. ...H.8]..UH.
0x7fff3fc708bc e5 48 89 f2 89 fe 65 48 8b 04 25 f0 00 00 00 48 .H....eH..%....H
0x7fff3fc708cc 8b 88 28 14 00 00 48 8b 38 5d ff e1 55 48 89 e5 ..(...H.8]..UH..
=================================================================
Managed Stacktrace:
=================================================================
at <unknown> <0xffffffff>
at System.Object:wrapper_native_0x10f8b4820 <0x00093>
at SkiaSharp.SkiaApi:sk_refcnt_safe_unref <0x00091>
at SkiaSharp.SKObjectExtensions:SafeUnRef <0x000ea>
at SkiaSharp.SKObject:DisposeNative <0x000c2>
at SkiaSharp.SKNativeObject:Dispose <0x000ab>
at SkiaSharp.SKObject:Dispose <0x0004a>
at SkiaSharp.GRContext:Dispose <0x0004a>
at SkiaSharp.SKNativeObject:Finalize <0x00041>
at System.Object:runtime_invoke_virtual_void__this__ <0x000ab>
=================================================================
[1] 39765 abort mono MyApp.exe
Does it work with .NET Core?
Hi @kekekeks ,
Yes, it works correctly with .NET Core. Disabling these sed commands and running dotnet MyApp/bin/Debug/netcoreapp3.0/MyApp.dll works fine.
Hi @kekekeks ,
FWIW, I get the _same crash_ on https://github.com/AvaloniaUI/ControlCatalogStandalone with its net472 example, however, the 'managed stacktrace' is different but still related to the finalizer:
=================================================================
Managed Stacktrace:
=================================================================
at <unknown> <0xffffffff>
at SkiaSharp.SkiaApi:gr_context_unref <0x000a2>
at SkiaSharp.GRContext:Dispose <0x0008a>
at SkiaSharp.SKNativeObject:Finalize <0x00039>
at System.Object:runtime_invoke_virtual_void__this__ <0x000ab>
=================================================================
(the native stacktrace is the same, finalizer_thread -> libSkia -> mono_dump_native_crash_info)
What happens here is SkiaSharp's GrContext wrapper finalizer attempting to release the native GrContextclass. Said native class kinda expects to be destroyed when its associated OpenGL context is currently active on the current thread.
Obviously we can't just set the context to be active on the finalizer thread.
Normally it's not a problem since GrContext is rooted by a static field, so it never becomes collected and finalized. But apparently Mono attempts to finalize it on app exit.
I don't think we have any control over that, so I guess our only option would be to call SuppressFinalize for our GrContext instance.
@mattleibow
@mattleibow this actually raises a problem with the current SkiaSharp bindings: GPU-backed objects aren't really finalizable on finalizer thread and need to be destroyed in application-controlled manner in a similar way to how STA COM objects are being destroyed by enqueuing them for finalization on their owner STA thread.
@kekekeks It might be trying to free GPU resources... What happens if you call AbandonContext
You most probably will be calling this when you are finished, or if suddenly the GPU context was lost. Then you can tell skia not to actually clean up anything... because it is already gone.
@mattleibow yes it does try to free resources. No, we can not call AbandonContext, because it happens when user code calls Environment.Exit or just exits Main. Avalonia code does not know that application is about to do that and therefore can not call AbandonContext beforehand.
When application exits Main Mono is calling finalizers for all finalizable objects.
It can be illustrated by this example:
class Finalizable
{
~Finalizable()
{
Console.WriteLine("Finalized");
}
public void DoSometing()
{
Console.WriteLine("Do something");
}
}
static class SomeClassWithCachedFinalizable
{
static Finalizable _staticInstance = new Finalizable();
public static void DoSomething()
{
_staticInstance.DoSometing();
}
}
class Program
{
static void Main(string[] args)
{
SomeClassWithCachedFinalizable.DoSomething();
}
}
As you can see here, SomeClassWithCachedFinalizable is not notified when application is exiting and that it should dispose the finalizable in a controlled manner. Same happens to Avalonia code.
Hmm. This is not actually very good. Mono is doing the "right" thing by actually cleaning up - if we don't then we might leak something.
@kekekeks Could you open an issue on SkiaSharp linking to this and with the code?
I'll have a look and see what can be done. I may have to look at switching to using SafeHandles because that is might solve this problem. But it may cause issues with WASM... I'll have to investigate.
I tried out the GC.SuppressFinalize on the code and it does stop the finalizer from being called. Is that going to cause issues for you later on? Technically, if the app is exiting, then probably not, but might still?
It shouldn't cause issues for GrContext, but if other GPU-backed resources such as SKSurface are somehow leaked, GC will attempt to finalize those on its own thread. We also might have some surfaces in memory when Environment.Exit(0) is called.
Hey guys,
Thanks for creating the issue on SkiaSharp. I am tracking it, but in the meantime, is there some workaround we can do on our application? For example, doing some calls on my MainWindow in order to dispose / destruct it and ensure finalization of everything on the main thread?
Is Process.GetCurrentProcess().Kill(); at the end of Main an acceptable workaround? It completely skips finalization.