MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.ComponentModel;6using System.Diagnostics.CodeAnalysis;7using System.Linq;8using System.Management.Automation.Language;9using System.Management.Automation.Runspaces;10using System.Management.Automation.Tracing;11using System.Runtime.Serialization;12using System.Text;13using System.Threading;14 15using Dbg = System.Management.Automation.Diagnostics;16 17// Stops compiler from warning about unknown warnings18#pragma warning disable 1634, 169119 20namespace System.Management.Automation21{22 #region PowerShell v3 Job Extensions23 24 /// <summary>25 /// New base class for a job that provides extended state26 /// management functionality on the job. Since the existing27 /// Job class is an abstract class and there are existing28 /// implementations of the same, it is required to have a29 /// new class that will have the extended functionality. This30 /// is to ensure that backwards compatibility is maintained31 ///32 /// However, this class will derive from the existing Job33 /// class. The option of deprecating the existing class was34 /// considered as well. In order to maintain backwards35 /// compatibility of PowerShell job cmdlets they will have36 /// to work with the old interface and hence deprecating37 /// the Job class did not add any benefit rather than38 /// deriving from the same.39 /// </summary>40 /// <remarks>The following are some of the notes about41 /// why the asynchronous operations are provided this way42 /// in this class. There are two possible options in which43 /// asynchronous support can be provided:44 /// 1. Classical pattern (Begin and End)45 /// 2. Event based pattern46 ///47 /// Although the PowerShell API uses the classical pattern48 /// and we would like the Job API and PowerShell API to be49 /// as close as possible, the classical pattern is inherently50 /// complex to use.</remarks>51 public abstract class Job2 : Job52 {53 #region Private Members54 55 /// <summary>56 /// These are the parameters that can be used by a job57 /// implementation when they want to specify parameters58 /// to start a job.59 /// </summary>60 private List<CommandParameterCollection> _parameters;61 62 /// <summary>63 /// Object that will be used for thread synchronization.64 /// </summary>65 private readonly object _syncobject = new object();66 67 private const int StartJobOperation = 1;68 private const int StopJobOperation = 2;69 private const int SuspendJobOperation = 3;70 private const int ResumeJobOperation = 4;71 private const int UnblockJobOperation = 5;72 73 private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource();74 75 #endregion Private Members76 77 #region Properties78 79 /// <summary>80 /// Parameters to be used to start a job.81 /// This is a property because CommandParameterCollection82 /// does not have a public constructor. Hence the83 /// infrastructure creates an instance and provides84 /// it for the implementations to use.85 /// </summary>86 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]87 [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")]88 public List<CommandParameterCollection> StartParameters89 {90 get91 {92 if (_parameters == null)93 {94 lock (_syncobject)95 {96 _parameters ??= new List<CommandParameterCollection>();97 }98 }99 100 return _parameters;101 }102 103 set104 {105 if (value == null)106 {107 throw PSTraceSource.NewArgumentNullException("value");108 }109 110 lock (_syncobject)111 {112 _parameters = value;113 }114 }115 }116 117 /// <summary>118 /// </summary>119 protected object SyncRoot120 {121 get { return syncObject; }122 }123 124 #endregion Properties125 126 #region Protected Methods127 128 /// <summary>129 /// Default no argument constructor.130 /// </summary>131 protected Job2() : base() { }132 133 /// <summary>134 /// Constructor which will initialize the job135 /// with the associated command string.136 /// </summary>137 /// <param name="command">string representation138 /// of the command the job is running</param>139 protected Job2(string command) : base(command) { }140 141 /// <summary>142 /// Creates an instance of this class.143 /// </summary>144 /// <param name="command">Command invoked by this job object.</param>145 /// <param name="name">Friendly name for the job object.</param>146 protected Job2(string command, string name)147 : base(command, name)148 {149 }150 151 /// <summary>152 /// Creates an instance of this class.153 /// </summary>154 /// <param name="command">Command invoked by this job object.</param>155 /// <param name="name">Friendly name for the job object.</param>156 /// <param name="childJobs">Child jobs of this job object.</param>157 protected Job2(string command, string name, IList<Job> childJobs)158 : base(command, name, childJobs)159 {160 }161 162 /// <summary>163 /// Creates an instance of this class.164 /// </summary>165 /// <param name="command">Command invoked by this job object.</param>166 /// <param name="name">Friendly name for the job object.</param>167 /// <param name="token">JobIdentifier token used to assign Id and InstanceId.</param>168 protected Job2(string command, string name, JobIdentifier token)169 : base(command, name, token)170 {171 }172 173 /// <summary>174 /// Creates an instance of this class.175 /// </summary>176 /// <param name="command">Command string.</param>177 /// <param name="name">Friendly name for the job.</param>178 /// <param name="instanceId">Instance ID to allow job identification across sessions.</param>179 protected Job2(string command, string name, Guid instanceId)180 : base(command, name, instanceId)181 {182 }183 184 /// <summary>185 /// There is an internal method in Job which is not made186 /// public. In order to make this available to someone187 /// implementing a job it has to be added here. If the188 /// original method is made public it has changes of189 /// colliding with some implementation which may have190 /// added that method.191 /// </summary>192 /// <param name="state">State of the job.</param>193 /// <param name="reason">exception associated with the194 /// job entering this state</param>195 protected new void SetJobState(JobState state, Exception reason)196 {197 base.SetJobState(state, reason);198 }199 200 #endregion Protected Methods201 202 #region State Management203 204 /// <summary>205 /// Start a job. The job will be started with the parameters206 /// specified in StartParameters.207 /// </summary>208 /// <remarks>It is redundant to have a method named StartJob209 /// on a job class. However, this is done so as to avoid210 /// an FxCop violation "CA1716:IdentifiersShouldNotMatchKeywords"211 /// Stop and Resume are reserved keyworks in C# and hence cannot212 /// be used as method names. Therefore to be consistent it has213 /// been decided to use *Job in the name of the methods</remarks>214 public abstract void StartJob();215 216 /// <summary>217 /// Start a job asynchronously.218 /// </summary>219 public abstract void StartJobAsync();220 221 /// <summary>222 /// Event to be raise when the start job activity is completed.223 /// This event should not be raised for224 /// synchronous operation.225 /// </summary>226 public event EventHandler<AsyncCompletedEventArgs> StartJobCompleted;227 228 /// <summary>229 /// Method which can be extended or called by derived230 /// classes to raise the event when start of231 /// the job is completed.232 /// </summary>233 /// <param name="eventArgs">arguments describing234 /// an exception that is associated with the event</param>235 protected virtual void OnStartJobCompleted(AsyncCompletedEventArgs eventArgs)236 {237 RaiseCompletedHandler(StartJobOperation, eventArgs);238 }239 240 /// <summary>241 /// Method which can be extended or called by derived242 /// classes to raise the event when stopping a243 /// job is completed.244 /// </summary>245 /// <param name="eventArgs">argument describing246 /// an exception that is associated with the event</param>247 protected virtual void OnStopJobCompleted(AsyncCompletedEventArgs eventArgs)248 {249 RaiseCompletedHandler(StopJobOperation, eventArgs);250 }251 252 /// <summary>253 /// Method which can be extended or called by derived254 /// classes to raise the event when suspending a255 /// job is completed.256 /// </summary>257 /// <param name="eventArgs">argument describing258 /// an exception that is associated with the event</param>259 protected virtual void OnSuspendJobCompleted(AsyncCompletedEventArgs eventArgs)260 {261 RaiseCompletedHandler(SuspendJobOperation, eventArgs);262 }263 264 /// <summary>265 /// Method which can be extended or called by derived266 /// classes to raise the event when resuming a267 /// suspended job is completed.268 /// </summary>269 /// <param name="eventArgs">argument describing270 /// an exception that is associated with the event</param>271 protected virtual void OnResumeJobCompleted(AsyncCompletedEventArgs eventArgs)272 {273 RaiseCompletedHandler(ResumeJobOperation, eventArgs);274 }275 276 /// <summary>277 /// Method which can be extended or called by derived278 /// classes to raise the event when unblocking a279 /// blocked job is completed.280 /// </summary>281 /// <param name="eventArgs">argument describing282 /// an exception that is associated with the event</param>283 protected virtual void OnUnblockJobCompleted(AsyncCompletedEventArgs eventArgs)284 {285 RaiseCompletedHandler(UnblockJobOperation, eventArgs);286 }287 288 /// <summary>289 /// Raises the appropriate event based on the operation290 /// and the associated event arguments.291 /// </summary>292 /// <param name="operation">operation for which the event293 /// needs to be raised</param>294 /// <param name="eventArgs"></param>295 private void RaiseCompletedHandler(int operation, AsyncCompletedEventArgs eventArgs)296 {297 // Make a temporary copy of the event to avoid possibility of298 // a race condition if the last subscriber unsubscribes299 // immediately after the null check and before the event is raised.300 EventHandler<AsyncCompletedEventArgs> handler = null;301 302 switch (operation)303 {304 case StartJobOperation:305 {306 handler = StartJobCompleted;307 }308 309 break;310 case StopJobOperation:311 {312 handler = StopJobCompleted;313 }314 315 break;316 case SuspendJobOperation:317 {318 handler = SuspendJobCompleted;319 }320 321 break;322 case ResumeJobOperation:323 {324 handler = ResumeJobCompleted;325 }326 327 break;328 case UnblockJobOperation:329 {330 handler = UnblockJobCompleted;331 }332 333 break;334 default:335 {336 Dbg.Assert(false, "this condition should not be hit, check the value of operation that you passed");337 }338 339 break;340 }341#pragma warning disable 56500342 try343 {344 handler?.Invoke(this, eventArgs);345 }346 catch (Exception exception)347 {348 // errors in the handlers are not errors in the operation349 // silently ignore them350 _tracer.TraceException(exception);351 }352#pragma warning restore 56500353 }354 355 /// <summary>356 /// Stop a job asynchronously.357 /// </summary>358 public abstract void StopJobAsync();359 360 /// <summary>361 /// Event to be raised when the asynchronous stopping of a job362 /// is completed.This event should not be raised for363 /// synchronous operation.364 /// </summary>365 public event EventHandler<AsyncCompletedEventArgs> StopJobCompleted;366 367 /// <summary>368 /// Suspend a job.369 /// </summary>370 public abstract void SuspendJob();371 372 /// <summary>373 /// Asynchronously suspend a job.374 /// </summary>375 public abstract void SuspendJobAsync();376 377 /// <summary>378 /// This event should be raised whenever the asynchronous suspend of379 /// a job is completed. This event should not be raised for380 /// synchronous operation.381 /// </summary>382 public event EventHandler<AsyncCompletedEventArgs> SuspendJobCompleted;383 384 /// <summary>385 /// Resume a suspended job.386 /// </summary>387 public abstract void ResumeJob();388 389 /// <summary>390 /// Resume a suspended job asynchronously.391 /// </summary>392 public abstract void ResumeJobAsync();393 394 /// <summary>395 /// This event should be raised whenever the asynchronous resume of396 /// a suspended job is completed. This event should not be raised for397 /// synchronous operation.398 /// </summary>399 public event EventHandler<AsyncCompletedEventArgs> ResumeJobCompleted;400 401 /// <summary>402 /// Unblock a blocked job.403 /// </summary>404 public abstract void UnblockJob();405 406 /// <summary>407 /// Unblock a blocked job asynchronously.408 /// </summary>409 public abstract void UnblockJobAsync();410 411 /// <summary>412 /// StopJob.413 /// </summary>414 /// <param name="force"></param>415 /// <param name="reason"></param>416 public abstract void StopJob(bool force, string reason);417 418 /// <summary>419 /// StopJobAsync.420 /// </summary>421 /// <param name="force"></param>422 /// <param name="reason"></param>423 public abstract void StopJobAsync(bool force, string reason);424 425 /// <summary>426 /// SuspendJob.427 /// </summary>428 /// <param name="force"></param>429 /// <param name="reason"></param>430 public abstract void SuspendJob(bool force, string reason);431 432 /// <summary>433 /// SuspendJobAsync.434 /// </summary>435 /// <param name="force"></param>436 /// <param name="reason"></param>437 public abstract void SuspendJobAsync(bool force, string reason);438 439 /// <summary>440 /// This event should be raised whenever the asynchronous unblock441 /// of a blocked job is completed. This event should not be raised for442 /// synchronous operation.443 /// </summary>444 public event EventHandler<AsyncCompletedEventArgs> UnblockJobCompleted;445 446 #endregion State Management447 }448 449 /// <summary>450 /// Specifies the various thread options that can be used451 /// for the ThreadBasedJob.452 /// </summary>453 public enum JobThreadOptions454 {455 /// <summary>456 /// Use the default behavior, which is to use a457 /// ThreadPoolThread.458 /// </summary>459 Default = 0,460 461 /// <summary>462 /// Use a thread pool thread.463 /// </summary>464 UseThreadPoolThread = 1,465 466 /// <summary>467 /// Create a new thread everything and reuse.468 /// </summary>469 UseNewThread = 2,470 }471 472 /*/// <summary>473 /// This job will provide asynchronous behavior by running474 /// the user specified script block in a separate process.475 /// There will be options for running the scriptblock476 /// in a new process or an existing process.477 /// </summary>478 /// <remarks>Jobs for the out-of-process activity manager479 /// can be implemented using this interface</remarks>480 public abstract class ProcessBasedJob : Job2481 {482 public override void Start()483 {484 throw new NotImplementedException();485 }486 487 public override void StartAsync()488 {489 throw new NotImplementedException();490 }491 }*/492 493 /// <summary>494 /// Top level container job.495 /// </summary>496 public sealed class ContainerParentJob : Job2497 {498 #region Private Members499 500 private const string TraceClassName = "ContainerParentJob";501 502 private bool _moreData = true;503 private readonly object _syncObject = new object();504 private int _isDisposed = 0;505 506 private const int DisposedTrue = 1;507 private const int DisposedFalse = 0;508 // This variable is set to true if at least one child job failed.509 510 // count of number of child jobs which have finished511 private int _finishedChildJobsCount = 0;512 513 // count of number of child jobs which are blocked514 private int _blockedChildJobsCount = 0;515 516 // count of number of child jobs which are suspended517 private int _suspendedChildJobsCount = 0;518 519 // count of number of child jobs which are suspending520 private int _suspendingChildJobsCount = 0;521 522 // count of number of child jobs which failed523 private int _failedChildJobsCount = 0;524 525 // count of number of child jobs which stopped526 private int _stoppedChildJobsCount = 0;527 528 private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource();529 private readonly PSDataCollection<ErrorRecord> _executionError = new PSDataCollection<ErrorRecord>();530 531 private PSEventManager _eventManager;532 533 internal PSEventManager EventManager534 {535 get536 {537 return _eventManager;538 }539 540 set541 {542 _tracer.WriteMessage("Setting event manager for Job ", InstanceId);543 _eventManager = value;544 }545 }546 547 private ManualResetEvent _jobRunning;548 549 private ManualResetEvent JobRunning550 {551 get552 {553 if (_jobRunning == null)554 {555 lock (_syncObject)556 {557 if (_jobRunning == null)558 {559 // this assert is required so that a wait handle560 // is not created after the object is disposed561 // which will result in a leak562 AssertNotDisposed();563 _jobRunning = new ManualResetEvent(false);564 }565 }566 }567 568 return _jobRunning;569 }570 }571 572 private ManualResetEvent _jobSuspendedOrAborted;573 574 private ManualResetEvent JobSuspendedOrAborted575 {576 get577 {578 if (_jobSuspendedOrAborted == null)579 {580 lock (_syncObject)581 {582 if (_jobSuspendedOrAborted == null)583 {584 // this assert is required so that a wait handle585 // is not created after the object is disposed586 // which will result in a leak587 AssertNotDisposed();588 _jobSuspendedOrAborted = new ManualResetEvent(false);589 }590 }591 }592 593 return _jobSuspendedOrAborted;594 }595 }596 597 #endregion Private Members598 599 #region Constructors600 601 /// <summary>602 /// Create a container parent job with the603 /// specified command string and name.604 /// </summary>605 /// <param name="command">Command string.</param>606 /// <param name="name">Friendly name for display.</param>607 public ContainerParentJob(string command, string name)608 : base(command, name)609 {610 StateChanged += HandleMyStateChanged;611 }612 613 /// <summary>614 /// Create a container parent job with the615 /// specified command string.616 /// </summary>617 /// <param name="command">Command string.</param>618 public ContainerParentJob(string command)619 : base(command)620 {621 StateChanged += HandleMyStateChanged;622 }623 624 /// <summary>625 /// Create a container parent job with the626 /// specified command string.627 /// </summary>628 /// <param name="command">Command string.</param>629 /// <param name="name">Friendly name for the job.</param>630 /// <param name="jobId">JobIdentifier token that allows reuse of an Id and Instance Id.</param>631 public ContainerParentJob(string command, string name, JobIdentifier jobId)632 : base(command, name, jobId)633 {634 StateChanged += HandleMyStateChanged;635 }636 637 /// <summary>638 /// Create a container parent job with the639 /// specified command string.640 /// </summary>641 /// <param name="command">Command string.</param>642 /// <param name="name">Friendly name for the job.</param>643 /// <param name="instanceId">Instance ID to allow job identification across sessions.</param>644 public ContainerParentJob(string command, string name, Guid instanceId)645 : base(command, name, instanceId)646 {647 StateChanged += HandleMyStateChanged;648 }649 650 /// <summary>651 /// Create a container parent job with the652 /// specified command string.653 /// </summary>654 /// <param name="command">Command string.</param>655 /// <param name="name">Friendly name for the job.</param>656 /// <param name="jobId">JobIdentifier token that allows reuse of an Id and Instance Id.</param>657 /// <param name="jobType">Job type name.</param>658 public ContainerParentJob(string command, string name, JobIdentifier jobId, string jobType)659 : base(command, name, jobId)660 {661 PSJobTypeName = jobType;662 StateChanged += HandleMyStateChanged;663 }664 665 /// <summary>666 /// Create a container parent job with the667 /// specified command string.668 /// </summary>669 /// <param name="command">Command string.</param>670 /// <param name="name">Friendly name for the job.</param>671 /// <param name="instanceId">Instance ID to allow job identification across sessions.</param>672 /// <param name="jobType">Job type name.</param>673 public ContainerParentJob(string command, string name, Guid instanceId, string jobType)674 : base(command, name, instanceId)675 {676 PSJobTypeName = jobType;677 StateChanged += HandleMyStateChanged;678 }679 680 /// <summary>681 /// Create a container parent job with the specified command, name,682 /// job type strings.683 /// </summary>684 /// <param name="command">Command string.</param>685 /// <param name="name">Friendly name for the job.</param>686 /// <param name="jobType">Job type name.</param>687 public ContainerParentJob(string command, string name, string jobType)688 : base(command, name)689 {690 PSJobTypeName = jobType;691 StateChanged += HandleMyStateChanged;692 }693 694 #endregion Constructors695 696 internal PSDataCollection<ErrorRecord> ExecutionError { get { return _executionError; } }697 698 #region Public Methods699 700 /// <summary>701 /// Add a child job to the parent job.702 /// </summary>703 /// <param name="childJob">Child job to add.</param>704 /// <exception cref="ObjectDisposedException">Thrown if the job is disposed.</exception>705 /// <exception cref="ArgumentNullException">Thrown if child being added is null.</exception>706 public void AddChildJob(Job2 childJob)707 {708 AssertNotDisposed();709 710 ArgumentNullException.ThrowIfNull(childJob);711 712 _tracer.WriteMessage(TraceClassName, "AddChildJob", Guid.Empty, childJob, "Adding Child to Parent with InstanceId : ", InstanceId.ToString());713 714 JobStateInfo childJobStateInfo;715 lock (childJob.syncObject)716 {717 // Store job's state and subscribe to State Changed event. Locking here will718 // ensure that the jobstateinfo we get is the state before any state changed events are handled by ContainerParentJob.719 childJobStateInfo = childJob.JobStateInfo;720 childJob.StateChanged += HandleChildJobStateChanged;721 }722 723 ChildJobs.Add(childJob);724 ParentJobStateCalculation(new JobStateEventArgs(childJobStateInfo, new JobStateInfo(JobState.NotStarted)));725 }726 727 /// <summary>728 /// Indicates if more data is available.729 /// </summary>730 /// <remarks>731 /// This has more data if any of the child jobs have more data.732 /// </remarks>733 public override bool HasMoreData734 {735 get736 {737 // moreData is initially set to true, and it738 // will remain so until the async result739 // object has completed execution.740 if (_moreData && IsFinishedState(JobStateInfo.State))741 {742 bool atleastOneChildHasMoreData = false;743 744 for (int i = 0; i < ChildJobs.Count; i++)745 {746 if (ChildJobs[i].HasMoreData)747 {748 atleastOneChildHasMoreData = true;749 break;750 }751 }752 753 _moreData = atleastOneChildHasMoreData;754 }755 756 return _moreData;757 }758 }759 760 /// <summary>761 /// Message indicating status of the job.762 /// </summary>763 public override string StatusMessage764 {765 get766 {767 return ConstructStatusMessage();768 }769 }770 771 /// <summary>772 /// Starts all jobs.773 /// </summary>774 /// <exception cref="ObjectDisposedException">Thrown if job is disposed.</exception>775 public override void StartJob()776 {777 AssertNotDisposed();778 _tracer.WriteMessage(TraceClassName, "StartJob", Guid.Empty, this, "Entering method", null);779 s_structuredTracer.BeginContainerParentJobExecution(InstanceId);780 781 // If parent contains no child jobs then this method will not respond. Throw error in this case.782 if (ChildJobs.Count == 0)783 {784 throw PSTraceSource.NewInvalidOperationException(RemotingErrorIdStrings.JobActionInvalidWithNoChildJobs);785 }786 787 foreach (Job2 job in this.ChildJobs)788 {789 if (job == null) throw PSTraceSource.NewInvalidOperationException(RemotingErrorIdStrings.JobActionInvalidWithNullChild);790 }791 792 // If there is only one child job, call the synchronous method on the child to avoid use of another thread.793 // If there are multiple, we can run them in parallel using the asynchronous versions.794 if (ChildJobs.Count == 1)795 {796 Job2 child = ChildJobs[0] as Job2;797 Dbg.Assert(child != null, "Job is null after initial null check");798#pragma warning disable 56500799 try800 {801 _tracer.WriteMessage(TraceClassName, "StartJob", Guid.Empty, this,802 "Single child job synchronously, child InstanceId: {0}", child.InstanceId.ToString());803 child.StartJob();804 JobRunning.WaitOne();805 }806 catch (Exception e)807 {808 // These exceptions are thrown by third party code. Adding them here to the collection809 // of execution errors to present consistent behavior of the object.810 811 ExecutionError.Add(new ErrorRecord(e, "ContainerParentJobStartError",812 ErrorCategory.InvalidResult, child));813 _tracer.WriteMessage(TraceClassName, "StartJob", Guid.Empty, this,814 "Single child job threw exception, child InstanceId: {0}", child.InstanceId.ToString());815 _tracer.TraceException(e);816 }817#pragma warning restore 56500818 return;819 }820 821 var completed = new AutoResetEvent(false);822 // Count of StartJobCompleted events from children.823 var startedChildJobsCount = 0;824 EventHandler<AsyncCompletedEventArgs> eventHandler = (object sender, AsyncCompletedEventArgs e) =>825 {826 var childJob = sender as Job2;827 Dbg.Assert(childJob != null,828 "StartJobCompleted only available on Job2");829 _tracer.WriteMessage(TraceClassName, "StartJob-Handler", Guid.Empty, this,830 "Finished starting child job asynchronously, child InstanceId: {0}", childJob.InstanceId.ToString());831 if (e.Error != null)832 {833 ExecutionError.Add(834 new ErrorRecord(e.Error,835 "ContainerParentJobStartError",836 ErrorCategory.InvalidResult,837 childJob));838 _tracer.WriteMessage(TraceClassName, "StartJob-Handler", Guid.Empty, this,839 "Child job asynchronously had error, child InstanceId: {0}", childJob.InstanceId.ToString());840 _tracer.TraceException(e.Error);841 }842 843 Interlocked.Increment(ref startedChildJobsCount);844 if (startedChildJobsCount == ChildJobs.Count)845 {846 _tracer.WriteMessage(TraceClassName, "StartJob-Handler", Guid.Empty, this,847 "Finished starting all child jobs asynchronously", null);848 JobRunning.WaitOne();849 completed.Set();850 }851 };852 853 foreach (Job2 job in ChildJobs)854 {855 Dbg.Assert(job != null, "Job is null after initial null check");856 857 job.StartJobCompleted += eventHandler;858 _tracer.WriteMessage(TraceClassName, "StartJob", Guid.Empty, this,859 "Child job asynchronously, child InstanceId: {0}", job.InstanceId.ToString());860 861 // This child job is created to run synchronously and so can be debugged. Set862 // the IJobDebugger.IsAsync accordingly.863 ScriptDebugger.SetDebugJobAsync(job as IJobDebugger, false);864 job.StartJobAsync();865 }866 867 completed.WaitOne();868 foreach (Job2 job in ChildJobs)869 {870 Dbg.Assert(job != null, "Job is null after initial null check");871 872 job.StartJobCompleted -= eventHandler;873 }874 875 /*876 if (ExecutionError.Count > 0)877 {878 // Check to see expected behavior if one child job fails to start.879 }880 881 if (ExecutionError.Count == 1)882 {883 throw ExecutionError[0];884 } */885 _tracer.WriteMessage(TraceClassName, "StartJob", Guid.Empty, this, "Exiting method", null);886 }887 888 private static readonly Tracer s_structuredTracer = new Tracer();889 890 /// <summary>891 /// Starts all child jobs asynchronously.892 /// When all child jobs are started, StartJobCompleted event is raised.893 /// </summary>894 public override void StartJobAsync()895 {896 if (_isDisposed == DisposedTrue)897 {898 OnStartJobCompleted(new AsyncCompletedEventArgs(new ObjectDisposedException(TraceClassName), false, null));899 return;900 }901 902 _tracer.WriteMessage(TraceClassName, "StartJobAsync", Guid.Empty, this, "Entering method", null);903 s_structuredTracer.BeginContainerParentJobExecution(InstanceId);904 foreach (Job2 job in this.ChildJobs)905 {906 if (job == null) throw PSTraceSource.NewInvalidOperationException(RemotingErrorIdStrings.JobActionInvalidWithNullChild);907 }908 909 // Count of StartJobCompleted events from children.910 var startedChildJobsCount = 0;911 EventHandler<AsyncCompletedEventArgs> eventHandler = null;912 eventHandler = (sender, e) =>913 {914 var childJob = sender as Job2;915 Dbg.Assert(childJob != null, "StartJobCompleted only available on Job2");916 _tracer.WriteMessage(TraceClassName, "StartJobAsync-Handler", Guid.Empty, this,917 "Finished starting child job asynchronously, child InstanceId: {0}", childJob.InstanceId.ToString());918 if (e.Error != null)919 {920 ExecutionError.Add(new ErrorRecord(e.Error, "ContainerParentJobStartAsyncError",921 ErrorCategory.InvalidResult, childJob));922 _tracer.WriteMessage(TraceClassName, "StartJobAsync-Handler", Guid.Empty, this,923 "Child job asynchronously had error, child InstanceId: {0}", childJob.InstanceId.ToString());924 _tracer.TraceException(e.Error);925 }926 927 Interlocked.Increment(ref startedChildJobsCount);928 Dbg.Assert(eventHandler != null, "Event handler magically disappeared");929 childJob.StartJobCompleted -= eventHandler;930 931 if (startedChildJobsCount == ChildJobs.Count)932 {933 _tracer.WriteMessage(TraceClassName, "StartJobAsync-Handler", Guid.Empty, this,934 "Finished starting all child jobs asynchronously", null);935 936 JobRunning.WaitOne();937 // There may be multiple exceptions raised. They938 // are stored in the Error stream of this job object, which is otherwise939 // unused.940 OnStartJobCompleted(new AsyncCompletedEventArgs(null, false, null));941 }942 };943 944 foreach (Job2 job in ChildJobs)945 {946 Dbg.Assert(job != null, "Job is null after initial null check");947 job.StartJobCompleted += eventHandler;948 949 _tracer.WriteMessage(TraceClassName, "StartJobAsync", Guid.Empty, this,950 "Child job asynchronously, child InstanceId: {0}", job.InstanceId.ToString());951 job.StartJobAsync();952 }953 954 _tracer.WriteMessage(TraceClassName, "StartJobAsync", Guid.Empty, this, "Exiting method", null);955 }956 957 /// <summary>958 /// Resume all jobs.959 /// </summary>960 /// <exception cref="ObjectDisposedException">Thrown if job is disposed.</exception>961 public override void ResumeJob()962 {963 AssertNotDisposed();964 _tracer.WriteMessage(TraceClassName, "ResumeJob", Guid.Empty, this, "Entering method", null);965 966 // If parent contains no child jobs then this method will not respond. Throw error in this case.967 if (ChildJobs.Count == 0)968 {969 throw PSTraceSource.NewInvalidOperationException(RemotingErrorIdStrings.JobActionInvalidWithNoChildJobs);970 }971 972 foreach (Job2 job in this.ChildJobs)973 {974 if (job == null) throw PSTraceSource.NewInvalidOperationException(RemotingErrorIdStrings.JobActionInvalidWithNullChild);975 }976 977 // If there is only one child job, call the synchronous method on the child to avoid use of another thread.978 // If there are multiple, we can run them in parallel using the asynchronous versions.979 if (ChildJobs.Count == 1)980 {981 Job2 child = ChildJobs[0] as Job2;982 Dbg.Assert(child != null, "Job is null after initial null check");983#pragma warning disable 56500984 try985 {986 _tracer.WriteMessage(TraceClassName, "ResumeJob", Guid.Empty, this,987 "Single child job synchronously, child InstanceId: {0}", child.InstanceId.ToString());988 child.ResumeJob();989 JobRunning.WaitOne();990 }991 catch (Exception e)992 {993 // These exceptions are thrown by third party code. Adding them here to the collection994 // of execution errors to present consistent behavior of the object.995 996 ExecutionError.Add(new ErrorRecord(e, "ContainerParentJobResumeError",997 ErrorCategory.InvalidResult, child));998 _tracer.WriteMessage(TraceClassName, "ResumeJob", Guid.Empty, this,999 "Single child job threw exception, child InstanceId: {0}", child.InstanceId.ToString());1000 _tracer.TraceException(e);1001 }1002#pragma warning restore 565001003 return;1004 }1005 1006 var completed = new AutoResetEvent(false);1007 // Count of ResumeJobCompleted events from children.1008 var resumedChildJobsCount = 0;1009 EventHandler<AsyncCompletedEventArgs> eventHandler = null;1010 foreach (Job2 job in ChildJobs)1011 {1012 Dbg.Assert(job != null, "Job is null after initial null check");1013 1014 eventHandler = (object sender, AsyncCompletedEventArgs e) =>1015 {1016 var childJob = sender as Job2;1017 Dbg.Assert(childJob != null, "ResumeJobCompleted only available on Job2");1018 _tracer.WriteMessage(TraceClassName, "ResumeJob-Handler", Guid.Empty, this,1019 "Finished resuming child job asynchronously, child InstanceId: {0}", job.InstanceId.ToString());1020 if (e.Error != null)1021 {1022 ExecutionError.Add(new ErrorRecord(e.Error, "ContainerParentJobResumeError",1023 ErrorCategory.InvalidResult, job));1024 _tracer.WriteMessage(TraceClassName, "ResumeJob-Handler", Guid.Empty, this,1025 "Child job asynchronously had error, child InstanceId: {0}", job.InstanceId.ToString());1026 _tracer.TraceException(e.Error);1027 }1028 1029 Interlocked.Increment(ref resumedChildJobsCount);1030 if (resumedChildJobsCount == ChildJobs.Count)1031 {1032 _tracer.WriteMessage(TraceClassName, "ResumeJob-Handler", Guid.Empty, this,1033 "Finished resuming all child jobs asynchronously", null);1034 JobRunning.WaitOne();1035 completed.Set();1036 }1037 };1038 job.ResumeJobCompleted += eventHandler;1039 _tracer.WriteMessage(TraceClassName, "ResumeJob", Guid.Empty, this,1040 "Child job asynchronously, child InstanceId: {0}", job.InstanceId.ToString());1041 job.ResumeJobAsync();1042 }1043 1044 completed.WaitOne();1045 Dbg.Assert(eventHandler != null, "Event handler magically disappeared");1046 foreach (Job2 job in ChildJobs)1047 {1048 Dbg.Assert(job != null, "Job is null after initial null check");1049 1050 job.ResumeJobCompleted -= eventHandler;1051 }1052 1053 _tracer.WriteMessage(TraceClassName, "ResumeJob", Guid.Empty, this, "Exiting method", null);1054 1055 // Errors are taken from the Error collection by the cmdlet for ContainerParentJob.1056 }1057 1058 /// <summary>1059 /// Resume all jobs asynchronously.1060 /// </summary>1061 public override void ResumeJobAsync()1062 {1063 if (_isDisposed == DisposedTrue)1064 {1065 OnResumeJobCompleted(new AsyncCompletedEventArgs(new ObjectDisposedException(TraceClassName), false, null));1066 return;1067 }1068 1069 _tracer.WriteMessage(TraceClassName, "ResumeJobAsync", Guid.Empty, this, "Entering method", null);1070 foreach (Job2 job in this.ChildJobs)1071 {1072 if (job == null) throw PSTraceSource.NewInvalidOperationException(RemotingErrorIdStrings.JobActionInvalidWithNullChild);1073 }1074 1075 // Count of ResumeJobCompleted events from children.1076 var resumedChildJobsCount = 0;1077 foreach (Job2 job in ChildJobs)1078 {1079 Dbg.Assert(job != null, "Job is null after initial null check");1080 1081 EventHandler<AsyncCompletedEventArgs> eventHandler = null;1082 eventHandler = (sender, e) =>1083 {1084 var childJob = sender as Job2;1085 Dbg.Assert(childJob != null, "ResumeJobCompleted only available on Job2");1086 _tracer.WriteMessage(TraceClassName, "ResumeJobAsync-Handler", Guid.Empty, this,1087 "Finished resuming child job asynchronously, child InstanceId: {0}", job.InstanceId.ToString());1088 if (e.Error != null)1089 {1090 ExecutionError.Add(new ErrorRecord(e.Error, "ContainerParentJobResumeAsyncError",1091 ErrorCategory.InvalidResult, job));1092 _tracer.WriteMessage(TraceClassName, "ResumeJobAsync-Handler", Guid.Empty, this,1093 "Child job asynchronously had error, child InstanceId: {0}", job.InstanceId.ToString());1094 _tracer.TraceException(e.Error);1095 }1096 1097 Interlocked.Increment(ref resumedChildJobsCount);1098 Dbg.Assert(eventHandler != null, "Event handler magically disappeared");1099 childJob.ResumeJobCompleted -= eventHandler;1100 if (resumedChildJobsCount == ChildJobs.Count)1101 {1102 _tracer.WriteMessage(TraceClassName, "ResumeJobAsync-Handler", Guid.Empty, this,1103 "Finished resuming all child jobs asynchronously", null);1104 1105 JobRunning.WaitOne();1106 // There may be multiple exceptions raised. They1107 // are stored in the Error stream of this job object, which is otherwise1108 // unused.1109 OnResumeJobCompleted(new AsyncCompletedEventArgs(null, false, null));1110 }1111 };1112 job.ResumeJobCompleted += eventHandler;1113 _tracer.WriteMessage(TraceClassName, "ResumeJobAsync", Guid.Empty, this,1114 "Child job asynchronously, child InstanceId: {0}", job.InstanceId.ToString());1115 job.ResumeJobAsync();1116 }1117 1118 _tracer.WriteMessage(TraceClassName, "ResumeJobAsync", Guid.Empty, this, "Exiting method", null);1119 }1120 1121 /// <summary>1122 /// Suspends all jobs.1123 /// </summary>1124 /// <exception cref="ObjectDisposedException">Thrown if job is disposed.</exception>1125 public override void SuspendJob()1126 {1127 SuspendJobInternal(null, null);1128 }1129 1130 /// <summary>1131 /// Suspends all jobs forcefully.1132 /// </summary>1133 /// <param name="force">Force flag for suspending forcefully.</param>1134 /// <param name="reason">Reason for doing forceful suspend.</param>1135 public override void SuspendJob(bool force, string reason)1136 {1137 SuspendJobInternal(force, reason);1138 }1139 1140 /// <summary>1141 /// Suspends all jobs asynchronously.1142 /// When all jobs have been suspended, SuspendJobCompleted is raised.1143 /// </summary>1144 public override void SuspendJobAsync()1145 {1146 SuspendJobAsyncInternal(null, null);1147 }1148 1149 /// <summary>1150 /// Suspends all jobs asynchronously with force flag.1151 /// When all jobs have been suspended, SuspendJobCompleted is raised.1152 /// </summary>1153 /// <param name="force">Force flag for suspending forcefully.</param>1154 /// <param name="reason">Reason for doing forceful suspend.</param>1155 public override void SuspendJobAsync(bool force, string reason)1156 {1157 SuspendJobAsyncInternal(force, reason);1158 }1159 1160 /// <summary>1161 /// Stop all child jobs.1162 /// </summary>1163 public override void StopJob()1164 {1165 StopJobInternal(null, null);1166 }1167 1168 /// <summary>1169 /// Stops all child jobs asynchronously.1170 /// Once all child jobs are stopped, StopJobCompleted event is raised.1171 /// </summary>1172 public override void StopJobAsync()1173 {1174 StopJobAsyncInternal(null, null);1175 }1176 1177 /// <summary>1178 /// StopJob.1179 /// </summary>1180 /// <param name="force"></param>1181 /// <param name="reason"></param>1182 public override void StopJob(bool force, string reason)1183 {1184 StopJobInternal(force, reason);1185 }1186 1187 /// <summary>1188 /// StopJobAsync.1189 /// </summary>1190 /// <param name="force"></param>1191 /// <param name="reason"></param>1192 public override void StopJobAsync(bool force, string reason)1193 {1194 StopJobAsyncInternal(force, reason);1195 }1196 1197 /// <summary>1198 /// Unblock all child jobs.1199 /// </summary>1200 /// <exception cref="ObjectDisposedException">Thrown if job is disposed.</exception>