Getting ContextSwitchDeadlock on MonoGame 3.2

I am using MonoGame 3.2 (OpenGL) on Windows 8 and I’m getting a Managed Debugging Assistant ‘ContextSwitchDeadlock’.
The stack trace for the thread that triggered the issue is not available. The Visual Studio Threads window indicates that it is named “Worker Thread”.

Some investigation suggests that this issue is not critical, but indicates that the might be problems associated with it anyway (such as lack of responsiveness and memory leaks) on the long term.
Note that the description for this “exception” (or whatever it is) suggests that the application should be unresponsive for over 1 minute. This is not the case, the application is calling the Update method just fine.

I managed to greatly simplify my program and come up with the following test.
This test should throw an Exception saying “It worked” after 65 seconds, but instead it throws the ContextSwitchDeadlock after around 61 seconds.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Threading.Tasks;
using System.IO;
using System.Xml.Serialization;

namespace MyNamespace
{
    [Serializable]
    [XmlRoot("test")]
    public class TestFile
    {
    }

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        Task pendingTask;

        protected override void LoadContent()
        {
            pendingTask = LaunchTasks();
        }

        private async Task LaunchTasks()
        {
            string fontFile = Path.Combine(Content.RootDirectory, "test.xml");
            XmlSerializer deserializer = new XmlSerializer(typeof(TestFile));
            for (int i = 0; i < 10000; ++i)
            {
                using (var textReader = new StreamReader(fontFile))
                {
                    await textReader.ReadToEndAsync();
                    deserializer.Deserialize(new StringReader("<test></test>"));
                }
            }
        }

        protected override void Update(GameTime gameTime)
        {
            if (pendingTask.IsCompleted)
            {
                // Rethrow any exceptions.
                pendingTask.Wait();
            }

            if (gameTime.TotalGameTime.TotalSeconds > 65)
            {
                throw new Exception("It worked.");
            }

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);

            base.Draw(gameTime);
        }
    }
}

Removing the call to ReadToEndAsync prevents the exception from occuring. Same with the serializer.
The XML file contains the following:

<?xml version="1.0"?>
<test>
</test>

The Main method is just:

using System;

namespace MyNamespace
{
    /// <summary>
    /// The main class.
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            using (var game = new Game1())
                game.Run();
        }
    }
}

I have run tested this only in Debug mode. The SharpDX backend does not throw the ContextSwitchDeadlock, though it could be due to timing differences that simply hide the problem.

Is this a bug in MonoGame or am I doing something wrong?

I suspect this is just incorrect async code. In particular this…

Async does not mean threaded… something has to be executing the awaiter and I don’t think anything is here.

I know tasks aren’t threads, but in this case it is definitely being executed (both with and without ConfigureAwait(false)).
Regardless, I can’t seem to reproduce it anymore. I must have disabled it somehow (though the exceptions dialog has the corresponding checkbox checked, so I have no idea what I did wrong. It’ll try again later)

Is there a “right” way to use tasks in MonoGame? They definitely seem to work the way I’d normally expect them to. Without ConfigureAwait it continues on the same thread, with ConfigureAwait(false) it jumps around threads…

in this case it is definitely being executed

But not to completion. I suspect your file is not so big for it to take over 60 seconds for the task to complete. I suspect your task is getting to the ReadToEndAsync() and never continuing. This is exactly what would happen if you don’t give the await object a chance to advance.

Interesting that I see that 60 seconds is exactly how long people report it takes to trigger a ContextSwitchDeadlock exception.

Regardless, I can’t seem to reproduce it anymore.

That is a bad sign… things don’t magically fix themselves.

Is there a “right” way to use tasks in MonoGame?

Not really… tasks are tasks. The right way is the same right way for any other .NET app.

The only thing to look out for is the general XNA rules for thread safety.

If I use

string fontFile = Path.Combine(Content.RootDirectory, "test.xml");
XmlSerializer deserializer = new XmlSerializer(typeof(TestFile));
for (int i = 0; i < 10000; ++i)
{
    using (var textReader = new StreamReader(fontFile))
    {
        await textReader.ReadToEndAsync();
        Console.WriteLine(i);
        deserializer.Deserialize(new StringReader("<test></test>"));
    }
}

Then it definitely prints the iteration numbers.
Though it does take a long time to run all the iterations.

Not really… tasks are tasks. The right way is the same right way for any other .NET app.
The only thing to look out for is the general XNA rules for thread safety.

Yeah, that’s what I thought too.

Either way, the version I posted was not truly the original. The original game was far too big to post, so I made a SSCCE by deleting as much as I could while still having this exception. If the minimalistic version somehow no longer has the bug, then I can repeat the process and come up with a new SSCCE.
I think it might be some weird timing issue. I guess threads bugs are kind of unpredictable.
The original game definitely still has the problem. I’ll post again when I get a new code example with the ContextSwitchDeadlock.

I successfully reproduced it again. Here’s my new Game1.cs:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Threading.Tasks;
using System.IO;
using System.Xml.Serialization;

namespace MyNamespace
{
    [Serializable]
    [XmlRoot("test")]
    public class TestFile
    {
    }

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        Task pendingTask;

        protected override void LoadContent()
        {
            pendingTask = LaunchTasks();
        }

        private async Task LaunchTasks()
        {
            string fontFile = Path.Combine(Content.RootDirectory, "test.xml");
            XmlSerializer deserializer = new XmlSerializer(typeof(TestFile));
            for (int i = 0; i < 10000; ++i)
            {
                Console.WriteLine(i);
                using (var textReader = new StreamReader(fontFile))
                {
                    await textReader.ReadToEndAsync();
                    deserializer.Deserialize(new StringReader("<test></test>"));
                }
            }
        }

        protected override void Update(GameTime gameTime)
        {
            if (pendingTask.IsCompleted)
            {
                // Rethrow any exceptions.
                pendingTask.Wait();
            }

            if (gameTime.TotalGameTime.TotalSeconds > 65)
            {
                throw new Exception("It worked");
            }

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);

            base.Draw(gameTime);
        }
    }
}

I am definitely seeing a ContextSwitchDeadlock here. This is confusing since it’s pretty much identical to my first version, but I deleted the project and started again.
Regardless, the Console.WriteLine pretty much proves the task is being properly executed.