public void New_TaskEnv_does_not_carry_state() { var state = new State(); TaskEnv.Current.SetState("foo", state); Assert.IsNull(TaskEnv.New().GetState <State>("foo")); }
private void CheckTimer(DateTime when) { if (_expireTimer.Status == TaskTimerStatus.Done || _expireTimer.When > when) { _expireTimer.Change(when, TaskEnv.New()); } }
/// <summary> /// Asynchronous copying of one stream to another. /// </summary> /// <param name="source">Source <see cref="Stream"/>.</param> /// <param name="target">Target <see cref="Stream"/>.</param> /// <param name="length">Number of bytes to copy from source to target.</param> /// <param name="result">The <see cref="Result"/> instance to be returned by the call.</param> /// <returns>Synchronization handle for the number of bytes copied.</returns> public static Result <long> CopyToStream(this Stream source, Stream target, long length, Result <long> result) { if (!SysUtil.UseAsyncIO) { return(AsyncUtil.Fork(() => CopyToStream(source, target, length), result)); } // NOTE (steveb): intermediary copy steps already have a timeout operation, no need to limit the duration of the entire copy operation if ((source == Stream.Null) || (length == 0)) { result.Return(0); } else if (source.IsStreamMemorized() && target.IsStreamMemorized()) { // source & target are memory streams; let's do the copy inline as fast as we can result.Return(CopyToStream(source, target, length)); } else { // use new task environment so we don't copy the task state over and over again TaskEnv.ExecuteNew(() => Coroutine.Invoke(CopyTo_Helper, source, target, length, result)); } return(result); }
public void Copied_TaskEnv_has_current_state() { var state = new State(); TaskEnv.Current.SetState("foo", state); Assert.AreEqual(state, TaskEnv.Clone()["foo"]); }
private Result <IssueData[]> ProcessIssueBatch(ElasticThreadPool pool, string projectId, string filterId, int pageNumber, int issuesInBatch, Tuplet <bool> canceled, Result <IssueData[]> result) { pool.QueueWorkItem(HandlerUtil.WithEnv(delegate { // TODO (steveb): use result.IsCanceled instead of shared tuple once cancellation is supported on the result object // check if request has been canceled if (!canceled.Item1) { IssueData[] issuesForBatch; if (!string.IsNullOrEmpty(filterId)) { issuesForBatch = _service.mc_filter_get_issues(_username, _password, projectId, filterId, pageNumber.ToString(), issuesInBatch.ToString()); } else { issuesForBatch = _service.mc_project_get_issues(_username, _password, projectId, pageNumber.ToString(), issuesInBatch.ToString()); } result.Return(issuesForBatch); } else { // TODO (steveb): throw a more specific exception result.Throw(new Exception("unspecified error")); } }, TaskEnv.Clone())); return(result); }
/// <summary> /// Asychronously Pad a stream with a sequence of bytes /// </summary> /// <param name="stream">Target <see cref="Stream"/></param> /// <param name="count">Number of bytes to pad</param> /// <param name="value">Byte value to use for padding</param> /// <param name="result">The <see cref="Result"/> instance to be returned by the call.</param> /// <returns>Synchronization handle for completion of padding action.</returns> public static Result Pad(this Stream stream, long count, byte value, Result result) { if (count < 0) { throw new ArgumentException("count must be non-negative"); } // NOTE (steveb): intermediary copy steps already have a timeout operation, no need to limit the duration of the entire copy operation if (count == 0) { result.Return(); } else { // initialize buffer so we can write in large chunks if need be byte[] bytes = new byte[(int)Math.Min(4096L, count)]; for (int i = 0; i < bytes.Length; ++i) { bytes[i] = value; } // use new task environment so we don't copy the task state over and over again TaskEnv.ExecuteNew(() => Coroutine.Invoke(Pad_Helper, stream, count, bytes, result)); } return(result); }
public void AttachToCurrentTaskEnv() { theLogger.Debug("Attaching Context to current TaskEnv"); lock (this) { DreamContext dreamCtx = DreamContext.CurrentOrNull; if (dreamCtx != null) { dreamCtx.SetState<Context>(this); } else { var env = TaskEnv.Current; if (env.GetState<Context>() != null) { throw new DreamContextAccessException("tried to attach context to env that already has a dreamcontext"); } if (theOwnerEnv != null && theOwnerEnv == env) { throw new DreamContextAccessException("tried to re-attach dreamcontext to env it is already attached to"); } if (theOwnerEnv != null) { throw new DreamContextAccessException("tried to attach dreamcontext to an env, when it already is attached to another"); } theOwnerEnv = env; env.SetState(this); } } }
/// <summary> /// Invoke an action in the context of a service feature. /// </summary> /// <remarks> /// Assumes that there exists a current <see cref="DreamContext"/> that belongs to a request to another feature of this service. /// </remarks> /// <param name="verb">Http verb.</param> /// <param name="path">Feature path.</param> /// <param name="handler">Action to perform in this context.</param> /// <returns>Exception thrown by handler or null.</returns> public Exception InvokeInServiceContext(string verb, string path, Action handler) { if (handler == null) { throw new ArgumentNullException("handler"); } if (string.IsNullOrEmpty(verb)) { throw new ArgumentNullException("verb"); } if (path == null) { throw new ArgumentNullException("path"); } // create new new environment for execution XUri uri = Self.AtPath(path); DreamContext current = DreamContext.Current; Exception e = TaskEnv.ExecuteNew(delegate { DreamFeatureStage[] stages = new[] { new DreamFeatureStage("InServiceInvokeHandler", InServiceInvokeHandler, DreamAccess.Private) }; // BUGBUGBUG (steveb): when invoking a remote function this way, we're are not running the proloques and epilogues, which leads to errors; // also dream-access attributes are being ignored (i.e. 'public' vs. 'private') DreamMessage message = DreamUtil.AppendHeadersToInternallyForwardedMessage(current.Request, DreamMessage.Ok()); var context = current.CreateContext(verb, uri, new DreamFeature(this, Self, 0, stages, verb, path), message); context.AttachToCurrentTaskEnv(); // pass along host and public-uri information handler(); }, TimerFactory); return(e); }
private void InsertRecord(DirectoryRecord record) { // add value to directory lock (_directory) { _directory[record.Name] = record; } // check if value has an expiration time if (record.HasExpiration) { TimerFactory.New(record.Expiration, OnExpire, record.Name, TaskEnv.New()); } SaveToFileSystem(record.Name, record.Value); // notify event channel XDoc notify = new XDoc("insert").Attr("name", record.Name); if (record.HasExpiration) { notify.Attr("expire", record.Expiration); } notify.Add(record.Value); _events.Post(notify, new Result <DreamMessage>(TimeSpan.MaxValue)); }
private static string CachedWebGet(XUri uri, double?ttl, bool?nilIfMissing) { // fetch message from cache or from the web string result; lock (_webTextCache) { if (_webTextCache.TryGetValue(uri, out result)) { return(result); } } // do the web request Result <DreamMessage> response = new Result <DreamMessage>(); Plug.New(uri).WithTimeout(DEFAULT_WEB_TIMEOUT).InvokeEx("GET", DreamMessage.Ok(), response); DreamMessage message = response.Wait(); try { // check message status if (!message.IsSuccessful) { if (nilIfMissing.GetValueOrDefault()) { return(null); } return(message.Status == DreamStatus.UnableToConnect ? string.Format("(unable to fetch text document from uri [status: {0} ({1}), message: \"{2}\"])", (int)message.Status, message.Status, message.ToDocument()["message"].AsText) : string.Format("(unable to fetch text document from uri [status: {0} ({1})])", (int)message.Status, message.Status)); } // check message size Result resMemorize = message.Memorize(InsertTextLimit, new Result()).Block(); if (resMemorize.HasException) { return(nilIfMissing.GetValueOrDefault() ? null : "(text document is too large)"); } // detect encoding and decode response var stream = message.AsStream(); var encoding = stream.DetectEncoding() ?? message.ContentType.CharSet; result = encoding.GetString(stream.ReadBytes(-1)); } finally { message.Close(); } // start timer to clean-up cached result lock (_webTextCache) { _webTextCache[uri] = result; } double timeout = Math.Min(60 * 60 * 24, Math.Max(ttl ?? MinCacheTtl, 60)); TaskEnv.ExecuteNew(() => TaskTimer.New(TimeSpan.FromSeconds(timeout), timer => { lock (_webTextCache) { _webTextCache.Remove((XUri)timer.State); } }, uri, TaskEnv.None)); return(result); }
public void TaskEnv_invoke_with_custom_dispatchqueue_sets_task_state() { var dispatchQueue = new TestDispatchQueue(); _log.Debug("setting up envs"); var currentState = new State(); State newState = null; TaskEnv.Current.SetState(currentState); var copiedEnv = TaskEnv.Clone(dispatchQueue); var newEnv = TaskEnv.New(dispatchQueue); var resetEvent = new AutoResetEvent(false); bool?hasState = null; // Note: have to over acquire otherwise env is wiped after invokenow copiedEnv.Acquire(); copiedEnv.Acquire(); copiedEnv.Invoke(() => { _log.Debug("copied env invoke"); hasState = currentState == TaskEnv.Current.GetState <State>(); resetEvent.Set(); }); resetEvent.WaitOne(); dispatchQueue.LastItem.Wait(); Assert.IsTrue(hasState.HasValue); Assert.IsTrue(hasState.Value); hasState = null; // Note: have to over acquire otherwise env is wiped after invokenow newEnv.Acquire(); newEnv.Acquire(); newEnv.Acquire(); newEnv.Invoke(() => { _log.Debug("new env invoke"); hasState = currentState == TaskEnv.Current.GetState <State>(); newState = new State(); TaskEnv.Current.SetState(newState); resetEvent.Set(); }); resetEvent.WaitOne(); dispatchQueue.LastItem.Wait(); Assert.IsTrue(hasState.HasValue); Assert.IsFalse(hasState.Value); Assert.IsNotNull(newState); Assert.AreEqual(currentState, TaskEnv.Current.GetState <State>()); Assert.AreNotEqual(currentState, newState); hasState = null; newEnv.Invoke(() => { _log.Debug("new env invoke 2"); hasState = newState == TaskEnv.Current.GetState <State>(); newEnv = TaskEnv.Current; resetEvent.Set(); }); resetEvent.WaitOne(); dispatchQueue.LastItem.Wait(); Assert.IsTrue(hasState.HasValue); Assert.IsTrue(hasState.Value); }
private static void FibonacciThreadPool(IDispatchQueue stp, int n, TimeSpan delay, out int value, out TimeSpan elapsed) { _log.Debug("FibonacciThreadPool"); var sw = Stopwatch.StartNew(); value = Fibonacci(stp, n, delay, new Result <int>(TimeSpan.MaxValue, TaskEnv.New(stp))).Wait(); sw.Stop(); elapsed = sw.Elapsed; }
//--- Methods --- /// <summary> /// Change when the timer will execute. /// </summary> /// <param name="timespan">The relative time.</param> /// <param name="env">The environment to use for invocation.</param> public void Change(TimeSpan timespan, TaskEnv env) { if (timespan != TimeSpan.MaxValue) { Change(DateTime.UtcNow.Add(timespan), env); } else { Change(DateTime.MaxValue, env); } }
/// <summary> /// Detach the context from the its task environment. /// </summary> /// <remarks> /// Must be done in the context's task environment. /// </remarks> public void DetachFromTaskEnv() { lock (this) { if (TaskEnv.CurrentOrNull != _ownerEnv) { _log.Warn("detaching context in env other than owning end"); } _ownerEnv.RemoveState(this); _ownerEnv = null; } }
public void TaskEnv_invoke_sets_task_state() { _log.Debug("setting up envs"); var currentState = new State(); State newState = null; TaskEnv.Current.SetState(currentState); var currentEnv = TaskEnv.Current; var newEnv = TaskEnv.New(); var resetEvent = new AutoResetEvent(false); bool?hasState = null; // Note: have to over acquire otherwise env is wiped after invokenow currentEnv.Acquire(); currentEnv.Acquire(); currentEnv.Invoke(() => { _log.Debug("current env invoke"); hasState = currentState == TaskEnv.Current.GetState <State>(); resetEvent.Set(); }); resetEvent.WaitOne(); Assert.IsTrue(hasState.HasValue); Assert.IsTrue(hasState.Value); hasState = null; // Note: have to over acquire otherwise env is wiped after invokenow newEnv.Acquire(); newEnv.Acquire(); newEnv.Acquire(); newEnv.Invoke(() => { _log.Debug("new env invoke"); hasState = currentState == TaskEnv.Current.GetState <State>(); newState = new State(); TaskEnv.Current.SetState(newState); resetEvent.Set(); }); resetEvent.WaitOne(); Assert.IsTrue(hasState.HasValue); Assert.IsFalse(hasState.Value); Assert.IsNotNull(newState); Assert.AreEqual(currentState, TaskEnv.Current.GetState <State>()); Assert.AreNotEqual(currentState, newState); hasState = null; newEnv.Invoke(() => { _log.Debug("new env invoke 2"); hasState = newState == TaskEnv.Current.GetState <State>(); newEnv = TaskEnv.Current; resetEvent.Set(); }); resetEvent.WaitOne(); Assert.IsTrue(hasState.HasValue); Assert.IsTrue(hasState.Value); }
public void ITaskCloneable_is_cloned_on_task_copy() { var state = new TaskLifeSpanState("baz"); TaskEnv.Current.SetState("foo", state); bool?hasState = null; bool stateExists = false; var env = TaskEnv.Clone(); env.Acquire(); env.InvokeNow(() => { stateExists = TaskEnv.Current.ContainsKey("foo"); hasState = state == TaskEnv.Current.GetState <TaskLifeSpanState>("foo"); }); Assert.IsTrue(hasState.HasValue); Assert.IsFalse(hasState.Value); }
private void OnExpire(TaskTimer timer) { var now = DateTime.UtcNow; List <Entry> expirations = null; lock (_expirationLookup) { // Note (arnec): Sort is cheap here since we're generally dealing with an already sorted or mostly sorted set at this point _orderedExpirations.Sort((a, b) => a.When.CompareTo(b.When)); while (_orderedExpirations.Count > 0) { var entry = _orderedExpirations[0]; if (entry.Removed) { // this item was already removed from the set, and no longer requires expiration, but does need removal from ordered set _orderedExpirations.RemoveAt(0); continue; } if (entry.When > now) { _expireTimer.Change(entry.When, TaskEnv.New()); break; } entry.Removed = true; _expirationLookup.Remove(entry.Key); _orderedExpirations.RemoveAt(0); _log.DebugFormat("expired item with key '{0}'", entry.Key); if (EntriesExpired == null) { continue; } if (expirations == null) { expirations = new List <Entry>(); } expirations.Add(entry); } } if (expirations == null) { return; } OnEntriesExpired(expirations); OnCollectionChange(); }
public void TaskEnv_invokenow_sets_task_state() { var currentState = new State(); State newState = null; TaskEnv.Current.SetState(currentState); var currentEnv = TaskEnv.Current; var newEnv = TaskEnv.New(); bool?hasState = null; // Note: have to over acquire otherwise env is wiped after invokenow currentEnv.Acquire(); currentEnv.Acquire(); currentEnv.InvokeNow(() => { hasState = currentState == TaskEnv.Current.GetState <State>(); }); Assert.IsTrue(hasState.HasValue); Assert.IsTrue(hasState.Value); hasState = null; // Note: have to over acquire otherwise env is wiped after invokenow newEnv.Acquire(); newEnv.Acquire(); newEnv.Acquire(); newEnv.InvokeNow(() => { hasState = currentState == TaskEnv.Current.GetState <State>(); newState = new State(); TaskEnv.Current.SetState(newState); }); Assert.IsTrue(hasState.HasValue); Assert.IsFalse(hasState.Value); Assert.IsNotNull(newState); Assert.AreEqual(currentState, TaskEnv.Current.GetState <State>()); Assert.AreNotEqual(currentState, newState); hasState = null; newEnv.InvokeNow(() => { hasState = newState == TaskEnv.Current.GetState <State>(); newEnv = TaskEnv.Current; }); Assert.IsTrue(hasState.HasValue); Assert.IsTrue(hasState.Value); }
/// <summary> /// Asynchrounously copy a <see cref="Stream"/> to several targets /// </summary> /// <param name="source">Source <see cref="Stream"/></param> /// <param name="targets">Array of target <see cref="Stream"/> objects</param> /// <param name="length">Number of bytes to copy from source to targets</param> /// <param name="result">The <see cref="Result"/> instance to be returned by the call.</param> /// <returns>Synchronization handle for the number of bytes copied to each target.</returns> public static Result <long?[]> CopyTo(this Stream source, Stream[] targets, long length, Result <long?[]> result) { // NOTE (steveb): intermediary copy steps already have a timeout operation, no need to limit the duration of the entire copy operation if ((source == Stream.Null) || (length == 0)) { long?[] totals = new long?[targets.Length]; for (int i = 0; i < totals.Length; ++i) { totals[i] = 0; } result.Return(totals); } else { // use new task environment so we don't copy the task state over and over again TaskEnv.ExecuteNew(() => Coroutine.Invoke(CopyTo_Helper, source, targets, length, result)); } return(result); }
//--- Methods --- /// <summary> /// Attach the context to the current context. /// </summary> /// <remarks> /// Throws <see cref="DreamContextAccessException"/> if the context is already attached to /// a task environemnt of the task environment already has a context. /// </remarks> /// <exception cref="DreamContextAccessException">Context is either attached to a <see cref="TaskEnv"/> or the current <see cref="TaskEnv"/> /// already has a context attached.</exception> public void AttachToCurrentTaskEnv() { lock (this) { var env = TaskEnv.Current; if (env.GetState <DreamContext>() != null) { throw new DreamContextAccessException("tried to attach dreamcontext to env that already has a dreamcontext"); } if (_ownerEnv != null && _ownerEnv == env) { throw new DreamContextAccessException("tried to re-attach dreamcontext to env it is already attached to"); } if (_ownerEnv != null) { throw new DreamContextAccessException("tried to attach dreamcontext to an env, when it already is attached to another"); } _ownerEnv = env; env.SetState(this); } }
private static Result <int> Fibonacci(IDispatchQueue stp, int n, TimeSpan delay, Result <int> result) { if (!ReferenceEquals(result.Env.DispatchQueue, stp)) { _log.Error(string.Format("ERROR: wrong task env {0}, expected {1}.", result.Env.DispatchQueue, stp)); } stp.QueueWorkItem(delegate { Interlocked.Increment(ref _counter); switch (n) { case 0: if (delay > TimeSpan.Zero) { Thread.Sleep(delay); } result.Return(0); break; case 1: if (delay > TimeSpan.Zero) { Thread.Sleep(delay); } result.Return(1); break; default: Result <int> a = Fibonacci(stp, n - 1, delay, new Result <int>(TimeSpan.MaxValue, TaskEnv.New(stp))); Result <int> b = Fibonacci(stp, n - 2, delay, new Result <int>(TimeSpan.MaxValue, TaskEnv.New(stp))); new AResult[] { a, b }.Join(new Result(TimeSpan.MaxValue, TaskEnv.New(stp))).WhenDone(_ => { if (!ReferenceEquals(AsyncUtil.CurrentDispatchQueue, stp)) { _log.Error(string.Format("ERROR: wrong queue {0}, expected {1}.", AsyncUtil.CurrentDispatchQueue, stp)); } result.Return(a.Value + b.Value); }); break; } }); return(result); }
private void OnExpire(TaskTimer timer) { string name = (string)timer.State; lock (_directory) { // check if the record still exists DirectoryRecord record; if (_directory.TryGetValue(name, out record)) { // verify if the record should still be deleted if (record.Expiration <= timer.When) { _directory.Remove(record.Name); } else { timer.Change(record.Expiration, TaskEnv.Clone()); } } } }
public void ElasticThreadPool_Multi_Staged_Fibonacci_Min_1_Max_30() { const int test = 4; // initialize data structures ElasticThreadPool[] stp = new ElasticThreadPool[test]; Result <int>[] results = new Result <int> [test]; for (int i = 0; i < test; ++i) { stp[i] = new ElasticThreadPool(1, 30); results[i] = new Result <int>(TimeSpan.MaxValue, TaskEnv.New(stp[i])); } // start test var sw = Stopwatch.StartNew(); for (int i = 0; i < results.Length; ++i) { _log.DebugFormat("--- FIBONACCI KICK-OFF: {0}", i); Fibonacci(stp[i], 30, TimeSpan.Zero, results[i]); Thread.Sleep(TimeSpan.FromSeconds(1)); } results.Join(new Result(TimeSpan.MaxValue)).Wait(); sw.Stop(); TimeSpan elapsed = sw.Elapsed; // check results for (int i = 0; i < test; ++i) { Assert.AreEqual(832040, results[i].Value, "result {0} did not match", i); } _log.Debug("Time: " + elapsed); _log.Debug("Work items processed: " + _counter); for (int i = 0; i < test; ++i) { stp[i].Dispose(); Assert.AreEqual(0, stp[i].WorkItemCount, "WorkQueue[{0}] items", i); Assert.AreEqual(0, stp[i].ThreadCount, "WorkQueue[{0}] threads", i); } }
private Result <long> CopyStream(Action <string> activity, Stream source, Stream target, long length, Result <long> result) { // NOTE (steveb): intermediary copy steps already have a timeout operation, no need to limit the duration of the entire copy operation if ((source == Stream.Null) || (length == 0)) { activity("return CopyStream 1"); result.Return(0); } else if (!SysUtil.UseAsyncIO || (source.IsStreamMemorized() && target.IsStreamMemorized())) { // source & target are memory streams; let's do the copy inline as fast as we can byte[] buffer = new byte[StreamUtil.BUFFER_SIZE]; long total = 0; while (length != 0) { long count = source.Read(buffer, 0, buffer.Length); if (count == 0) { break; } target.Write(buffer, 0, (int)count); total += count; length -= count; } activity("return CopyStream 2"); result.Return(total); } else { // use new task environment so we don't copy the task state over and over again TaskEnv.ExecuteNew(delegate() { activity("pre CopyStream_Handler"); Coroutine.Invoke(CopyStream_Handler, activity, source, target, length, result); activity("post CopyStream_Handler"); }); } return(result); }
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); } }
/// <summary> /// Change when the timer will execute. /// </summary> /// <param name="when">The absolute time.</param> /// <param name="env">The environment to use for invocation.</param> public void Change(DateTime when, TaskEnv env) { DateTime now = GlobalClock.UtcNow; // determine new status int next; if(when <= now.AddSeconds(QUEUE_CUTOFF)) { next = (int)TaskTimerStatus.Queued; } else if(when < DateTime.MaxValue) { next = (int)TaskTimerStatus.Pending; } else { next = (int)TaskTimerStatus.Done; } // ensure we have a behavior if we need one and we don't if we do not if(next != (int)TaskTimerStatus.Done) { if(env == null) { throw new ArgumentNullException("env"); } } else { env = null; } // attempt to change current status retry: int current; switch(_status) { case (int)TaskTimerStatus.Done: // nothing to do break; case (int)TaskTimerStatus.Pending: // attempt to remove timer from pending list current = Interlocked.CompareExchange(ref _status, (int)TaskTimerStatus.Done, (int)TaskTimerStatus.Pending); switch(current) { case (int)TaskTimerStatus.Done: // nothing to do break; case (int)TaskTimerStatus.Pending: // remove timer from pending list _owner.RemoveFromPending(this); break; case (int)TaskTimerStatus.Queued: // we changed states; retry Interlocked.Increment(ref _retries); goto retry; case (int)TaskTimerStatus.Locked: // somebody else is already changing the timer; no need to compete return; } break; case (int)TaskTimerStatus.Queued: // attempto remove timer from queue current = Interlocked.CompareExchange(ref _status, (int)TaskTimerStatus.Done, (int)TaskTimerStatus.Queued); switch(current) { case (int)TaskTimerStatus.Done: // nothing to do break; case (int)TaskTimerStatus.Pending: // we changed states; retry Interlocked.Increment(ref _retries); goto retry; case (int)TaskTimerStatus.Queued: // remove timer from queue _owner.RemoveFromQueue(this); break; case (int)TaskTimerStatus.Locked: // somebody else is already changing the timer; no need to compete return; } break; case (int)TaskTimerStatus.Locked: // somebody else is already changing the timer; no need to compete return; } // register timer according to new status if(Interlocked.CompareExchange(ref _status, (int)TaskTimerStatus.Locked, (int)TaskTimerStatus.Done) == (int)TaskTimerStatus.Done) { _when = when; switch(next) { case (int)TaskTimerStatus.Done: // release Task Environment if(Env != null) { Env.Release(); } Env = null; Interlocked.Exchange(ref _status, next); return; case (int)TaskTimerStatus.Pending: // add timer to pending list _owner.AddToPending(this, env, (TaskTimerStatus)next); break; case (int)TaskTimerStatus.Queued: // add timer to active queue _owner.AddToQueue(this, env, (TaskTimerStatus)next); break; case (int)TaskTimerStatus.Locked: Interlocked.Exchange(ref _status, (int)TaskTimerStatus.Done); throw new InvalidOperationException("should never happen"); } } }
//--- Methods --- /// <summary> /// Change when the timer will execute. /// </summary> /// <param name="timespan">The relative time.</param> /// <param name="env">The environment to use for invocation.</param> public void Change(TimeSpan timespan, TaskEnv env) { if(timespan != TimeSpan.MaxValue) { Change(GlobalClock.UtcNow.Add(timespan), env); } else { Change(DateTime.MaxValue, env); } }
/// <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; }
/// <summary> /// Enqueue a callback as a work item to be invoked with a clone of the current <see cref="TaskEnv"/>. /// </summary> /// <param name="dispatchQueue">DispatchQueue to enqueue work into.</param> /// <param name="callback">Work item callback.</param> /// <param name="result">Synchronization handle for work item.</param> /// <returns>The synchronization handle provided to the method.</returns> public static Result QueueWorkItemWithClonedEnv(this IDispatchQueue dispatchQueue, Action callback, Result result) { return(dispatchQueue.QueueWorkItemWithEnv(callback, TaskEnv.Clone(), result)); }
//--- Methods --- protected override Yield Start(XDoc config, Result result) { yield return(Coroutine.Invoke(base.Start, config, new Result())); // set up defaults _insertTextLimit = config["max-size"].AsLong ?? DEFAULT_TEXT_LIMIT; _memoryCacheTime = config["memory-cache-time"].AsDouble ?? DEFAULT_MEMORY_CACHE_TIME; _log.DebugFormat("max-size: {0}, memory-cache-time: {1}", _insertTextLimit, _memoryCacheTime); // load current cache state Async.Fork(() => Coroutine.Invoke(RefreshCache, new Result(TimeSpan.MaxValue)), TaskEnv.Clone(), null); result.Return(); }
/// <summary> /// Enqueue a callback as a work item to be invoked with a clone of the current <see cref="TaskEnv"/>. /// </summary> /// <param name="dispatchQueue">DispatchQueue to enqueue work into.</param> /// <param name="callback">Work item callback.</param> public static void QueueWorkItemWithClonedEnv(this IDispatchQueue dispatchQueue, Action callback) { dispatchQueue.QueueWorkItemWithEnv(callback, TaskEnv.Clone(), null); }
/// <summary> /// Enqueue a callback as a work item to be invoked with a provided <see cref="TaskEnv"/>. /// </summary> /// <param name="dispatchQueue">DispatchQueue to enqueue work into.</param> /// <param name="callback">Work item callback.</param> /// <param name="env">Environment for work item invocation.</param> public static void QueueWorkItemWithEnv(this IDispatchQueue dispatchQueue, Action callback, TaskEnv env) { dispatchQueue.QueueWorkItemWithEnv(callback, env, null); }
/// <summary> /// Enqueue a callback as a work item to be invoked with a provided <see cref="TaskEnv"/>. /// </summary> /// <typeparam name="T">Result value type of callback.</typeparam> /// <param name="dispatchQueue">DispatchQueue to enqueue work into.</param> /// <param name="callback">Work item callback.</param> /// <param name="env">Environment for work item invocation.</param> /// <param name="result">Synchronization handle for work item.</param> /// <returns>The synchronization handle provided to the method.</returns> public static Result <T> QueueWorkItemWithEnv <T>(this IDispatchQueue dispatchQueue, Func <T> callback, TaskEnv env, Result <T> result) { if (env == null) { throw new ArgumentException("env"); } dispatchQueue.QueueWorkItem(env.MakeAction(callback, result)); return(result); }
/// <summary> /// Enqueue a callback as a work item to be invoked with a clone of the current <see cref="TaskEnv"/>. /// </summary> /// <typeparam name="T">Result value type of callback.</typeparam> /// <param name="dispatchQueue">DispatchQueue to enqueue work into.</param> /// <param name="callback">Work item callback.</param> /// <param name="result">Synchronization handle for work item.</param> /// <returns>The synchronization handle provided to the method.</returns> public static Result <T> QueueWorkItemWithClonedEnv <T>(this IDispatchQueue dispatchQueue, Func <T> callback, Result <T> result) { return(dispatchQueue.QueueWorkItemWithEnv(callback, TaskEnv.Clone(), result)); }
public static TaskTimer New(DateTime when, Action<TaskTimer> handler, object state, TaskEnv env) { return TaskTimerFactory.Current.New(when, handler, state, env); }
internal void ExecuteNow(TaskEnv env) { env.InvokeNow(() => _handler(this)); }
//--- Class Methods --- /// <summary> /// Set an interval at which Garbage collection should be forced. /// </summary> /// <param name="t">Interval length.</param> public static void SetCollectionInterval(TimeSpan t) { _collectInterval = t; _collectTimer.Change(t, TaskEnv.New()); _log.DebugFormat("set collection interval to {0:0} seconds", t.TotalSeconds); }
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); } }
internal void Execute(TaskEnv env) { env.Invoke(_handler, this); }