Skip to content

Instantly share code, notes, and snippets.

@ritalin
Created June 15, 2012 05:47
Show Gist options
  • Save ritalin/2934876 to your computer and use it in GitHub Desktop.
Save ritalin/2934876 to your computer and use it in GitHub Desktop.
awaitable runner host (deleved from /gists/2880770)
public class AbstractLocalSyncRunner {
protected AbstractLocalSyncRunner() {
}
protected virtual void RunAsyncCore(ILocalSynchronizationContextRef inLocalContext, Func<SynchronizationContext, Task> inTestAction) {
inLocalContext.Start();
try {
inLocalContext.Reference.Post(obj => {
var task = inTestAction(inLocalContext.Reference);
task.AsAsyncAction().Completed = (info, state) => {
inLocalContext.Stop();
if (state == AsyncStatus.Error) {
throw info.ErrorCode;
}
};
}, null);
inLocalContext.RunMessagePump();
}
finally {
inLocalContext.Stop();
}
}
protected virtual TResult RunAsyncCore<TResult>(ILocalSynchronizationContextRef<TResult> inLocalContext, Func<SynchronizationContext, Task<TResult>> inTestAction) {
inLocalContext.Start();
try {
inLocalContext.Reference.Post(obj => {
var task = inTestAction(inLocalContext.Reference);
task.AsAsyncOperation().Completed = (info, state) => {
inLocalContext.Stop();
if (state == AsyncStatus.Error) {
throw info.ErrorCode;
}
inLocalContext.Result = info.GetResults();
};
}, null);
inLocalContext.RunMessagePump();
return inLocalContext.Result;
}
finally {
inLocalContext.Stop();
}
}
}
public interface ILocalSynchronizationContextRef {
void Start();
void Stop();
void RunOneRound();
void RunMessagePump();
SynchronizationContext Reference { get; }
}
public interface ILocalSynchronizationContextRef<TResult> : ILocalSynchronizationContextRef {
TResult Result { get; set; }
}
internal class LocalSynchronizationContext<TResult> : SynchronizationContext, ILocalSynchronizationContextRef<TResult>, IDisposable {
private readonly Queue<Action> messagesToProcess = new Queue<Action>();
private readonly object syncHandle = new object();
private bool isRunning = false;
private SynchronizationContext mParentContext;
private string mId;
public LocalSynchronizationContext(string inId) {
mId = inId;
}
public override void Send(SendOrPostCallback codeToRun, object state) {
throw new NotImplementedException();
}
public override void Post(SendOrPostCallback codeToRun, object state) {
lock (syncHandle) {
if (isRunning) {
messagesToProcess.Enqueue(() => {
codeToRun(state);
});
SignalContinue();
}
}
}
void ILocalSynchronizationContextRef.Start() {
lock (syncHandle) {
if (!isRunning) {
mParentContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(this);
isRunning = true;
SignalContinue();
}
}
}
void ILocalSynchronizationContextRef.Stop() {
lock (syncHandle) {
if (isRunning) {
isRunning = false;
SignalContinue();
SynchronizationContext.SetSynchronizationContext(mParentContext);
}
}
}
public void RunMessagePump() {
while (CanContinue()) {
RunOneRound();
}
}
public void RunOneRound() {
Action nextToRun = GrabItem();
nextToRun();
}
public void Dispose() {
lock (syncHandle) {
if (isRunning) {
isRunning = false;
SignalContinue();
}
}
}
private Action GrabItem() {
lock (syncHandle) {
while (CanContinue() && messagesToProcess.Count == 0) {
this.Wait();
}
return messagesToProcess.Dequeue();
}
}
private bool CanContinue() {
lock (syncHandle) {
return isRunning;
}
}
private void SignalContinue() {
Monitor.Pulse(syncHandle);
}
private void Wait() {
Monitor.Wait(syncHandle, 1);
}
SynchronizationContext ILocalSynchronizationContextRef.Reference {
get {
return this;
}
}
TResult ILocalSynchronizationContextRef<TResult>.Result { get; set; }
}
public class LocalSyncRunner : AbstractLocalSyncRunner {
public static void RunAsync(Func<SynchronizationContext, Task> inTestAction) {
using (var context = new LocalSynchronizationContext<int>(Guid.NewGuid().ToString())) {
new LocalSyncRunner().RunAsyncCore(context, inTestAction);
}
}
public static TResult RunAsync<TResult>(Func<SynchronizationContext, Task<TResult>> inTestAction) {
using (var context = new LocalSynchronizationContext<TResult>(Guid.NewGuid().ToString())) {
return new LocalSyncRunner().RunAsyncCore(context, inTestAction);
}
}
private LocalSyncRunner() { }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment