A recent post on Shawn Hargreaves’ blog reminded me that I never got around to sharing my technique for interacting with the XNA Guide API and doing other asynchronous operations during my game’s execution. It’s a simple application of some of the cooperative-threading techniques I’ve blogged about previously, and makes what would otherwise be a somewhat painful exercise relatively simple in practice. So, for those who are working on XNA games or just curious, I’ll go into detail on the technique here and share the source code with you!
The basic goal here is to be able to write game code in an imperative, sequential manner, without having to deal with locks, callbacks, polling, or race conditions. You rarely achieve perfection when dealing with concurrency, but even getting within sight of perfection can be worth the effort if it means you don’t have to spend time pulling your hair out trying to reproduce a rare threading bug.
In my game, any operation that can be performed off the main thread is done without blocking the main thread. Loading content, loading files, saving games, loading games, interacting with the guide, etc. This means that I can seamlessly load things in the background while the game is running without any noticeable stuttering. Normally, you’d need to do lots of juggling to handle this correctly – locking, threads, explicit synchronization – but thanks to cooperative threading, I don’t have to deal with any of that. The techniques I describe in this post are the same basic techniques I’m using for my game. Hopefully, that means they should work for yours too (though unfortunately that means if there are bugs in them, you’ve probably just found a bug in my game… whoops.)
Unless you’re working on an XNA game that uses networking (which sadly, I am not, so I can’t speak to the difficulties there), the biggest concurrency related issues you’re likely to deal with are twofold:
- Interacting with the XNA Guide APIs
- Performing I/O
By the end of this article I’ll have shown you how you can tackle these problems in your XNA games without having to deal directly with threads or synchronization, in a simple, imperative manner that works equally well on the PC and XBox 360.

