Update `NIOTSEventLoop` per apple/swift-nio#1508 (#84)
Motivation: See apple/swift-nio#1508 for rationale Modifications: Provides implementations of `preconditionInEventLoop(_:file:line:)` and `preconditionNotInEventLoop(_:file:line:)`. Result: NIOTSEventLoop will now use dispatchPrecondition(condition:) for the in/not in preconditions instead of relying on the default implementation, yielding improved correctness.
This commit is contained in:
parent
7c74f62ea7
commit
fe7e340f41
|
|
@ -50,7 +50,6 @@ fileprivate enum LifecycleState {
|
|||
case closed
|
||||
}
|
||||
|
||||
|
||||
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
|
||||
internal class NIOTSEventLoop: QoSEventLoop {
|
||||
private let loop: DispatchQueue
|
||||
|
|
@ -81,6 +80,18 @@ internal class NIOTSEventLoop: QoSEventLoop {
|
|||
/// implementation *could* return `true`, but this version will be unable to prove that and will return `false`.
|
||||
/// If you need to write an assertion about being in the event loop that must be correct, use SwiftNIO 1.11 or
|
||||
/// later and call `preconditionInEventLoop` and `assertInEventLoop`.
|
||||
///
|
||||
/// Further detail: The use of `DispatchQueue.sync(execute:)` to submit a block to a queue synchronously has the
|
||||
/// effect of creating a state where the currently executing code is on two queues simultaneously - the one which
|
||||
/// submitted the block, and the one on which the block runs. If another synchronous block is dispatched to a
|
||||
/// third queue, that block is effectively running all three at once. Unfortunately, libdispatch maintains only
|
||||
/// one "current" queue at a time as far as `DispatchQueue.getSpecific(key:)` is concerned, and it's always the
|
||||
/// one actually executing code at the time. Therefore the queue belonging to the original event loop can't be
|
||||
/// detected using its queue-specific data. No alternative API for the purpose exists (aside from assertions via
|
||||
/// `dispatchPrecondition(condition:)`). Under these circumstances, `inEventLoop` will incorrectly be `false`,
|
||||
/// even though the current code _is_ actually on the loop's queue. The only way to avoid this is to ensure no
|
||||
/// callers ever use synchronous dispatch (which is impossible to enforce), or to hope that a future version of
|
||||
/// libdispatch will provide a solution.
|
||||
public var inEventLoop: Bool {
|
||||
return DispatchQueue.getSpecific(key: self.inQueueKey) == self.loopID
|
||||
}
|
||||
|
|
@ -149,9 +160,18 @@ internal class NIOTSEventLoop: QoSEventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
func preconditionInEventLoop(file: StaticString, line: UInt) {
|
||||
public func preconditionInEventLoop(_ message: @autoclosure() -> String, file: StaticString, line: UInt) {
|
||||
/// `dispatchPrecondition(condition:)` is unable to accept an error message for the failure case, and we
|
||||
/// can not safely rely on `self.inEventLoop` in this implementation. Any message provided by the caller
|
||||
/// must therefore be ignored. We do not foresee ever being able to fix this behavior, given the design
|
||||
/// constraints of the respective subsystems.
|
||||
dispatchPrecondition(condition: .onQueue(self.loop))
|
||||
}
|
||||
|
||||
public func preconditionNotInEventLoop(_ message: @autoclosure() -> String = "", file: StaticString, line: UInt) {
|
||||
/// As in our counterpart method, we can't do anything with the `message` parameter. See above for details.
|
||||
dispatchPrecondition(condition: .notOnQueue(self.loop))
|
||||
}
|
||||
}
|
||||
|
||||
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
|
||||
|
|
|
|||
Loading…
Reference in New Issue