Scheduler.Recursive.cs 12 KB

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