Why NativeWindow can't be finalized
The premise of this blog post is flawed as the post gives the impression that you would want to finalize a window. But this is in-fact a Bad Idea™. You don’t want windows randomly staying around until the garbage collector decides it should finalize the window, that is a very weird user experience. Instead we can see the exception in the NativeWindow finalizer as a kind of leak detection that tells you that you’ve forgotten to dispose it properly. So the perspective should be more, why does the NativeWindow throw an exception if you don’t properly dispose of it before it gets finalized.
In the previous post we looked at GLFWProvider.CheckForMainThread.
There we concluded that most GLFW functions need to be called from the main thread.
In this post we are going to look at a another consequence of that: NativeWindow cannot be finalized.
If you ever try to do this by letting the garbage collector collect a NativeWindow, you will be greeted with a GLFWException saying:
You can only dispose windows on the main thread. The window needs to be disposed as it cannot safely be disposed in the finalizer.
So, why is this? Well, a simple answer is provided in the previous blog post: Most GLFW APIs need to be called on the main thread. And finalizers in C# are not run on the main thread, they are run in their own special finalizer thread. This means that we cannot call GLFW functions in the finalizer, which means we can’t destroy the GLFW window. So, instead we throw an exception to hopefully notify the programmer of their wrongdoing.
But now you might be asking: I don’t dispose of my NativeWindow and I don’t get any crashes, what gives?
Well, dotnet makes no guarantee that the garbage collector will actually run the finalizer of an object before the program exits. From the documentation on finalizers:
… you can’t guarantee the garbage collector calls all finalizers before exit, you must use Dispose or DisposeAsync to ensure resources are freed.
So, make sure to dispose your NativeWindow either with using or by manually calling .Dispose().
Comments