Skip to content

Instantly share code, notes, and snippets.

@whitelynx
Created September 19, 2012 18:04
Show Gist options
  • Save whitelynx/3751181 to your computer and use it in GitHub Desktop.
Save whitelynx/3751181 to your computer and use it in GitHub Desktop.
public class Deferred<T>
{
protected Logger logger = null;
protected EventWaitHandle waitForResult = null;
protected HashSet<Deferred<T>> dependentDeferreds = null;
public string name
{
get
{
return _name;
} // end get
set
{
_name = value;
if(_name != null)
{
logger = LogManager.GetLogger(String.Format("Deferred[{0}]", _name));
}
else
{
logger = LogManager.GetLogger("Deferred");
} // end if
} // end set
} // end name
private string _name = null;
public bool isfinished
{
get
{
return waitForResult.WaitOne(0);
} // end get
} // end iserror
protected Boolean iserror_ = false;
public bool iserror
{
get
{
if(waitForResult.WaitOne(0))
{
return iserror_;
}
else
{
return false;
} // end if
} // end get
} // end iserror
public bool issuccess
{
get
{
if(waitForResult.WaitOne(0))
{
return !iserror_;
}
else
{
return false;
} // end if
} // end get
} // end issuccess
public DeferredStatus status
{
get
{
if(waitForResult.WaitOne(0))
{
if(iserror_)
{
return DeferredStatus.Failed;
}
else
{
return DeferredStatus.Succeeded;
} // end if
}
else
{
return DeferredStatus.InProgress;
} // end if
} // end get
} // end status
public T result
{
get
{
if(waitForResult.WaitOne(0))
{
lock(waitForResult)
{
return result_;
} // end lock
}
else
{
return default(T);
} // end if
} // end get
} // end result
protected T result_ = default(T);
public string message
{
get
{
if(waitForResult.WaitOne(0))
{
lock(waitForResult)
{
return message_;
} // end lock
}
else
{
return null;
} // end if
} // end get
} // end message
protected string message_ = "";
protected virtual event EventHandler<DeferredEventArgs<T>> callback_ = delegate { };
protected virtual event EventHandler<DeferredEventArgs<T>> errback_ = delegate { };
public event EventHandler<DeferredEventArgs<T>> callback
{
add
{
callback_ += value;
if(waitForResult.WaitOne(0) && !iserror)
{
value(this, new DeferredEventArgs<T>(this));
} // end if
} // end add
remove
{
callback_ -= value;
} // end remove
} // end callback
public event EventHandler<DeferredEventArgs<T>> errback
{
add
{
errback_ += value;
if(waitForResult.WaitOne(0) && iserror)
{
value(this, new DeferredEventArgs<T>(this));
} // end if
} // end add
remove
{
errback_ -= value;
} // end remove
} // end errback
public event EventHandler<DeferredEventArgs<T>> onFinished
{
add
{
callback += value;
errback += value;
} // end add
remove
{
callback -= value;
errback -= value;
} // end remove
} // end onFinished
public Deferred() : this(null)
{
} // end Deferred
public Deferred(string name, params object[] args) : this(new HashSet<Deferred<T>>(), name, args)
{
} // end Deferred
protected Deferred(HashSet<Deferred<T>> dependentDeferredSet, string name, params object[] args)
{
this.name = name;
if(args.Length > 0)
{
this.name = String.Format(name, args);
} // end if
waitForResult = new ManualResetEvent(false);
dependentDeferreds = dependentDeferredSet;
} // end Deferred
public static Deferred<T> createSucceeded(T val, string message = null)
{
Deferred<T> def = new Deferred<T>();
return def.succeed(val, message);
} // end createSucceeded
public static Deferred<T> createFailed(string message)
{
Deferred<T> def = new Deferred<T>();
return def.fail(message);
} // end createFailed
public void succeedOn(Deferred<T> other)
{
other.callback += chainCallback;
other.errback += chainErrback;
} // end succeedOn
public void allOf(params Deferred<T>[] others)
{
// First, add each deferred to our list of dependencies.
lock(dependentDeferreds)
{
foreach(Deferred<T> other in others)
{
dependentDeferreds.Add(other);
} // end foreach
} // end lock
// Next, add callbacks to each so we get notified when they succeed or fail.
//NOTE: We don't want to do this in the above foreach, because otherwise we might succeed early if any
// Deferred other than the last has already succeeded.
foreach(Deferred<T> other in others)
{
try
{
logger.Debug("allOf: Adding Deferred {0} to list of dependencies.", other);
other.callback += checkDependencies;
other.errback += chainErrback;
}
catch(NullReferenceException ex)
{
logger.ErrorException("Got null Deferred<T> while adding dependencies!", ex);
throw;
} // end catch
} // end foreach
} // end allOf
public void chainCallback(object source, DeferredEventArgs<T> args)
{
succeed(args.deferred.result, args.deferred.message);
} // end chainCallback
public void chainErrback(object source, DeferredEventArgs<T> args)
{
fail(args.deferred.message);
} // end chainErrback
public virtual Deferred<T> succeed(T result, string message = null)
{
lock(waitForResult)
{
if(waitForResult.WaitOne(0))
{
logger.Error("Deferred already finished! Ignoring succeed({0}, {1}) call.", result, message);
return this;
} // end if
logger.Debug("succeed({0}, {1})", result, message);
message_ = message;
result_ = result;
iserror_ = false;
waitForResult.Set();
} // end lock
//TODO: Threading?
callback_(this, new DeferredEventArgs<T>(this));
return this;
} // end succeed
public virtual Deferred<T> fail(string message)
{
lock(waitForResult)
{
if(waitForResult.WaitOne(0))
{
logger.Error("Deferred already finished! Ignoring fail({0}) call.", message);
return this;
} // end if
logger.Debug("fail({0})", message);
message_ = message;
iserror_ = true;
waitForResult.Set();
} // end lock
//TODO: Threading?
errback_(this, new DeferredEventArgs<T>(this));
return this;
} // end fail
public override string ToString()
{
if(name == null)
{
return String.Format("{0}<{1}>", GetType(), GetHashCode());
}
else
{
return String.Format("{0}[{1}]<{2}>", GetType(), name, GetHashCode());
} // end if
} // end ToString
public bool waitForSuccess()
{
if(waitForResult.WaitOne())
{
return !iserror_;
}
else
{
return false;
} // end if
} // end issuccess
public bool waitForSuccess(TimeSpan timeout)
{
if(waitForResult.WaitOne(timeout))
{
return !iserror_;
}
else
{
return false;
} // end if
} // end issuccess
private void checkDependencies(object source, DeferredEventArgs<T> args)
{
lock(dependentDeferreds)
{
foreach(Deferred<T> def in dependentDeferreds)
{
switch(def.status)
{
case DeferredStatus.Failed:
logger.Debug("{0}.checkDependencies: {1} failed.", this, def);
return;
case DeferredStatus.InProgress:
logger.Debug("{0}.checkDependencies: {1} is not yet finished.", this, def);
return;
case DeferredStatus.Succeeded:
default:
break;
} // end switch
} // end foreach
} // end lock
if(args != null)
{
succeed(args.deferred.result, args.deferred.message);
}
else
{
succeed(default(T));
} // end if
} // end checkDependencies
} // end Deferred
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment