-
Notifications
You must be signed in to change notification settings - Fork 679
Add scheduleCallback
APIs to NIOIsolatedEventLoop
#3263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Sources/NIOCore/EventLoop.swift
Outdated
@discardableResult | ||
func _scheduleCallbackIsolatedUnsafeUnchecked( | ||
at deadline: NIODeadline, | ||
handler: some NIOScheduledCallbackHandler | ||
) throws -> NIOScheduledCallback | ||
|
||
@discardableResult | ||
func _scheduleCallbackIsolatedUnsafeUnchecked( | ||
in amount: TimeAmount, | ||
handler: some NIOScheduledCallbackHandler | ||
) throws -> NIOScheduledCallback |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Lukasa do we need those additional hooks on the EL? What was the reasoning behind adding the _scheduleCallbackIsolatedUnsafeUnchecked
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We needed it because the existing EL hooks require Sendable
, and they require a specific type. So it isn't possible to pass a T
through them that isn't Sendable
, because wrapping it into a NIOLoopBound
makes it not a T
anymore. In this case, a NIOLoopBound<some (NIOScheduledCallbackHandler & Sendable)>
isn't a some (NIOScheduledCallbackHandler & Sendable)
, so it cannot be passed through there.
Wrapping things in a sufficiently large number of loop bounds can work, but performs poorly, and we don't want to pessimise the performance of the on-loop case. That's what we want to work best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, if I understand you correctly adding the above methods is our intend outcome.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean, we want to work with our own unsafe transfer method in this PR, so that we don't use NIOLoopBound at all? To work around the preconditions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, so we do want to use assertions (and therefore the ...UnsafeUnchecked
method) rather than the NIOLoopBound
which provides a precondition
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I think we wrote that at the same time 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, and we should also implement a version that doesn't require this fallback for SelectableEventLoop
, EmbeddedEventLoop
, and AsyncTestingEventLoop
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, done. 97deac6
Should I leave those changes for a different PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much for the progress so far and for tackling this issue! @Lukasa can you please also give this an early look?
Sources/NIOCore/EventLoop.swift
Outdated
@discardableResult | ||
func _scheduleCallbackIsolatedUnsafeUnchecked( | ||
at deadline: NIODeadline, | ||
handler: some NIOScheduledCallbackHandler | ||
) throws -> NIOScheduledCallback | ||
|
||
@discardableResult | ||
func _scheduleCallbackIsolatedUnsafeUnchecked( | ||
in amount: TimeAmount, | ||
handler: some NIOScheduledCallbackHandler | ||
) throws -> NIOScheduledCallback |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We needed it because the existing EL hooks require Sendable
, and they require a specific type. So it isn't possible to pass a T
through them that isn't Sendable
, because wrapping it into a NIOLoopBound
makes it not a T
anymore. In this case, a NIOLoopBound<some (NIOScheduledCallbackHandler & Sendable)>
isn't a some (NIOScheduledCallbackHandler & Sendable)
, so it cannot be passed through there.
Wrapping things in a sufficiently large number of loop bounds can work, but performs poorly, and we don't want to pessimise the performance of the on-loop case. That's what we want to work best.
Okay so I think this is ready for a proper review |
@@ -170,3 +170,26 @@ extension EventLoop { | |||
} | |||
} | |||
} | |||
|
|||
@usableFromInline | |||
struct UnsafeUncheckedScheduledCallbackHandlerWrapper<Handler: NIOScheduledCallbackHandler>: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this needs to be unsafe. Instead, let's implement this in terms of NIOLoopBound
. That way we preserve the logical semantics, and avoid an @unchecked Sendable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we decide on using assertions rather than preconditions a few messages above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the unchecked fast path, yes. But the slower fallback is just that: slower. As a result, we can safely re-use things we already have, and avoid proliferating @unchecked Sendable
.
In general I have a plan to increasingly remove the @unchecked Sendable
s in NIO because there are far too many, and they can be very damaging. So wherever we can use an existing spelling for this, I'd prefer we did.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixes #3262 by adding the missing APIs.
Motivation:
As explained by the issue linked above, having a non-
Sendable
-requiring variant of the newscheduleCallback
APIs onNIOIsolatedEventLoop
can be useful since we're not always dealing withSendable
types.Modifications:
This adds the required
scheduleCallback
APIs toNIOIsolatedEventLoop
by wrapping the non-Sendable
NIOScheduleCallbackHandler
in aNIOLoopBound
-based handler.Result:
The two
scheduleCallback
s can be used onNIOIsolatedEventLoop
too.