Remove not on eventLoop precondition for NIOPipeBootstrap (#1977)

Motivation:

Currently you cannot create a NIOPipeBootstrap when you are on
an event loop due to a precondition check. This check seeks to prevent
consumers of the API from passing in a file descriptor thats referencing
a file on disk or on the network.

The method at hand 'validateFileDescriptorIsNotAFile' uses fstat, which potentially
could block the event loop especially if the fd is referencing a file over the network.
This check however prevents certain use cases of SwiftNIO, where the downsides
of a consumer of this api blocking their own event loop do not weigh in against preventing
an entire use case from SwiftNIO.

Modifications:

Removed the precondition

Result:

After this change it will be possible to feed file descriptors into
the bootstrap which potentially block the current event loop.
A potential (portable) replacement for fstat still has to be found in order to solve this problem completely.
This commit is contained in:
Antwan van Houdt 2021-10-18 14:02:07 +02:00 committed by GitHub
parent d906b890d5
commit 787e2287d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 2 deletions

View File

@ -1023,8 +1023,6 @@ public final class NIOPipeBootstrap {
}
private func validateFileDescriptorIsNotAFile(_ descriptor: CInt) throws {
precondition(MultiThreadedEventLoopGroup.currentEventLoop == nil,
"limitation in SwiftNIO: cannot bootstrap PipeChannel on EventLoop")
var s: stat = .init()
try withUnsafeMutablePointer(to: &s) { ptr in
try Posix.fstat(descriptor: descriptor, outStat: ptr)

View File

@ -39,6 +39,7 @@ extension BootstrapTest {
("testPreConnectedSocketSetsChannelOptionsBeforeChannelInitializer", testPreConnectedSocketSetsChannelOptionsBeforeChannelInitializer),
("testDatagramBootstrapSetsChannelOptionsBeforeChannelInitializer", testDatagramBootstrapSetsChannelOptionsBeforeChannelInitializer),
("testPipeBootstrapSetsChannelOptionsBeforeChannelInitializer", testPipeBootstrapSetsChannelOptionsBeforeChannelInitializer),
("testPipeBootstrapInEventLoop", testPipeBootstrapInEventLoop),
("testServerBootstrapAddsAcceptHandlerAfterServerChannelInitialiser", testServerBootstrapAddsAcceptHandlerAfterServerChannelInitialiser),
("testClientBootstrapValidatesWorkingELGsCorrectly", testClientBootstrapValidatesWorkingELGsCorrectly),
("testClientBootstrapRejectsNotWorkingELGsCorrectly", testClientBootstrapRejectsNotWorkingELGsCorrectly),

View File

@ -389,6 +389,32 @@ class BootstrapTest: XCTestCase {
})
}
func testPipeBootstrapInEventLoop() {
let testGrp = DispatchGroup()
testGrp.enter()
let eventLoop = self.group.next()
eventLoop.execute {
do {
let pipe = Pipe()
let readHandle = NIOFileHandle(descriptor: pipe.fileHandleForReading.fileDescriptor)
let writeHandle = NIOFileHandle(descriptor: pipe.fileHandleForWriting.fileDescriptor)
_ = NIOPipeBootstrap(group: self.group)
.withPipes(inputDescriptor: try readHandle.takeDescriptorOwnership(), outputDescriptor: try writeHandle.takeDescriptorOwnership())
.flatMap({ channel in
channel.close()
}).always({ _ in
testGrp.leave()
})
} catch {
XCTFail("Failed to bootstrap pipechannel in eventloop: \(error)")
testGrp.leave()
}
}
testGrp.wait()
}
func testServerBootstrapAddsAcceptHandlerAfterServerChannelInitialiser() {
// It's unclear if this is the right solution, see https://github.com/apple/swift-nio/issues/1392
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)