< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Scripting.KestrunRunspacePoolManager
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Scripting/KestrunRunspacePoolManager.cs
Tag: Kestrun/Kestrun@5f1d2b981c9d7292c11fd448428c6ab6c811c5de
Line coverage
82%
Covered lines: 122
Uncovered lines: 25
Coverable lines: 147
Total lines: 358
Line coverage: 82.9%
Branch coverage
89%
Covered branches: 57
Total branches: 64
Branch coverage: 89%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 11/19/2025 - 17:40:50 Line coverage: 72.5% (87/120) Branch coverage: 77.7% (42/54) Total lines: 296 Tag: Kestrun/Kestrun@fcf33342333cef0516fe0d0912a86709874fd02612/12/2025 - 17:27:19 Line coverage: 69.4% (91/131) Branch coverage: 74.1% (43/58) Total lines: 327 Tag: Kestrun/Kestrun@826bf9dcf9db118c5de4c78a3259bce9549f0dcd12/15/2025 - 02:23:46 Line coverage: 72.5% (95/131) Branch coverage: 75.8% (44/58) Total lines: 327 Tag: Kestrun/Kestrun@7a3839f4de2254e22daae81ab8dc7cb2f40c833012/15/2025 - 04:25:23 Line coverage: 74.8% (98/131) Branch coverage: 79.3% (46/58) Total lines: 327 Tag: Kestrun/Kestrun@e333660af9731cab5ae4c14a12f3bb84a8fabc7d12/15/2025 - 18:01:19 Line coverage: 72.5% (95/131) Branch coverage: 75.8% (44/58) Total lines: 327 Tag: Kestrun/Kestrun@7127e76ef631ac4d0965a9a37239d5089469e32812/15/2025 - 18:44:50 Line coverage: 90% (118/131) Branch coverage: 96.5% (56/58) Total lines: 327 Tag: Kestrun/Kestrun@6b9e56ea2de904fc3597033ef0f9bc7839d5d61812/18/2025 - 21:41:58 Line coverage: 87.7% (115/131) Branch coverage: 93.1% (54/58) Total lines: 327 Tag: Kestrun/Kestrun@0d738bf294e6281b936d031e1979d928007495ff12/24/2025 - 04:10:40 Line coverage: 87% (114/131) Branch coverage: 93.1% (54/58) Total lines: 327 Tag: Kestrun/Kestrun@01149d8cad8f1672f7e8f35b2dcfbc5ba8866aad12/25/2025 - 19:20:44 Line coverage: 87.7% (115/131) Branch coverage: 93.1% (54/58) Total lines: 327 Tag: Kestrun/Kestrun@5251f12f253e29f8a1dfb77edc2ef50b90a4f26f03/03/2026 - 12:38:30 Line coverage: 86.2% (113/131) Branch coverage: 89.6% (52/58) Total lines: 327 Tag: Kestrun/Kestrun@7602da75ddb0abe534368b9e9ce2f45ae966769a03/04/2026 - 19:40:34 Line coverage: 87.7% (115/131) Branch coverage: 93.1% (54/58) Total lines: 327 Tag: Kestrun/Kestrun@eeafbe813231ed23417e7b339e170e307b2c86f903/27/2026 - 14:18:40 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@63388ea9aed376ffbb41cd2727be2fb7646f640203/27/2026 - 17:48:11 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@8ee2ba81b95d460c8379f7e7429f1e24eb38cbe103/28/2026 - 19:32:00 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@84d0d2071c053497504fc6f8a83b92eb3b0e4e2103/29/2026 - 02:16:24 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@3590b1b12f900c48d303185cdc6c041ddd94e63d03/31/2026 - 03:50:30 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@d074b25f81cc58b56c7b08ec8154fc3d76930f0e03/31/2026 - 19:39:33 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@2d5df2f7d7e824cf801e2e16c0cb944fcb160e3803/31/2026 - 21:12:25 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@3fc5338bacdc6d0760b8f01db17b7d9d5245bbd204/02/2026 - 03:50:52 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@beff90ee4b823bd1ac2b160eae6b329b43eab48404/10/2026 - 15:43:15 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@ff6fa380dac2906300f9b9c0cce2df7f5f4f47ba05/02/2026 - 18:28:30 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@88b693fdbf4365fb787c0c8279e7a1dec36288fd05/07/2026 - 03:56:32 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@5f1d2b981c9d7292c11fd448428c6ab6c811c5de 11/19/2025 - 17:40:50 Line coverage: 72.5% (87/120) Branch coverage: 77.7% (42/54) Total lines: 296 Tag: Kestrun/Kestrun@fcf33342333cef0516fe0d0912a86709874fd02612/12/2025 - 17:27:19 Line coverage: 69.4% (91/131) Branch coverage: 74.1% (43/58) Total lines: 327 Tag: Kestrun/Kestrun@826bf9dcf9db118c5de4c78a3259bce9549f0dcd12/15/2025 - 02:23:46 Line coverage: 72.5% (95/131) Branch coverage: 75.8% (44/58) Total lines: 327 Tag: Kestrun/Kestrun@7a3839f4de2254e22daae81ab8dc7cb2f40c833012/15/2025 - 04:25:23 Line coverage: 74.8% (98/131) Branch coverage: 79.3% (46/58) Total lines: 327 Tag: Kestrun/Kestrun@e333660af9731cab5ae4c14a12f3bb84a8fabc7d12/15/2025 - 18:01:19 Line coverage: 72.5% (95/131) Branch coverage: 75.8% (44/58) Total lines: 327 Tag: Kestrun/Kestrun@7127e76ef631ac4d0965a9a37239d5089469e32812/15/2025 - 18:44:50 Line coverage: 90% (118/131) Branch coverage: 96.5% (56/58) Total lines: 327 Tag: Kestrun/Kestrun@6b9e56ea2de904fc3597033ef0f9bc7839d5d61812/18/2025 - 21:41:58 Line coverage: 87.7% (115/131) Branch coverage: 93.1% (54/58) Total lines: 327 Tag: Kestrun/Kestrun@0d738bf294e6281b936d031e1979d928007495ff12/24/2025 - 04:10:40 Line coverage: 87% (114/131) Branch coverage: 93.1% (54/58) Total lines: 327 Tag: Kestrun/Kestrun@01149d8cad8f1672f7e8f35b2dcfbc5ba8866aad12/25/2025 - 19:20:44 Line coverage: 87.7% (115/131) Branch coverage: 93.1% (54/58) Total lines: 327 Tag: Kestrun/Kestrun@5251f12f253e29f8a1dfb77edc2ef50b90a4f26f03/03/2026 - 12:38:30 Line coverage: 86.2% (113/131) Branch coverage: 89.6% (52/58) Total lines: 327 Tag: Kestrun/Kestrun@7602da75ddb0abe534368b9e9ce2f45ae966769a03/04/2026 - 19:40:34 Line coverage: 87.7% (115/131) Branch coverage: 93.1% (54/58) Total lines: 327 Tag: Kestrun/Kestrun@eeafbe813231ed23417e7b339e170e307b2c86f903/27/2026 - 14:18:40 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@63388ea9aed376ffbb41cd2727be2fb7646f640203/27/2026 - 17:48:11 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@8ee2ba81b95d460c8379f7e7429f1e24eb38cbe103/28/2026 - 19:32:00 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@84d0d2071c053497504fc6f8a83b92eb3b0e4e2103/29/2026 - 02:16:24 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@3590b1b12f900c48d303185cdc6c041ddd94e63d03/31/2026 - 03:50:30 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@d074b25f81cc58b56c7b08ec8154fc3d76930f0e03/31/2026 - 19:39:33 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@2d5df2f7d7e824cf801e2e16c0cb944fcb160e3803/31/2026 - 21:12:25 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@3fc5338bacdc6d0760b8f01db17b7d9d5245bbd204/02/2026 - 03:50:52 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@beff90ee4b823bd1ac2b160eae6b329b43eab48404/10/2026 - 15:43:15 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@ff6fa380dac2906300f9b9c0cce2df7f5f4f47ba05/02/2026 - 18:28:30 Line coverage: 82.3% (121/147) Branch coverage: 87.5% (56/64) Total lines: 358 Tag: Kestrun/Kestrun@88b693fdbf4365fb787c0c8279e7a1dec36288fd05/07/2026 - 03:56:32 Line coverage: 82.9% (122/147) Branch coverage: 89% (57/64) Total lines: 358 Tag: Kestrun/Kestrun@5f1d2b981c9d7292c11fd448428c6ab6c811c5de

Coverage delta

Coverage delta 21 -21

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%1212100%
get_Host()100%11100%
get_MinRunspaces()100%11100%
get_MaxRunspaces()100%11100%
get_OpenApiClassesPath()100%11100%
get_ThreadOptions()100%11100%
Acquire()75%201262.5%
AcquireAsync()85.71%161476.92%
Release(...)100%8663.16%
CreateRunspace()100%66100%
Dispose()85.71%141490.32%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Scripting/KestrunRunspacePoolManager.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using System.Management.Automation.Runspaces;
 3using Kestrun.Hosting;
 4using Serilog.Events;
 5
 6namespace Kestrun.Scripting;
 7
 8/// <summary>
 9/// Manages a pool of PowerShell runspaces for efficient reuse and resource control.
 10/// </summary>
 11public sealed class KestrunRunspacePoolManager : IDisposable
 12{
 16313    private readonly ConcurrentBag<Runspace> _stash = [];
 14    private readonly InitialSessionState _iss;
 15    private int _count;        // total live runspaces
 16    private bool _disposed;
 17
 18    /// <summary>
 19    /// Track all runspaces ever created (for cleanup)
 20    /// </summary>
 21    private readonly ConcurrentDictionary<Runspace, byte> _all;
 22
 23    /// <summary>
 24    /// KestrunHost is needed for logging, config, etc.
 25    /// </summary>
 265126    public KestrunHost Host { get; private set; }
 27
 28    /// <summary>
 29    /// Gets the minimum number of runspaces maintained in the pool.
 30    /// </summary>
 231    public int MinRunspaces { get; }
 32    /// <summary>
 33    /// Gets the maximum number of runspaces allowed in the pool.
 34    /// </summary>
 10735    public int MaxRunspaces { get; }
 36
 37    /// <summary>
 38    /// Path to the OpenAPI class definitions to be injected into each runspace.
 39    /// </summary>
 25340    public string? OpenApiClassesPath { get; init; }
 41
 42    /// <summary>
 43    /// Thread‑affinity strategy for *future* runspaces.
 44    /// Default is <see cref="PSThreadOptions.ReuseThread"/>.
 45    /// </summary>
 67646    public PSThreadOptions ThreadOptions { get; set; } = PSThreadOptions.ReuseThread;
 47
 48    // ───────────────── constructor ──────────────────────────
 49    /// <summary>
 50    /// Initializes a new instance of the <see cref="KestrunRunspacePoolManager"/> class with the specified minimum and 
 51    /// </summary>
 52    /// <param name="host">The Kestrun host instance.</param>
 53    /// <param name="minRunspaces">The minimum number of runspaces to maintain in the pool.</param>
 54    /// <param name="maxRunspaces">The maximum number of runspaces allowed in the pool.</param>
 55    /// <param name="initialSessionState">The initial session state for each runspace (optional).</param>
 56    /// <param name="threadOptions">The thread affinity strategy for runspaces (optional).</param>
 57    /// <param name="openApiClassesPath">The file path to the OpenAPI class definitions to be injected into each runspac
 16358    public KestrunRunspacePoolManager(
 16359        KestrunHost host,
 16360        int minRunspaces,
 16361        int maxRunspaces,
 16362        InitialSessionState? initialSessionState = null,
 16363        PSThreadOptions threadOptions = PSThreadOptions.ReuseThread,
 16364        string? openApiClassesPath = null)
 65    {
 16366        if (host.Logger.IsEnabled(LogEventLevel.Debug))
 67        {
 11168            host.Logger.Debug("Initializing RunspacePoolManager: Min={Min}, Max={Max}", minRunspaces, maxRunspaces);
 69        }
 70
 16271        ArgumentOutOfRangeException.ThrowIfNegative(minRunspaces);
 16172        ArgumentNullException.ThrowIfNull(host);
 16173        ArgumentOutOfRangeException.ThrowIfNegative(maxRunspaces);
 74        // sanity check
 16075        if (maxRunspaces < 1 || maxRunspaces < minRunspaces)
 76        {
 277            throw new ArgumentOutOfRangeException(nameof(maxRunspaces));
 78        }
 15879        _all = new();
 15880        Host = host;
 15881        MinRunspaces = minRunspaces;
 15882        MaxRunspaces = maxRunspaces;
 15883        _iss = initialSessionState ?? InitialSessionState.CreateDefault();
 15884        ThreadOptions = threadOptions;
 85
 86        // warm the stash
 62287        for (var i = 0; i < minRunspaces; i++)
 88        {
 15389            _stash.Add(CreateRunspace());
 90        }
 91
 15892        _count = minRunspaces;
 93
 94        // Store OpenAPI classes
 15895        OpenApiClassesPath = openApiClassesPath;
 15896        if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 97        {
 10798            Host.Logger.Debug("Warm-started pool with {Count} runspaces", _count);
 99        }
 158100    }
 101
 102    // ───────────────── public API ────────────────────────────
 103    /// <summary>Borrow a runspace (creates one if under the cap).</summary>
 104    public Runspace Acquire()
 105    {
 69106        if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 107        {
 63108            Host.Logger.Debug("Acquiring runspace from pool: CurrentCount={Count}, Max={Max}", _count, MaxRunspaces);
 109        }
 110
 69111        ObjectDisposedException.ThrowIf(_disposed, nameof(KestrunRunspacePoolManager));
 112
 67113        if (_stash.TryTake(out var rs))
 114        {
 43115            if (rs.RunspaceStateInfo.State != RunspaceState.Opened)
 116            {
 0117                Host.Logger.Warning("Runspace from stash is not opened: {State}. Discarding and acquiring a new one.", r
 118                // If the runspace is not open, we cannot use it.
 119                // Discard and try again
 0120                var removed = _all.TryRemove(rs, out _);
 0121                rs.Dispose();
 0122                if (removed)
 123                {
 0124                    _ = Interlocked.Decrement(ref _count);
 125                }
 0126                return Acquire();
 127            }
 43128            if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 129            {
 38130                Host.Logger.Debug("Reusing runspace from stash: StashCount={Count}", _stash.Count);
 131            }
 132
 43133            return rs;
 134        }
 135        // Need a new one?—but only if we haven’t reached max.
 24136        if (Interlocked.Increment(ref _count) <= MaxRunspaces)
 137        {
 23138            Host.Logger.Debug("Creating new runspace: TotalCount={Count}", _count);
 139            try
 140            {
 23141                return CreateRunspace();
 142            }
 0143            catch
 144            {
 0145                _ = Interlocked.Decrement(ref _count);
 0146                throw;
 147            }
 148        }
 149        // Overshot: roll back and complain.
 1150        _ = Interlocked.Decrement(ref _count);
 151
 1152        Host.Logger.Warning("Runspace limit reached: Max={Max}", MaxRunspaces);
 1153        throw new InvalidOperationException("Run-space limit reached.");
 23154    }
 155
 156    /// <summary>
 157    /// Asynchronously acquires a runspace from the pool, creating a new one if under the cap, or waits until one become
 158    /// </summary>
 159    /// <param name="cancellationToken">A cancellation token to observe while waiting for a runspace.</param>
 160    /// <returns>A task that represents the asynchronous operation, containing the acquired <see cref="Runspace"/>.</ret
 161    public async Task<Runspace> AcquireAsync(CancellationToken cancellationToken = default)
 162    {
 8163        if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 164        {
 8165            Host.Logger.Debug("Acquiring runspace (async) from pool: CurrentCount={Count}, Max={Max}", _count, MaxRunspa
 166        }
 167
 3168        while (true)
 169        {
 11170            ObjectDisposedException.ThrowIf(_disposed, nameof(KestrunRunspacePoolManager));
 171
 10172            if (_stash.TryTake(out var rs))
 173            {
 5174                if (rs.RunspaceStateInfo.State != RunspaceState.Opened)
 175                {
 0176                    Host.Logger.Warning("Runspace from stash is not opened: {State}. Discarding and continuing wait.", r
 0177                    var removed = _all.TryRemove(rs, out _);
 0178                    rs.Dispose();
 0179                    if (removed)
 180                    {
 0181                        _ = Interlocked.Decrement(ref _count);
 182                    }
 0183                    continue;
 184                }
 5185                if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 186                {
 5187                    Host.Logger.Debug("Reusing runspace from stash (async): StashCount={Count}", _stash.Count);
 188                }
 189
 5190                return rs;
 191            }
 192            // Need a new one?—but only if we haven’t reached max.
 5193            if (Interlocked.Increment(ref _count) <= MaxRunspaces)
 194            {
 1195                Host.Logger.Debug("Creating new runspace (async): TotalCount={Count}", _count);
 196                try
 197                {
 198                    // Runspace creation is synchronous, but we can offload to thread pool
 1199                    return await Task.Run(CreateRunspace, cancellationToken).ConfigureAwait(false);
 200                }
 1201                catch
 202                {
 1203                    _ = Interlocked.Decrement(ref _count);
 1204                    throw;
 205                }
 206            }
 207            // Overshot: roll back and try again.
 4208            _ = Interlocked.Decrement(ref _count);
 209
 210            // Wait for a runspace to be returned
 4211            if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 212            {
 4213                Host.Logger.Debug("Waiting for runspace to become available (async)");
 214            }
 215
 216            // Use a short delay to poll for availability
 4217            await Task.Delay(50, cancellationToken).ConfigureAwait(false);
 218        }
 5219    }
 220
 221    /// <summary>
 222    /// Returns a runspace to the pool for reuse, or disposes it if the pool has been disposed.
 223    /// </summary>
 224    /// <param name="rs">The <see cref="Runspace"/> to return to the pool.</param>
 225    public void Release(Runspace rs)
 226    {
 67227        if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 228        {
 61229            Host.Logger.Debug("Release() called: Disposed={Disposed}", _disposed);
 230        }
 231
 67232        if (_disposed)
 233        {
 1234            Host.Logger.Warning("Pool disposed; disposing returned runspace");
 1235            rs.Dispose();
 1236            return;
 237        }
 238
 239        try
 240        {
 241            // Put the genie back in the bottle: variables, funcs, modules…
 242            // This returns the runspace to the InitialSessionState baseline.
 66243            rs.ResetRunspaceState();
 66244        }
 0245        catch (Exception ex)
 246        {
 0247            Host.Logger.Warning(ex, "ResetRunspaceState failed; disposing runspace instead");
 0248            try { rs.Close(); }
 0249            catch (Exception closeEx) { Host.Logger.Verbose(exception: closeEx, messageTemplate: "Failed to close runspa
 0250            rs.Dispose();
 0251            _ = Interlocked.Decrement(ref _count);
 0252            return;
 253        }
 66254        _stash.Add(rs);
 66255        if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 256        {
 60257            Host.Logger.Debug("Runspace returned to stash: StashCount={Count}", _stash.Count);
 258        }
 259        // Note: we do not decrement _count here, as the pool size is fixed.
 260        // The pool will keep the runspace open for reuse.
 66261    }
 262
 263    // ───────────────── helpers ───────────────────────────────
 264    /// <summary>
 265    /// Creates a new PowerShell runspace with the configured initial session state and thread options.
 266    /// </summary>
 267    /// <returns>A new <see cref="Runspace"/> instance.</returns>
 268    private Runspace CreateRunspace()
 269    {
 176270        if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 271        {
 124272            Host.Logger.Debug("CreateRunspace() - creating new runspace");
 273        }
 274
 176275        if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 276        {
 124277            Host.Logger.Debug("Creating new runspace with InitialSessionState");
 278        }
 279        // Important: clone per runspace
 176280        var iss = _iss.Clone();
 176281        var rs = RunspaceFactory.CreateRunspace(iss);
 282
 283        // Apply the chosen thread‑affinity strategy **before** opening.
 176284        rs.ThreadOptions = ThreadOptions;
 176285        rs.ApartmentState = ApartmentState.MTA;     // always MTA
 176286        rs.Open();
 287
 176288        Host.Logger.Information("Opened new Runspace with ThreadOptions={ThreadOptions}", ThreadOptions);
 176289        if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 290        {
 124291            Host.Logger.Debug("New runspace created: {Runspace}", rs);
 292        }
 293
 176294        _ = _all.TryAdd(rs, 0);
 176295        return rs;
 296    }
 297
 298    // ───────────────── cleanup ───────────────────────────────
 299    /// <summary>
 300    /// Disposes the runspace pool manager and all pooled runspaces.
 301    /// </summary>
 302    public void Dispose()
 303    {
 109304        if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 305        {
 62306            Host.Logger.Debug("Disposing KestrunRunspacePoolManager: Disposed={Disposed}", _disposed);
 307        }
 308
 109309        if (_disposed)
 310        {
 20311            return;
 312        }
 313
 89314        if (!string.IsNullOrWhiteSpace(OpenApiClassesPath))
 315        {
 316            try
 317            {
 2318                File.Delete(OpenApiClassesPath);
 0319                if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 320                {
 0321                    Host.Logger.Debug("Deleted temporary OpenAPI classes script: {Path}", OpenApiClassesPath);
 322                }
 0323            }
 2324            catch (Exception ex)
 325            {
 2326                Host.Logger.Warning(ex, "Failed to delete temporary OpenAPI classes script: {Path}", OpenApiClassesPath)
 2327            }
 328        }
 89329        _disposed = true;
 330
 89331        Host.Logger.Information("Disposing RunspacePoolManager and all pooled runspaces");
 332
 333        // Drain the stash
 193334        while (_stash.TryTake(out var rs))
 335        {
 104336            if (Host.Logger.IsEnabled(LogEventLevel.Debug))
 337            {
 73338                Host.Logger.Debug("Disposing runspace: {Runspace}", rs);
 339            }
 208340            try { rs.ResetRunspaceState(); } catch (Exception ex) { Host.Logger.Verbose(exception: ex, messageTemplate: 
 208341            try { rs.Close(); } catch (Exception ex) { Host.Logger.Verbose(exception: ex, messageTemplate: "Failed to cl
 104342            rs.Dispose();
 104343            _ = _all.TryRemove(rs, out _);
 104344            _ = Interlocked.Decrement(ref _count);
 104345        }
 346
 347        // Anything still checked out? Close them too.
 184348        foreach (var kv in _all.Keys)
 349        {
 6350            try { kv.ResetRunspaceState(); } catch (Exception ex) { Host.Logger.Verbose(exception: ex, messageTemplate: 
 6351            try { kv.Close(); } catch (Exception ex) { Host.Logger.Verbose(exception: ex, messageTemplate: "Failed to cl
 3352            kv.Dispose();
 3353            _ = _all.TryRemove(kv, out _);
 3354            _ = Interlocked.Decrement(ref _count);
 355        }
 89356        Host.Logger.Information("RunspacePoolManager disposed");
 89357    }
 358}