Scheduler.Recursive.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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.Reactive.Disposables;
  5. namespace System.Reactive.Concurrency
  6. {
  7. public static partial class Scheduler
  8. {
  9. /// <summary>
  10. /// Schedules an action to be executed recursively.
  11. /// </summary>
  12. /// <param name="scheduler">Scheduler to execute the recursive action on.</param>
  13. /// <param name="action">Action to execute recursively. The parameter passed to the action is used to trigger recursive scheduling of the action.</param>
  14. /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
  15. /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is null.</exception>
  16. public static IDisposable Schedule(this IScheduler scheduler, Action<Action> action)
  17. {
  18. if (scheduler == null)
  19. throw new ArgumentNullException(nameof(scheduler));
  20. if (action == null)
  21. throw new ArgumentNullException(nameof(action));
  22. return scheduler.Schedule(action, (_action, self) => _action(() => self(_action)));
  23. }
  24. /// <summary>
  25. /// Schedules an action to be executed recursively.
  26. /// </summary>
  27. /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
  28. /// <param name="scheduler">Scheduler to execute the recursive action on.</param>
  29. /// <param name="state">State passed to the action to be executed.</param>
  30. /// <param name="action">Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state.</param>
  31. /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
  32. /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is null.</exception>
  33. public static IDisposable Schedule<TState>(this IScheduler scheduler, TState state, Action<TState, Action<TState>> action)
  34. {
  35. if (scheduler == null)
  36. throw new ArgumentNullException(nameof(scheduler));
  37. if (action == null)
  38. throw new ArgumentNullException(nameof(action));
  39. return scheduler.Schedule(new Pair<TState, Action<TState, Action<TState>>> { First = state, Second = action }, InvokeRec1);
  40. }
  41. static IDisposable InvokeRec1<TState>(IScheduler scheduler, Pair<TState, Action<TState, Action<TState>>> pair)
  42. {
  43. var group = new CompositeDisposable(1);
  44. var gate = new object();
  45. var state = pair.First;
  46. var action = pair.Second;
  47. Action<TState> recursiveAction = null;
  48. recursiveAction = state1 => action(state1, state2 =>
  49. {
  50. var isAdded = false;
  51. var isDone = false;
  52. var d = default(IDisposable);
  53. d = scheduler.Schedule(state2, (scheduler1, state3) =>
  54. {
  55. lock (gate)
  56. {
  57. if (isAdded)
  58. group.Remove(d);
  59. else
  60. isDone = true;
  61. }
  62. recursiveAction(state3);
  63. return Disposable.Empty;
  64. });
  65. lock (gate)
  66. {
  67. if (!isDone)
  68. {
  69. group.Add(d);
  70. isAdded = true;
  71. }
  72. }
  73. });
  74. recursiveAction(state);
  75. return group;
  76. }
  77. /// <summary>
  78. /// Schedules an action to be executed recursively after a specified relative due time.
  79. /// </summary>
  80. /// <param name="scheduler">Scheduler to execute the recursive action on.</param>
  81. /// <param name="action">Action to execute recursively. The parameter passed to the action is used to trigger recursive scheduling of the action at the specified relative time.</param>
  82. /// <param name="dueTime">Relative time after which to execute the action for the first time.</param>
  83. /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
  84. /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is null.</exception>
  85. public static IDisposable Schedule(this IScheduler scheduler, TimeSpan dueTime, Action<Action<TimeSpan>> action)
  86. {
  87. if (scheduler == null)
  88. throw new ArgumentNullException(nameof(scheduler));
  89. if (action == null)
  90. throw new ArgumentNullException(nameof(action));
  91. return scheduler.Schedule(action, dueTime, (_action, self) => _action(dt => self(_action, dt)));
  92. }
  93. /// <summary>
  94. /// Schedules an action to be executed recursively after a specified relative due time.
  95. /// </summary>
  96. /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
  97. /// <param name="scheduler">Scheduler to execute the recursive action on.</param>
  98. /// <param name="state">State passed to the action to be executed.</param>
  99. /// <param name="action">Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in the recursive due time and invocation state.</param>
  100. /// <param name="dueTime">Relative time after which to execute the action for the first time.</param>
  101. /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
  102. /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is null.</exception>
  103. public static IDisposable Schedule<TState>(this IScheduler scheduler, TState state, TimeSpan dueTime, Action<TState, Action<TState, TimeSpan>> action)
  104. {
  105. if (scheduler == null)
  106. throw new ArgumentNullException(nameof(scheduler));
  107. if (action == null)
  108. throw new ArgumentNullException(nameof(action));
  109. return scheduler.Schedule(new Pair<TState, Action<TState, Action<TState, TimeSpan>>> { First = state, Second = action }, dueTime, InvokeRec2);
  110. }
  111. static IDisposable InvokeRec2<TState>(IScheduler scheduler, Pair<TState, Action<TState, Action<TState, TimeSpan>>> pair)
  112. {
  113. var group = new CompositeDisposable(1);
  114. var gate = new object();
  115. var state = pair.First;
  116. var action = pair.Second;
  117. Action<TState> recursiveAction = null;
  118. recursiveAction = state1 => action(state1, (state2, dueTime1) =>
  119. {
  120. var isAdded = false;
  121. var isDone = false;
  122. var d = default(IDisposable);
  123. d = scheduler.Schedule(state2, dueTime1, (scheduler1, state3) =>
  124. {
  125. lock (gate)
  126. {
  127. if (isAdded)
  128. group.Remove(d);
  129. else
  130. isDone = true;
  131. }
  132. recursiveAction(state3);
  133. return Disposable.Empty;
  134. });
  135. lock (gate)
  136. {
  137. if (!isDone)
  138. {
  139. group.Add(d);
  140. isAdded = true;
  141. }
  142. }
  143. });
  144. recursiveAction(state);
  145. return group;
  146. }
  147. /// <summary>
  148. /// Schedules an action to be executed recursively at a specified absolute due time.
  149. /// </summary>
  150. /// <param name="scheduler">Scheduler to execute the recursive action on.</param>
  151. /// <param name="action">Action to execute recursively. The parameter passed to the action is used to trigger recursive scheduling of the action at the specified absolute time.</param>
  152. /// <param name="dueTime">Absolute time at which to execute the action for the first time.</param>
  153. /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
  154. /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is null.</exception>
  155. public static IDisposable Schedule(this IScheduler scheduler, DateTimeOffset dueTime, Action<Action<DateTimeOffset>> action)
  156. {
  157. if (scheduler == null)
  158. throw new ArgumentNullException(nameof(scheduler));
  159. if (action == null)
  160. throw new ArgumentNullException(nameof(action));
  161. return scheduler.Schedule(action, dueTime, (_action, self) => _action(dt => self(_action, dt)));
  162. }
  163. /// <summary>
  164. /// Schedules an action to be executed recursively at a specified absolute due time.
  165. /// </summary>
  166. /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
  167. /// <param name="scheduler">Scheduler to execute the recursive action on.</param>
  168. /// <param name="state">State passed to the action to be executed.</param>
  169. /// <param name="action">Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in the recursive due time and invocation state.</param>
  170. /// <param name="dueTime">Absolute time at which to execute the action for the first time.</param>
  171. /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
  172. /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is null.</exception>
  173. public static IDisposable Schedule<TState>(this IScheduler scheduler, TState state, DateTimeOffset dueTime, Action<TState, Action<TState, DateTimeOffset>> action)
  174. {
  175. if (scheduler == null)
  176. throw new ArgumentNullException(nameof(scheduler));
  177. if (action == null)
  178. throw new ArgumentNullException(nameof(action));
  179. return scheduler.Schedule(new Pair<TState, Action<TState, Action<TState, DateTimeOffset>>> { First = state, Second = action }, dueTime, InvokeRec3);
  180. }
  181. static IDisposable InvokeRec3<TState>(IScheduler scheduler, Pair<TState, Action<TState, Action<TState, DateTimeOffset>>> pair)
  182. {
  183. var group = new CompositeDisposable(1);
  184. var gate = new object();
  185. var state = pair.First;
  186. var action = pair.Second;
  187. Action<TState> recursiveAction = null;
  188. recursiveAction = state1 => action(state1, (state2, dueTime1) =>
  189. {
  190. var isAdded = false;
  191. var isDone = false;
  192. var d = default(IDisposable);
  193. d = scheduler.Schedule(state2, dueTime1, (scheduler1, state3) =>
  194. {
  195. lock (gate)
  196. {
  197. if (isAdded)
  198. group.Remove(d);
  199. else
  200. isDone = true;
  201. }
  202. recursiveAction(state3);
  203. return Disposable.Empty;
  204. });
  205. lock (gate)
  206. {
  207. if (!isDone)
  208. {
  209. group.Add(d);
  210. isAdded = true;
  211. }
  212. }
  213. });
  214. recursiveAction(state);
  215. return group;
  216. }
  217. #if !NO_SERIALIZABLE
  218. [Serializable]
  219. #endif
  220. struct Pair<T1, T2>
  221. {
  222. public T1 First;
  223. public T2 Second;
  224. }
  225. }
  226. }