-
Notifications
You must be signed in to change notification settings - Fork 20
Schedule timers with a NetworkDuration instead of whole milliseconds
#115
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,9 +83,7 @@ public final class NetworkContext: NetworkContextProtocol, @unchecked Sendable { | |
| /// Runs an immediate task. No assumptions are made about how the task is run. | ||
| func runImmediate(_ task: @escaping (() -> Void)) | ||
| /// Schedules a task to run after a delay, using a reference. | ||
| /// | ||
| /// The `milliseconds` parameter specifies the delay before the task runs. | ||
| func schedule(_ task: @escaping (() -> Void), milliseconds: Int64, reference: TimerReference) | ||
| func schedule(_ task: @escaping (() -> Void), after delay: NetworkDuration, reference: TimerReference) | ||
| /// Unschedules a task with a reference. | ||
| func unschedule(reference: TimerReference) | ||
| /// A Boolean value that indicates whether the current code is running in the scheduler. | ||
|
|
@@ -368,10 +366,18 @@ extension NetworkContext { | |
| globals.queue.async(execute: DispatchWorkItem(block: task)) | ||
| } | ||
| /// Schedules a task to run after a delay, using a reference. | ||
| /// | ||
| /// The `milliseconds` parameter specifies the delay before the task runs. | ||
| func schedule(_ task: @escaping (() -> Void), milliseconds: Int64, reference: TimerReference) { | ||
| let targetTime = DispatchTime.now() + DispatchTimeInterval.milliseconds(Int(milliseconds)) | ||
| func schedule(_ task: @escaping (() -> Void), after delay: NetworkDuration, reference: TimerReference) { | ||
| let nanoseconds = max(delay.nanoseconds, 0) | ||
| let seconds = nanoseconds / 1_000_000_000 | ||
| // A sub-second delay is the common case and its nanosecond count fits an `Int` on every | ||
| // platform, so it costs one addition. Longer delays are split because | ||
| // `DispatchTimeInterval` takes an `Int`, which is 32 bits on 32-bit watchOS. | ||
| let targetTime = | ||
| seconds == 0 | ||
| ? DispatchTime.now() + .nanoseconds(Int(nanoseconds)) | ||
| : DispatchTime.now() | ||
| + .seconds(Int(clamping: seconds)) | ||
| + .nanoseconds(Int(clamping: nanoseconds % 1_000_000_000)) | ||
| globals.timerList.insert(targetTime: targetTime, reference: reference, task: task) | ||
| } | ||
| /// Unschedules a task with a reference. | ||
|
|
@@ -417,12 +423,16 @@ extension NetworkContext { | |
|
|
||
| enum FutureTime { | ||
| case unschedule | ||
| case milliseconds(UInt64, () -> Void) // Milliseconds into the future | ||
| /// A delay and the task to run once it elapses. | ||
| /// | ||
| /// A negative delay runs the task at the first opportunity. The delay resolves nanoseconds, | ||
| /// but how finely a scheduler can honour it is the scheduler's own limit. | ||
| case after(NetworkDuration, () -> Void) | ||
| } | ||
|
|
||
| public func scheduleTimer(duration: NetworkDuration, completion: @escaping () -> Void) -> TimerReference { | ||
| let newReference = TimerReference() | ||
| resetTimer(for: newReference, to: .milliseconds(UInt64(duration.milliseconds), completion)) | ||
| resetTimer(for: newReference, to: .after(duration, completion)) | ||
| return newReference | ||
| } | ||
|
|
||
|
|
@@ -435,8 +445,8 @@ extension NetworkContext { | |
| switch time { | ||
| case .unschedule: | ||
| scheduler.unschedule(reference: reference) | ||
| case .milliseconds(let milliseconds, let block): | ||
| scheduler.schedule(block, milliseconds: Int64(milliseconds), reference: reference) | ||
| case .after(let delay, let block): | ||
| scheduler.schedule(block, after: delay, reference: reference) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am guessing its a wash, but just to make sure can you profile QUICTransfer with CPU trace to make sure that since we are operating on an object now that we did not incur a spike in CPU?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From what I can see the only difference is that the construction of the |
||
| } | ||
| } | ||
| #endif | ||
|
|
||
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 think you marked this with the corresponding label but this will force a change here:
https://github.com/apple/swift-nio-quic/blob/main/Sources/NIOQUIC/SwiftNetwork/QUICChannelEventLoop.swift#L60
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.
Yes, I believe that Tommy either has or is planning API-breaking changes so getting them all in the same release can be good for adopters.
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 this will be breaking