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 suspends 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.Suspend, "Job", SupportsShouldProcess = true, DefaultParameterSetName = JobCmdletBase.SessionIdParameterSet,21 HelpUri = "https://go.microsoft.com/fwlink/?LinkID=210613")]22 [OutputType(typeof(Job))]23#endif24 public class SuspendJobCommand : 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 /// If state of the job is running , this will forcefully suspend it.65 /// </summary>66 [Parameter(ParameterSetName = RemoveJobCommand.InstanceIdParameterSet)]67 [Parameter(ParameterSetName = RemoveJobCommand.JobParameterSet)]68 [Parameter(ParameterSetName = RemoveJobCommand.NameParameterSet)]69 [Parameter(ParameterSetName = RemoveJobCommand.SessionIdParameterSet)]70 [Parameter(ParameterSetName = RemoveJobCommand.FilterParameterSet)]71 [Parameter(ParameterSetName = RemoveJobCommand.StateParameterSet)]72 [Alias("F")]73 public SwitchParameter Force74 {75 get76 {77 return _force;78 }79 80 set81 {82 _force = value;83 }84 }85 86 private bool _force = false;87 88 /// <summary>89 /// </summary>90 [Parameter()]91 public SwitchParameter Wait92 {93 get94 {95 return _wait;96 }97 98 set99 {100 _wait = value;101 }102 }103 104 private bool _wait = false;105 106 #endregion Parameters107 108 #region Overrides109 110 /// <summary>111 /// Suspend the Job.112 /// </summary>113 protected override void ProcessRecord()114 {115 // List of jobs to suspend116 List<Job> jobsToSuspend = null;117 118 switch (ParameterSetName)119 {120 case NameParameterSet:121 {122 jobsToSuspend = FindJobsMatchingByName(true, false, true, false);123 }124 125 break;126 127 case InstanceIdParameterSet:128 {129 jobsToSuspend = FindJobsMatchingByInstanceId(true, false, true, false);130 }131 132 break;133 134 case SessionIdParameterSet:135 {136 jobsToSuspend = FindJobsMatchingBySessionId(true, false, true, false);137 }138 139 break;140 141 case StateParameterSet:142 {143 jobsToSuspend = FindJobsMatchingByState(false);144 }145 146 break;147 148 case FilterParameterSet:149 {150 jobsToSuspend = FindJobsMatchingByFilter(false);151 }152 153 break;154 155 default:156 {157 jobsToSuspend = CopyJobsToList(_jobs, false, false);158 }159 160 break;161 }162 163 _allJobsToSuspend.AddRange(jobsToSuspend);164 165 foreach (Job job in jobsToSuspend)166 {167 var job2 = job as Job2;168 169 // If the job is not Job2, the suspend operation is not supported.170 if (job2 == null)171 {172 WriteError(173 new ErrorRecord(174 PSTraceSource.NewNotSupportedException(RemotingErrorIdStrings.JobSuspendNotSupported, job.Id),175 "Job2OperationNotSupportedOnJob", ErrorCategory.InvalidType, (object)job));176 continue;177 }178 179 string targetString =180 PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.RemovePSJobWhatIfTarget,181 job.Command, job.Id);182 if (ShouldProcess(targetString, VerbsLifecycle.Suspend))183 {184 if (_wait)185 {186 _cleanUpActions.Add(job2, HandleSuspendJobCompleted);187 }188 else189 {190 if (job2.IsFinishedState(job2.JobStateInfo.State) || job2.JobStateInfo.State == JobState.Stopping)191 {192 _warnInvalidState = true;193 continue;194 }195 196 if (job2.JobStateInfo.State == JobState.Suspending || job2.JobStateInfo.State == JobState.Suspended)197 continue;198 199 job2.StateChanged += noWait_Job2_StateChanged;200 }201 202 job2.SuspendJobCompleted += HandleSuspendJobCompleted;203 204 lock (_syncObject)205 {206 if (!_pendingJobs.Contains(job2.InstanceId))207 {208 _pendingJobs.Add(job2.InstanceId);209 }210 }211 212 // there could be possibility that the job gets completed before or after the213 // subscribing to nowait_job2_statechanged event so checking it again.214 if (!_wait && (job2.IsFinishedState(job2.JobStateInfo.State) || job2.JobStateInfo.State == JobState.Suspending || job2.JobStateInfo.State == JobState.Suspended))215 {216 this.ProcessExecutionErrorsAndReleaseWaitHandle(job2);217 }218 219 job2.SuspendJobAsync(_force, RemotingErrorIdStrings.ForceSuspendJob);220 }221 }222 }223 224 private bool _warnInvalidState = false;225 private readonly HashSet<Guid> _pendingJobs = new HashSet<Guid>();226 private readonly ManualResetEvent _waitForJobs = new ManualResetEvent(false);227 private readonly Dictionary<Job2, EventHandler<AsyncCompletedEventArgs>> _cleanUpActions =228 new Dictionary<Job2, EventHandler<AsyncCompletedEventArgs>>();229 private readonly List<ErrorRecord> _errorsToWrite = new List<ErrorRecord>();230 private readonly List<Job> _allJobsToSuspend = new List<Job>();231 private readonly object _syncObject = new object();232 private bool _needToCheckForWaitingJobs;233 234 private void noWait_Job2_StateChanged(object sender, JobStateEventArgs e)235 {236 Job job = sender as Job;237 238 switch (e.JobStateInfo.State)239 {240 case JobState.Completed:241 case JobState.Stopped:242 case JobState.Failed:243 case JobState.Suspended:244 case JobState.Suspending:245 this.ProcessExecutionErrorsAndReleaseWaitHandle(job);246 break;247 }248 }249 250 private void HandleSuspendJobCompleted(object sender, AsyncCompletedEventArgs eventArgs)251 {252 Job job = sender as Job;253 254 if (eventArgs.Error != null && eventArgs.Error is InvalidJobStateException)255 {256 _warnInvalidState = true;257 }258 259 this.ProcessExecutionErrorsAndReleaseWaitHandle(job);260 }261 262 private void ProcessExecutionErrorsAndReleaseWaitHandle(Job job)263 {264 bool releaseWait = false;265 lock (_syncObject)266 {267 if (_pendingJobs.Contains(job.InstanceId))268 {269 _pendingJobs.Remove(job.InstanceId);270 }271 else272 {273 // there could be a possibility of race condition where this function is getting called twice274 // so if job doesn't present in the _pendingJobs then just return275 return;276 }277 278 if (_needToCheckForWaitingJobs && _pendingJobs.Count == 0)279 releaseWait = true;280 }281 282 if (!_wait)283 {284 job.StateChanged -= noWait_Job2_StateChanged;285 Job2 job2 = job as Job2;286 if (job2 != null)287 job2.SuspendJobCompleted -= HandleSuspendJobCompleted;288 }289 290 var parentJob = job as ContainerParentJob;291 if (parentJob != null && parentJob.ExecutionError.Count > 0)292 {293 foreach (294 var e in295 parentJob.ExecutionError.Where(static e => e.FullyQualifiedErrorId == "ContainerParentJobSuspendAsyncError")296 )297 {298 if (e.Exception is InvalidJobStateException)299 {300 // if any errors were invalid job state exceptions, warn the user.301 // This is to support Get-Job | Resume-Job scenarios when many jobs302 // are Completed, etc.303 _warnInvalidState = true;304 }305 else306 {307 _errorsToWrite.Add(e);308 }309 }310 }311 312 // end processing has been called313 // set waithandle if this is the last one314 if (releaseWait)315 _waitForJobs.Set();316 }317 318 /// <summary>319 /// End Processing.320 /// </summary>321 protected override void EndProcessing()322 {323 bool haveToWait = false;324 lock (_syncObject)325 {326 _needToCheckForWaitingJobs = true;327 if (_pendingJobs.Count > 0)328 {329 haveToWait = true;330 }331 }332 333 if (haveToWait)334 {335 _waitForJobs.WaitOne();336 }337 338 if (_warnInvalidState) 339 {340 WriteWarning(RemotingErrorIdStrings.SuspendJobInvalidJobState);341 }342 343 foreach (var e in _errorsToWrite)344 {345 WriteError(e);346 }347 348 foreach (var j in _allJobsToSuspend)349 {350 WriteObject(j);351 }352 353 base.EndProcessing();354 }355 356 /// <summary>357 /// </summary>358 protected override void StopProcessing()359 {360 _waitForJobs.Set();361 }362 363 #endregion Overrides364 365 #region Dispose366 367 /// <summary>368 /// </summary>369 public void Dispose()370 {371 Dispose(true);372 GC.SuppressFinalize(this);373 }374 375 /// <summary>376 /// </summary>377 /// <param name="disposing"></param>378 protected void Dispose(bool disposing)379 {380 if (!disposing)381 {382 return;383 }384 385 foreach (var pair in _cleanUpActions)386 {387 pair.Key.SuspendJobCompleted -= pair.Value;388 }389 390 _waitForJobs.Dispose();391 }392 #endregion Dispose393 }394}395 