/// <summary> /// Invoke an action in place. /// </summary> /// <param name="handler">Action to invoke.</param> /// <returns><see langword="null"/> if the handler was executed sucessfully, the captured exception otherwise.</returns> public Exception InvokeNow(Action handler) { if (handler == null) { throw new ArgumentNullException("handler"); } if (_referenceCount < 1) { throw new InvalidOperationException("Cannot call invoke an unaquired TaskEnv"); } // store current task environment TaskEnv previousEnv = _currentEnv; try { // set task environment _currentEnv = this; // execute handler handler(); } catch (Exception e) { return(e); } finally { // restore current task environment _currentEnv = previousEnv; } return(null); }
/// <summary> /// De-schedule the current execution environment to sleep for some period. /// </summary> /// <param name="duration">Time to sleep.</param> /// <returns>Synchronization handle to continue execution after the sleep period.</returns> public static Result Sleep(TimeSpan duration) { Result result = new Result(TimeSpan.MaxValue); TaskTimerFactory.Current.New(duration, _ => result.Return(), null, TaskEnv.New()); return(result); }
public static Action WithEnv <T>(this Func <T> handler, TaskEnv env, Result <T> result) { if (handler == null) { throw new ArgumentNullException("handler"); } System.Diagnostics.StackTrace stacktrace = DebugUtil.GetStackTrace(); env.Acquire(); return(() => { try { T response = default(T); Exception exception = env.InvokeNow(() => { response = handler(); }); env.Release(); // check if a result object was provided if (result != null) { if (exception != null) { result.Throw(exception); } else { result.Return(response); } } } catch (Exception e) { _log.ErrorExceptionMethodCall(e, "Execution failed for state wrapped handler", stacktrace, handler.Method.Name); } }); }
private static Yield ExecuteProcess_Helper(string application, string cmdline, Stream input, Stream output, Stream error, Result <int> result) { // start process var proc = new Process(); proc.StartInfo.FileName = application; proc.StartInfo.Arguments = cmdline; proc.StartInfo.UseShellExecute = false; proc.StartInfo.CreateNoWindow = true; proc.StartInfo.RedirectStandardInput = (input != null); proc.StartInfo.RedirectStandardOutput = true; proc.StartInfo.RedirectStandardError = true; proc.Start(); // inject input if (input != null) { input.CopyTo(proc.StandardInput.BaseStream, long.MaxValue, new Result <long>(TimeSpan.MaxValue)).WhenDone(_ => { // trying closing the original input stream try { input.Close(); } catch { } // try closing the process input pipe try { proc.StandardInput.Close(); } catch { } }); } // extract output stream Result <long> outputDone = proc.StandardOutput.BaseStream.CopyTo(output, long.MaxValue, new Result <long>(TimeSpan.MaxValue)); // extract error stream Result <long> errorDone = proc.StandardError.BaseStream.CopyTo(error, long.MaxValue, new Result <long>(TimeSpan.MaxValue)); TaskTimer timer = TaskTimerFactory.Current.New(result.Timeout, delegate(TaskTimer t) { try { // NOTE (steveb): we had to add the try..catch handler because mono throws an exception when killing a terminated process (why? who knows!) proc.Kill(); } catch { } }, null, TaskEnv.New()); // wait for output and error streams to be done yield return(new AResult[] { outputDone, errorDone }.Join()); int?exitCode = WaitForExit(proc, result.Timeout); timer.Cancel(); proc.Close(); if (exitCode.HasValue) { result.Return(exitCode.Value); } else { result.Throw(new InvalidOperationException("Unable to access process exit code")); } }
/// <summary> /// Invoke a one argument action. /// </summary> /// <typeparam name="T1">Type of first argument.</typeparam> /// <param name="handler">Action to invoke.</param> /// <param name="arg1">First argument.</param> public void Invoke <T1>(Action <T1> handler, T1 arg1) { if (handler == null) { throw new ArgumentNullException("handler"); } if (_referenceCount < 1) { throw new InvalidOperationException("Cannot call invoke an unaquired TaskEnv"); } #pragma warning disable 219 System.Diagnostics.StackTrace stacktrace = DebugUtil.GetStackTrace(); #pragma warning restore 219 // check if handler can be invoked in-place or needs to queued up if (_dispatchQueue != null) { _dispatchQueue.QueueWorkItem(() => { // store current thread-specific settings TaskEnv previousEnv = _currentEnv; try { // set thread-specific settings _currentEnv = this; // execute handler handler(arg1); } catch (Exception e) { _log.WarnExceptionMethodCall(e, "Invoke: unhandled exception in handler"); } finally { Release(); // restore thread-specific settings _currentEnv = previousEnv; } }); } else { // store current thread-specific settings TaskEnv previousEnv = _currentEnv; try { // set thread-specific settings _currentEnv = this; // execute handler handler(arg1); } catch (Exception e) { _log.WarnExceptionMethodCall(e, "Invoke: unhandled exception in handler"); } finally { Release(); // restore thread-specific settings _currentEnv = previousEnv; } } }
private TaskEnv(TaskEnv env, IDispatchQueue dispatchQueue, TaskTimerFactory taskTimerFactory) { if (env != null) { foreach (var entry in env) { var cloneable = entry.Value as ITaskLifespan; Add(entry.Key, cloneable == null ? entry.Value : cloneable.Clone()); } } _taskTimerFactory = taskTimerFactory; _dispatchQueue = (dispatchQueue is ImmediateDispatchQueue) ? null : dispatchQueue; }
/// <summary> /// Shut down all timers related to this factory. /// </summary> /// <remarks> /// Warning: this call is thread-blocking. It will try to execute all pending timers immediately, but /// will wait for each timer to complete. /// </remarks> public void Shutdown() { lock (_factories) { _factories.Remove(this); } List <KeyValuePair <TaskTimer, TaskEnv> > timers = null; // stop the thread timer _shutdown = true; GlobalClock.RemoveCallback(Tick); _owner.Target = null; // schedule all queued items for immediate execution // Note (arnec): should run the below in a helper so we can respect the timeout on this part as well // (or maybe this should just be part of the thread clean-up) lock (_queue) { while (_queue.Count > 0) { TaskTimer timer = _queue.Dequeue(); if (timer.TryLockPending()) { // retrieve the associated behavior and reset the timer TaskEnv env = timer.Env; timer.Env = null; timer.SetStatus(TaskTimerStatus.Done); // add timer timers = timers ?? new List <KeyValuePair <TaskTimer, TaskEnv> >(); timers.Add(new KeyValuePair <TaskTimer, TaskEnv>(timer, env)); } } } // BUGBUGBUG (arnec): we don't actually do anything with timeout, but let every timer take // an indefinite time. // check if any timers were gathered for immediate execution if (timers != null) { foreach (KeyValuePair <TaskTimer, TaskEnv> entry in timers) { entry.Key.Execute(entry.Value); } } _running = false; }
void ITaskTimerOwner.AddToQueue(TaskTimer timer, TaskEnv env, TaskTimerStatus next) { env.Acquire(); if (timer.Env != null) { timer.Env.Release(); } if (_running) { lock (_queue) { timer.Env = env; timer.SetStatus(next); _queue.Enqueue(timer); } } else { env.Release(); timer.Env = null; timer.SetStatus(TaskTimerStatus.Done); } }
void ITaskTimerOwner.AddToPending(TaskTimer timer, TaskEnv env, TaskTimerStatus next) { env.Acquire(); if (timer.Env != null) { timer.Env.Release(); } if (_running) { lock (_pending) { timer.Env = env; timer.SetStatus(next); _pending[timer] = null; } } else { env.Release(); timer.Env = null; timer.SetStatus(TaskTimerStatus.Done); } }
private void Tick(DateTime now, TimeSpan elapsed) { // check if some timers are ready List <KeyValuePair <TaskTimer, TaskEnv> > timers = null; System.Threading.Interlocked.Increment(ref _counter); _last = now; lock (_queue) { // dequeue all timers that are ready to go while ((_queue.Count > 0) && (_queue.Peek().When <= now)) { TaskTimer timer = _queue.Dequeue(); // check if timer can be transitioned if (timer.TryLockQueued()) { // retrieve the associated behavior and reset the timer TaskEnv env = timer.Env; timer.Env = null; timer.SetStatus(TaskTimerStatus.Done); // add timer timers = timers ?? new List <KeyValuePair <TaskTimer, TaskEnv> >(); timers.Add(new KeyValuePair <TaskTimer, TaskEnv>(timer, env)); } } // check if a maintance run is due if (_maintenance <= now) { _maintenance = now.AddSeconds(TaskTimer.QUEUE_RESCAN); DateTime horizon = now.AddSeconds(TaskTimer.QUEUE_CUTOFF); lock (_pending) { List <TaskTimer> activate = new List <TaskTimer>(); foreach (TaskTimer timer in _pending.Keys) { if (timer.When <= horizon) { activate.Add(timer); } } foreach (TaskTimer timer in activate) { _pending.Remove(timer); if (timer.TryQueuePending()) { _queue.Enqueue(timer); } } } } } // run schedule on its own thread to avoid re-entrancy issues if (timers != null) { foreach (KeyValuePair <TaskTimer, TaskEnv> entry in timers) { entry.Key.Execute(entry.Value); } } }
/// <summary> /// Create a new timer and set its fire time. /// </summary> /// <param name="when">Relateive time from now until when the timer should fire.</param> /// <param name="handler">The action to invoke when the timer fires.</param> /// <param name="state">A state object to associate with the timer.</param> /// <param name="env">The environment in which the timer should fire.</param> /// <returns>New timer instance.</returns> public TaskTimer New(TimeSpan when, Action <TaskTimer> handler, object state, TaskEnv env) { var result = new TaskTimer(this, handler, state); result.Change(when, env); return(result); }
public static Action WithEnv <T>(this Func <T> handler, TaskEnv env) { return(handler.WithEnv(env, null)); }
public static Action WithEnv(this Action handler, TaskEnv env) { return(handler.WithEnv(env, null)); }
/// <summary> /// Dispatch an action to be executed with a new, dedicated backgrouns thread. /// </summary> /// <typeparam name="T">Type of result value produced by action.</typeparam> /// <param name="handler">Action to enqueue for execution.</param> /// <param name="result">The <see cref="Result{T}"/>instance to be returned by this method.</param> /// <returns>Synchronization handle for the action's execution.</returns> public static Result <T> ForkThread <T>(Func <T> handler, Result <T> result) { ForkThread(TaskEnv.Clone().MakeAction(handler, result)); return(result); }
/// <summary> /// Dispatch an action to be executed with a new, dedicated backgrouns thread. /// </summary> /// <param name="handler">Action to enqueue for execution.</param> /// <param name="result">The <see cref="Result"/>instance to be returned by this method.</param> /// <returns>Synchronization handle for the action's execution.</returns> public static Result ForkThread(Action handler, Result result) { ForkThread(TaskEnv.Clone().MakeAction(handler, result)); return(result); }
/// <summary> /// Dispatch an action to be executed via the <see cref="GlobalDispatchQueue"/>. /// </summary> /// <param name="handler">Action to enqueue for execution.</param> /// <param name="env">Environment in which to execute the action.</param> /// <param name="result">The <see cref="Result"/>instance to be returned by this method.</param> /// <returns>Synchronization handle for the action's execution.</returns> public static Result Fork(Action handler, TaskEnv env, Result result) { return(GlobalDispatchQueue.QueueWorkItemWithEnv(handler, env, result)); }