Skip to content
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

Add EventLoop.now API for getting the current time #3015

Merged
merged 1 commit into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Sources/NIOCore/EventLoop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ public protocol EventLoop: EventLoopGroup {
@preconcurrency
func submit<T>(_ task: @escaping @Sendable () throws -> T) -> EventLoopFuture<T>

/// The current time of the event loop.
var now: NIODeadline { get }

/// Schedule a `task` that is executed by this `EventLoop` at the given time.
///
/// - Parameters:
Expand Down Expand Up @@ -394,6 +397,11 @@ public protocol EventLoop: EventLoopGroup {
func cancelScheduledCallback(_ scheduledCallback: NIOScheduledCallback)
}

extension EventLoop {
/// Default implementation of `now`: Returns `NIODeadline.now()`.
public var now: NIODeadline { .now() }
}

extension EventLoop {
/// Default implementation of `makeSucceededVoidFuture`: Return a fresh future (which will allocate).
public func makeSucceededVoidFuture() -> EventLoopFuture<Void> {
Expand Down
4 changes: 3 additions & 1 deletion Sources/NIOEmbedded/AsyncTestingEventLoop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ public final class NIOAsyncTestingEventLoop: EventLoop, @unchecked Sendable {
/// The current "time" for this event loop. This is an amount in nanoseconds.
/// As we need to access this from any thread, we store this as an atomic.
private let _now = ManagedAtomic<UInt64>(0)
internal var now: NIODeadline {

/// The current "time" for this event loop. This is an amount in nanoseconds.
public var now: NIODeadline {
NIODeadline.uptimeNanoseconds(self._now.load(ordering: .relaxed))
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/NIOEmbedded/Embedded.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ extension EmbeddedScheduledTask: Comparable {
/// responsible for ensuring they never call into the `EmbeddedEventLoop` in an
/// unsynchronized fashion.
public final class EmbeddedEventLoop: EventLoop, CustomStringConvertible {
private var _now: NIODeadline = .uptimeNanoseconds(0)
/// The current "time" for this event loop. This is an amount in nanoseconds.
internal var _now: NIODeadline = .uptimeNanoseconds(0)
public var now: NIODeadline { _now }

private enum State { case open, closing, closed }
private var state: State = .open
Expand Down
6 changes: 6 additions & 0 deletions Sources/NIOPosix/SelectableEventLoop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ internal final class SelectableEventLoop: EventLoop {
thread.isCurrent
}

/// - see: `EventLoop.now`
@usableFromInline
internal var now: NIODeadline {
.now()
}

/// - see: `EventLoop.scheduleTask(deadline:_:)`
@inlinable
internal func scheduleTask<T>(deadline: NIODeadline, _ task: @escaping () throws -> T) -> Scheduled<T> {
Expand Down
10 changes: 10 additions & 0 deletions Tests/NIOEmbeddedTests/AsyncTestingEventLoopTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -619,4 +619,14 @@ final class NIOAsyncTestingEventLoopTests: XCTestCase {
await eventLoop.advanceTime(by: .seconds(1))
XCTAssertEqual(counter.load(ordering: .relaxed), 3)
}

func testCurrentTime() async {
let eventLoop = NIOAsyncTestingEventLoop()

await eventLoop.advanceTime(to: .uptimeNanoseconds(42))
XCTAssertEqual(eventLoop.now, .uptimeNanoseconds(42))

await eventLoop.advanceTime(by: .nanoseconds(42))
XCTAssertEqual(eventLoop.now, .uptimeNanoseconds(84))
}
}
33 changes: 32 additions & 1 deletion Tests/NIOEmbeddedTests/EmbeddedEventLoopTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ public final class EmbeddedEventLoopTest: XCTestCase {
try eventLoop.syncShutdownGracefully()
childTasks.append(
scheduleRecursiveTask(
at: eventLoop._now + childTaskStartDelay,
at: eventLoop.now + childTaskStartDelay,
andChildTaskAfter: childTaskStartDelay
)
)
Expand Down Expand Up @@ -497,4 +497,35 @@ public final class EmbeddedEventLoopTest: XCTestCase {
eventLoop.advanceTime(by: .seconds(1))
XCTAssertEqual(counter, 3)
}

func testCurrentTime() {
let eventLoop = EmbeddedEventLoop()

eventLoop.advanceTime(to: .uptimeNanoseconds(42))
XCTAssertEqual(eventLoop.now, .uptimeNanoseconds(42))

eventLoop.advanceTime(by: .nanoseconds(42))
XCTAssertEqual(eventLoop.now, .uptimeNanoseconds(84))
}

func testScheduleRepeatedTask() {
let eventLoop = EmbeddedEventLoop()

var counter = 0
eventLoop.scheduleRepeatedTask(initialDelay: .seconds(1), delay: .seconds(1)) { repeatedTask in
guard counter < 10 else {
repeatedTask.cancel()
return
}
counter += 1
}

XCTAssertEqual(counter, 0)

eventLoop.advanceTime(by: .seconds(1))
XCTAssertEqual(counter, 1)

eventLoop.advanceTime(by: .seconds(9))
XCTAssertEqual(counter, 10)
}
}
8 changes: 8 additions & 0 deletions Tests/NIOPosixTests/EventLoopTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1962,6 +1962,10 @@ private class EventLoopWithPreSucceededFuture: EventLoop {
preconditionFailure("not implemented")
}

var now: NIODeadline {
preconditionFailure("not implemented")
}

@discardableResult
func scheduleTask<T>(deadline: NIODeadline, _ task: @escaping () throws -> T) -> Scheduled<T> {
preconditionFailure("not implemented")
Expand Down Expand Up @@ -2013,6 +2017,10 @@ private class EventLoopWithoutPreSucceededFuture: EventLoop {
preconditionFailure("not implemented")
}

var now: NIODeadline {
preconditionFailure("not implemented")
}

@discardableResult
func scheduleTask<T>(deadline: NIODeadline, _ task: @escaping () throws -> T) -> Scheduled<T> {
preconditionFailure("not implemented")
Expand Down
Loading