/// <summary> /// Schedules an action to be executed after dueTime. /// </summary> /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam> /// <param name="state">State passed to the action to be executed.</param> /// <param name="action">Action to be executed.</param> /// <param name="dueTime">Relative time after which to execute the action.</param> /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns> /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception> /// <exception cref="ObjectDisposedException">The scheduler has been disposed and doesn't accept new work.</exception> public override IDisposable Schedule <TState>(TState state, TimeSpan dueTime, Func <IScheduler, TState, IDisposable> action) { if (null == action) { throw new ArgumentNullException(nameof(action)); } var due = stopwatch.Elapsed + dueTime; var si = new ScheduledItem <TimeSpan, TState>(this, state, action, due); lock (gate) { if (disposed) { throw new ObjectDisposedException(""); } if (dueTime <= TimeSpan.Zero) { readyList.Enqueue(si); _evt.Release(); } else { queue.Enqueue(si); _evt.Release(); } EnsureThread(); } return(si); }
/// <summary> /// Compares the work item with another work item based on absolute time values. /// </summary> /// <param name="other">Work item to compare the current work item to.</param> /// <returns>Relative ordering between this and the specified work item.</returns> /// <remarks>The inequality operators are overloaded to provide results consistent with the <see cref="IComparable"/> implementation. Equality operators implement traditional reference equality semantics.</remarks> public int CompareTo(ScheduledItem <TAbsolute> other) { // MSDN: By definition, any object compares greater than null, and two null references compare equal to each other. if (other is null) { return(1); } return(comparer.Compare(DueTime, other.DueTime)); }
private void Tick(object state) { lock (gate) { if (!disposed) { var item = (ScheduledItem <TimeSpan>)state; if (item == nextItem) { nextItem = null; } if (queue.Remove(item)) { readyList.Enqueue(item); } _evt.Release(); } } }
/// <summary> /// Removes the specified work item from the scheduler queue. /// </summary> /// <param name="scheduledItem">Work item to be removed from the scheduler queue.</param> /// <returns><c>true</c> if the item was found; <c>false</c> otherwise.</returns> public bool Remove(ScheduledItem <TAbsolute> scheduledItem) => queue.Remove(scheduledItem);
/// <summary> /// Enqueues the specified work item to be scheduled. /// </summary> /// <param name="scheduledItem">Work item to be scheduled.</param> public void Enqueue(ScheduledItem <TAbsolute> scheduledItem) => queue.Enqueue(scheduledItem);
/// <summary> /// Event loop scheduled on the designated event loop thread. The loop is suspended/resumed using the event /// which gets set by calls to Schedule, the next item timer, or calls to Dispose. /// </summary> private void Run() { while (true) { _evt.Wait(); var ready = default(ScheduledItem <TimeSpan>[]); lock (gate) { // // Bug fix that ensures the number of calls to Release never greatly exceeds the number of calls to Wait. // See work item #37: https://rx.codeplex.com/workitem/37 // while (_evt.CurrentCount > 0) { _evt.Wait(); } // // The event could have been set by a call to Dispose. This takes priority over anything else. We quit the // loop immediately. Subsequent calls to Schedule won't ever create a new thread. // if (disposed) { _evt.Dispose(); return; } while (queue.Count > 0 && queue.Peek().DueTime <= stopwatch.Elapsed) { var item = queue.Dequeue(); readyList.Enqueue(item); } if (queue.Count > 0) { var next = queue.Peek(); if (next != nextItem) { nextItem = next; var due = next.DueTime - stopwatch.Elapsed; Disposable.TrySetSerial(ref nextTimer, ConcurrencyAbstraction.Current.StartTimer(Tick, next, due)); } } if (readyList.Count > 0) { ready = readyList.ToArray(); readyList.Clear(); } } if (null != ready) { foreach (var item in ready) { if (!item.IsCanceled) { item.Invoke(); } } } if (ExitIfEmpty) { lock (gate) { if (readyList.Count == 0 && queue.Count == 0) { thread = null; return; } } } } }
/// <summary> /// Schedules an action to be executed after dueTime. /// </summary> /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam> /// <param name="state">State passed to the action to be executed.</param> /// <param name="action">Action to be executed.</param> /// <param name="dueTime">Relative time after which to execute the action.</param> /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns> /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception> public override IDisposable Schedule <TState>(TState state, TimeSpan dueTime, Func <IScheduler, TState, IDisposable> action) { if (null == action) { throw new ArgumentNullException(nameof(action)); } SchedulerQueue <TimeSpan> queue; // There is no timed task and no task is currently running if (false == running) { running = true; if (TimeSpan.Zero < dueTime) { ConcurrencyAbstraction.Current.Sleep(dueTime); } // execute directly without queueing IDisposable disposable; try { disposable = action(this, state); } catch { SetQueue(null); running = false; throw; } // did recursive tasks arrive? queue = GetQueue(); // yes, run those in the queue as well if (null != queue) { try { Trampoline.Run(queue); } finally { SetQueue(null); running = false; } } else { running = false; } return(disposable); } queue = GetQueue(); // if there is a task running or there is a queue if (null == queue) { queue = new SchedulerQueue <TimeSpan>(4); SetQueue(queue); } var dt = Time + Scheduler.Normalize(dueTime); // queue up more work var si = new ScheduledItem <TimeSpan, TState>(this, state, action, dt); queue.Enqueue(si); return(si); }