To learn C# native interop, I've been working on an OpenGL wrapper. The OpenGL API itself is a state machine which is bound to a specific thread. When an object containing native resources is garbage collected, the finalizer is running in the GC thread, and cannot directly free the resources.
The workaround I currently have is to have a list in the context object, which the objects add their resources to and at a safe point in the draw loop it iterates through and frees them.
The problem with this, however, is that if the GC collects while it's iterating through that list, the foreach fails as the collection has been modified. I can't just put a mutex around the list as the GC is stop-the-world in most implementations and if the draw loop had locked it, it'd never complete the iteration and unlock it again.
Typically the MTBF is about two hours of gameplay, but if intentionally stress tested with a few thousand objects per second it happens in just a few seconds.
What might be the best approach here?
Then you're going to bite the bullet and stop relying on the GC to do your resource management for you. You're going to have to have your asset manager have an explicit function to delete the objects it allocates, rather than relying on the asset manager's finalizer function. And you're going to have to call that function at a specific place in your code.
Just because you have GC doesn't mean it's the best or only solution.