Why does GC.Collect cause crash due to possible disposed SoundEffects?

This one took me a while to track down.

To reproduce:

  • Create a ContentManager
  • Load a SoundEffect from XNB
  • Play the SoundEffect (not SoundEffectInstance)
  • Dispose the ContentManager that was used to play the SoundEffect
  • Call GC.Collect() and GC.WaitForPendingFinalizers()
  • Repeat it in a loop a few times, letting a number of frames pass, but less than the length of the sound effect (3 seems to be the magic number for me)
  • Observe crash

Here’s the code I used to get it to reproduce:

var contentManager = new Microsoft.Xna.Framework.Content.ContentManager(game.Services);
var soundEffect = contentManager.Load("content/screens/gamescreen/audio/rain_loop");
soundEffect.Play();
contentManager.Dispose();

GC.Collect();
GC.WaitForPendingFinalizers();

The end result is a crash as follows:

Clicking “Break” shows the following:

My uneducated guess is that the buffer which is being played is being cleaned by the garbage collector. Is there any solution to this? The only only solutions I an think of are to not cal GC.Collet/WaitForPendingFinalizers, but I do this inbetween levels to minimize the chance of GCs happening mid-game. The other option would be to use SoundEffectInstance.

If the solution is to use SoundEffectInstance, then it seems like the “fire and forget” approach of SoundEffect is essentially invalidated, unless you have very tight control on when you dispose content managers vs. when sound effects play.

Has anyone encountered this? any thoughts on how to solve it? Is it something that could be solved internally in MG (perhaps by stopping a buffer when its associated content manager is disposed)? Also, I have tested this on Microsoft’s XNA4 and have been unable to reproduce the problem.