| | 1 | | // Copyright (c) Microsoft Corporation. All rights reserved. |
| | 2 | | // Licensed under the MIT License. See License.txt in the project root for license information. |
| | 3 | |
|
| | 4 | | namespace Microsoft.Azure.Batch |
| | 5 | | { |
| | 6 | | using System; |
| | 7 | | using System.Collections.Generic; |
| | 8 | | using System.Globalization; |
| | 9 | | using System.Threading; |
| | 10 | | using System.Threading.Tasks; |
| | 11 | |
|
| | 12 | | /// <summary> |
| | 13 | | /// Provides utilities to help monitor CloudTask states. |
| | 14 | | /// </summary> |
| | 15 | | public class TaskStateMonitor : IInheritedBehaviors |
| | 16 | | { |
| | 17 | | private readonly Utilities _parentUtilities; |
| | 18 | |
|
| | 19 | | #region constructors |
| | 20 | |
|
| 0 | 21 | | private TaskStateMonitor() |
| | 22 | | { |
| 0 | 23 | | } |
| | 24 | |
|
| 3 | 25 | | internal TaskStateMonitor(Utilities parentUtilities, IEnumerable<BatchClientBehavior> baseBehaviors) |
| | 26 | | { |
| 3 | 27 | | _parentUtilities = parentUtilities; |
| | 28 | |
|
| | 29 | | // inherit from parent |
| 3 | 30 | | InheritUtil.InheritClientBehaviorsAndSetPublicProperty(this, baseBehaviors); |
| 3 | 31 | | } |
| | 32 | |
|
| | 33 | | #endregion constructors |
| | 34 | |
|
| | 35 | |
|
| | 36 | | #region IInheritedBehaviors |
| | 37 | |
|
| | 38 | | /// <summary> |
| | 39 | | /// Gets or sets a list of behaviors that modify or customize requests to the Batch service |
| | 40 | | /// made via this <see cref="TaskStateMonitor"/>. |
| | 41 | | /// </summary> |
| | 42 | | /// <remarks> |
| | 43 | | /// <para>These behaviors are inherited by child objects.</para> |
| | 44 | | /// <para>Modifications are applied in the order of the collection. The last write wins.</para> |
| | 45 | | /// </remarks> |
| 6 | 46 | | public IList<BatchClientBehavior> CustomBehaviors { get; set; } |
| | 47 | |
|
| | 48 | | #endregion IInheritedBehaviors |
| | 49 | |
|
| | 50 | | #region TaskStateMonitor |
| | 51 | |
|
| | 52 | | /// <summary> |
| | 53 | | /// Monitors a <see cref="CloudTask"/> collection until each of its members has reached a desired state at least |
| | 54 | | /// </summary> |
| | 55 | | /// <remarks> |
| | 56 | | /// <para> |
| | 57 | | /// The state of each <see cref="CloudTask"/> instance is assumed to be authoritative at the time of the call. |
| | 58 | | /// Instances that are already at the <paramref name="desiredState"/> are ignored. |
| | 59 | | /// The <see cref="CloudTask"/> instances in the collection are treated as read-only. |
| | 60 | | /// This means that when the call completes (timeout or not) the <see cref="CloudTask"/> instances should be ref |
| | 61 | | /// </para> |
| | 62 | | /// <para> |
| | 63 | | /// This method runs asynchronously. |
| | 64 | | /// </para> |
| | 65 | | /// </remarks> |
| | 66 | | /// <param name="tasksToMonitor">The collection of tasks to monitor.</param> |
| | 67 | | /// <param name="desiredState">The target state of the tasks. The method will exit when all tasks have reached t |
| | 68 | | /// <param name="timeout">The maximum amount of time this call will wait before timing out.</param> |
| | 69 | | /// <param name="controlParams">Controls various settings of the monitor, such as delay between each poll.</para |
| | 70 | | /// <param name="additionalBehaviors">A collection of <see cref="BatchClientBehavior"/> instances that are appli |
| | 71 | | /// <returns>A <see cref="System.Threading.Tasks.Task"/> that represents the asynchronous operation.</returns> |
| | 72 | | /// <exception cref="TimeoutException">Thrown if the <paramref name="timeout"/> has elapsed.</exception> |
| | 73 | | public async Task WhenAll( |
| | 74 | | IEnumerable<CloudTask> tasksToMonitor, |
| | 75 | | Common.TaskState desiredState, |
| | 76 | | TimeSpan timeout, |
| | 77 | | ODATAMonitorControl controlParams = null, |
| | 78 | | IEnumerable<BatchClientBehavior> additionalBehaviors = null) |
| | 79 | | { |
| 1 | 80 | | using (CancellationTokenSource tokenSource = new CancellationTokenSource(timeout)) |
| | 81 | | { |
| | 82 | | try |
| | 83 | | { |
| 1 | 84 | | await this.WhenAllImplAsync( |
| 1 | 85 | | tasksToMonitor, |
| 1 | 86 | | desiredState, |
| 1 | 87 | | tokenSource.Token, |
| 1 | 88 | | controlParams, |
| 1 | 89 | | additionalBehaviors).ConfigureAwait(continueOnCapturedContext: false); |
| 0 | 90 | | } |
| 1 | 91 | | catch (OperationCanceledException cancellationException) |
| | 92 | | { |
| 1 | 93 | | if (cancellationException.CancellationToken == tokenSource.Token) |
| | 94 | | { |
| 1 | 95 | | throw new TimeoutException( |
| 1 | 96 | | string.Format(CultureInfo.InvariantCulture, BatchErrorMessages.ODataMonitorTimedOut, timeout |
| 1 | 97 | | cancellationException); |
| | 98 | | } |
| | 99 | |
|
| 0 | 100 | | throw; |
| | 101 | | } |
| 0 | 102 | | } |
| 0 | 103 | | } |
| | 104 | |
|
| | 105 | | /// <summary> |
| | 106 | | /// Monitors a <see cref="CloudTask"/> collection until each of its members has reached a desired state at least |
| | 107 | | /// </summary> |
| | 108 | | /// <remarks> |
| | 109 | | /// <para> |
| | 110 | | /// The state of each <see cref="CloudTask"/> instance is assumed to be authoritative at the time of the call. |
| | 111 | | /// Instances that are already at the <paramref name="desiredState"/> are ignored. |
| | 112 | | /// The <see cref="CloudTask"/> instances in the collection are treated as read-only. |
| | 113 | | /// This means that when the call completes (timeout or not) the <see cref="CloudTask"/> instances should be ref |
| | 114 | | /// </para> |
| | 115 | | /// <para> |
| | 116 | | /// This method runs asynchronously. |
| | 117 | | /// </para> |
| | 118 | | /// </remarks> |
| | 119 | | /// <param name="tasksToMonitor">The collection of tasks to monitor.</param> |
| | 120 | | /// <param name="desiredState">The target state of the tasks. The method will exit when all tasks have reached t |
| | 121 | | /// <param name="cancellationToken">A <see cref="CancellationToken"/> for controlling the lifetime of the asynch |
| | 122 | | /// <param name="controlParams">Controls various settings of the monitor, such as delay between each poll.</para |
| | 123 | | /// <param name="additionalBehaviors">A collection of <see cref="BatchClientBehavior"/> instances that are appli |
| | 124 | | /// <returns>A <see cref="System.Threading.Tasks.Task"/> that represents the asynchronous operation.</returns> |
| | 125 | | /// <exception cref="OperationCanceledException">Thrown if the <paramref name="cancellationToken"/> was cancelle |
| | 126 | | public async Task WhenAll( |
| | 127 | | IEnumerable<CloudTask> tasksToMonitor, |
| | 128 | | Common.TaskState desiredState, |
| | 129 | | CancellationToken cancellationToken, |
| | 130 | | ODATAMonitorControl controlParams = null, |
| | 131 | | IEnumerable<BatchClientBehavior> additionalBehaviors = null) |
| | 132 | | { |
| 2 | 133 | | await this.WhenAllImplAsync( |
| 2 | 134 | | tasksToMonitor, |
| 2 | 135 | | desiredState, |
| 2 | 136 | | cancellationToken, |
| 2 | 137 | | controlParams, |
| 2 | 138 | | additionalBehaviors).ConfigureAwait(continueOnCapturedContext: false); |
| 0 | 139 | | } |
| | 140 | |
|
| | 141 | | /// <summary> |
| | 142 | | /// Monitors a <see cref="CloudTask"/> collection until each of its members has reached a desired state at least |
| | 143 | | /// </summary> |
| | 144 | | /// <remarks> |
| | 145 | | /// <para> |
| | 146 | | /// The state of each <see cref="CloudTask"/> instance is assumed to be authoritative at the time of the call. |
| | 147 | | /// Instances that are already at the <paramref name="desiredState"/> are ignored. |
| | 148 | | /// The <see cref="CloudTask"/> instances in the collection are treated as read-only. |
| | 149 | | /// This means that when the call completes (timeout or not) the <see cref="CloudTask"/> instances should be ref |
| | 150 | | /// </para> |
| | 151 | | /// <para> |
| | 152 | | /// This is a blocking operation. For a non-blocking equivalent, see |
| | 153 | | /// <see cref="WhenAll(System.Collections.Generic.IEnumerable{Microsoft.Azure.Batch.CloudTask},Microsoft.Azure.B |
| | 154 | | /// </para> |
| | 155 | | /// </remarks> |
| | 156 | | /// <param name="tasksToMonitor">The collection of tasks to monitor.</param> |
| | 157 | | /// <param name="desiredState">The target state of the tasks. The method will exit when all tasks have reached t |
| | 158 | | /// <param name="timeout">The maximum amount of time this call will wait before timing out.</param> |
| | 159 | | /// <param name="controlParams">Controls various settings of the monitor, such as delay between each poll.</para |
| | 160 | | /// <param name="additionalBehaviors">A collection of <see cref="BatchClientBehavior"/> instances that are appli |
| | 161 | | /// <exception cref="TimeoutException">Thrown if the <paramref name="timeout"/> has elapsed.</exception> |
| | 162 | | public void WaitAll( |
| | 163 | | IEnumerable<CloudTask> tasksToMonitor, |
| | 164 | | Common.TaskState desiredState, |
| | 165 | | TimeSpan timeout, |
| | 166 | | ODATAMonitorControl controlParams = null, |
| | 167 | | IEnumerable<BatchClientBehavior> additionalBehaviors = null) |
| | 168 | | { |
| 0 | 169 | | Task asyncTask = this.WhenAll(tasksToMonitor, desiredState, timeout, controlParams, additionalBehaviors); |
| 0 | 170 | | asyncTask.WaitAndUnaggregateException(this.CustomBehaviors, additionalBehaviors); |
| 0 | 171 | | } |
| | 172 | |
|
| | 173 | | private async Task WhenAllImplAsync( |
| | 174 | | IEnumerable<CloudTask> tasksToMonitor, |
| | 175 | | Common.TaskState desiredState, |
| | 176 | | CancellationToken cancellationToken, |
| | 177 | | ODATAMonitorControl controlParams, |
| | 178 | | IEnumerable<BatchClientBehavior> additionalBehaviors) |
| | 179 | | { |
| 3 | 180 | | if (null == tasksToMonitor) |
| | 181 | | { |
| 0 | 182 | | throw new ArgumentNullException("tasksToMonitor"); |
| | 183 | | } |
| | 184 | |
|
| | 185 | | // we only need the id and state for this monitor. the filter clause will be updated by the monitor |
| 3 | 186 | | ODATADetailLevel odataSuperOptimalPredicates = new ODATADetailLevel() { SelectClause = "id,state" }; |
| | 187 | |
|
| | 188 | | // for validation and list calls we need the parent name values |
| 3 | 189 | | string jobId = null; |
| | 190 | |
|
| | 191 | | // set up behaviors |
| 3 | 192 | | BehaviorManager bhMgr = new BehaviorManager(this.CustomBehaviors, additionalBehaviors); |
| | 193 | |
|
| | 194 | | // set up control params if needed |
| 3 | 195 | | if (null == controlParams) |
| | 196 | | { |
| 2 | 197 | | controlParams = new ODATAMonitorControl(); // use defaults |
| | 198 | | } |
| | 199 | |
|
| 3 | 200 | | tasksToMonitor = await UtilitiesInternal.EnumerateIfNeededAsync(tasksToMonitor, cancellationToken).Configure |
| | 201 | |
|
| | 202 | | // validation: job schedule id and jobId |
| 18 | 203 | | foreach (CloudTask curTask in tasksToMonitor) |
| | 204 | | { |
| | 205 | | // can only monitor bound objects |
| 6 | 206 | | if (curTask.BindingState != BindingState.Bound) |
| | 207 | | { |
| 0 | 208 | | Exception ex = UtilitiesInternal.OperationForbiddenOnUnboundObjects; |
| | 209 | |
|
| 0 | 210 | | throw ex; |
| | 211 | | } |
| | 212 | |
|
| | 213 | | // set or validate job Id |
| 6 | 214 | | if (null == jobId) |
| | 215 | | { |
| 3 | 216 | | jobId = curTask.ParentJobId; |
| | 217 | | } |
| | 218 | | else |
| | 219 | | { |
| | 220 | | // all instances must have same parent |
| 3 | 221 | | if (!jobId.Equals(curTask.ParentJobId, StringComparison.OrdinalIgnoreCase)) |
| | 222 | | { |
| 0 | 223 | | Exception ex = UtilitiesInternal.MonitorRequiresConsistentHierarchyChain; |
| | 224 | |
|
| 0 | 225 | | throw ex; |
| | 226 | | } |
| | 227 | | } |
| | 228 | | } |
| | 229 | |
|
| | 230 | | // start call |
| 3 | 231 | | Task asyncTask = ODATAMonitor.WhenAllAsync( |
| 3 | 232 | | tasksToMonitor, |
| 3 | 233 | | x => |
| 3 | 234 | | { |
| 3 | 235 | | // return true if is desired state |
| 15 | 236 | | bool hasReachedDesiredState = x.State == desiredState; |
| 15 | 237 | | return hasReachedDesiredState; |
| 3 | 238 | | }, |
| 12 | 239 | | x => { return x.Id; }, // return the Id of the task |
| 6 | 240 | | () => _parentUtilities.ParentBatchClient.JobOperations.ListTasksImpl(jobId, bhMgr, odataSuperOptimalPred |
| 3 | 241 | | cancellationToken, |
| 3 | 242 | | odataSuperOptimalPredicates, |
| 3 | 243 | | controlParams); |
| | 244 | |
|
| 3 | 245 | | await asyncTask.ConfigureAwait(continueOnCapturedContext: false); |
| 0 | 246 | | } |
| | 247 | |
|
| | 248 | | #endregion TaskStateMonitor |
| | 249 | |
|
| | 250 | | } |
| | 251 | | } |