diff --git a/IntegrationTests/tests_04_performance/Thresholds/5.10.json b/IntegrationTests/tests_04_performance/Thresholds/5.10.json index fd2d7003ad2..4d7ee39d60d 100644 --- a/IntegrationTests/tests_04_performance/Thresholds/5.10.json +++ b/IntegrationTests/tests_04_performance/Thresholds/5.10.json @@ -34,7 +34,7 @@ "encode_1000_ws_frames_new_buffer_with_space_with_mask": 5050, "execute_hop_10000_tasks": 0, "flat_schedule_10000_tasks": 130100, - "flat_schedule_assume_isolated_10000_tasks": 150100, + "flat_schedule_assume_isolated_10000_tasks": 110100, "future_assume_isolated_lots_of_callbacks": 74050, "future_erase_result": 4050, "future_lots_of_callbacks": 74050, diff --git a/IntegrationTests/tests_04_performance/Thresholds/5.9.json b/IntegrationTests/tests_04_performance/Thresholds/5.9.json index 760bb3b20b6..d130a3f008a 100644 --- a/IntegrationTests/tests_04_performance/Thresholds/5.9.json +++ b/IntegrationTests/tests_04_performance/Thresholds/5.9.json @@ -34,7 +34,7 @@ "encode_1000_ws_frames_new_buffer_with_space_with_mask": 5050, "execute_hop_10000_tasks": 0, "flat_schedule_10000_tasks": 130100, - "flat_schedule_assume_isolated_10000_tasks": 150100, + "flat_schedule_assume_isolated_10000_tasks": 110100, "future_assume_isolated_lots_of_callbacks": 74050, "future_erase_result": 4050, "future_lots_of_callbacks": 74050, diff --git a/IntegrationTests/tests_04_performance/Thresholds/6.0.json b/IntegrationTests/tests_04_performance/Thresholds/6.0.json index fd2d7003ad2..4d7ee39d60d 100644 --- a/IntegrationTests/tests_04_performance/Thresholds/6.0.json +++ b/IntegrationTests/tests_04_performance/Thresholds/6.0.json @@ -34,7 +34,7 @@ "encode_1000_ws_frames_new_buffer_with_space_with_mask": 5050, "execute_hop_10000_tasks": 0, "flat_schedule_10000_tasks": 130100, - "flat_schedule_assume_isolated_10000_tasks": 150100, + "flat_schedule_assume_isolated_10000_tasks": 110100, "future_assume_isolated_lots_of_callbacks": 74050, "future_erase_result": 4050, "future_lots_of_callbacks": 74050, diff --git a/IntegrationTests/tests_04_performance/Thresholds/nightly-6.0.json b/IntegrationTests/tests_04_performance/Thresholds/nightly-6.0.json index fd2d7003ad2..4d7ee39d60d 100644 --- a/IntegrationTests/tests_04_performance/Thresholds/nightly-6.0.json +++ b/IntegrationTests/tests_04_performance/Thresholds/nightly-6.0.json @@ -34,7 +34,7 @@ "encode_1000_ws_frames_new_buffer_with_space_with_mask": 5050, "execute_hop_10000_tasks": 0, "flat_schedule_10000_tasks": 130100, - "flat_schedule_assume_isolated_10000_tasks": 150100, + "flat_schedule_assume_isolated_10000_tasks": 110100, "future_assume_isolated_lots_of_callbacks": 74050, "future_erase_result": 4050, "future_lots_of_callbacks": 74050, diff --git a/IntegrationTests/tests_04_performance/Thresholds/nightly-main.json b/IntegrationTests/tests_04_performance/Thresholds/nightly-main.json index 194000e8e0a..273fc30e86a 100644 --- a/IntegrationTests/tests_04_performance/Thresholds/nightly-main.json +++ b/IntegrationTests/tests_04_performance/Thresholds/nightly-main.json @@ -34,7 +34,7 @@ "encode_1000_ws_frames_new_buffer_with_space_with_mask": 5050, "execute_hop_10000_tasks": 0, "flat_schedule_10000_tasks": 130100, - "flat_schedule_assume_isolated_10000_tasks": 150100, + "flat_schedule_assume_isolated_10000_tasks": 110100, "future_assume_isolated_lots_of_callbacks": 74050, "future_erase_result": 4050, "future_lots_of_callbacks": 74050, diff --git a/Sources/NIOCore/EventLoop.swift b/Sources/NIOCore/EventLoop.swift index 3a3fa4a6c08..98653bc5632 100644 --- a/Sources/NIOCore/EventLoop.swift +++ b/Sources/NIOCore/EventLoop.swift @@ -395,6 +395,69 @@ public protocol EventLoop: EventLoopGroup { /// /// - NOTE: Event loops only need to implemented this if they provide a custom scheduled callback implementation. func cancelScheduledCallback(_ scheduledCallback: NIOScheduledCallback) + + /// Submit a given task to be executed by the ``EventLoop``, from a context where the caller + /// statically knows that the context is isolated. + /// + /// This is an optional performance hook. ``EventLoop`` implementers are not required to implement + /// this witness, but may choose to do so to enable better performance of the isolated EL views. If + /// they do so, ``EventLoop/Isolated/execute`` will perform better. + func _executeIsolatedUnsafeUnchecked(_ task: @escaping () -> Void) + + /// Submit a given task to be executed by the ``EventLoop```, from a context where the caller + /// statically knows that the context is isolated. + /// + /// Once the execution is complete the returned ``EventLoopFuture`` is notified. + /// + /// This is an optional performance hook. ``EventLoop`` implementers are not required to implement + /// this witness, but may choose to do so to enable better performance of the isolated EL views. If + /// they do so, ``EventLoop/Isolated/submit`` will perform better. + /// + /// - Parameters: + /// - task: The closure that will be submitted to the ``EventLoop`` for execution. + /// - Returns: ``EventLoopFuture`` that is notified once the task was executed. + func _submitIsolatedUnsafeUnchecked(_ task: @escaping () throws -> T) -> EventLoopFuture + + /// Schedule a `task` that is executed by this ``EventLoop`` at the given time, from a context where the caller + /// statically knows that the context is isolated. + /// + /// This is an optional performance hook. ``EventLoop`` implementers are not required to implement + /// this witness, but may choose to do so to enable better performance of the isolated EL views. If + /// they do so, ``EventLoop/Isolated/scheduleTask(deadline:_:)`` will perform better. + /// + /// - Parameters: + /// - deadline: The instant in time before which the task will not execute. + /// - task: The synchronous task to run. As with everything that runs on the ``EventLoop```, it must not block. + /// - Returns: A ``Scheduled``` object which may be used to cancel the task if it has not yet run, or to wait + /// on the completion of the task. + /// + /// - Note: You can only cancel a task before it has started executing. + @discardableResult + func _scheduleTaskIsolatedUnsafeUnchecked( + deadline: NIODeadline, + _ task: @escaping () throws -> T + ) -> Scheduled + + /// Schedule a `task` that is executed by this ``EventLoop`` after the given amount of time, from a context where the caller + /// statically knows that the context is isolated. + /// + /// This is an optional performance hook. ``EventLoop`` implementers are not required to implement + /// this witness, but may choose to do so to enable better performance of the isolated EL views. If + /// they do so, ``EventLoop/Isolated/scheduleTask(in:_:)`` will perform better. + /// + /// - Parameters: + /// - in: The amount of time before which the task will not execute. + /// - task: The synchronous task to run. As with everything that runs on the ``EventLoop``, it must not block. + /// - Returns: A ``Scheduled`` object which may be used to cancel the task if it has not yet run, or to wait + /// on the completion of the task. + /// + /// - Note: You can only cancel a task before it has started executing. + /// - Note: The `in` value is clamped to a maximum when running on a Darwin-kernel. + @discardableResult + func _scheduleTaskIsolatedUnsafeUnchecked( + in: TimeAmount, + _ task: @escaping () throws -> T + ) -> Scheduled } extension EventLoop { @@ -422,6 +485,54 @@ extension EventLoop { { nil } + + /// Default implementation: wraps the task in an UnsafeTransfer. + @inlinable + public func _executeIsolatedUnsafeUnchecked(_ task: @escaping () -> Void) { + self.assertInEventLoop() + let unsafeTransfer = UnsafeTransfer(task) + self.execute { + unsafeTransfer.wrappedValue() + } + } + + /// Default implementation: wraps the task in an UnsafeTransfer. + @inlinable + public func _submitIsolatedUnsafeUnchecked(_ task: @escaping () throws -> T) -> EventLoopFuture { + self.assertInEventLoop() + let unsafeTransfer = UnsafeTransfer(task) + return self.submit { + try unsafeTransfer.wrappedValue() + } + } + + /// Default implementation: wraps the task in an UnsafeTransfer. + @inlinable + @discardableResult + public func _scheduleTaskIsolatedUnsafeUnchecked( + deadline: NIODeadline, + _ task: @escaping () throws -> T + ) -> Scheduled { + self.assertInEventLoop() + let unsafeTransfer = UnsafeTransfer(task) + return self.scheduleTask(deadline: deadline) { + try unsafeTransfer.wrappedValue() + } + } + + /// Default implementation: wraps the task in an UnsafeTransfer. + @inlinable + @discardableResult + public func _scheduleTaskIsolatedUnsafeUnchecked( + in delay: TimeAmount, + _ task: @escaping () throws -> T + ) -> Scheduled { + self.assertInEventLoop() + let unsafeTransfer = UnsafeTransfer(task) + return self.scheduleTask(in: delay) { + try unsafeTransfer.wrappedValue() + } + } } extension EventLoop { diff --git a/Sources/NIOCore/EventLoopFuture+AssumeIsolated.swift b/Sources/NIOCore/EventLoopFuture+AssumeIsolated.swift index f743550fd59..49593c1f533 100644 --- a/Sources/NIOCore/EventLoopFuture+AssumeIsolated.swift +++ b/Sources/NIOCore/EventLoopFuture+AssumeIsolated.swift @@ -34,10 +34,7 @@ public struct NIOIsolatedEventLoop { @available(*, noasync) @inlinable public func execute(_ task: @escaping () -> Void) { - let unsafeTransfer = UnsafeTransfer(task) - self._wrapped.execute { - unsafeTransfer.wrappedValue() - } + self._wrapped._executeIsolatedUnsafeUnchecked(task) } /// Submit a given task to be executed by the `EventLoop`. Once the execution is complete the returned `EventLoopFuture` is notified. @@ -48,10 +45,7 @@ public struct NIOIsolatedEventLoop { @available(*, noasync) @inlinable public func submit(_ task: @escaping () throws -> T) -> EventLoopFuture { - let unsafeTransfer = UnsafeTransfer(task) - return self._wrapped.submit { - try unsafeTransfer.wrappedValue() - } + self._wrapped._submitIsolatedUnsafeUnchecked(task) } /// Schedule a `task` that is executed by this `EventLoop` at the given time. @@ -70,10 +64,7 @@ public struct NIOIsolatedEventLoop { deadline: NIODeadline, _ task: @escaping () throws -> T ) -> Scheduled { - let unsafeTransfer = UnsafeTransfer(task) - return self._wrapped.scheduleTask(deadline: deadline) { - try unsafeTransfer.wrappedValue() - } + self._wrapped._scheduleTaskIsolatedUnsafeUnchecked(deadline: deadline, task) } /// Schedule a `task` that is executed by this `EventLoop` after the given amount of time. @@ -93,10 +84,7 @@ public struct NIOIsolatedEventLoop { in delay: TimeAmount, _ task: @escaping () throws -> T ) -> Scheduled { - let unsafeTransfer = UnsafeTransfer(task) - return self._wrapped.scheduleTask(in: delay) { - try unsafeTransfer.wrappedValue() - } + self._wrapped._scheduleTaskIsolatedUnsafeUnchecked(in: delay, task) } /// Schedule a `task` that is executed by this `EventLoop` at the given time. @@ -122,10 +110,11 @@ public struct NIOIsolatedEventLoop { line: UInt = #line, _ task: @escaping () throws -> EventLoopFuture ) -> Scheduled { - let unsafeTransfer = UnsafeTransfer(task) - return self._wrapped.flatScheduleTask(deadline: deadline, file: file, line: line) { - try unsafeTransfer.wrappedValue() - } + let promise: EventLoopPromise = self._wrapped.makePromise(file: file, line: line) + let scheduled = self.scheduleTask(deadline: deadline, task) + + scheduled.futureResult.flatMap { $0 }.cascade(to: promise) + return .init(promise: promise, cancellationTask: { scheduled.cancel() }) } /// Returns the wrapped event loop.