Skip to content

Commit

Permalink
Add inverse of preconditionInEventLoop() and ability to add message…
Browse files Browse the repository at this point in the history
…s to the assertion (apple#1508)

Motivation:

It is valuable to be able to provide additional context for any kind of assertion failure, such as obvious points of API misuse or when the purpose of the assertion itself is unclear.

Modifications:

Added an overload of preconditionInEventLoop() that accepts a message autoclosure parameter in the same fashion as `Swift.precondition(_:file:line:)`. Default the implementation of the original version to calling the new one with an empty message. Add defaulted implementation of the new version which forwards the message closure to precondition(). Add the new message parameter directly to assertInEventLoop() without a forwarder since it is not a customization point.

Result:

It is now possible to optionally provide explanatory messages to be included when the "in event loop" precondition fails, as with any other precondition.
  • Loading branch information
gwynne authored and pull[bot] committed May 11, 2020
1 parent 5ef735c commit fea4e19
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 8 deletions.
33 changes: 28 additions & 5 deletions Sources/NIO/EventLoop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,13 @@ public protocol EventLoop: EventLoopGroup {
@discardableResult
func scheduleTask<T>(in: TimeAmount, _ task: @escaping () throws -> T) -> Scheduled<T>

/// Checks that this call is run from the `EventLoop`. If this is called from within the `EventLoop` this function
/// will have no effect, if called from outside the `EventLoop` it will crash the process with a trap.
/// Asserts that the current thread is the one tied to this `EventLoop`.
/// Otherwise, the process will be abnormally terminated as per the semantics of `preconditionFailure(_:file:line:)`.
func preconditionInEventLoop(file: StaticString, line: UInt)

/// Asserts that the current thread is _not_ the one tied to this `EventLoop`.
/// Otherwise, the process will be abnormally terminated as per the semantics of `preconditionFailure(_:file:line:)`.
func preconditionNotInEventLoop(file: StaticString, line: UInt)
}

extension EventLoopGroup {
Expand Down Expand Up @@ -580,9 +584,9 @@ extension EventLoop {
return EventLoopIterator([self])
}

/// Checks that this call is run from the EventLoop. If this is called from within the EventLoop this function will
/// have no effect, if called from outside the EventLoop it will crash the process with a trap if run in debug mode.
/// In release mode this function never has any effect.
/// Asserts that the current thread is the one tied to this `EventLoop`.
/// Otherwise, if running in debug mode, the process will be abnormally terminated as per the semantics of
/// `preconditionFailure(_:file:line:)`. Never has any effect in release mode.
///
/// - note: This is not a customization point so calls to this function can be fully optimized out in release mode.
@inlinable
Expand All @@ -592,10 +596,29 @@ extension EventLoop {
}
}

/// Asserts that the current thread is _not_ the one tied to this `EventLoop`.
/// Otherwise, if running in debug mode, the process will be abnormally terminated as per the semantics of
/// `preconditionFailure(_:file:line:)`. Never has any effect in release mode.
///
/// - note: This is not a customization point so calls to this function can be fully optimized out in release mode.
@inlinable
public func assertNotInEventLoop(file: StaticString = #file, line: UInt = #line) {
debugOnly {
self.preconditionNotInEventLoop(file: file, line: line)
}
}

/// Checks the necessary condition of currently running on the called `EventLoop` for making forward progress.
@inlinable
public func preconditionInEventLoop(file: StaticString = #file, line: UInt = #line) {
precondition(self.inEventLoop, file: file, line: line)
}

/// Checks the necessary condition of currently _not_ running on the called `EventLoop` for making forward progress.
@inlinable
public func preconditionNotInEventLoop(file: StaticString = #file, line: UInt = #line) {
precondition(!self.inEventLoop, file: file, line: line)
}
}

/// Internal representation of a `Registration` to an `Selector`.
Expand Down
6 changes: 3 additions & 3 deletions Sources/NIOTestUtils/NIOHTTP1TestServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -283,14 +283,14 @@ extension NIOHTTP1TestServer {
}

public func readInbound(deadline: NIODeadline = .now() + .seconds(10)) throws -> HTTPServerRequestPart {
assert(!self.eventLoop.inEventLoop)
self.eventLoop.assertNotInEventLoop()
return try self.eventLoop.submit { () -> BlockingQueue<HTTPServerRequestPart> in
self.inboundBuffer
}.wait().popFirst(deadline: deadline)
}

public func writeOutbound(_ data: HTTPServerResponsePart) throws {
assert(!self.eventLoop.inEventLoop)
self.eventLoop.assertNotInEventLoop()
try self.eventLoop.flatSubmit { () -> EventLoopFuture<Void> in
if let channel = self.currentClientChannel {
return channel.writeAndFlush(data)
Expand All @@ -301,7 +301,7 @@ extension NIOHTTP1TestServer {
}

public var serverPort: Int {
assert(!self.eventLoop.inEventLoop)
self.eventLoop.assertNotInEventLoop()
return self.serverChannel!.localAddress!.port!
}
}
Expand Down

0 comments on commit fea4e19

Please # to comment.