MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.ComponentModel;7using System.Diagnostics.CodeAnalysis;8using System.Linq;9using System.Management.Automation;10using System.Management.Automation.Remoting;11using System.Threading;12 13namespace Microsoft.PowerShell.Commands14{15 /// <summary>16 /// This cmdlet resumes the jobs that are Job2. Errors are added for each Job that is not Job2.17 /// </summary>18#if !CORECLR19 [SuppressMessage("Microsoft.PowerShell", "PS1012:CallShouldProcessOnlyIfDeclaringSupport")]20 [Cmdlet(VerbsLifecycle.Resume, "Job", SupportsShouldProcess = true, DefaultParameterSetName = JobCmdletBase.SessionIdParameterSet,21 HelpUri = "https://go.microsoft.com/fwlink/?LinkID=210611")]22#endif23 [OutputType(typeof(Job))]24 public class ResumeJobCommand : JobCmdletBase, IDisposable25 {26 #region Parameters27 /// <summary>28 /// Specifies the Jobs objects which need to be29 /// suspended.30 /// </summary>31 [Parameter(Mandatory = true,32 Position = 0,33 ValueFromPipeline = true,34 ValueFromPipelineByPropertyName = true,35 ParameterSetName = JobParameterSet)]36 [ValidateNotNullOrEmpty]37 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]38 public Job[] Job39 {40 get41 {42 return _jobs;43 }44 45 set46 {47 _jobs = value;48 }49 }50 51 private Job[] _jobs;52 53 /// <summary>54 /// </summary>55 public override string[] Command56 {57 get58 {59 return null;60 }61 }62 63 /// <summary>64 /// Specifies whether to delay returning from the cmdlet until all jobs reach a running state.65 /// This could take significant time due to workflow throttling.66 /// </summary>67 [Parameter(ParameterSetName = ParameterAttribute.AllParameterSets)]68 public SwitchParameter Wait { get; set; }69 70 #endregion Parameters71 72 #region Overrides73 74 /// <summary>75 /// Resume the Job.76 /// </summary>77 protected override void ProcessRecord()78 {79 // List of jobs to resume80 List<Job> jobsToResume = null;81 82 switch (ParameterSetName)83 {84 case NameParameterSet:85 {86 jobsToResume = FindJobsMatchingByName(true, false, true, false);87 }88 89 break;90 91 case InstanceIdParameterSet:92 {93 jobsToResume = FindJobsMatchingByInstanceId(true, false, true, false);94 }95 96 break;97 98 case SessionIdParameterSet:99 {100 jobsToResume = FindJobsMatchingBySessionId(true, false, true, false);101 }102 103 break;104 105 case StateParameterSet:106 {107 jobsToResume = FindJobsMatchingByState(false);108 }109 110 break;111 112 case FilterParameterSet:113 {114 jobsToResume = FindJobsMatchingByFilter(false);115 }116 117 break;118 119 default:120 {121 jobsToResume = CopyJobsToList(_jobs, false, false);122 }123 124 break;125 }126 127 _allJobsToResume.AddRange(jobsToResume);128 129 // Blue: 151804 When resuming a single suspended workflow job, Resume-job cmdlet doesn't wait for the job to be in running state130 // Setting Wait to true so that this cmdlet will wait for the running job state.131 if (_allJobsToResume.Count == 1)132 Wait = true;133 134 foreach (Job job in jobsToResume)135 {136 var job2 = job as Job2;137 138 // If the job is not Job2, the resume operation is not supported.139 if (job2 == null)140 {141 WriteError(new ErrorRecord(PSTraceSource.NewNotSupportedException(RemotingErrorIdStrings.JobResumeNotSupported, job.Id), "Job2OperationNotSupportedOnJob", ErrorCategory.InvalidType, (object)job));142 continue;143 }144 145 string targetString = PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.RemovePSJobWhatIfTarget, job.Command, job.Id);146 if (ShouldProcess(targetString, VerbsLifecycle.Resume))147 {148 _cleanUpActions.Add(job2, HandleResumeJobCompleted);149 job2.ResumeJobCompleted += HandleResumeJobCompleted;150 151 lock (_syncObject)152 {153 if (!_pendingJobs.Contains(job2.InstanceId))154 {155 _pendingJobs.Add(job2.InstanceId);156 }157 }158 159 job2.ResumeJobAsync();160 }161 }162 }163 164 private bool _warnInvalidState = false;165 private readonly HashSet<Guid> _pendingJobs = new HashSet<Guid>();166 private readonly ManualResetEvent _waitForJobs = new ManualResetEvent(false);167 private readonly Dictionary<Job2, EventHandler<AsyncCompletedEventArgs>> _cleanUpActions =168 new Dictionary<Job2, EventHandler<AsyncCompletedEventArgs>>();169 private readonly List<ErrorRecord> _errorsToWrite = new List<ErrorRecord>();170 private readonly List<Job> _allJobsToResume = new List<Job>();171 private readonly object _syncObject = new object();172 private bool _needToCheckForWaitingJobs;173 174 private void HandleResumeJobCompleted(object sender, AsyncCompletedEventArgs eventArgs)175 {176 Job job = sender as Job;177 178 if (eventArgs.Error != null && eventArgs.Error is InvalidJobStateException)179 {180 _warnInvalidState = true;181 }182 183 var parentJob = job as ContainerParentJob;184 if (parentJob != null && parentJob.ExecutionError.Count > 0)185 {186 foreach (187 var e in188 parentJob.ExecutionError.Where(static e => e.FullyQualifiedErrorId == "ContainerParentJobResumeAsyncError")189 )190 {191 if (e.Exception is InvalidJobStateException)192 {193 // if any errors were invalid job state exceptions, warn the user.194 // This is to support Get-Job | Resume-Job scenarios when many jobs195 // are Completed, etc.196 _warnInvalidState = true;197 }198 else199 {200 _errorsToWrite.Add(e);201 }202 }203 204 parentJob.ExecutionError.Clear();205 }206 207 bool releaseWait = false;208 lock (_syncObject)209 {210 if (_pendingJobs.Contains(job.InstanceId))211 {212 _pendingJobs.Remove(job.InstanceId);213 }214 215 if (_needToCheckForWaitingJobs && _pendingJobs.Count == 0)216 releaseWait = true;217 }218 // end processing has been called219 // set waithandle if this is the last one220 if (releaseWait)221 _waitForJobs.Set();222 }223 224 /// <summary>225 /// End Processing.226 /// </summary>227 protected override void EndProcessing()228 {229 bool jobsPending = false;230 lock (_syncObject)231 {232 _needToCheckForWaitingJobs = true;233 if (_pendingJobs.Count > 0)234 {235 jobsPending = true;236 }237 }238 239 if (Wait && jobsPending)240 {241 _waitForJobs.WaitOne();242 }243 244 if (_warnInvalidState)245 {246 WriteWarning(RemotingErrorIdStrings.ResumeJobInvalidJobState);247 }248 249 foreach (var e in _errorsToWrite)250 {251 WriteError(e);252 }253 254 foreach (var j in _allJobsToResume)255 {256 WriteObject(j);257 }258 259 base.EndProcessing();260 }261 262 /// <summary>263 /// </summary>264 protected override void StopProcessing()265 {266 _waitForJobs.Set();267 }268 269 #endregion Overrides270 271 #region Dispose272 273 /// <summary>274 /// </summary>275 public void Dispose()276 {277 Dispose(true);278 GC.SuppressFinalize(this);279 }280 281 /// <summary>282 /// </summary>283 /// <param name="disposing"></param>284 protected void Dispose(bool disposing)285 {286 if (!disposing)287 {288 return;289 }290 291 foreach (var pair in _cleanUpActions)292 {293 pair.Key.ResumeJobCompleted -= pair.Value;294 }295 296 _waitForJobs.Dispose();297 }298 #endregion Dispose299 }300}301 