PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.Management.Automation/engine/remoting/commands/SuspendJob.cs

https://gitlab.com/unofficial-mirrors/PowerShell
C# | 365 lines | 278 code | 42 blank | 45 comment | 40 complexity | 682c784fade0d14c4fa2438f31a4f3ab MD5 | raw file
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // Licensed under the MIT License.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Diagnostics.CodeAnalysis;
  7. using System.Linq;
  8. using System.Management.Automation;
  9. using System.Management.Automation.Remoting;
  10. using System.Threading;
  11. namespace Microsoft.PowerShell.Commands
  12. {
  13. /// <summary>
  14. /// This cmdlet suspends the jobs that are Job2. Errors are added for each Job that is not Job2.
  15. /// </summary>
  16. #if !CORECLR
  17. [SuppressMessage("Microsoft.PowerShell", "PS1012:CallShouldProcessOnlyIfDeclaringSupport")]
  18. [Cmdlet(VerbsLifecycle.Suspend, "Job", SupportsShouldProcess = true, DefaultParameterSetName = JobCmdletBase.SessionIdParameterSet,
  19. HelpUri = "https://go.microsoft.com/fwlink/?LinkID=210613")]
  20. [OutputType(typeof(Job))]
  21. #endif
  22. public class SuspendJobCommand : JobCmdletBase, IDisposable
  23. {
  24. #region Parameters
  25. /// <summary>
  26. /// Specifies the Jobs objects which need to be
  27. /// suspended
  28. /// </summary>
  29. [Parameter(Mandatory = true,
  30. Position = 0,
  31. ValueFromPipeline = true,
  32. ValueFromPipelineByPropertyName = true,
  33. ParameterSetName = JobParameterSet)]
  34. [ValidateNotNullOrEmpty]
  35. [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
  36. public Job[] Job
  37. {
  38. get
  39. {
  40. return _jobs;
  41. }
  42. set
  43. {
  44. _jobs = value;
  45. }
  46. }
  47. private Job[] _jobs;
  48. /// <summary>
  49. ///
  50. /// </summary>
  51. public override String[] Command
  52. {
  53. get
  54. {
  55. return null;
  56. }
  57. }
  58. /// <summary>
  59. /// If state of the job is running , this will forcefully suspend it.
  60. /// </summary>
  61. [Parameter(ParameterSetName = RemoveJobCommand.InstanceIdParameterSet)]
  62. [Parameter(ParameterSetName = RemoveJobCommand.JobParameterSet)]
  63. [Parameter(ParameterSetName = RemoveJobCommand.NameParameterSet)]
  64. [Parameter(ParameterSetName = RemoveJobCommand.SessionIdParameterSet)]
  65. [Parameter(ParameterSetName = RemoveJobCommand.FilterParameterSet)]
  66. [Parameter(ParameterSetName = RemoveJobCommand.StateParameterSet)]
  67. [Alias("F")]
  68. public SwitchParameter Force
  69. {
  70. get
  71. {
  72. return _force;
  73. }
  74. set
  75. {
  76. _force = value;
  77. }
  78. }
  79. private bool _force = false;
  80. /// <summary>
  81. ///
  82. /// </summary>
  83. [Parameter()]
  84. public SwitchParameter Wait
  85. {
  86. get
  87. {
  88. return _wait;
  89. }
  90. set
  91. {
  92. _wait = value;
  93. }
  94. }
  95. private bool _wait = false;
  96. #endregion Parameters
  97. #region Overrides
  98. /// <summary>
  99. /// Suspend the Job.
  100. /// </summary>
  101. protected override void ProcessRecord()
  102. {
  103. //List of jobs to suspend
  104. List<Job> jobsToSuspend = null;
  105. switch (ParameterSetName)
  106. {
  107. case NameParameterSet:
  108. {
  109. jobsToSuspend = FindJobsMatchingByName(true, false, true, false);
  110. }
  111. break;
  112. case InstanceIdParameterSet:
  113. {
  114. jobsToSuspend = FindJobsMatchingByInstanceId(true, false, true, false);
  115. }
  116. break;
  117. case SessionIdParameterSet:
  118. {
  119. jobsToSuspend = FindJobsMatchingBySessionId(true, false, true, false);
  120. }
  121. break;
  122. case StateParameterSet:
  123. {
  124. jobsToSuspend = FindJobsMatchingByState(false);
  125. }
  126. break;
  127. case FilterParameterSet:
  128. {
  129. jobsToSuspend = FindJobsMatchingByFilter(false);
  130. }
  131. break;
  132. default:
  133. {
  134. jobsToSuspend = CopyJobsToList(_jobs, false, false);
  135. }
  136. break;
  137. }
  138. _allJobsToSuspend.AddRange(jobsToSuspend);
  139. foreach (Job job in jobsToSuspend)
  140. {
  141. var job2 = job as Job2;
  142. // If the job is not Job2, the suspend operation is not supported.
  143. if (job2 == null)
  144. {
  145. WriteError(
  146. new ErrorRecord(
  147. PSTraceSource.NewNotSupportedException(RemotingErrorIdStrings.JobSuspendNotSupported, job.Id),
  148. "Job2OperationNotSupportedOnJob", ErrorCategory.InvalidType, (object)job));
  149. continue;
  150. }
  151. string targetString =
  152. PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.RemovePSJobWhatIfTarget,
  153. job.Command, job.Id);
  154. if (ShouldProcess(targetString, VerbsLifecycle.Suspend))
  155. {
  156. if (_wait)
  157. {
  158. _cleanUpActions.Add(job2, HandleSuspendJobCompleted);
  159. }
  160. else
  161. {
  162. if (job2.IsFinishedState(job2.JobStateInfo.State) || job2.JobStateInfo.State == JobState.Stopping)
  163. {
  164. _warnInvalidState = true;
  165. continue;
  166. }
  167. if (job2.JobStateInfo.State == JobState.Suspending || job2.JobStateInfo.State == JobState.Suspended)
  168. continue;
  169. job2.StateChanged += noWait_Job2_StateChanged;
  170. }
  171. job2.SuspendJobCompleted += HandleSuspendJobCompleted;
  172. lock (_syncObject)
  173. {
  174. if (!_pendingJobs.Contains(job2.InstanceId))
  175. {
  176. _pendingJobs.Add(job2.InstanceId);
  177. }
  178. }
  179. // there could be possibility that the job gets completed before or after the
  180. // subscribing to nowait_job2_statechanged event so checking it again.
  181. if (!_wait && (job2.IsFinishedState(job2.JobStateInfo.State) || job2.JobStateInfo.State == JobState.Suspending || job2.JobStateInfo.State == JobState.Suspended))
  182. {
  183. this.ProcessExecutionErrorsAndReleaseWaitHandle(job2);
  184. }
  185. job2.SuspendJobAsync(_force, RemotingErrorIdStrings.ForceSuspendJob);
  186. }
  187. }
  188. }
  189. private bool _warnInvalidState = false;
  190. private readonly HashSet<Guid> _pendingJobs = new HashSet<Guid>();
  191. private readonly ManualResetEvent _waitForJobs = new ManualResetEvent(false);
  192. private readonly Dictionary<Job2, EventHandler<AsyncCompletedEventArgs>> _cleanUpActions =
  193. new Dictionary<Job2, EventHandler<AsyncCompletedEventArgs>>();
  194. private readonly List<ErrorRecord> _errorsToWrite = new List<ErrorRecord>();
  195. private readonly List<Job> _allJobsToSuspend = new List<Job>();
  196. private readonly object _syncObject = new object();
  197. private bool _needToCheckForWaitingJobs;
  198. private void noWait_Job2_StateChanged(object sender, JobStateEventArgs e)
  199. {
  200. Job job = sender as Job;
  201. switch (e.JobStateInfo.State)
  202. {
  203. case JobState.Completed:
  204. case JobState.Stopped:
  205. case JobState.Failed:
  206. case JobState.Suspended:
  207. case JobState.Suspending:
  208. this.ProcessExecutionErrorsAndReleaseWaitHandle(job);
  209. break;
  210. }
  211. }
  212. private void HandleSuspendJobCompleted(object sender, AsyncCompletedEventArgs eventArgs)
  213. {
  214. Job job = sender as Job;
  215. if (eventArgs.Error != null && eventArgs.Error is InvalidJobStateException)
  216. {
  217. _warnInvalidState = true;
  218. }
  219. this.ProcessExecutionErrorsAndReleaseWaitHandle(job);
  220. }
  221. private void ProcessExecutionErrorsAndReleaseWaitHandle(Job job)
  222. {
  223. bool releaseWait = false;
  224. lock (_syncObject)
  225. {
  226. if (_pendingJobs.Contains(job.InstanceId))
  227. {
  228. _pendingJobs.Remove(job.InstanceId);
  229. }
  230. else
  231. {
  232. // there could be a possibility of race condition where this function is getting called twice
  233. // so if job doesn't present in the _pendingJobs then just return
  234. return;
  235. }
  236. if (_needToCheckForWaitingJobs && _pendingJobs.Count == 0)
  237. releaseWait = true;
  238. }
  239. if (!_wait)
  240. {
  241. job.StateChanged -= noWait_Job2_StateChanged;
  242. Job2 job2 = job as Job2;
  243. if (job2 != null)
  244. job2.SuspendJobCompleted -= HandleSuspendJobCompleted;
  245. }
  246. var parentJob = job as ContainerParentJob;
  247. if (parentJob != null && parentJob.ExecutionError.Count > 0)
  248. {
  249. foreach (
  250. var e in
  251. parentJob.ExecutionError.Where(e => e.FullyQualifiedErrorId == "ContainerParentJobSuspendAsyncError")
  252. )
  253. {
  254. if (e.Exception is InvalidJobStateException)
  255. {
  256. // if any errors were invalid job state exceptions, warn the user.
  257. // This is to support Get-Job | Resume-Job scenarios when many jobs
  258. // are Completed, etc.
  259. _warnInvalidState = true;
  260. }
  261. else
  262. {
  263. _errorsToWrite.Add(e);
  264. }
  265. }
  266. }
  267. // end processing has been called
  268. // set waithandle if this is the last one
  269. if (releaseWait)
  270. _waitForJobs.Set();
  271. }
  272. /// <summary>
  273. /// End Processing.
  274. /// </summary>
  275. protected override void EndProcessing()
  276. {
  277. bool haveToWait = false;
  278. lock (_syncObject)
  279. {
  280. _needToCheckForWaitingJobs = true;
  281. if (_pendingJobs.Count > 0)
  282. haveToWait = true;
  283. }
  284. if (haveToWait)
  285. _waitForJobs.WaitOne();
  286. if (_warnInvalidState) WriteWarning(RemotingErrorIdStrings.SuspendJobInvalidJobState);
  287. foreach (var e in _errorsToWrite) WriteError(e);
  288. foreach (var j in _allJobsToSuspend) WriteObject(j);
  289. base.EndProcessing();
  290. }
  291. /// <summary>
  292. ///
  293. /// </summary>
  294. protected override void StopProcessing()
  295. {
  296. _waitForJobs.Set();
  297. }
  298. #endregion Overrides
  299. #region Dispose
  300. /// <summary>
  301. ///
  302. /// </summary>
  303. public void Dispose()
  304. {
  305. Dispose(true);
  306. GC.SuppressFinalize(this);
  307. }
  308. /// <summary>
  309. ///
  310. /// </summary>
  311. /// <param name="disposing"></param>
  312. protected void Dispose(bool disposing)
  313. {
  314. if (!disposing) return;
  315. foreach (var pair in _cleanUpActions)
  316. {
  317. pair.Key.SuspendJobCompleted -= pair.Value;
  318. }
  319. _waitForJobs.Dispose();
  320. }
  321. #endregion Dispose
  322. }
  323. }