Skip to content

Commit ef380ee

Browse files
committed
BaseStreamSocketChannel half-close allows outstanding writes to complete
Motivation: At the moment half-closes are actioned immediately and fails all outstanding writes. We should refuse new writes but allow these writes to complete before completing the close. Modifications: Modify the PendingWritesManager internal buffer to hold an enum of either writes or close events. We use this to store the close and only action it when the preceding writes have been handled. Result: Outbound close should no longer fail outstanding writes
1 parent dc06cf8 commit ef380ee

File tree

4 files changed

+276
-100
lines changed

4 files changed

+276
-100
lines changed

Diff for: Sources/NIOPosix/BaseStreamSocketChannel.swift

+12-4
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,19 @@ class BaseStreamSocketChannel<Socket: SocketProtocol>: BaseSocketChannel<Socket>
194194
self.close0(error: error, mode: .all, promise: promise)
195195
return
196196
}
197-
try self.shutdownSocket(mode: mode)
198-
// Fail all pending writes and so ensure all pending promises are notified
199-
self.pendingWrites.failAll(error: error, close: false)
200197
self.unregisterForWritable()
201-
promise?.succeed(())
198+
199+
// Shutdown the socket only when the pending writes are dealt with
200+
let closePromise = self.eventLoop.makePromise(of: Void.self)
201+
closePromise.futureResult.whenComplete { _ in
202+
do {
203+
try self.shutdownSocket(mode: mode)
204+
promise?.succeed(())
205+
} catch let err {
206+
promise?.fail(err)
207+
}
208+
}
209+
self.pendingWrites.close(closePromise)
202210

203211
self.pipeline.fireUserInboundEventTriggered(ChannelEvent.outputClosed)
204212
case .input:

Diff for: Sources/NIOPosix/PendingDatagramWritesManager.swift

+7-4
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,8 @@ private struct PendingDatagramWritesState {
268268
// When we've hit an error we treat it like fully writing the first datagram. We aren't going to try to
269269
// send it again.
270270
let promiseFiller = self.wroteFirst(error: error)
271-
let result: OneWriteOperationResult = self.pendingWrites.hasMark ? .writtenPartially : .writtenCompletely
271+
let result: OneWriteOperationResult =
272+
self.pendingWrites.hasMark ? .writtenPartially : .writtenCompletely(closePromise: nil)
272273

273274
return (promiseFiller, result)
274275
}
@@ -305,7 +306,8 @@ private struct PendingDatagramWritesState {
305306
}
306307

307308
// If we no longer have a mark, we wrote everything.
308-
let result: OneWriteOperationResult = self.pendingWrites.hasMark ? .writtenPartially : .writtenCompletely
309+
let result: OneWriteOperationResult =
310+
self.pendingWrites.hasMark ? .writtenPartially : .writtenCompletely(closePromise: nil)
309311
return (promiseFiller, result)
310312
}
311313

@@ -322,7 +324,8 @@ private struct PendingDatagramWritesState {
322324
)
323325
let writeFiller = self.wroteFirst()
324326
// If we no longer have a mark, we wrote everything.
325-
let result: OneWriteOperationResult = self.pendingWrites.hasMark ? .writtenPartially : .writtenCompletely
327+
let result: OneWriteOperationResult =
328+
self.pendingWrites.hasMark ? .writtenPartially : .writtenCompletely(closePromise: nil)
326329
return (writeFiller, result)
327330
}
328331

@@ -565,7 +568,7 @@ final class PendingDatagramWritesManager: PendingWritesManager {
565568
preconditionFailure("PendingDatagramWritesManager was handed a file write")
566569
case .nothingToBeWritten:
567570
assertionFailure("called \(#function) with nothing available to be written")
568-
return OneWriteOperationResult.writtenCompletely
571+
return OneWriteOperationResult.writtenCompletely(closePromise: nil)
569572
}
570573
}
571574
}

0 commit comments

Comments
 (0)