@@ -59,48 +59,64 @@ public class AbstractConsoleLogger<T: OutputStream>: ConsoleLogger{
5959 originalStdout = dup ( STDOUT_FILENO)
6060 originalStderr = dup ( STDERR_FILENO)
6161
62+ let redirectedOutStream = self . outPipe? . fileHandleForWriting. fileDescriptor ?? - 1
63+ let redirectedErrStream = self . errPipe? . fileHandleForWriting. fileDescriptor ?? - 1
64+
6265 // Redirect stdout and stderr to our pipes
63- dup2 ( self . outPipe ? . fileHandleForWriting . fileDescriptor ?? - 1 , STDOUT_FILENO)
64- dup2 ( self . errPipe ? . fileHandleForWriting . fileDescriptor ?? - 1 , STDERR_FILENO)
66+ dup2 ( redirectedOutStream , STDOUT_FILENO)
67+ dup2 ( redirectedErrStream , STDERR_FILENO)
6568
69+ // Disable libc-level buffering
70+ // (libc by default uses bufferring except its own console/TTYs such as for pipes)
71+ // we do have our own buffering so we disable stdlib io level bufferring
72+ setvbuf ( stdout, nil , _IONBF, 0 ) // disable buffering for stdout
73+ setvbuf ( stderr, nil , _IONBF, 0 ) // disable buffering for stderr
74+
6675 // Setup readability handlers for raw data
6776 setupReadabilityHandler ( for: outputHandle, isError: false )
6877 setupReadabilityHandler ( for: errorHandle, isError: true )
6978 }
7079
80+ let shutdownLock = NSLock ( )
81+
7182 private func setupReadabilityHandler( for handle: FileHandle ? , isError: Bool ) {
72- handle? . readabilityHandler = { [ weak self] handle in
73- let data = handle. availableData
74- if !data. isEmpty {
75- self ? . writeQueue. async {
76- try ? self ? . writeData ( data)
77- }
78-
79- // Forward to original std stream
80- if let originalFD = isError ? self ? . originalStderr : self ? . originalStdout {
81- data. withUnsafeBytes { ( bufferPointer) -> Void in
82- if let baseAddress = bufferPointer. baseAddress, bufferPointer. count > 0 {
83- write ( originalFD, baseAddress, bufferPointer. count)
84- }
85- }
86- }
83+ handle? . readabilityHandler = readHandler ( for: handle, isError: isError)
84+ }
85+
86+ private func readHandler( for handle: FileHandle ? , isError: Bool ) -> ( FileHandle ) -> Void {
87+ return { [ weak self] _ in
88+ guard let self, let data = handle? . availableData else { return }
89+
90+ shutdownLock. lock ( )
91+ defer { shutdownLock. unlock ( ) }
92+
93+ writeQueue. async {
94+ try ? self . writeData ( data)
95+ }
96+
97+ if let fd = isError ? self . originalStderr : self . originalStdout {
98+ data. withUnsafeBytes { _ = write ( fd, $0. baseAddress, $0. count) }
8799 }
88100 }
89101 }
102+
90103
91104 func writeData( _ data: Data ) throws {
92105 throw AbstractClassError . abstractMethodInvoked
93106 }
94107
95108 func stopCapturing( ) {
109+ shutdownLock. lock ( )
110+ defer { shutdownLock. unlock ( ) }
111+
96112 ostream. close ( )
97113
98114 // Restore original stdout and stderr
99- if let stdout = originalStdout {
115+ if let stdout = originalStdout, stdout != STDOUT_FILENO {
100116 dup2 ( stdout, STDOUT_FILENO)
101117 close ( stdout)
102118 }
103- if let stderr = originalStderr {
119+ if let stderr = originalStderr, stderr != STDERR_FILENO {
104120 dup2 ( stderr, STDERR_FILENO)
105121 close ( stderr)
106122 }
0 commit comments