Repository: PowerShell/PowerShell
File: src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs (C#)

Code before:
        public override void CloseAsync()
        {
            base.CloseAsync();

            if (!_connectionEstablished)
            {
                // If the connection is not yet estalished then clean up any existing connection state.
                CloseConnection();
            }
        }

        #endregion

        #region Private Methods

        private void CloseConnection()
        {
            // Ensure message queue is disposed.
            DisposeMessageQueue();

            var connectionTimer = Interlocked.Exchange(ref _connectionTimer, null);
            connectionTimer?.Dispose();

            // On Windows, the stdIn, stdOut, stdErr writer/readers are also disposed in the 'Exited' event of the ssh process.
            // In that case, the 'Dispose' method could be called in parallel on them, which might result in a race condition.
            // So, we handle their disposal here in a thread-safe manner.
            var stdInWriter = Interlocked.Exchange(ref _stdInWriter, null);
            Utils.SafeDispose(stdInWriter);

            var stdOutReader = Interlocked.Exchange(ref _stdOutReader, null);
            Utils.SafeDispose(stdOutReader);

            var stdErrReader = Interlocked.Exchange(ref _stdErrReader, null);
            Utils.SafeDispose(stdErrReader);

            // The CloseConnection() method can be called multiple times from multiple places.
            // Set the _sshProcessId to zero here so that we go through the work of finding
            // and terminating the SSH process just once.
            var sshProcessId = Interlocked.Exchange(ref _sshProcessId, 0);
            if (sshProcessId != 0)
            {
                try
                {
                    _connectionInfo.KillSSHProcess(sshProcessId);
                }
                catch (ArgumentException) { }
                catch (InvalidOperationException) { }
                catch (NotSupportedException) { }
                catch (System.ComponentModel.Win32Exception) { }
            }
        }

Review comment (suggestion):
From my understanding correctly implemented Dispose() is thread-safe.
StreamRead and StreamWrite come from standard library and have correctly implemented Dispose() method.

Code after:
        public override void CloseAsync()
        {
            base.CloseAsync();

            if (!_connectionEstablished)
            {
                // If the connection is not yet estalished then clean up any existing connection state.
                CloseConnection();
            }
        }

        #endregion

        #region Private Methods

        private void CloseConnection()
        {
            // Ensure message queue is disposed.
            DisposeMessageQueue();

            var connectionTimer = Interlocked.Exchange(ref _connectionTimer, null);
            connectionTimer?.Dispose();

            var stdInWriter = Interlocked.Exchange(ref _stdInWriter, null);
            stdInWriter?.Dispose();

            var stdOutReader = Interlocked.Exchange(ref _stdOutReader, null);
            stdOutReader?.Dispose();

            var stdErrReader = Interlocked.Exchange(ref _stdErrReader, null);
            stdErrReader?.Dispose();

            // The CloseConnection() method can be called multiple times from multiple places.
            // Set the _sshProcessId to zero here so that we go through the work of finding
            // and terminating the SSH process just once.
            var sshProcessId = Interlocked.Exchange(ref _sshProcessId, 0);
            if (sshProcessId != 0)
            {
                try
                {
                    _connectionInfo.KillSSHProcess(sshProcessId);
                }
                catch (ArgumentException) { }
                catch (InvalidOperationException) { }
                catch (NotSupportedException) { }
                catch (System.ComponentModel.Win32Exception) { }
            }
        }

        private void StartErrorThread(
            StreamReader stdErrReader)