|  |  | 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 |  | } |