Avalonia: Native skia crash with avalonia.mvvm template targeting net48

Created on 1 Aug 2020  路  12Comments  路  Source: AvaloniaUI/Avalonia

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

All 12 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TheColonel2688 picture TheColonel2688  路  3Comments

khoshroomahdi picture khoshroomahdi  路  4Comments

grokys picture grokys  路  4Comments

x2bool picture x2bool  路  4Comments

stdcall picture stdcall  路  4Comments