Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Sync context for testing async background workers
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
// Custom synchronization context for testing asynchronous background workers.
// See http://int.li/15 for the full story!
public class AsyncTester : SynchronizationContext, IDisposable
{
private readonly int _timeoutMs;
private readonly SynchronizationContext _prevSyncContext;
private readonly BlockingCollection<Tuple<SendOrPostCallback, object>> _queue =
new BlockingCollection<Tuple<SendOrPostCallback, object>>();
public static AsyncTester WithTimeout(int timeoutMs)
{
var sc = new AsyncTester(timeoutMs, Current);
SetSynchronizationContext(sc);
return sc;
}
public void Uninstall()
{
if (this != Current) return;
ProcessQueue();
SetSynchronizationContext(_prevSyncContext);
}
private AsyncTester(int timeoutMs, SynchronizationContext prevContext)
{
_timeoutMs = timeoutMs;
_prevSyncContext = prevContext;
}
public void Dispose()
{
Uninstall();
}
public override void Post(SendOrPostCallback d, object state)
{
_queue.Add(Tuple.Create(d, state));
}
public void ProcessQueue(int timeoutMs = 0)
{
// Use the constructor-specified timeout if not overridden.
if (timeoutMs <= 0)
timeoutMs = _timeoutMs;
var sw = Stopwatch.StartNew();
while (true)
{
var timeLeft = (int)(timeoutMs - sw.ElapsedMilliseconds);
if (timeLeft <= 0)
break; // we're done waiting
Tuple<SendOrPostCallback, object> tuple;
if (_queue.TryTake(out tuple, timeLeft))
{
// Item1 = the callback, Item2 = the state object.
tuple.Item1(tuple.Item2);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment