private async Task StartHostAsync(CancellationToken cancellationToken, int attemptCount = 0, JobHostStartupMode startupMode = JobHostStartupMode.Normal, Guid?parentOperationId = null) { // Add this to the list of trackable startup operations. Restarts can use this to cancel any ongoing or pending operations. var activeOperation = ScriptHostStartupOperation.Create(cancellationToken, _logger, parentOperationId); using (_metricsLogger.LatencyEvent(MetricEventNames.ScriptHostManagerStartService)) { try { await _hostStartSemaphore.WaitAsync(); // Now that we're inside the semaphore, set this task as completed. This prevents // restarts from being invoked (via the PlaceholderSpecializationMiddleware) before // the IHostedService has ever started. _hostStartedSource.TrySetResult(true); await UnsynchronizedStartHostAsync(activeOperation, attemptCount, startupMode); } finally { activeOperation.Dispose(); _hostStartSemaphore.Release(); } } }
public async Task RestartHostAsync(CancellationToken cancellationToken) { if (ShutdownRequested) { return; } using (_metricsLogger.LatencyEvent(MetricEventNames.ScriptHostManagerRestartService)) { // Do not invoke a restart if the host has not yet been started. This can lead // to invalid state. if (!_hostStarted.IsCompleted) { _logger.RestartBeforeStart(); await _hostStarted; } _logger.EnteringRestart(); // If anything is mid-startup, cancel it. _startupLoopTokenSource?.Cancel(); foreach (var startupOperation in ScriptHostStartupOperation.ActiveOperations) { _logger.CancelingStartupOperationForRestart(startupOperation.Id); try { startupOperation.CancellationTokenSource.Cancel(); } catch (ObjectDisposedException) { // This can be disposed at any time. } } try { await _hostStartSemaphore.WaitAsync(); if (State == ScriptHostState.Stopping || State == ScriptHostState.Stopped) { _logger.SkipRestart(State.ToString()); return; } State = ScriptHostState.Default; _logger.Restarting(); var previousHost = ActiveHost; ActiveHost = null; using (var activeOperation = ScriptHostStartupOperation.Create(cancellationToken, _logger)) { Task startTask, stopTask; // If we are running in development mode with core tools, do not overlap the restarts. // Overlapping restarts are problematic when language worker processes are listening // to the same debug port if (ShouldEnforceSequentialRestart()) { stopTask = Orphan(previousHost, cancellationToken); await stopTask; startTask = UnsynchronizedStartHostAsync(activeOperation); } else { startTask = UnsynchronizedStartHostAsync(activeOperation); stopTask = Orphan(previousHost, cancellationToken); } await startTask; } _logger.Restarted(); } catch (OperationCanceledException) { if (cancellationToken.IsCancellationRequested) { _logger.ScriptHostServiceRestartCanceledByRuntime(); throw; } // If the exception was triggered by our startup operation cancellation token, just ignore as // it doesn't indicate an issue. } finally { _hostStartSemaphore.Release(); } } }