The Guide APIs are almost all designed in a manner that requires use of either threads, callbacks/continuations, or polling, because they come as Begin/End pairs that require management of ordering and state beyond a single frame. This is for the most part a necessity, as detailed in the blog post I mentioned above. So instead of trying to find a workaround, the best course of action is to simplify the process of working with those APIs – if possible, without introducing new issues or restricting functionality.
I/O is a more interesting problem; even when writing desktop applications, you often perform large I/O operations that have the potential to block, and in those cases, a well-written application uses concurrency techniques – threading, callbacks, etc – to avoid ‘hanging’ and frustrating the user. However, on almost any modern desktop machine, you can typically get away with tiny I/O operations if done correctly – a good example is Firefox 3, which still regularly performs short disk operations on its UI thread, which can cause it to hang if your machine is under extreme I/O load, but works fine in almost every other situation.
The XBox 360 makes I/O particularly challenging because you can’t rely on latency and throughput characteristics that you might be used to on a desktop machine: Not only do you have to deal with the downsides inherent in hard disks, but you also have to deal with the possibility of a player using a memory card, and even worse, the possiblity of the available storage devices changing while your game is running (due to memory card insertion/removal, etc). This means that doing I/O in the main thread is pretty much a non-starter. You’re stuck: It’s concurrency time.
Luckily, cooperative threading provides a great solution for tackling both of these issues: It lets you reason about your problems in a manner you’re familiar with, and solve them by writing imperative code that still behaves correctly in tough situations, at the expense of some slight overhead and minor changes to your game code. You don’t have to build a tremendously complex state machine (since the C# compiler can be convinced to do the heavy lifting for us), and you don’t have to worry about locking and synchronization since all your code is guaranteed to run sequentially in the same thread, at the appropriate time.
So, for the initial scaffolding. As you might have guessed, we’ll be trying out the cooperative threading framework from my open source library, Squared.Task. This library is inspired by the techniques used in Twisted and imvu.task. The library is designed to perform well under tight constraints, so you can use it just fine on the XBox 360 without any garbage creation* or thread contention issues.
The first thing we’re going to need is a way to manage our cooperative threads. We’ll do this by creating a TaskScheduler owned by our Game object, that will run our ‘Tasks’ (cooperative threads) on the same thread as the rest of our game logic, and asking it to Step all pending tasks every time the Game’s Update method runs:
public class TaskExampleGame : Microsoft.Xna.Framework.Game {
Squared.Task.TaskScheduler taskScheduler;
...
public TaskExampleGame () {
...
taskScheduler = new Squared.Task.TaskScheduler();
}
...
protected override void Update (GameTime gameTime) {
...
taskScheduler.Step();
}
Pretty trivial so far. Creating a TaskScheduler is extremely cheap and quick, so you can do it right in your Game’s constructor; any particular expensive resources it requires are created on-demand. Calling the Step method every Update ensures that any waiting tasks are processed on a regular basis, as long as your game is updating at a solid framerate – which you always want it to be. If you don’t have any tasks pending, Step is effectively free, so when you’re not using the scheduler you don’t have to think about it.
Of course, this is still boilerplate. What we really want to do is actually get something done! So, let’s come up with a contrived example: We’ll make the A button increment an onscreen counter, the B button read the counter from a file on the player’s storage device, and the X button write the counter to that same file. This means we have to handle storage device selection, error conditions, and disk I/O – and we want to do it without causing any stuttering or hanging!
To begin with, we need the counter, and some basic scaffolding so that our example at least slightly resembles a well-behaved game – we’ll make sure to display the counter on screen so you can actually tell the example is working, and suspend our handling of user input if the Guide is open so that things don’t happen mysteriously in the background while you’re reading a message somebody sent you on MSN. I’m going to omit this scaffolding since you can see it in the source code (included at the bottom), and it’s tremendously boring.
So, now we can hold the A button to increase the counter, and it just keeps going up! Great fun, really – just add a rope to burn and we’re ready for an IGF submission. Anyway, let’s get to the meat of it: We want to write the counter out to a file when the player hits the X button. To begin with, let’s just spike a basic implementation:
void SaveCounterToDiskViaCallbacks () {
Guide.BeginShowStorageDeviceSelector((asyncResult) => {
var storageDevice = Guide.EndShowStorageDeviceSelector(asyncResult);
System.Threading.ThreadPool.QueueUserWorkItem((_) => {
var container = storageDevice.OpenContainer("TaskExample");
var file = System.IO.File.Open(
System.IO.Path.Combine(container.Path, "counter.bin"),
System.IO.FileMode.OpenOrCreate,
System.IO.FileAccess.Write,
System.IO.FileShare.None
);
byte[] bytes;
lock (CounterLock)
bytes = BitConverter.GetBytes(Counter);
file.Write(bytes, 0, bytes.Length);
file.Close();
container.Dispose();
});
}, null);
}
Even this trivial example has lots of problems – not only am I completely ignoring error handling, but there are plenty of other issues that make this approach fail to scale up to large projects. At least it works, right?
Next, let’s spike a callback-based implementation of reading the counter:
void LoadCounterFromDiskViaCallbacks () {
Guide.BeginShowStorageDeviceSelector((asyncResult) => {
var storageDevice = Guide.EndShowStorageDeviceSelector(asyncResult);
System.Threading.ThreadPool.QueueUserWorkItem((_) => {
var container = storageDevice.OpenContainer("TaskExample");
var filePath = System.IO.Path.Combine(container.Path, "counter.bin");
if (!System.IO.File.Exists(filePath))
return;
var file = System.IO.File.Open(
filePath,
System.IO.FileMode.Open,
System.IO.FileAccess.Read,
System.IO.FileShare.None
);
var bytes = new byte[4];
file.Read(bytes, 0, 4);
int newCounter = BitConverter.ToInt32(bytes, 0);
lock (CounterLock)
Counter = newCounter;
file.Close();
container.Dispose();
});
}, null);
}
If you look carefully, you might even see a bit of error handling in there. Now that we all know what a trivial stall-free implementation looks like, we can try out our sample and see that it’s fully functional. In most cases on a PC, it will work more or less perfectly, since I/O is lightning fast, despite the many bugs and errors experienced .NET programmers can find in it. One obvious problem is the fact that it basically does everything in one threadpool work item, because anything other than that is painful. This means that you have to put locks around basically any data and objects you use within the implementation, which gets complicated quickly. Even worse, being in the thread pool means that if we need to call a function that can only be run from the main thread, we have to marshal back and forth manually.
If the above spikes confused you, you can find explanatory comments in the downloadable source code. Let’s continue.
Now, to flex our mental muscles, let’s try and build a relatively ideal implementation of this example using our task scheduler. We’ll even handle errors and inform the user about them. As a result, you’ll see some code that mysteriously resembles scaffolding… Let’s get started.
To begin, we want to create tasks for our load and save operations. Let’s just write them, and worry about the functions they call later.
IEnumerator<object> SaveCounterToDisk (int counterValue) {
var storageDeviceFuture = SelectStorageDevice();
yield return storageDeviceFuture;
var storageContainerFuture = OpenStorageContainer(
storageDeviceFuture.Result,
"TaskExample"
);
yield return storageContainerFuture;
using (var container = storageContainerFuture.Result) {
var filePath = System.IO.Path.Combine(container.Path, "counter.bin");
var fileFuture = GetFileAdapter(
filePath,
System.IO.FileMode.OpenOrCreate,
System.IO.FileAccess.Write
);
yield return fileFuture;
using (var file = fileFuture.Result) {
var bytes = BitConverter.GetBytes(counterValue);
var writeFuture = file.Write(bytes, 0, bytes.Length);
yield return writeFuture;
}
}
}
}
So, let’s address the most confusing parts of this first before we continue on to the load function:
The function has to return an IEnumerator<object> so that the task system will recognize it as a task, and it can yield any waitable object out as a result. Unfortunately, we can’t use a strong type here because the compiler will only allow iterator functions to return IEnumerator or IEnumerable.
One of the patterns you see is retrieving a future from a function that begins an operation, and then yielding the future to wait for it to acquire a result. We don’t have to do these two operations in sequence, so if we wished, we could acquire futures for multiple operations in sequence before yielding on all of them at once – easy parallelization.
Once a future has a result, we’re free to retrieve it (using the Result property) or simply discard it. A future’s result will either be a value of the type you expect (the result of the operation), or an exception (caused by the operation failing). If the future’s result is an exception, attempting to retrieve it automatically causes it to be rethrown within a FutureException, preserving the original stack trace. This means that code using futures doesn’t ignore unexpected exceptions – it bubbles them up, just like single-threaded imperative code.
Another bonus here is that a discarded future is still monitored by the task system as a result of waiting on it – if you discard a future you’ve waited on, and the future contained an unhandled exception, it is automatically bubbled up through your task, causing resources to be cleaned up and allowing a higher up function or task to handle it. This means that futures with out a result we care about can simply be discarded without sacrificing correctness.
You may also wish to note that we pass the original value of the counter into the task to preserve it. This means that if the counter continues going up after the save begins, the original value will still be saved. No locking or state consistency issues to worry about; the compiler handles storing that argument for us while our task is running.
Most of the logic you’re familiar with from the original callback-based implementations has been replaced with simple functions that return futures. I’ll show those after the load function, which follows:
IEnumerator<object> LoadCounterFromDisk () {
var storageDeviceFuture = SelectStorageDevice();
yield return storageDeviceFuture;
var storageContainerFuture = OpenStorageContainer(
storageDeviceFuture.Result,
"TaskExample"
);
yield return storageContainerFuture;
using (var container = storageContainerFuture.Result) {
var filePath = System.IO.Path.Combine(container.Path, "counter.bin");
var fileFuture = GetFileAdapter(
filePath,
System.IO.FileMode.Open,
System.IO.FileAccess.Read
);
yield return fileFuture;
if (fileFuture.Failed)
yield break;
using (var file = fileFuture.Result) {
var bytes = new byte[4];
var readFuture = file.Read(bytes, 0, bytes.Length);
yield return readFuture;
Counter = BitConverter.ToInt32(bytes, 0);
}
}
}
Not much new here. One thing of note: If the GetFileAdapter call failed, we just abort the task immediately. The using blocks clean up all the resources we were using for us automatically, which means we don’t attempt to read from a nonexistent file. If we wanted, we could show an error dialog to the player here. In contrast to this, we don’t bother checking the number of bytes that were read, even though readFuture contains them. If I were a serious game developer instead of a blogger writing an example, I’d check the result and show an error if it’s anything other than four bytes (or perhaps just throw an exception).
Hopefully you’re all following me so far. For completeness, I’ll now show the wrapper functions used above:
Squared.Task.Future<StorageDevice> SelectStorageDevice () {
var f = new Squared.Task.Future<StorageDevice>();
Guide.BeginShowStorageDeviceSelector((asyncResult) => {
try {
var result = Guide.EndShowStorageDeviceSelector(asyncResult);
f.SetResult(result, null);
} catch (Exception ex) {
f.SetResult(null, ex);
}
}, null);
return f;
}
Squared.Task.Future<StorageContainer> OpenStorageContainer (StorageDevice device, string titleName) {
return Squared.Task.Future.RunInThread(
() => device.OpenContainer(titleName)
);
}
Squared.Task.Future<Squared.Task.IO.FileDataAdapter> GetFileAdapter (string fileName, System.IO.FileMode mode, System.IO.FileAccess access) {
return Squared.Task.Future.RunInThread(
() => new Squared.Task.IO.FileDataAdapter(
fileName,
mode,
access,
System.IO.FileShare.None
)
);
}
These wrappers are no more complicated than the original imperative code, with the exception of the try block in the Guide wrapper (necessary to propagate any errors out through the Future).
One advantage you might not notice is that since the Guide wrapper issues the original Begin call directly, it will bubble right up into the caller without any delay, allowing you to handle it immediately without waiting, while errors that occur later on will be stored into the Future and bubbled up into the caller.
We invoke OpenContainer on a thread to avoid it blocking the main thread. Those of you who are sticklers for correctness may have noticed that this isn’t 100% correct, since there’s no guarantee that this function is thread-safe. We’re relatively okay since we’re certain it won’t be used on any other threads at the same time, thanks to the ordering guarantees provided by the task system, but there’s still a slight potential for badness here. Caveat emptor. If you feel nervous, just call it directly from your task instead – no thread safety issues there.
One somewhat major difference is that I’m now constructing a FileDataAdapter object instead of opening a file using the standard System API. A FileDataAdapter is a utility class provided by my framework that simplifies the task of performing asynchronous I/O at a low level. It implements a generic interface that allows manipulating other data sources, like sockets and streams, in the same manner. For those seeking a higher-level interface, there are utility classes for performing WriteLine, ReadLine, and similar operations on an adapter – without needing a thread! Just because you want to write concurrent code doesn’t mean you have to suffer.
To run these tasks, the code you write isn’t much more complicated than calling the original functions:
taskScheduler.Start( SaveCounterToDisk(Counter), Squared.Task.TaskExecutionPolicy.RunAsBackgroundTask );
The reason for the ‘RunAsBackgroundTask’ argument is non-obvious, so I’ll explain: One of the features of the task system is lifetime management. Starting a task returns a Future that will be completed when the given task is done running. If you wish, you can Dispose the future, and the task will be cancelled. RunAsBackgroundTask is a special execution policy that tells the TaskScheduler that not only do you have no desire to manage the lifetime of the task, but you want it to automatically handle any errors that occur in the task.
Of course, the default error handler for background tasks knows nothing about XNA. So let’s write one that does, and use the Guide to show the error message to the user:
public TaskExampleGame () {
...
taskScheduler.ErrorHandler = (error) => {
taskScheduler.Start(HandleTaskError(error));
return true;
};
}
IEnumerator<object> HandleTaskError (Exception error) {
while (error.InnerException != null)
error = error.InnerException;
string message = error.Message;
if (message.Length > 255)
message = message.Substring(0, 255);
while (Guide.IsVisible)
yield return new Squared.Task.WaitForNextStep();
var f = new Squared.Task.SignalFuture();
Guide.BeginShowMessageBox(
"An unexpected error occurred",
message,
new string[] { "Okay
" },
0, MessageBoxIcon.Error,
(asyncResult) => {
f.SetResult(null, null);
}, null
);
yield return f;
}
A fairly simple error handler that schedules a new task to show a message box to alert the user about the error. You may notice I apply a simple polling technique here to avoid the need for any complex queueing or locking – if the error handler task discovers the guide is running, it simply suspends itself until the next step, allowing the guide to close. Obviously, you don’t want to be doing this in thousands of tasks every step if you want to scale, but for a task like this, it’s more than adequate.
You’ll notice that most of the trivial error cases work perfectly in this case. I even left one obvious case unhandled so you could easily trigger a failure – just cancel the storage device selection screen (though you’ll only see it if you have more than one), and you’ll see ‘NullReferenceException’ pop up. What about opening the guide while selecting a storage device? Handled just fine.
Now that we’ve got our example up and running, let’s add a little polish. We’ll add a helpful message to explain how the example works, and put a bouncing smiley face on the screen that moves smoothly based on the current time, so we can see if the game hangs by watching the smiley. We’ll also add a simple status display so that we can see what the example is doing, and avoid queueing up dozens of copies of an operation all stalled waiting for the same resource (like a storage device). I’ll omit the code for these changes since it’s mostly uninteresting, but as before, you can see it in the downloadable source code.

Now that we’ve walked through the process of building this example (and gotten tremendously bored), here’s the source code and binaries so you can try it out on your Windows PC or XBox 360:
http://luminance.org/xna/TaskExampleBinaries.zip
http://luminance.org/xna/TaskExampleSource.zip
You’ll need XNA Game Studio 3.1 installed, of course. You’ll need the latest version of my TaskLib and Util libraries, along with the XBox 360 versions of them (mostly identical), available on Google Code:
http://code.google.com/p/fracture/source/browse/#svn/trunk/Squared
The XBox 360 versions live in the ’360′ folder while the Windows versions live in their own named folders. You can either build them and add them as references, or add the projects themselves to the example as references. For my game, I’ve simply checked out the project as a subversion external so that it’s easy to keep it up to date, but my game doesn’t suddenly break if it changes.
I hope you’ve found this useful. This example, like all the source code for my libraries, is open source. I encourage you to share it, modify it, experiment, and maybe even use it in your own games. Please don’t hesitate to email me or leave a comment if you have questions, suggestions, or think you’ve found a mistake.




Pingback: Concurrency on a single thread – 22th Edition | ArcadePortal
#1 by Robert on February 15, 2010 - 2:54 pm
Quote
How can I use the TaskScheduler in a Windows Service application? In the sample console MUDServer, I see that you are using an infinite loop which does not allow the Main method to exit:
while (true) {
Scheduler.Step();
Scheduler.WaitForWorkItems();
}
How can I start the task scheduler in my service’s OnStart method? OnStart must finish so that Windows considers the service as successfully started.
There is also the complementary action: stopping… How can I end the taskscheduler in OnStop?
#2 by Kael on February 15, 2010 - 5:27 pm
Quote
Hi Robert,
I haven’t personally tried writing a Service using the TaskScheduler. My suggestion would be to try using the windows message pump to provide the Step/Wait functionality, because then the scheduler stops automatically with your message pump. I use this approach in graphical applications and servers and in both cases it works well because you can use the standard System.Windows.Forms.Application methods/events to handle things like system shutdown. Having a message pump within a Service is well understood in Win32 and I’m pretty sure there are examples for how to do it in C#.
FYI, to create a scheduler based on the windows message pump, you just do:
new TaskScheduler(JobQueue.WindowsMessageBased)
Any tasks queued to the scheduler will run automatically by the windows message pump (they get mapped to windows messages).
A few gotchas:
Scheduling using the windows message pump is considerably slower than the normal way (you peak at around 30000-50000 task “steps” per second, compared to millions/sec with the normal approach), so if you’re doing complex computations using communication between tasks, you’ll get bottlenecked. If you need to do complex computation via tasks, you can always run a normal scheduler in a worker thread.
WaitForNextStep will no longer work since windows’ message pump has no concept of ‘steps’.
Since you’re running on the UI thread inside the message pump, if your individual tasks block (by waiting on a WaitHandle, or doing a bunch of computation, etc) that will block your UI thread and Windows will act like your app has hung.