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 WallClock and MonotonicClock protocols #86

Merged
merged 1 commit into from
Apr 26, 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
101 changes: 101 additions & 0 deletions Sources/WASI/Clock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import SystemExtras

/// WASI wall clock interface based on WASI Preview 2 `wall-clock` interface.
///
/// See also https://github.com/WebAssembly/wasi-clocks/blob/v0.2.0/wit/wall-clock.wit
public protocol WallClock {
/// An instant in time, in seconds and nanoseconds.
typealias Duration = (
seconds: UInt64,
nanoseconds: UInt32
)

/// Read the current value of the clock.
func now() throws -> Duration

/// Query the resolution of the clock.
///
/// The nanoseconds field of the output is always less than 1000000000.
func resolution() throws -> Duration
}

/// A wall clock that uses the system's wall clock.
public struct SystemWallClock: WallClock {
private var underlying: SystemExtras.Clock {
#if os(Linux)
return .boottime
#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
return .rawMonotonic
#elseif os(OpenBSD) || os(FreeBSD) || os(WASI)
return .monotonic
#else
#error("Unsupported platform")
#endif
}

public init() {}

public func now() throws -> WallClock.Duration {
let timeSpec = try WASIAbi.Errno.translatingPlatformErrno {
try underlying.currentTime()
}
return (seconds: UInt64(timeSpec.seconds), nanoseconds: UInt32(timeSpec.nanoseconds))
}

public func resolution() throws -> WallClock.Duration {
let timeSpec = try WASIAbi.Errno.translatingPlatformErrno {
try underlying.resolution()
}
return (seconds: UInt64(timeSpec.seconds), nanoseconds: UInt32(timeSpec.nanoseconds))
}
}

/// WASI monotonic clock interface based on WASI Preview 2 `monotonic-clock` interface.
///
/// See also https://github.com/WebAssembly/wasi-clocks/blob/v0.2.0/wit/monotonic-clock.wit
public protocol MonotonicClock {
/// An instant in time, in nanoseconds.
typealias Instant = UInt64
/// A duration of time, in nanoseconds.
typealias Duration = UInt64

/// Read the current value of the clock.
func now() throws -> Instant

/// Query the resolution of the clock. Returns the duration of time
/// corresponding to a clock tick.
func resolution() throws -> Duration
}

