Skip to content

Commit c9cf303

Browse files
committed
Improve process output draining after termination
Refactored process termination handling in ProcessCommand, ProcessRsyncOpenrsync, and ProcessRsyncVer3x to more reliably drain and process remaining output data after process termination. Added logging for drain progress and ensured all output is appended before final termination.
1 parent 0633c7d commit c9cf303

File tree

3 files changed

+82
-32
lines changed

3 files changed

+82
-32
lines changed

RsyncUI/Model/Process/Main/ProcessCommand.swift

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,42 @@ final class ProcessCommand {
4141
outHandle.waitForDataInBackgroundAndNotify()
4242

4343
// AsyncSequence
44-
let sequencefilehandler = NotificationCenter.default.notifications(named: NSNotification.Name.NSFileHandleDataAvailable, object: outHandle)
45-
let sequencetermination = NotificationCenter.default.notifications(named: Process.didTerminateNotification, object: task)
44+
let sequencefilehandler = NotificationCenter.default.notifications(
45+
named: NSNotification.Name.NSFileHandleDataAvailable,
46+
object: outHandle)
47+
let sequencetermination = NotificationCenter.default.notifications(
48+
named: Process.didTerminateNotification,
49+
object: task)
4650

4751
sequenceFileHandlerTask = Task {
4852
for await _ in sequencefilehandler {
4953
await self.datahandle(pipe)
5054
}
51-
// Final drain - keep reading until no more data
52-
while pipe.fileHandleForReading.availableData.count > 0 {
53-
Logger.process.info("ProcessCommand: sequenceFileHandlerTask - drain remaining data")
54-
await self.datahandle(pipe)
55-
}
5655
}
5756

5857
sequenceTerminationTask = Task {
5958
for await _ in sequencetermination {
60-
// Small delay to let final data arrive
61-
try? await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds
59+
Logger.process.info("ProcessRsyncVer3x: Process terminated - starting potensial drain")
60+
sequenceFileHandlerTask?.cancel()
61+
try? await Task.sleep(nanoseconds: 50_000_000)
62+
var totalDrained = 0
63+
while true {
64+
let data: Data = pipe.fileHandleForReading.availableData
65+
if data.isEmpty {
66+
Logger.process.info("ProcessRsyncVer3x: Drain complete - \(totalDrained) bytes total")
67+
break
68+
}
69+
70+
totalDrained += data.count
71+
Logger.process.info("ProcessRsyncVer3x: Draining \(data.count) bytes")
72+
73+
// IMPORTANT: Actually process the drained data
74+
if let text = String(data: data, encoding: .utf8) {
75+
Logger.process.info("ProcessRsyncVer3x: Drained text: \(text)")
76+
self.output.append(text)
77+
}
78+
}
79+
6280
await self.termination()
6381
}
6482
}

RsyncUI/Model/Process/Main/ProcessRsyncOpenrsync.swift

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,42 @@ final class ProcessRsyncOpenrsync {
5252
outHandle.waitForDataInBackgroundAndNotify()
5353

5454
// AsyncSequence
55-
let sequencefilehandler = NotificationCenter.default.notifications(named: NSNotification.Name.NSFileHandleDataAvailable, object: outHandle)
56-
let sequencetermination = NotificationCenter.default.notifications(named: Process.didTerminateNotification, object: task)
55+
let sequencefilehandler = NotificationCenter.default.notifications(
56+
named: NSNotification.Name.NSFileHandleDataAvailable,
57+
object: outHandle)
58+
let sequencetermination = NotificationCenter.default.notifications(
59+
named: Process.didTerminateNotification,
60+
object: task)
5761

5862
sequenceFileHandlerTask = Task {
5963
for await _ in sequencefilehandler {
6064
await self.datahandle(pipe)
6165
}
62-
// Final drain - keep reading until no more data
63-
while pipe.fileHandleForReading.availableData.count > 0 {
64-
Logger.process.info("ProcessRsyncOpenrsync: sequenceFileHandlerTask - drain remaining data")
65-
await self.datahandle(pipe)
66-
}
6766
}
6867

6968
sequenceTerminationTask = Task {
7069
for await _ in sequencetermination {
71-
// Small delay to let final data arrive
72-
try? await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds
70+
Logger.process.info("ProcessRsyncOpenrsync: Process terminated - starting potensial drain")
71+
sequenceFileHandlerTask?.cancel()
72+
try? await Task.sleep(nanoseconds: 50_000_000)
73+
var totalDrained = 0
74+
while true {
75+
let data: Data = pipe.fileHandleForReading.availableData
76+
if data.isEmpty {
77+
Logger.process.info("ProcessRsyncOpenrsync: Drain complete - \(totalDrained) bytes total")
78+
break
79+
}
80+
81+
totalDrained += data.count
82+
Logger.process.info("ProcessRsyncOpenrsync: Draining \(data.count) bytes")
83+
84+
// IMPORTANT: Actually process the drained data
85+
if let text = String(data: data, encoding: .utf8) {
86+
Logger.process.info("ProcessRsyncOpenrsync: Drained text: \(text)")
87+
self.output.append(text)
88+
}
89+
}
90+
7391
await self.termination()
7492
}
7593
}

RsyncUI/Model/Process/Main/ProcessRsyncVer3x.swift

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,12 @@ final class ProcessRsyncVer3x {
6363
outHandle.waitForDataInBackgroundAndNotify()
6464

6565
// AsyncSequence
66-
let sequencefilehandler = NotificationCenter.default.notifications(named: NSNotification.Name.NSFileHandleDataAvailable, object: outHandle)
67-
let sequencetermination = NotificationCenter.default.notifications(named: Process.didTerminateNotification, object: task)
66+
let sequencefilehandler = NotificationCenter.default.notifications(
67+
named: NSNotification.Name.NSFileHandleDataAvailable,
68+
object: outHandle)
69+
let sequencetermination = NotificationCenter.default.notifications(
70+
named: Process.didTerminateNotification,
71+
object: task)
6872

6973
sequenceFileHandlerTask = Task {
7074
for await _ in sequencefilehandler {
@@ -74,25 +78,35 @@ final class ProcessRsyncVer3x {
7478
await self.datahandle(pipe)
7579
}
7680
}
77-
// Final drain - keep reading until no more data
78-
while pipe.fileHandleForReading.availableData.count > 0 {
79-
Logger.process.info("ProcessRsyncVer3x: sequenceFileHandlerTask - drain remaining data")
80-
if self.getrsyncversion == true {
81-
await self.datahandlersyncversion(pipe)
82-
} else {
83-
await self.datahandle(pipe)
84-
}
85-
}
8681
}
8782

8883
sequenceTerminationTask = Task {
8984
for await _ in sequencetermination {
90-
// Small delay to let final data arrive
91-
try? await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds
85+
Logger.process.info("ProcessRsyncVer3x: Process terminated - starting potensial drain")
86+
sequenceFileHandlerTask?.cancel()
87+
try? await Task.sleep(nanoseconds: 50_000_000)
88+
var totalDrained = 0
89+
while true {
90+
let data: Data = pipe.fileHandleForReading.availableData
91+
if data.isEmpty {
92+
Logger.process.info("ProcessRsyncVer3x: Drain complete - \(totalDrained) bytes total")
93+
break
94+
}
95+
96+
totalDrained += data.count
97+
Logger.process.info("ProcessRsyncVer3x: Draining \(data.count) bytes")
98+
99+
// IMPORTANT: Actually process the drained data
100+
if let text = String(data: data, encoding: .utf8) {
101+
Logger.process.info("ProcessRsyncVer3x: Drained text: \(text)")
102+
self.output.append(text)
103+
}
104+
}
105+
92106
await self.termination()
93107
}
94108
}
95-
109+
96110
SharedReference.shared.process = task
97111
do {
98112
try task.run()

0 commit comments

Comments
 (0)