Created
June 15, 2012 05:47
-
-
Save ritalin/2934876 to your computer and use it in GitHub Desktop.
awaitable runner host (deleved from /gists/2880770)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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