/// <summary> /// Blocks the caller until the given <paramref name="actionSetArray"/> completes (based on the chosen <paramref name="completionType"/>) or the <paramref name="spinEarlyExitCodeDelegate"/> returns a non-null string (typically based on a timer expiring, cancel requested, part shutdown requested, ...). /// This evaluation takes place in a spin loop with the spin's wait period given using the <paramref name="spinInterval"/> value. /// If the caller does not provide a spinInterval then 0.1 seconds will be used. The any given <paramref name="spinInterval"/> value will be clipped to be between 0.001 seconds and 0.5 seconds /// </summary> public static string WaitUntilSetComplete <TClientFacetType>(this TClientFacetType[] actionSetArray, Func <string> spinEarlyExitCodeDelegate, TimeSpan?spinInterval = null, WaitForSetCompletionType completionType = WaitForSetCompletionType.All) where TClientFacetType : IClientFacet { TimeSpan useSpinInterval = (spinInterval ?? (0.1).FromSeconds()).Clip(spinIntervalClipRangeMinValue, spinIntervalClipRangeMaxValue); spinEarlyExitCodeDelegate = spinEarlyExitCodeDelegate ?? (() => null); IClientFacet firstAction = actionSetArray.FirstOrDefault(); if (firstAction != null) { for (; ;) { IClientFacet firstWaitableAction = null; switch (completionType) { case WaitForSetCompletionType.Any: { var firstCompleteAction = actionSetArray.FirstOrDefault(a => a.ActionState.IsComplete); if (firstCompleteAction != null) { return(firstCompleteAction.ActionState.ResultCode); } firstWaitableAction = actionSetArray.FirstOrDefault(a => !a.ActionState.IsComplete); } break; case WaitForSetCompletionType.All: firstWaitableAction = actionSetArray.FirstOrDefault(a => !a.ActionState.IsComplete); if (firstWaitableAction == null) { var firstFailedAction = actionSetArray.FirstOrDefault(a => a.ActionState.Failed); if (firstFailedAction != null) { return(firstFailedAction.ActionState.ResultCode); } else { return(""); } } break; case WaitForSetCompletionType.AllOrAnyFailure: { // NOTE: in the following case the order of evaluation is important as the ActionStates of incomplete actions may change between the evaluation of the firstWaitableAction and of the firstFailedAction firstWaitableAction = actionSetArray.FirstOrDefault(a => !a.ActionState.IsComplete); var firstFailedAction = actionSetArray.FirstOrDefault(a => a.ActionState.Failed); if (firstFailedAction != null) { return(firstFailedAction.ActionState.ResultCode); } else if (firstWaitableAction == null) { return(""); } } break; } string earlyExitCode = spinEarlyExitCodeDelegate(); if (earlyExitCode != null) { return(earlyExitCode); } if (firstWaitableAction != null) { firstWaitableAction.WaitUntilComplete(useSpinInterval); } else { spinIntervalClipRangeMinValue.Sleep(); } } } return(""); // there were no actions to run }
/// <summary> /// Attempts to Start each of the given <paramref name="actionSetArray"/> of actions and then waits for them to complete. /// If some or all of them are started, gets the exitCode that results from passing the corresponding set of parameters to WaitUntilSetComplete for the set of successfully started actions. /// Then returns either the non-empty exit code from WaitUntilSetComplete, or the first non-empty failure code from attempting to start the actions, or the empty string to indicate success. /// </summary> public static string RunSet <TClientFacetType>(this TClientFacetType[] actionSetArray, Func <string> spinEarlyExitCodeDelegate = null, TimeSpan?spinInterval = null, WaitForSetCompletionType completionType = WaitForSetCompletionType.All) where TClientFacetType : IClientFacet { int successfullyStartedCount = 0; string firstStartFailureEC = null; foreach (var a in actionSetArray) { var ec = a.Start(); if (ec.IsNullOrEmpty()) { successfullyStartedCount++; } else { firstStartFailureEC = firstStartFailureEC ?? ec; } } if (successfullyStartedCount > 0) { string waitResultEC = null; if (firstStartFailureEC == null) { waitResultEC = actionSetArray.WaitUntilSetComplete(spinEarlyExitCodeDelegate: spinEarlyExitCodeDelegate, spinInterval: spinInterval, completionType: completionType); } else { waitResultEC = actionSetArray.Take(successfullyStartedCount).ToArray().WaitUntilSetComplete(spinEarlyExitCodeDelegate: spinEarlyExitCodeDelegate, spinInterval: spinInterval, completionType: completionType); } if (!waitResultEC.IsNullOrEmpty()) { return(waitResultEC); } } return(firstStartFailureEC ?? ""); }
/// <summary> /// Blocks the caller until the given actionSet completes (based on the chosen completionType) or the isWaitLimitReachedDelegate returns true (typically based on a timer expiring). /// This evaluation takes place in a spin loop with the spin's wait period given using the spinInterval value. /// If the caller does not provide a spinInterval then 0.1 seconds will be used. The any given spinInterval value will be clipped to be between 0.001 seconds and 0.5 seconds /// <para/>supports call chaining /// </summary> public static TClientFacetType [] WaitUntilSetComplete <TClientFacetType>(this TClientFacetType [] actionSetArray, Func <bool> isWaitLimitReachedDelegate = null, TimeSpan?spinInterval = null, WaitForSetCompletionType completionType = WaitForSetCompletionType.All) where TClientFacetType : IClientFacet { TimeSpan useSpinInterval = (spinInterval ?? (0.1).FromSeconds()).Clip(spinIntervalClipRangeMinValue, spinIntervalClipRangeMaxValue); Func <string> spinEarlyExitCodeDelegate = null; if (isWaitLimitReachedDelegate != null) { spinEarlyExitCodeDelegate = () => (isWaitLimitReachedDelegate() ? "" : null); } actionSetArray.WaitUntilSetComplete(spinEarlyExitCodeDelegate: spinEarlyExitCodeDelegate, spinInterval: spinInterval, completionType: completionType); return(actionSetArray); }