Channel.getOption(...) should return EventLoopFuture and not block

Motivation:

Channel.getOption(...) should return an EventLoopFuture to ensure we not end up with a deadlock.

Modifications:

Change getOption(...) to return EventLoopFuture.

Result:

No deadlocks and more consistent API.
This commit is contained in:
Norman Maurer 2018-02-21 11:55:15 +01:00
parent adbcd7c4a8
commit 2180d03690
4 changed files with 14 additions and 9 deletions

View File

@ -66,7 +66,7 @@ public protocol Channel : class, ChannelOutboundInvoker {
func setOption<T: ChannelOption>(option: T, value: T.OptionType) -> EventLoopFuture<Void>
/// Get the value of `option` for this `Channel`.
func getOption<T: ChannelOption>(option: T) throws -> T.OptionType
func getOption<T: ChannelOption>(option: T) -> EventLoopFuture<T.OptionType>
/// Returns if this `Channel` is currently writable.
var isWritable: Bool { get }

View File

@ -103,8 +103,8 @@ internal final class DeadChannel: Channel {
return EventLoopFuture(eventLoop: self.pipeline.eventLoop, error: ChannelError.ioOnClosedChannel, file: #file, line: #line)
}
func getOption<T>(option: T) throws -> T.OptionType where T : ChannelOption {
throw ChannelError.ioOnClosedChannel
func getOption<T>(option: T) -> EventLoopFuture<T.OptionType> where T : ChannelOption {
return eventLoop.newFailedFuture(error: ChannelError.ioOnClosedChannel)
}
let isWritable = false

View File

@ -336,9 +336,9 @@ public class EmbeddedChannel : Channel {
fatalError("no options supported")
}
public func getOption<T>(option: T) throws -> T.OptionType where T : ChannelOption {
public func getOption<T>(option: T) -> EventLoopFuture<T.OptionType> where T : ChannelOption {
if option is AutoReadOption {
return true as! T.OptionType
return self.eventLoop.newSucceededFuture(result: true as! T.OptionType)
}
fatalError("option \(option) not supported")
}

View File

@ -305,11 +305,15 @@ class BaseSocketChannel<T : BaseSocket> : SelectableChannel, ChannelCore {
}
}
public final func getOption<T: ChannelOption>(option: T) throws -> T.OptionType {
public func getOption<T>(option: T) -> EventLoopFuture<T.OptionType> where T : ChannelOption {
if eventLoop.inEventLoop {
return try getOption0(option: option)
do {
return eventLoop.newSucceededFuture(result: try getOption0(option: option))
} catch {
return eventLoop.newFailedFuture(error: error)
}
} else {
return try eventLoop.submit{ try self.getOption0(option: option) }.wait()
return eventLoop.submit { try self.getOption0(option: option) }
}
}
@ -575,7 +579,8 @@ class BaseSocketChannel<T : BaseSocket> : SelectableChannel, ChannelCore {
// ChannelError.eof is not something we want to fire through the pipeline as it just means the remote
// peer closed / shutdown the connection.
if let channelErr = err as? ChannelError, channelErr == ChannelError.eof {
if try! getOption(option: ChannelOptions.allowRemoteHalfClosure) {
// Directly call getOption0 as we are already on the EventLoop and so not need to create an extra future.
if try! getOption0(option: ChannelOptions.allowRemoteHalfClosure) {
// If we want to allow half closure we will just mark the input side of the Channel
// as closed.
pipeline.fireChannelReadComplete0()