private async Task ProcessExitedHandlerAsync() { try { using (await _disposeSemaphore.DisposableWaitAsync().ConfigureAwait(false)) { if (_processExitHandlerStatus == ProcessExitHandlerStatus.Hooked) { Process.Exited -= ProcessExitedHandler; _processExitHandlerStatus = ProcessExitHandlerStatus.Handled; // Should set _processExitHandlerStatus before calling OnProcessExited to avoid deadlocks. // Calling the host should be within the lock to prevent its disposing during the execution. } } var host = _host; if (host != null) { await host.OnProcessExited(Process).ConfigureAwait(false); } } catch (Exception e) when(FatalError.Report(e)) { throw ExceptionUtilities.Unreachable; } }
private volatile ProcessExitHandlerStatus _processExitHandlerStatus; // set to Handled on dispose internal RemoteService(InteractiveHost host, Process process, int processId, Service service) { Debug.Assert(host != null); Debug.Assert(process != null); Debug.Assert(service != null); Process = process; Service = service; _host = host; _joinOutputWritingThreadsOnDisposal = host._joinOutputWritingThreadsOnDisposal; _processId = processId; _processExitHandlerStatus = ProcessExitHandlerStatus.Uninitialized; // TODO (tomat): consider using single-thread async readers _readOutputThread = new Thread(() => ReadOutput(error: false)); _readOutputThread.Name = "InteractiveHost-OutputReader-" + processId; _readOutputThread.IsBackground = true; _readOutputThread.Start(); _readErrorOutputThread = new Thread(() => ReadOutput(error: true)); _readErrorOutputThread.Name = "InteractiveHost-ErrorOutputReader-" + processId; _readErrorOutputThread.IsBackground = true; _readErrorOutputThread.Start(); }
private volatile ProcessExitHandlerStatus _processExitHandlerStatus; // set to Handled on dispose internal RemoteService( InteractiveHost host, Process process, int processId, JsonRpc jsonRpc, InteractiveHostPlatformInfo platformInfo, InteractiveHostOptions options ) { Process = process; JsonRpc = jsonRpc; PlatformInfo = platformInfo; Options = options; _host = host; _joinOutputWritingThreadsOnDisposal = _host._joinOutputWritingThreadsOnDisposal; _processId = processId; _processExitHandlerStatus = ProcessExitHandlerStatus.Uninitialized; // TODO (tomat): consider using single-thread async readers _readOutputThread = new Thread(() => ReadOutput(error: false)); _readOutputThread.Name = "InteractiveHost-OutputReader-" + processId; _readOutputThread.IsBackground = true; _readOutputThread.Start(); _readErrorOutputThread = new Thread(() => ReadOutput(error: true)); _readErrorOutputThread.Name = "InteractiveHost-ErrorOutputReader-" + processId; _readErrorOutputThread.IsBackground = true; _readErrorOutputThread.Start(); }
internal void HookAutoRestartEvent() { using (_disposeSemaphore.DisposableWait()) { // hook the event only once per process: if (_processExitHandlerStatus == ProcessExitHandlerStatus.Uninitialized) { Process.Exited += ProcessExitedHandler; _processExitHandlerStatus = ProcessExitHandlerStatus.Hooked; } } }
// Dispose may called anytime, on any thread. internal void Dispose() { // There can be a call from host initiated from OnProcessExit. // We should not proceed with disposing if _disposeSemaphore is locked. using (_disposeSemaphore.DisposableWait()) { if (_processExitHandlerStatus == ProcessExitHandlerStatus.Hooked) { Process.Exited -= ProcessExitedHandler; _processExitHandlerStatus = ProcessExitHandlerStatus.Handled; } } InitiateTermination(Process, _processId); if (_joinOutputWritingThreadsOnDisposal) { try { _readOutputThread?.Join(); } catch (ThreadStateException) { // thread hasn't started } try { _readErrorOutputThread?.Join(); } catch (ThreadStateException) { // thread hasn't started } } // null the host so that we don't attempt to write to the buffer anymore: _host = null; _readOutputThread = _readErrorOutputThread = null; }
internal void Dispose(bool joinThreads) { // There can be a call from host initiated from OnProcessExit. // This check on the beginning helps to avoid a reentrancy. if (_processExitHandlerStatus == ProcessExitHandlerStatus.Hooked) { using (_disposeSemaphore.DisposableWait()) { if (_processExitHandlerStatus == ProcessExitHandlerStatus.Hooked) { Process.Exited -= ProcessExitedHandler; _processExitHandlerStatus = ProcessExitHandlerStatus.Handled; } } } InitiateTermination(Process, _processId); try { _readOutputThread?.Join(); } catch (ThreadStateException) { // thread hasn't started } try { _readErrorOutputThread?.Join(); } catch (ThreadStateException) { // thread hasn't started } // null the host so that we don't attempt to write to the buffer anymore: _host = null; _readOutputThread = _readErrorOutputThread = null; }