ln.threading/ln.threading/Promise.cs

282 lines
9.7 KiB
C#

using System;
using System.Threading;
namespace ln.threading
{
public class PromiseNotPendingException : Exception {}
public class PromiseRejectedException : Exception
{
public PromiseRejectedException(){}
public PromiseRejectedException(Exception caughtExcpetion) : base("Promise was rejected", caughtExcpetion) {}
}
public enum PromiseState { PENDING, RESOLVED, REJECTED }
public class Promise<T>
{
public event Action<T> Resolved;
public event Action<Exception> Rejected;
public event Action<Promise<T>> Settled;
public bool IsSettled { get; private set; }
public PromiseState State { get; private set; } = PromiseState.PENDING;
T value;
Exception rejectingException;
public Promise() {}
public Promise(Action<Action<T>,Action<Exception>> resolver)
{
Resolve(resolver);
}
void Resolve(Action<Action<T>,Action<Exception>> resolver)
{
DynamicThreadPool.DefaultPool.Enqueue(()=>{
try{
resolver(Resolve, Reject);
} catch (Exception e)
{
Reject(e);
}
});
}
public void Resolve(T value)
{
lock (this)
{
if (State == PromiseState.PENDING)
{
this.value = value;
State = PromiseState.RESOLVED;
IsSettled = true;
Monitor.PulseAll(this);
DynamicThreadPool.DefaultPool.Enqueue(()=> {
Resolved?.Invoke(this.value);
Settled?.Invoke(this);
});
} else
{
throw new PromiseNotPendingException();
}
}
}
public void Reject() => Reject(null);
public void Reject(Exception rejectingException)
{
lock (this)
{
if (State != PromiseState.PENDING)
throw new PromiseNotPendingException();
this.rejectingException = rejectingException;
IsSettled = true;
State = PromiseState.REJECTED;
Monitor.PulseAll(this);
DynamicThreadPool.DefaultPool.Enqueue(()=> {
Rejected?.Invoke(this.rejectingException);
Settled?.Invoke(this);
});
}
}
public T Value {
get {
lock (this)
{
while (State == PromiseState.PENDING)
Monitor.Wait(this);
if (State == PromiseState.REJECTED)
throw new PromiseRejectedException(rejectingException);
if (State == PromiseState.RESOLVED)
return value;
throw new Exception("serious bug in Promise implementation");
}
}
}
public Promise<S> Then<S>(Func<T,S> resolved) => Then(resolved, null);
public Promise<S> Then<S>(Func<T,S> resolved, Func<Exception,S> rejected)
{
lock (this)
{
Promise<S> chainedPromise = new Promise<S>();
Action resolveAction = ()=>{
lock (this)
{
switch (State)
{
case PromiseState.PENDING:
throw new Exception("serious bug in Promise.Then(..)");
case PromiseState.RESOLVED:
try{
chainedPromise.Resolve(resolved(value));
} catch (Exception e)
{
chainedPromise.Reject(e);
}
break;
case PromiseState.REJECTED:
if (rejected == null)
chainedPromise.Reject(rejectingException);
else
{
try{
chainedPromise.Resolve(rejected(rejectingException));
} catch (Exception e)
{
chainedPromise.Reject(e);
}
}
break;
}
}
};
if (IsSettled)
DynamicThreadPool.DefaultPool.Enqueue(resolveAction);
else
Settled += (p) => resolveAction();
return chainedPromise;
}
}
public Promise<T> Then(Action<T> resolved) => Then(resolved, null);
public Promise<T> Then(Action<T> resolved, Func<Exception,T> rejected)
{
lock (this)
{
Promise<T> chainedPromise = new Promise<T>();
Action resolveAction = ()=>{
lock (this)
{
switch (State)
{
case PromiseState.PENDING:
throw new Exception("serious bug in Promise.Then(..)");
case PromiseState.RESOLVED:
try{
resolved(value);
chainedPromise.Resolve(value);
} catch (Exception e)
{
chainedPromise.Reject(e);
}
break;
case PromiseState.REJECTED:
if (rejected == null)
chainedPromise.Reject(rejectingException);
else
{
try{
chainedPromise.Resolve(rejected(rejectingException));
} catch (Exception e)
{
chainedPromise.Reject(e);
}
}
break;
}
}
};
if (IsSettled)
DynamicThreadPool.DefaultPool.Enqueue(resolveAction);
else
Settled += (p) => resolveAction();
return chainedPromise;
}
}
public Promise<T> Catch(Func<Exception,T> rejectedHandler)
{
lock (this)
{
Promise<T> chainedPromise = new Promise<T>();
Action resolveAction = ()=>{
lock (this)
{
switch (State)
{
case PromiseState.PENDING:
throw new Exception("serious bug in Promise.Catch(..)");
case PromiseState.RESOLVED:
chainedPromise.Resolve(value);
break;
case PromiseState.REJECTED:
try
{
chainedPromise.Resolve( rejectedHandler(rejectingException));
} catch (Exception e)
{
chainedPromise.Reject(e);
}
break;
}
}
};
if (IsSettled)
DynamicThreadPool.DefaultPool.Enqueue(resolveAction);
else
Settled += (p) => resolveAction();
return chainedPromise;
}
}
public Promise<T> Finally(Action finallyHandler)
{
Promise<T> chainedPromise = new Promise<T>();
lock (this)
{
Action resolveAction = ()=>{
lock (this)
{
if (State == PromiseState.PENDING)
throw new Exception("serious bug in Promise.Finally(..)");
try{
finallyHandler();
switch (State)
{
case PromiseState.RESOLVED:
chainedPromise.Resolve(value);
break;
case PromiseState.REJECTED:
chainedPromise.Reject(rejectingException);
break;
}
} catch (Exception e)
{
chainedPromise.Reject(e);
}
}
};
if (IsSettled)
DynamicThreadPool.DefaultPool.Enqueue(resolveAction);
else
Settled += (p) => resolveAction();
return chainedPromise;
}
}
}
}