Skip to content

Commit

Permalink
Merge pull request #86 from swiftwasm/katei/clock-protocols
Browse files Browse the repository at this point in the history
Add `WallClock` and `MonotonicClock` protocols
  • Loading branch information
kateinoigakukun authored Apr 26, 2024
2 parents 949ff74 + 9622cc3 commit 43620d6
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 53 deletions.
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

0 comments on commit 43620d6

Please sign in to comment.