Skip to content
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

SkiaSharp.SKObject.Dispose can throw ArgumentNullException on finalization #482

Closed
davkean opened this issue Apr 6, 2018 · 5 comments · Fixed by #489
Closed

SkiaSharp.SKObject.Dispose can throw ArgumentNullException on finalization #482

davkean opened this issue Apr 6, 2018 · 5 comments · Fixed by #489
Assignees
Projects
Milestone

Comments

@davkean
Copy link

davkean commented Apr 6, 2018

Had a customer complaining about crashes in Visual Studio while using SkiaSharp in the WinForms designer: https://developercommunity.visualstudio.com/content/problem/228761/crashing-when-debugging-a-skiasharp-winforms-app-i.html.

Looking into his crashes it looks like SkiaSharp.SKObject.Dispose can throw on finalization:

   at System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
   at SkiaSharp.SKObject.Dispose(Boolean)
   at SkiaSharp.SKPaint.Dispose(Boolean)
   at SkiaSharp.SKNativeObject.Finalize()

If you look at Dispose, you can see multiple paths where it attempts to access managed instance and static fields that have no guarantee about already being cleaned up by the time the finalizer has run:

Any access to member/static reference fields should be guarded with if (disposing). Or Better yet, wrap the unmanaged handle in a SafeHandle and let it free it.

@davkean
Copy link
Author

davkean commented Apr 6, 2018

Actually I'm wrong here - while you shouldn't really touch reference fields in finalize, none of them should be null. What I suspect is happening, is that SKPaint is throwing in its constructor here:

public SKPaint ()
.

This leaves the base field ownedObjects null, but the finalizer still is run. The following demonstrates:

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                new SKPaint();
            }
            catch (Exception)
            {

            }
        }
    }


    class SKPaint : SKObject
    {
        public SKPaint()
            : base(sk_paint_new())
        {
        }

        private static IntPtr sk_paint_new() { throw new Exception(); }
    }

    class SKObject
    {
        private readonly List<string> ownedObjects = new List<string>();

        internal SKObject(IntPtr handle)
        {
        }

        ~SKObject()
        {
            lock (ownedObjects)
            {
            }
        }
    }

Which outputs:

Unhandled Exception: System.ArgumentNullException: Value cannot be null.
   at System.Threading.Monitor.ReliableEnter(Object obj, Boolean& lockTaken)
   at System.Threading.Monitor.Enter(Object obj, Boolean& lockTaken)
   at ConsoleApp237.SKObject.Finalize()

@mattleibow mattleibow added this to New in Triage Apr 10, 2018
@mattleibow
Copy link
Contributor

Do we have a larger stack trace? I see the two you provided are different.
Also, what is the constructor throwing? Are we able to see this at all?

We can guard against this by doing a null check, but it would be great to know more.

I'll write a test case and see if I can fix things.

@aktzbn
Copy link

aktzbn commented Apr 12, 2018

I encountered the same issue on Debian when libSkiaSharp.so was absent.

@mattleibow
Copy link
Contributor

Yeah, that is what I was thinking. The only time the methods would throw would be when the native library was not found. the C+ just returns null on error.

Thanks for the confirmation. And this issue relates to the designer and VS and debugging - that is a volatile combo that sometimes doesn't work.

@mattleibow mattleibow self-assigned this Apr 12, 2018
@mattleibow mattleibow added this to the 1.60.1 milestone Apr 12, 2018
mattleibow added a commit that referenced this issue Apr 12, 2018
…482

 - only dispose the list if the list was actually created
 - sign the tests so we can use InternalsVisibleTo
 - added a test to identify #482
Triage automation moved this from New to Complete / Invalid Apr 13, 2018
mattleibow added a commit that referenced this issue Apr 13, 2018
@mattleibow
Copy link
Contributor

@davkean Thanks for your info and research. I fixed this issue and it is now in master.

@mattleibow mattleibow removed this from Complete / Invalid in Triage May 5, 2018
@mattleibow mattleibow added this to Ready in v1.60.1 via automation May 5, 2018
@mattleibow mattleibow moved this from Ready to Done in v1.60.1 May 5, 2018
@mono mono locked as resolved and limited conversation to collaborators Aug 19, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
v1.60.1
  
Done
Development

Successfully merging a pull request may close this issue.

3 participants