ScheduledItem.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the Apache 2.0 License.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections.Generic;
  5. using System.Reactive.Disposables;
  6. namespace System.Reactive.Concurrency
  7. {
  8. /// <summary>
  9. /// Abstract base class for scheduled work items.
  10. /// </summary>
  11. /// <typeparam name="TAbsolute">Absolute time representation type.</typeparam>
  12. public abstract class ScheduledItem<TAbsolute> : IScheduledItem<TAbsolute>, IComparable<ScheduledItem<TAbsolute>>
  13. where TAbsolute : IComparable<TAbsolute>
  14. {
  15. private readonly SingleAssignmentDisposable _disposable = new SingleAssignmentDisposable();
  16. private readonly IComparer<TAbsolute> _comparer;
  17. /// <summary>
  18. /// Creates a new scheduled work item to run at the specified time.
  19. /// </summary>
  20. /// <param name="dueTime">Absolute time at which the work item has to be executed.</param>
  21. /// <param name="comparer">Comparer used to compare work items based on their scheduled time.</param>
  22. /// <exception cref="ArgumentNullException"><paramref name="comparer"/> is <c>null</c>.</exception>
  23. protected ScheduledItem(TAbsolute dueTime, IComparer<TAbsolute> comparer)
  24. {
  25. if (comparer == null)
  26. throw new ArgumentNullException(nameof(comparer));
  27. DueTime = dueTime;
  28. _comparer = comparer;
  29. }
  30. /// <summary>
  31. /// Gets the absolute time at which the item is due for invocation.
  32. /// </summary>
  33. public TAbsolute DueTime { get; }
  34. /// <summary>
  35. /// Invokes the work item.
  36. /// </summary>
  37. public void Invoke()
  38. {
  39. if (!_disposable.IsDisposed)
  40. {
  41. _disposable.Disposable = InvokeCore();
  42. }
  43. }
  44. /// <summary>
  45. /// Implement this method to perform the work item invocation, returning a disposable object for deep cancellation.
  46. /// </summary>
  47. /// <returns>Disposable object used to cancel the work item and/or derived work items.</returns>
  48. protected abstract IDisposable InvokeCore();
  49. #region Inequality
  50. /// <summary>
  51. /// Compares the work item with another work item based on absolute time values.
  52. /// </summary>
  53. /// <param name="other">Work item to compare the current work item to.</param>
  54. /// <returns>Relative ordering between this and the specified work item.</returns>
  55. /// <remarks>The inequality operators are overloaded to provide results consistent with the <see cref="IComparable"/> implementation. Equality operators implement traditional reference equality semantics.</remarks>
  56. public int CompareTo(ScheduledItem<TAbsolute> other)
  57. {
  58. // MSDN: By definition, any object compares greater than null, and two null references compare equal to each other.
  59. if (ReferenceEquals(other, null))
  60. {
  61. return 1;
  62. }
  63. return _comparer.Compare(DueTime, other.DueTime);
  64. }
  65. /// <summary>
  66. /// Determines whether one specified <see cref="ScheduledItem{TAbsolute}" /> object is due before a second specified <see cref="ScheduledItem{TAbsolute}" /> object.
  67. /// </summary>
  68. /// <param name="left">The first object to compare.</param>
  69. /// <param name="right">The second object to compare.</param>
  70. /// <returns><c>true</c> if the <see cref="DueTime"/> value of left is earlier than the <see cref="DueTime"/> value of right; otherwise, <c>false</c>.</returns>
  71. /// <remarks>This operator provides results consistent with the <see cref="IComparable"/> implementation.</remarks>
  72. public static bool operator <(ScheduledItem<TAbsolute> left, ScheduledItem<TAbsolute> right) => Comparer<ScheduledItem<TAbsolute>>.Default.Compare(left, right) < 0;
  73. /// <summary>
  74. /// Determines whether one specified <see cref="ScheduledItem{TAbsolute}" /> object is due before or at the same of a second specified <see cref="ScheduledItem{TAbsolute}" /> object.
  75. /// </summary>
  76. /// <param name="left">The first object to compare.</param>
  77. /// <param name="right">The second object to compare.</param>
  78. /// <returns><c>true</c> if the <see cref="DueTime"/> value of left is earlier than or simultaneous with the <see cref="DueTime"/> value of right; otherwise, <c>false</c>.</returns>
  79. /// <remarks>This operator provides results consistent with the <see cref="IComparable"/> implementation.</remarks>
  80. public static bool operator <=(ScheduledItem<TAbsolute> left, ScheduledItem<TAbsolute> right) => Comparer<ScheduledItem<TAbsolute>>.Default.Compare(left, right) <= 0;
  81. /// <summary>
  82. /// Determines whether one specified <see cref="ScheduledItem{TAbsolute}" /> object is due after a second specified <see cref="ScheduledItem{TAbsolute}" /> object.
  83. /// </summary>
  84. /// <param name="left">The first object to compare.</param>
  85. /// <param name="right">The second object to compare.</param>
  86. /// <returns><c>true</c> if the <see cref="DueTime"/> value of left is later than the <see cref="DueTime"/> value of right; otherwise, <c>false</c>.</returns>
  87. /// <remarks>This operator provides results consistent with the <see cref="IComparable"/> implementation.</remarks>
  88. public static bool operator >(ScheduledItem<TAbsolute> left, ScheduledItem<TAbsolute> right) => Comparer<ScheduledItem<TAbsolute>>.Default.Compare(left, right) > 0;
  89. /// <summary>
  90. /// Determines whether one specified <see cref="ScheduledItem{TAbsolute}" /> object is due after or at the same time of a second specified <see cref="ScheduledItem{TAbsolute}" /> object.
  91. /// </summary>
  92. /// <param name="left">The first object to compare.</param>
  93. /// <param name="right">The second object to compare.</param>
  94. /// <returns><c>true</c> if the <see cref="DueTime"/> value of left is later than or simultaneous with the <see cref="DueTime"/> value of right; otherwise, <c>false</c>.</returns>
  95. /// <remarks>This operator provides results consistent with the <see cref="IComparable"/> implementation.</remarks>
  96. public static bool operator >=(ScheduledItem<TAbsolute> left, ScheduledItem<TAbsolute> right) => Comparer<ScheduledItem<TAbsolute>>.Default.Compare(left, right) >= 0;
  97. #endregion
  98. #region Equality
  99. /// <summary>
  100. /// Determines whether two specified <see cref="ScheduledItem{TAbsolute, TValue}" /> objects are equal.
  101. /// </summary>
  102. /// <param name="left">The first object to compare.</param>
  103. /// <param name="right">The second object to compare.</param>
  104. /// <returns><c>true</c> if both <see cref="ScheduledItem{TAbsolute, TValue}" /> are equal; otherwise, <c>false</c>.</returns>
  105. /// <remarks>This operator does not provide results consistent with the IComparable implementation. Instead, it implements reference equality.</remarks>
  106. public static bool operator ==(ScheduledItem<TAbsolute> left, ScheduledItem<TAbsolute> right) => ReferenceEquals(left, right);
  107. /// <summary>
  108. /// Determines whether two specified <see cref="ScheduledItem{TAbsolute, TValue}" /> objects are inequal.
  109. /// </summary>
  110. /// <param name="left">The first object to compare.</param>
  111. /// <param name="right">The second object to compare.</param>
  112. /// <returns><c>true</c> if both <see cref="ScheduledItem{TAbsolute, TValue}" /> are inequal; otherwise, <c>false</c>.</returns>
  113. /// <remarks>This operator does not provide results consistent with the IComparable implementation. Instead, it implements reference equality.</remarks>
  114. public static bool operator !=(ScheduledItem<TAbsolute> left, ScheduledItem<TAbsolute> right) => !(left == right);
  115. /// <summary>
  116. /// Determines whether a <see cref="ScheduledItem{TAbsolute}" /> object is equal to the specified object.
  117. /// </summary>
  118. /// <param name="obj">The object to compare to the current <see cref="ScheduledItem{TAbsolute}" /> object.</param>
  119. /// <returns><c>true</c> if the obj parameter is a <see cref="ScheduledItem{TAbsolute}" /> object and is equal to the current <see cref="ScheduledItem{TAbsolute}" /> object; otherwise, <c>false</c>.</returns>
  120. public override bool Equals(object obj) => ReferenceEquals(this, obj);
  121. /// <summary>
  122. /// Returns the hash code for the current <see cref="ScheduledItem{TAbsolute}" /> object.
  123. /// </summary>
  124. /// <returns>A 32-bit signed integer hash code.</returns>
  125. public override int GetHashCode() => base.GetHashCode();
  126. #endregion
  127. /// <summary>
  128. /// Cancels the work item by disposing the resource returned by <see cref="InvokeCore"/> as soon as possible.
  129. /// </summary>
  130. public void Cancel() => _disposable.Dispose();
  131. /// <summary>
  132. /// Gets whether the work item has received a cancellation request.
  133. /// </summary>
  134. public bool IsCanceled => _disposable.IsDisposed;
  135. }
  136. /// <summary>
  137. /// Represents a scheduled work item based on the materialization of an IScheduler.Schedule method call.
  138. /// </summary>
  139. /// <typeparam name="TAbsolute">Absolute time representation type.</typeparam>
  140. /// <typeparam name="TValue">Type of the state passed to the scheduled action.</typeparam>
  141. public sealed class ScheduledItem<TAbsolute, TValue> : ScheduledItem<TAbsolute>
  142. where TAbsolute : IComparable<TAbsolute>
  143. {
  144. private readonly IScheduler _scheduler;
  145. private readonly TValue _state;
  146. private readonly Func<IScheduler, TValue, IDisposable> _action;
  147. /// <summary>
  148. /// Creates a materialized work item.
  149. /// </summary>
  150. /// <param name="scheduler">Recursive scheduler to invoke the scheduled action with.</param>
  151. /// <param name="state">State to pass to the scheduled action.</param>
  152. /// <param name="action">Scheduled action.</param>
  153. /// <param name="dueTime">Time at which to run the scheduled action.</param>
  154. /// <param name="comparer">Comparer used to compare work items based on their scheduled time.</param>
  155. /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> or <paramref name="comparer"/> is <c>null</c>.</exception>
  156. public ScheduledItem(IScheduler scheduler, TValue state, Func<IScheduler, TValue, IDisposable> action, TAbsolute dueTime, IComparer<TAbsolute> comparer)
  157. : base(dueTime, comparer)
  158. {
  159. if (scheduler == null)
  160. throw new ArgumentNullException(nameof(scheduler));
  161. if (action == null)
  162. throw new ArgumentNullException(nameof(action));
  163. _scheduler = scheduler;
  164. _state = state;
  165. _action = action;
  166. }
  167. /// <summary>
  168. /// Creates a materialized work item.
  169. /// </summary>
  170. /// <param name="scheduler">Recursive scheduler to invoke the scheduled action with.</param>
  171. /// <param name="state">State to pass to the scheduled action.</param>
  172. /// <param name="action">Scheduled action.</param>
  173. /// <param name="dueTime">Time at which to run the scheduled action.</param>
  174. /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is <c>null</c>.</exception>
  175. public ScheduledItem(IScheduler scheduler, TValue state, Func<IScheduler, TValue, IDisposable> action, TAbsolute dueTime)
  176. : this(scheduler, state, action, dueTime, Comparer<TAbsolute>.Default)
  177. {
  178. }
  179. /// <summary>
  180. /// Invokes the scheduled action with the supplied recursive scheduler and state.
  181. /// </summary>
  182. /// <returns>Cancellation resource returned by the scheduled action.</returns>
  183. protected override IDisposable InvokeCore() => _action(_scheduler, _state);
  184. }
  185. }