MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Management.Automation.Internal;7using System.Management.Automation.Security;8using System.Management.Automation.Tracing;9using System.Threading;10 11using Dbg = System.Management.Automation.Diagnostics;12using PSHost = System.Management.Automation.Host.PSHost;13 14namespace System.Management.Automation.Runspaces.Internal15{16 /// <summary>17 /// Class which supports pooling local powerShell runspaces.18 /// </summary>19 internal class RunspacePoolInternal : IDisposable20 {21 #region Private data22 23 protected int maxPoolSz;24 protected int minPoolSz;25 // we need total active runspaces to avoid lock() statements everywhere26 protected int totalRunspaces;27 protected List<Runspace> runspaceList = new List<Runspace>(); // info of all the runspaces in the pool.28 protected Stack<Runspace> pool; // stack of runspaces that are available.29 protected Queue<GetRunspaceAsyncResult> runspaceRequestQueue; // request queue.30 // let requesters request on the runspaceRequestQueue..internally31 // pool services on this queue.32 protected Queue<GetRunspaceAsyncResult> ultimateRequestQueue;33 protected RunspacePoolStateInfo stateInfo;34 protected InitialSessionState _initialSessionState;35 protected PSHost host;36 protected Guid instanceId;37 private bool _isDisposed;38 protected bool isServicingRequests;39 protected object syncObject = new object();40 41 private static readonly TimeSpan s_defaultCleanupPeriod = new TimeSpan(0, 15, 0); // 15 minutes.42 private TimeSpan _cleanupInterval;43 private readonly Timer _cleanupTimer;44 45 #endregion46 47 #region Constructor48 49 /// <summary>50 /// Constructor which creates a RunspacePool using the51 /// supplied <paramref name="configuration"/>, <paramref name="minRunspaces"/>52 /// and <paramref name="maxRunspaces"/>53 /// </summary>54 /// <param name="maxRunspaces">55 /// The maximum number of Runspaces that can exist in this pool.56 /// Should be greater than or equal to 1.57 /// </param>58 /// <param name="minRunspaces">59 /// The minimum number of Runspaces that can exist in this pool.60 /// Should be greater than or equal to 1.61 /// </param>62 /// <param name="host">63 /// The explicit PSHost implementation.64 /// </param>65 /// <exception cref="ArgumentNullException">66 /// Host is null.67 /// </exception>68 /// <exception cref="ArgumentException">69 /// Maximum runspaces is less than 1.70 /// Minimum runspaces is less than 1.71 /// </exception>72 public RunspacePoolInternal(int minRunspaces,73 int maxRunspaces,74 PSHost host)75 : this(minRunspaces, maxRunspaces)76 {77 if (host == null)78 {79 throw PSTraceSource.NewArgumentNullException(nameof(host));80 }81 82 this.host = host;83 pool = new Stack<Runspace>();84 runspaceRequestQueue = new Queue<GetRunspaceAsyncResult>();85 ultimateRequestQueue = new Queue<GetRunspaceAsyncResult>();86 _initialSessionState = InitialSessionState.CreateDefault();87 }88 89 /// <summary>90 /// Constructor which creates a RunspacePool using the91 /// supplied <paramref name="configuration"/>, <paramref name="minRunspaces"/>92 /// and <paramref name="maxRunspaces"/>93 /// </summary>94 /// <param name="initialSessionState">95 /// InitialSessionState to use when creating a new Runspace.96 /// </param>97 /// <param name="maxRunspaces">98 /// The maximum number of Runspaces that can exist in this pool.99 /// Should be greater than or equal to 1.100 /// </param>101 /// <param name="minRunspaces">102 /// The minimum number of Runspaces that can exist in this pool.103 /// Should be greater than or equal to 1.104 /// </param>105 /// <param name="host">106 /// The explicit PSHost implementation.107 /// </param>108 /// <exception cref="ArgumentNullException">109 /// initialSessionState is null.110 /// Host is null.111 /// </exception>112 /// <exception cref="ArgumentException">113 /// Maximum runspaces is less than 1.114 /// Minimum runspaces is less than 1.115 /// </exception>116 public RunspacePoolInternal(int minRunspaces,117 int maxRunspaces,118 InitialSessionState initialSessionState,119 PSHost host)120 : this(minRunspaces, maxRunspaces)121 {122 if (initialSessionState == null)123 {124 throw PSTraceSource.NewArgumentNullException(nameof(initialSessionState));125 }126 127 if (host == null)128 {129 throw PSTraceSource.NewArgumentNullException(nameof(host));130 }131 132 _initialSessionState = initialSessionState.Clone();133 this.host = host;134 ThreadOptions = initialSessionState.ThreadOptions;135 this.ApartmentState = initialSessionState.ApartmentState;136 pool = new Stack<Runspace>();137 runspaceRequestQueue = new Queue<GetRunspaceAsyncResult>();138 ultimateRequestQueue = new Queue<GetRunspaceAsyncResult>();139 }140 141 /// <summary>142 /// Constructor for doing common initialization between143 /// this class and its derivatives.144 /// </summary>145 /// <param name="maxRunspaces">146 /// The maximum number of Runspaces that can exist in this pool.147 /// Should be greater than or equal to 1.148 /// </param>149 /// <param name="minRunspaces">150 /// The minimum number of Runspaces that can exist in this pool.151 /// Should be greater than or equal to 1.152 /// </param>153 protected RunspacePoolInternal(int minRunspaces, int maxRunspaces)154 {155 if (maxRunspaces < 1)156 {157 throw PSTraceSource.NewArgumentException(nameof(maxRunspaces), RunspacePoolStrings.MaxPoolLessThan1);158 }159 160 if (minRunspaces < 1)161 {162 throw PSTraceSource.NewArgumentException(nameof(minRunspaces), RunspacePoolStrings.MinPoolLessThan1);163 }164 165 if (minRunspaces > maxRunspaces)166 {167 throw PSTraceSource.NewArgumentException(nameof(minRunspaces), RunspacePoolStrings.MinPoolGreaterThanMaxPool);168 }169 170 maxPoolSz = maxRunspaces;171 minPoolSz = minRunspaces;172 stateInfo = new RunspacePoolStateInfo(RunspacePoolState.BeforeOpen, null);173 instanceId = Guid.NewGuid();174 PSEtwLog.SetActivityIdForCurrentThread(instanceId);175 176 _cleanupInterval = s_defaultCleanupPeriod;177 _cleanupTimer = new Timer(new TimerCallback(CleanupCallback), null, Timeout.Infinite, Timeout.Infinite);178 }179 180 /// <summary>181 /// Default constructor.182 /// </summary>183 internal RunspacePoolInternal() { }184 185 #endregion186 187 #region Public Properties188 189 /// <summary>190 /// Get unique id for this instance of runspace pool. It is primarily used191 /// for logging purposes.192 /// </summary>193 public Guid InstanceId194 {195 get196 {197 return instanceId;198 }199 }200 201 /// <summary>202 /// Gets a boolean which describes if the runspace pool is disposed.203 /// </summary>204 public bool IsDisposed205 {206 get207 {208 return _isDisposed;209 }210 }211 212 /// <summary>213 /// Gets State of the current runspace pool.214 /// </summary>215 public RunspacePoolStateInfo RunspacePoolStateInfo216 {217 get218 {219 return stateInfo;220 }221 }222 223 /// <summary>224 /// Private data to be used by applications built on top of PowerShell.225 ///226 /// Local runspace pool is created with application private data set to an empty <see cref="PSPrimitiveDictionary"/>.227 /// </summary>228 internal virtual PSPrimitiveDictionary GetApplicationPrivateData()229 {230 if (_applicationPrivateData == null)231 {232 lock (this.syncObject)233 {234 _applicationPrivateData ??= new PSPrimitiveDictionary();235 }236 }237 238 return _applicationPrivateData;239 }240 241 internal virtual void PropagateApplicationPrivateData(Runspace runspace)242 {243 runspace.SetApplicationPrivateData(this.GetApplicationPrivateData());244 }245 246 private PSPrimitiveDictionary _applicationPrivateData;247 248 /// <summary>249 /// Gets the InitialSessionState object that this pool uses250 /// to create the runspaces.251 /// </summary>252 public InitialSessionState InitialSessionState253 {254 get255 {256 return _initialSessionState;257 }258 }259 260 /// <summary>261 /// The connection associated with this runspace pool.262 /// </summary>263 public virtual RunspaceConnectionInfo ConnectionInfo264 {265 get266 {267 return null;268 }269 }270 271 /// <summary>272 /// Specifies how often unused runspaces are disposed.273 /// </summary>274 public TimeSpan CleanupInterval275 {276 get277 {278 return _cleanupInterval;279 }280 281 set282 {283 lock (this.syncObject)284 {285 _cleanupInterval = value;286 }287 }288 }289 290 /// <summary>291 /// Returns runspace pool availability.292 /// </summary>293 public virtual RunspacePoolAvailability RunspacePoolAvailability294 {295 get296 {297 return (stateInfo.State == RunspacePoolState.Opened) ?298 RunspacePoolAvailability.Available :299 RunspacePoolAvailability.None;300 }301 }302 303 #endregion304 305 #region events306 307 /// <summary>308 /// Event raised when RunspacePoolState changes.309 /// </summary>310 public event EventHandler<RunspacePoolStateChangedEventArgs> StateChanged;311 312 /// <summary>313 /// Event raised when one of the runspaces in the pool forwards an event to this instance.314 /// </summary>315 public event EventHandler<PSEventArgs> ForwardEvent;316 317 /// <summary>318 /// Event raised when a new Runspace is created by the pool.319 /// </summary>320 internal event EventHandler<RunspaceCreatedEventArgs> RunspaceCreated;321 322 #endregion events323 324 #region Disconnect-Connect Methods325 326 /// <summary>327 /// Synchronously disconnect runspace pool.328 /// </summary>329 public virtual void Disconnect()330 {331 throw PSTraceSource.NewInvalidOperationException(RunspacePoolStrings.RunspaceDisconnectConnectNotSupported);332 }333 334 /// <summary>335 /// Asynchronously disconnect runspace pool.336 /// </summary>337 /// <param name="callback"></param>338 /// <param name="state"></param>339 /// <returns></returns>340 public virtual IAsyncResult BeginDisconnect(AsyncCallback callback, object state)341 {342 throw PSTraceSource.NewInvalidOperationException(RunspacePoolStrings.RunspaceDisconnectConnectNotSupported);343 }344 345 /// <summary>346 /// Wait for BeginDisconnect to complete.347 /// </summary>348 /// <param name="asyncResult"></param>349 public virtual void EndDisconnect(IAsyncResult asyncResult)350 {351 throw PSTraceSource.NewInvalidOperationException(RunspacePoolStrings.RunspaceDisconnectConnectNotSupported);352 }353 354 /// <summary>355 /// Synchronously connect runspace pool.356 /// </summary>357 public virtual void Connect()358 {359 throw PSTraceSource.NewInvalidOperationException(RunspacePoolStrings.RunspaceDisconnectConnectNotSupported);360 }361 362 /// <summary>363 /// Asynchronously connect runspace pool.364 /// </summary>365 /// <param name="callback"></param>366 /// <param name="state"></param>367 /// <returns></returns>368 public virtual IAsyncResult BeginConnect(AsyncCallback callback, object state)369 {370 throw PSTraceSource.NewInvalidOperationException(RunspacePoolStrings.RunspaceDisconnectConnectNotSupported);371 }372 373 /// <summary>374 /// Wait for BeginConnect to complete.375 /// </summary>376 /// <param name="asyncResult"></param>377 public virtual void EndConnect(IAsyncResult asyncResult)378 {379 throw PSTraceSource.NewInvalidOperationException(RunspacePoolStrings.RunspaceDisconnectConnectNotSupported);380 }381 382 /// <summary>383 /// Creates an array of PowerShell objects that are in the Disconnected state for384 /// all currently disconnected running commands associated with this runspace pool.385 /// </summary>386 /// <returns></returns>387 public virtual Collection<PowerShell> CreateDisconnectedPowerShells(RunspacePool runspacePool)388 {389 throw PSTraceSource.NewInvalidOperationException(RunspacePoolStrings.RunspaceDisconnectConnectNotSupported);390 }391 392 /// <summary>393 /// Returns RunspacePool capabilities.394 /// </summary>395 /// <returns>RunspacePoolCapability.</returns>396 public virtual RunspacePoolCapability GetCapabilities()397 {398 return RunspacePoolCapability.Default;399 }400 401 #endregion402 403 #region Public Methods404 405 /// <summary>406 /// Resets the runspace state on a runspace pool with a single407 /// runspace.408 /// This is currently supported *only* for remote runspaces.409 /// </summary>410 /// <returns>True if successful.</returns>411 internal virtual bool ResetRunspaceState()412 {413 throw new PSNotSupportedException();414 }415 416 /// <summary>417 /// Sets the maximum number of Runspaces that can be active concurrently418 /// in the pool. All requests above that number remain queued until419 /// runspaces become available.420 /// </summary>421 /// <param name="maxRunspaces">422 /// The maximum number of runspaces in the pool.423 /// </param>424 /// <returns>425 /// true if the change is successful; otherwise, false.426 /// </returns>427 /// <remarks>428 /// You cannot set the number of runspaces to a number smaller than429 /// the minimum runspaces.430 /// </remarks>431 internal virtual bool SetMaxRunspaces(int maxRunspaces)432 {433 bool isSizeIncreased = false;434 435 lock (pool)436 {437 if (maxRunspaces < this.minPoolSz)438 {439 return false;440 }441 442 if (maxRunspaces > this.maxPoolSz)443 {444 isSizeIncreased = true;445 }446 else447 {448 // since maxrunspaces limit is decreased449 // destroy unwanted runspaces from the top450 // of the pool.451 while (pool.Count > maxRunspaces)452 {453 Runspace rsToDestroy = pool.Pop();454 DestroyRunspace(rsToDestroy);455 }456 }457 458 maxPoolSz = maxRunspaces;459 }460 461 // pool size is incremented.. check if we can release462 // some requests.463 if (isSizeIncreased)464 {465 EnqueueCheckAndStartRequestServicingThread(null, false);466 }467 468 return true;469 }470 471 /// <summary>472 /// Retrieves the maximum number of runspaces the pool maintains.473 /// </summary>474 /// <returns>475 /// The maximum number of runspaces in the pool476 /// </returns>477 public int GetMaxRunspaces()478 {479 return maxPoolSz;480 }481 482 /// <summary>483 /// Sets the minimum number of Runspaces that the pool maintains484 /// in anticipation of new requests.485 /// </summary>486 /// <param name="minRunspaces">487 /// The minimum number of runspaces in the pool.488 /// </param>489 /// <returns>490 /// true if the change is successful; otherwise, false.491 /// </returns>492 /// <remarks>493 /// You cannot set the number of idle runspaces to a number smaller than494 /// 1 or greater than maximum number of active runspaces.495 /// </remarks>496 internal virtual bool SetMinRunspaces(int minRunspaces)497 {498 lock (pool)499 {500 if ((minRunspaces < 1) || (minRunspaces > this.maxPoolSz))501 {502 return false;503 }504 505 minPoolSz = minRunspaces;506 }507 508 return true;509 }510 511 /// <summary>512 /// Retrieves the minimum number of runspaces the pool maintains.513 /// </summary>514 /// <returns>515 /// The minimum number of runspaces in the pool516 /// </returns>517 public int GetMinRunspaces()518 {519 return minPoolSz;520 }521 522 /// <summary>523 /// Retrieves the number of runspaces available at the time of calling524 /// this method.525 /// </summary>526 /// <exception cref="ArgumentException">527 /// If the RunspacePool failed or has been closed528 /// </exception>529 /// <returns>530 /// The number of available runspace in the pool.531 /// </returns>532 internal virtual int GetAvailableRunspaces()533 {534 // Dont allow state changes while we get the count535 lock (syncObject)536 {537 if (stateInfo.State == RunspacePoolState.Opened)538 {539 // Win8: 169492 RunspacePool can report that there are negative runspaces available.540 // totalRunspaces represents all the runspaces that were ever created by ths RunspacePool541 // pool.Count represents the runspaces that are currently available542 // maxPoolSz represents the total capacity w.r.t runspaces for this RunspacePool543 // Once the RunspacePool allocates a runspace to a consumer, RunspacePool cannot reclaim the544 // runspace until the consumer released the runspace back to the pool. A SetMaxRunspaces()545 // call can arrive before the runspace is released..It is bad to make SetMaxRunspaces()546 // wait for the consumers to release runspaces, so we let SetMaxRunspaces() go by changing547 // maxPoolSz. Because of this there may be cases where maxPoolSz - totalRunspaces will become548 // less than 0.549 int unUsedCapacity = (maxPoolSz - totalRunspaces) < 0 ? 0 : (maxPoolSz - totalRunspaces);550 return (pool.Count + unUsedCapacity);551 }552 else if (stateInfo.State == RunspacePoolState.Disconnected)553 {554 throw new InvalidOperationException(RunspacePoolStrings.CannotWhileDisconnected);555 }556 else if (stateInfo.State != RunspacePoolState.BeforeOpen && stateInfo.State != RunspacePoolState.Opening)557 {558 throw new InvalidOperationException(HostInterfaceExceptionsStrings.RunspacePoolNotOpened);559 }560 else561 {562 return maxPoolSz;563 }564 }565 }566 567 /// <summary>568 /// Opens the runspacepool synchronously. RunspacePool must569 /// be opened before it can be used.570 /// </summary>571 /// <exception cref="InvalidRunspacePoolStateException">572 /// RunspacePoolState is not BeforeOpen573 /// </exception>574 public virtual void Open()575 {576 CoreOpen(false, null, null);577 }578 579 /// <summary>580 /// Opens the RunspacePool asynchronously. RunspacePool must581 /// be opened before it can be used.582 /// To get the exceptions that might have occurred, call583 /// EndOpen.584 /// </summary>585 /// <param name="callback">586 /// A AsyncCallback to call once the BeginOpen completes.587 /// </param>588 /// <param name="state">589 /// A user supplied state to call the <paramref name="callback"/>590 /// with.591 /// </param>592 /// <returns>593 /// An AsyncResult object to monitor the state of the async594 /// operation.595 /// </returns>596 public IAsyncResult BeginOpen(AsyncCallback callback, object state)597 {598 return CoreOpen(true, callback, state);599 }600 601 /// <summary>602 /// Waits for the pending asynchronous BeginOpen to complete.603 /// </summary>604 /// <exception cref="ArgumentNullException">605 /// asyncResult is a null reference.606 /// </exception>607 /// <exception cref="ArgumentException">608 /// asyncResult object was not created by calling BeginOpen609 /// on this runspacepool instance.610 /// </exception>611 /// <exception cref="InvalidRunspacePoolStateException">612 /// RunspacePoolState is not BeforeOpen.613 /// </exception>614 /// <remarks>615 /// TODO: Behavior if EndOpen is called multiple times.616 /// </remarks>617 public void EndOpen(IAsyncResult asyncResult)618 {619 if (asyncResult == null)620 {621 throw PSTraceSource.NewArgumentNullException(nameof(asyncResult));622 }623 624 RunspacePoolAsyncResult rsAsyncResult = asyncResult as RunspacePoolAsyncResult;625 626 if ((rsAsyncResult == null) ||627 (rsAsyncResult.OwnerId != instanceId) ||628 (!rsAsyncResult.IsAssociatedWithAsyncOpen))629 {630 throw PSTraceSource.NewArgumentException(nameof(asyncResult),631 RunspacePoolStrings.AsyncResultNotOwned,632 "IAsyncResult",633 "BeginOpen");634 }635 636 rsAsyncResult.EndInvoke();637 }638 639 /// <summary>640 /// Closes the RunspacePool and cleans all the internal641 /// resources. This will close all the runspaces in the642 /// runspacepool and release all the async operations643 /// waiting for a runspace. If the pool is already closed644 /// or broken or closing this will just return.645 /// </summary>646 public virtual void Close()647 {648 CoreClose(false, null, null);649 }650 651 /// <summary>652 /// Closes the RunspacePool asynchronously and cleans all the internal653 /// resources. This will close all the runspaces in the654 /// runspacepool and release all the async operations655 /// waiting for a runspace. If the pool is already closed656 /// or broken or closing this will just return.657 /// </summary>658 /// <param name="callback">659 /// A AsyncCallback to call once the BeginClose completes.660 /// </param>661 /// <param name="state">662 /// A user supplied state to call the <paramref name="callback"/>663 /// with.664 /// </param>665 /// <returns>666 /// An AsyncResult object to monitor the state of the async667 /// operation.668 /// </returns>669 public virtual IAsyncResult BeginClose(AsyncCallback callback, object state)670 {671 return CoreClose(true, callback, state);672 }673 674 /// <summary>675 /// Waits for the pending asynchronous BeginClose to complete.676 /// </summary>677 /// <exception cref="ArgumentException">678 /// asyncResult object was not created by calling BeginClose679 /// on this runspacepool instance.680 /// </exception>681 /// <remarks>682 /// TODO: Behavior if EndClose is called multiple times.683 /// </remarks>684 public virtual void EndClose(IAsyncResult asyncResult)685 {686 if (asyncResult == null)687 {688 throw PSTraceSource.NewArgumentNullException(nameof(asyncResult));689 }690 691 RunspacePoolAsyncResult rsAsyncResult = asyncResult as RunspacePoolAsyncResult;692 693 if ((rsAsyncResult == null) ||694 (rsAsyncResult.OwnerId != instanceId) ||695 (rsAsyncResult.IsAssociatedWithAsyncOpen))696 {697 throw PSTraceSource.NewArgumentException(nameof(asyncResult),698 RunspacePoolStrings.AsyncResultNotOwned,699 "IAsyncResult",700 "BeginClose");701 }702 703 rsAsyncResult.EndInvoke();704 }705 706 /// <summary>707 /// Gets a Runspace from the pool. If no free runspace is available708 /// and if max pool size is not reached, a new runspace is created.709 /// Otherwise this will block a runspace is released and available.710 /// </summary>711 /// <returns>712 /// An opened Runspace.713 /// </returns>714 /// <exception cref="InvalidRunspacePoolStateException">715 /// Cannot perform operation because RunspacePool is716 /// not in the opened state.717 /// </exception>718 public Runspace GetRunspace()719 {720 AssertPoolIsOpen();721 // Get the runspace asynchronously.722 GetRunspaceAsyncResult asyncResult = (GetRunspaceAsyncResult)BeginGetRunspace(null, null);723 // Wait for async operation to complete.724 asyncResult.AsyncWaitHandle.WaitOne();725 726 // throw the exception that occurred while727 // processing the async operation728 if (asyncResult.Exception != null)729 {730 throw asyncResult.Exception;731 }732 733 return asyncResult.Runspace;734 }735 736 /// <summary>737 /// Releases a Runspace to the pool. If pool is closed, this738 /// will be a no-op.739 /// </summary>740 /// <param name="runspace">741 /// Runspace to release to the pool.742 /// </param>743 /// <exception cref="ArgumentException">744 /// <paramref name="runspace"/> is null.745 /// </exception>746 /// <exception cref="InvalidRunspacePoolStateException">747 /// Runspool is not in Opened state.748 /// </exception>749 /// <exception cref="InvalidOperationException">750 /// Cannot release the runspace to this pool as the runspace751 /// doesn't belong to this pool.752 /// </exception>753 public void ReleaseRunspace(Runspace runspace)754 {755 if (runspace == null)756 {757 throw PSTraceSource.NewArgumentNullException(nameof(runspace));758 }759 760 AssertPoolIsOpen();761 762 bool isRunspaceReleased = false;763 bool destroyRunspace = false;764 765 // check if the runspace is owned by the pool766 lock (runspaceList)767 {768 if (!runspaceList.Contains(runspace))769 {770 throw PSTraceSource.NewInvalidOperationException(RunspacePoolStrings.RunspaceNotBelongsToPool);771 }772 }773 774 // Release this runspace only if it is in valid state and is775 // owned by this pool.776 if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)777 {778 lock (pool)779 {780 if (pool.Count < maxPoolSz)781 {782 isRunspaceReleased = true;783 pool.Push(runspace);784 }785 else786 {787 // this runspace is not going to be pooled as maxPoolSz is reduced.788 // so release the runspace and destroy it.789 isRunspaceReleased = true;790 destroyRunspace = true;791 }792 }793 }794 else795 {796 destroyRunspace = true;797 isRunspaceReleased = true;798 }799 800 if (destroyRunspace)801 {802 // Destroying a runspace might be costly.803 // so doing this outside of the lock.804 DestroyRunspace(runspace);805 }806 807 // it is important to release lock on Pool so that808 // other threads can service requests.809 if (isRunspaceReleased)810 {811 // service any pending runspace requests.812 EnqueueCheckAndStartRequestServicingThread(null, false);813 }814 }815 816 /// <summary>817 /// Release all resources.818 /// </summary>819 public void Dispose()820 {821 Dispose(true);822 823 GC.SuppressFinalize(this);824 }825 826 /// <summary>827 /// Dispose off the current runspace pool.828 /// </summary>829 /// <param name="disposing">830 /// true to release all the internal resources.831 /// </param>832 protected virtual void Dispose(bool disposing)833 {834 if (!_isDisposed)835 {836 if (disposing)837 {838 Close();839 _cleanupTimer.Dispose();840 _initialSessionState = null;841 host = null;842 }843 844 _isDisposed = true;845 }846 }847 848 #endregion849 850 #region Internal Methods851 852 /// <summary>853 /// The value of this property is propagated to all the Runspaces in this pool;854 /// it determines whether a new thread is create when a pipeline is executed.855 /// </summary>856 /// <remarks>857 /// Any updates to the value of this property must be done before the RunspacePool is opened858 /// </remarks>859 internal PSThreadOptions ThreadOptions { get; set; } = PSThreadOptions.Default;860 861 /// <summary>862 /// The value of this property is propagated to all the Runspaces in this pool.863 /// </summary>864 /// <remarks>865 /// Any updates to the value of this property must be done before the RunspacePool is opened866 /// </remarks>867 internal ApartmentState ApartmentState { get; set; } = Runspace.DefaultApartmentState;868 869 /// <summary>870 /// Gets Runspace asynchronously from the runspace pool. The caller871 /// will get notified with the runspace using <paramref name="callback"/>872 /// </summary>873 /// <param name="callback">874 /// A AsyncCallback to call once the runspace is available.875 /// </param>876 /// <param name="state">877 /// A user supplied state to call the <paramref name="callback"/>878 /// with.879 /// </param>880 /// <returns>881 /// An IAsyncResult object to track the status of the Async operation.882 /// </returns>883 internal IAsyncResult BeginGetRunspace(884 AsyncCallback callback, object state)885 {886 AssertPoolIsOpen();887 888 GetRunspaceAsyncResult asyncResult = new GetRunspaceAsyncResult(this.InstanceId,889 callback, state);890 891 // Enqueue and start servicing thread in one go..saving multiple locks.892 EnqueueCheckAndStartRequestServicingThread(asyncResult, true);893 894 return asyncResult;895 }896 897 /// <summary>898 /// Cancels the pending asynchronous BeginGetRunspace operation.899 /// </summary>900 /// <param name="asyncResult">901 /// </param>902 internal void CancelGetRunspace(IAsyncResult asyncResult)903 {904 if (asyncResult == null)905 {906 throw PSTraceSource.NewArgumentNullException(nameof(asyncResult));907 }908 909 GetRunspaceAsyncResult grsAsyncResult =910 asyncResult as GetRunspaceAsyncResult;911 912 if ((grsAsyncResult == null) || (grsAsyncResult.OwnerId != instanceId))913 {914 throw PSTraceSource.NewArgumentException(nameof(asyncResult),915 RunspacePoolStrings.AsyncResultNotOwned,916 "IAsyncResult",917 "BeginGetRunspace");918 }919 920 grsAsyncResult.IsActive = false;921 }922 923 /// <summary>924 /// Waits for the pending asynchronous BeginGetRunspace to complete.925 /// </summary>926 /// <param name="asyncResult">927 /// </param>928 /// <exception cref="ArgumentNullException">929 /// asyncResult is a null reference.930 /// </exception>931 /// <exception cref="ArgumentException">932 /// asyncResult object was not created by calling BeginGetRunspace933 /// on this runspacepool instance.934 /// </exception>935 /// <exception cref="InvalidRunspacePoolStateException">936 /// RunspacePoolState is not BeforeOpen.937 /// </exception>938 /// <remarks>939 /// TODO: Behavior if EndGetRunspace is called multiple times.940 /// </remarks>941 internal Runspace EndGetRunspace(IAsyncResult asyncResult)942 {943 if (asyncResult == null)944 {945 throw PSTraceSource.NewArgumentNullException(nameof(asyncResult));946 }947 948 GetRunspaceAsyncResult grsAsyncResult =949 asyncResult as GetRunspaceAsyncResult;950 951 if ((grsAsyncResult == null) || (grsAsyncResult.OwnerId != instanceId))952 {953 throw PSTraceSource.NewArgumentException(nameof(asyncResult),954 RunspacePoolStrings.AsyncResultNotOwned,955 "IAsyncResult",956 "BeginGetRunspace");957 }958 959 grsAsyncResult.EndInvoke();960 return grsAsyncResult.Runspace;961 }962 963 /// <summary>964 /// Opens the runspacepool synchronously / asynchronously.965 /// Runspace pool must be opened before it can be used.966 /// </summary>967 /// <param name="isAsync">968 /// true to open asynchronously969 /// </param>970 /// <param name="callback">971 /// A AsyncCallback to call once the BeginOpen completes.972 /// </param>973 /// <param name="asyncState">974 /// A user supplied state to call the <paramref name="callback"/>975 /// with.976 /// </param>977 /// <returns>978 /// asyncResult object to monitor status of the async979 /// open operation. This is returned only if <paramref name="isAsync"/>980 /// is true.981 /// </returns>982 /// <exception cref="InvalidRunspacePoolStateException">983 /// Cannot open RunspacePool because RunspacePool is not in984 /// the BeforeOpen state.985 /// </exception>986 /// <exception cref="OutOfMemoryException">987 /// There is not enough memory available to start this asynchronously.988 /// </exception>989 protected virtual IAsyncResult CoreOpen(bool isAsync, AsyncCallback callback,990 object asyncState)991 {992 lock (syncObject)993 {994 AssertIfStateIsBeforeOpen();995 996 stateInfo = new RunspacePoolStateInfo(RunspacePoolState.Opening, null);997 }998 999 // only one thread will reach here, so no1000 // need to lock.1001 RaiseStateChangeEvent(stateInfo);1002 1003 if (isAsync)1004 {1005 AsyncResult asyncResult = new RunspacePoolAsyncResult(instanceId, callback, asyncState, true);1006 // Open pool in another thread1007 ThreadPool.QueueUserWorkItem(new WaitCallback(OpenThreadProc), asyncResult);1008 return asyncResult;1009 }1010 1011 // open the runspace synchronously1012 OpenHelper();1013 return null;1014 }1015 1016 /// <summary>1017 /// Creates a Runspace + opens it synchronously and1018 /// pushes it into the stack.1019 /// </summary>1020 /// <remarks>1021 /// Caller to make sure this is thread safe.1022 /// </remarks>1023 protected void OpenHelper()1024 {1025 try1026 {1027 PSEtwLog.SetActivityIdForCurrentThread(this.InstanceId);1028 // Create a Runspace and store it in the pool1029 // for future use. This will validate whether1030 // a runspace can be created + opened successfully1031 Runspace rs = CreateRunspace();1032 pool.Push(rs);1033 }1034 catch (Exception exception)1035 {1036 SetStateToBroken(exception);1037 // rethrow the exception1038 throw;1039 }1040 1041 bool shouldRaiseEvents = false;1042 // RunspacePool might be closed while we are still opening1043 // we should not change state from closed to opened..1044 lock (syncObject)1045 {1046 if (stateInfo.State == RunspacePoolState.Opening)1047 {1048 // Change state to opened and notify the user.1049 stateInfo = new RunspacePoolStateInfo(RunspacePoolState.Opened, null);1050 shouldRaiseEvents = true;1051 }1052 }1053 1054 if (shouldRaiseEvents)1055 {1056 RaiseStateChangeEvent(stateInfo);1057 }1058 }1059 1060 private void SetStateToBroken(Exception reason)1061 {1062 bool shouldRaiseEvents = false;1063 lock (syncObject)1064 {1065 if ((stateInfo.State == RunspacePoolState.Opening) ||1066 (stateInfo.State == RunspacePoolState.Opened) ||1067 (stateInfo.State == RunspacePoolState.Disconnecting) ||1068 (stateInfo.State == RunspacePoolState.Disconnected) ||1069 (stateInfo.State == RunspacePoolState.Connecting))1070 {1071 stateInfo = new RunspacePoolStateInfo(RunspacePoolState.Broken, null);1072 shouldRaiseEvents = true;1073 }1074 }1075 1076 if (shouldRaiseEvents)1077 {1078 RunspacePoolStateInfo stateInfo = new RunspacePoolStateInfo(this.stateInfo.State,1079 reason);1080 RaiseStateChangeEvent(stateInfo);1081 }1082 }1083 1084 /// <summary>1085 /// Starting point for asynchronous thread.1086 /// </summary>1087 /// <remarks>1088 /// asyncResult object1089 /// </remarks>1090 protected void OpenThreadProc(object o)1091 {1092 Dbg.Assert(o is AsyncResult, "OpenThreadProc expects AsyncResult");1093 // Since this is an internal method, we can safely cast the1094 // object to AsyncResult object.1095 AsyncResult asyncObject = (AsyncResult)o;1096 // variable to keep track of exceptions.1097 Exception exception = null;1098 1099 try1100 {1101 OpenHelper();1102 }1103 catch (Exception e)1104 {1105 // report non-severe exceptions to the user via the1106 // asyncresult object1107 exception = e;1108 }1109 finally1110 {1111 asyncObject.SetAsCompleted(exception);1112 }1113 }1114 1115 /// <summary>1116 /// Closes the runspacepool synchronously / asynchronously.1117 /// </summary>1118 /// <param name="isAsync">1119 /// true to close asynchronously1120 /// </param>1121 /// <param name="callback">1122 /// A AsyncCallback to call once the BeginClose completes.1123 /// </param>1124 /// <param name="asyncState">1125 /// A user supplied state to call the <paramref name="callback"/>1126 /// with.1127 /// </param>1128 /// <returns>1129 /// asyncResult object to monitor status of the async1130 /// open operation. This is returned only if <paramref name="isAsync"/>1131 /// is true.1132 /// </returns>1133 private IAsyncResult CoreClose(bool isAsync, AsyncCallback callback, object asyncState)1134 {1135 lock (syncObject)1136 {1137 if ((stateInfo.State == RunspacePoolState.Closed) ||1138 (stateInfo.State == RunspacePoolState.Broken) ||1139 (stateInfo.State == RunspacePoolState.Closing) ||1140 (stateInfo.State == RunspacePoolState.Disconnecting) ||1141 (stateInfo.State == RunspacePoolState.Disconnected))1142 {1143 if (isAsync)1144 {1145 RunspacePoolAsyncResult asyncResult = new RunspacePoolAsyncResult(instanceId, callback, asyncState, false);1146 asyncResult.SetAsCompleted(null);1147 return asyncResult;1148 }1149 else1150 {1151 return null;1152 }1153 }1154 1155 stateInfo = new RunspacePoolStateInfo(RunspacePoolState.Closing, null);1156 }1157 1158 // only one thread will reach here.1159 RaiseStateChangeEvent(stateInfo);1160 1161 if (isAsync)1162 {1163 RunspacePoolAsyncResult asyncResult = new RunspacePoolAsyncResult(instanceId, callback, asyncState, false);1164 // Open pool in another thread1165 ThreadPool.QueueUserWorkItem(new WaitCallback(CloseThreadProc), asyncResult);1166 return asyncResult;1167 }1168 1169 // open the runspace synchronously1170 CloseHelper();1171 return null;1172 }1173 1174 private void CloseHelper()1175 {1176 try1177 {1178 InternalClearAllResources();1179 }1180 finally1181 {1182 stateInfo = new RunspacePoolStateInfo(RunspacePoolState.Closed, null);1183 RaiseStateChangeEvent(stateInfo);1184 }1185 }1186 1187 private void CloseThreadProc(object o)1188 {1189 Dbg.Assert(o is AsyncResult, "CloseThreadProc expects AsyncResult");1190 // Since this is an internal method, we can safely cast the1191 // object to AsyncResult object.1192 AsyncResult asyncObject = (AsyncResult)o;1193 // variable to keep track of exceptions.1194 Exception exception = null;1195 1196 try1197 {1198 CloseHelper();1199 }1200 catch (Exception e)