/// A monotonic clock that uses the system's monotonic clock.
public struct SystemMonotonicClock: MonotonicClock {
private var underlying: SystemExtras.Clock {
#if os(Linux)
return .monotonic
#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
return .rawUptime
#elseif os(WASI)
return .monotonic
#elseif os(OpenBSD) || os(FreeBSD)
return .uptime
#else
#error("Unsupported platform")
#endif
}

public init() {}

public func now() throws -> MonotonicClock.Instant {
let timeSpec = try WASIAbi.Errno.translatingPlatformErrno {
try underlying.currentTime()
}
return WASIAbi.Timestamp(platformTimeSpec: timeSpec)
}

public func resolution() throws -> MonotonicClock.Duration {
let timeSpec = try WASIAbi.Errno.translatingPlatformErrno {
try underlying.resolution()
}
return WASIAbi.Timestamp(platformTimeSpec: timeSpec)
}
}
11 changes: 8 additions & 3 deletions Sources/WASI/Platform/PlatformTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,17 @@ extension WASIAbi.Filestat {

extension WASIAbi.Timestamp {

fileprivate init(seconds: Int, nanoseconds: Int) {
self = UInt64(nanoseconds + seconds * 1_000_000_000)
fileprivate init(seconds: UInt64, nanoseconds: UInt64) {
self = nanoseconds + seconds * 1_000_000_000
}

init(platformTimeSpec timespec: Clock.TimeSpec) {
self.init(seconds: timespec.rawValue.tv_sec, nanoseconds: timespec.rawValue.tv_nsec)
self.init(seconds: UInt64(timespec.rawValue.tv_sec),
nanoseconds: UInt64(timespec.rawValue.tv_nsec))
}

init(wallClockDuration duration: WallClock.Duration) {
self.init(seconds: duration.seconds, nanoseconds: UInt64(duration.nanoseconds))
}
}

Expand Down
60 changes: 10 additions & 50 deletions Sources/WASI/WASI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,8 @@ public class WASIBridgeToHost: WASI {
private let args: [String]
private let environment: [String: String]
private var fdTable: FdTable
private let wallClock: WallClock
private let monotonicClock: MonotonicClock
private var randomGenerator: RandomBufferGenerator

public init(
Expand All @@ -1364,6 +1366,8 @@ public class WASIBridgeToHost: WASI {
stdin: FileDescriptor = .standardInput,
stdout: FileDescriptor = .standardOutput,
stderr: FileDescriptor = .standardError,
wallClock: WallClock = SystemWallClock(),
monotonicClock: MonotonicClock = SystemMonotonicClock(),
randomGenerator: RandomBufferGenerator = SystemRandomNumberGenerator()
) throws {
self.args = args
Expand All @@ -1387,6 +1391,8 @@ public class WASIBridgeToHost: WASI {
}
}
self.fdTable = fdTable
self.wallClock = wallClock
self.monotonicClock = monotonicClock
self.randomGenerator = randomGenerator
}

Expand Down Expand Up @@ -1446,73 +1452,27 @@ public class WASIBridgeToHost: WASI {
}

func clock_res_get(id: WASIAbi.ClockId) throws -> WASIAbi.Timestamp {
let clock: SystemExtras.Clock
switch id {
case .REALTIME:
#if os(Linux)
clock = .boottime
#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
clock = .rawMonotonic
#elseif os(OpenBSD) || os(FreeBSD) || os(WASI)
clock = .monotonic
#else
#error("Unsupported platform")
#endif
return WASIAbi.Timestamp(wallClockDuration: try wallClock.resolution())
case .MONOTONIC:
#if os(Linux)
clock = .monotonic
#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
clock = .rawUptime
#elseif os(WASI)
clock = .monotonic
#elseif os(OpenBSD) || os(FreeBSD)
clock = .uptime
#else
#error("Unsupported platform")
#endif
return try monotonicClock.resolution()
case .PROCESS_CPUTIME_ID, .THREAD_CPUTIME_ID:
throw WASIAbi.Errno.EBADF
}
let timeSpec = try WASIAbi.Errno.translatingPlatformErrno {
try clock.resolution()
}
return WASIAbi.Timestamp(platformTimeSpec: timeSpec)
}

func clock_time_get(
id: WASIAbi.ClockId, precision: WASIAbi.Timestamp
) throws -> WASIAbi.Timestamp {
let clock: SystemExtras.Clock
switch id {
case .REALTIME:
#if os(Linux)
clock = .boottime
#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
clock = .rawMonotonic
#elseif os(OpenBSD) || os(FreeBSD) || os(WASI)
clock = .monotonic
#else
#error("Unsupported platform")
#endif
return WASIAbi.Timestamp(wallClockDuration: try wallClock.now())
case .MONOTONIC:
#if os(Linux)
clock = .monotonic
#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
clock = .rawUptime
#elseif os(WASI)
clock = .monotonic
#elseif os(OpenBSD) || os(FreeBSD)
clock = .uptime
#else
#error("Unsupported platform")
#endif
return try monotonicClock.now()
case .PROCESS_CPUTIME_ID, .THREAD_CPUTIME_ID:
throw WASIAbi.Errno.EBADF
}
let timeSpec = try WASIAbi.Errno.translatingPlatformErrno {
try clock.currentTime()
}
return WASIAbi.Timestamp(platformTimeSpec: timeSpec)
}

func fd_advise(fd: WASIAbi.Fd, offset: WASIAbi.FileSize, length: WASIAbi.FileSize, advice: WASIAbi.Advice) throws {
Expand Down
Loading