Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 99c9965

Browse files
Support Clock-based sleep APIs (#216)
1 parent 60ca6b5 commit 99c9965

File tree

3 files changed

+98
-32
lines changed

3 files changed

+98
-32
lines changed

‎IntegrationTests/TestSuites/Sources/ConcurrencyTests/main.swift‎

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ import Darwin
88

99
#if compiler(>=5.5)
1010

11+
func performanceNow() -> Double {
12+
return JSObject.global.performance.now.function!().number!
13+
}
14+
15+
func measure(_ block: () async throws -> Void) async rethrows -> Double {
16+
let start = performanceNow()
17+
try await block()
18+
return performanceNow() - start
19+
}
20+
1121
func entrypoint() async throws {
1222
struct E: Error, Equatable {
1323
let value: Int
@@ -61,10 +71,10 @@ func entrypoint() async throws {
6171
}
6272

6373
try await asyncTest("Task.sleep(_:)") {
64-
let start = time(nil)
65-
try await Task.sleep(nanoseconds: 2_000_000_000)
66-
letdiff=difftime(time(nil), start);
67-
try expectGTE(diff, 2)
74+
let diff = tryawaitmeasure{
75+
try await Task.sleep(nanoseconds: 200_000_000)
76+
}
77+
try expectGTE(diff, 200)
6878
}
6979

7080
try await asyncTest("Job reordering based on priority") {
@@ -102,19 +112,19 @@ func entrypoint() async throws {
102112

103113
try await asyncTest("Async JSClosure") {
104114
let delayClosure = JSClosure.async { _ -> JSValue in
105-
try await Task.sleep(nanoseconds: 2_000_000_000)
115+
try await Task.sleep(nanoseconds: 200_000_000)
106116
return JSValue.number(3)
107117
}
108118
let delayObject = JSObject.global.Object.function!.new()
109119
delayObject.closure = delayClosure.jsValue
110120

111-
let start = time(nil)
112-
let promise = JSPromise(from: delayObject.closure!())
113-
try expectNotNil(promise)
114-
let result = try await promise!.value
115-
letdiff=difftime(time(nil), start)
116-
tryexpectGTE(diff,2)
117-
try expectEqual(result,.number(3))
121+
let diff = tryawaitmeasure{
122+
let promise = JSPromise(from: delayObject.closure!())
123+
try expectNotNil(promise)
124+
let result = try await promise!.value
125+
tryexpectEqual(result,.number(3))
126+
}
127+
try expectGTE(diff,200)
118128
}
119129

120130
try await asyncTest("Async JSPromise: then") {
@@ -124,18 +134,18 @@ func entrypoint() async throws {
124134
resolve(.success(JSValue.number(3)))
125135
return .undefined
126136
}.jsValue,
127-
1_000
137+
100
128138
)
129139
}
130140
let promise2 = promise.then { result in
131-
try await Task.sleep(nanoseconds: 1_000_000_000)
141+
try await Task.sleep(nanoseconds: 100_000_000)
132142
return String(result.number!)
133143
}
134-
let start = time(nil)
135-
let result = try await promise2.value
136-
letdiff=difftime(time(nil), start)
137-
tryexpectGTE(diff,2)
138-
try expectEqual(result,.string("3.0"))
144+
let diff = tryawaitmeasure{
145+
let result = try await promise2.value
146+
tryexpectEqual(result,.string("3.0"))
147+
}
148+
try expectGTE(diff,200)
139149
}
140150

141151
try await asyncTest("Async JSPromise: then(success:failure:)") {
@@ -145,7 +155,7 @@ func entrypoint() async throws {
145155
resolve(.failure(JSError(message: "test").jsValue))
146156
return .undefined
147157
}.jsValue,
148-
1_000
158+
100
149159
)
150160
}
151161
let promise2 = promise.then { _ in
@@ -164,26 +174,43 @@ func entrypoint() async throws {
164174
resolve(.failure(JSError(message: "test").jsValue))
165175
return .undefined
166176
}.jsValue,
167-
1_000
177+
100
168178
)
169179
}
170180
let promise2 = promise.catch { err in
171-
try await Task.sleep(nanoseconds: 1_000_000_000)
181+
try await Task.sleep(nanoseconds: 100_000_000)
172182
return err
173183
}
174-
let start = time(nil)
175-
let result = try await promise2.value
176-
letdiff=difftime(time(nil), start)
177-
tryexpectGTE(diff,2)
178-
try expectEqual(result.object?.message,.string("test"))
184+
let diff = tryawaitmeasure{
185+
let result = try await promise2.value
186+
tryexpectEqual(result.object?.message,.string("test"))
187+
}
188+
try expectGTE(diff,200)
179189
}
180190

181-
// FIXME(katei): Somehow it doesn't work due to a mysterious unreachable inst
182-
// at the end of thunk.
183-
// This issue is not only on JS host environment, but also on standalone coop executor.
184191
try await asyncTest("Task.sleep(nanoseconds:)") {
185-
try await Task.sleep(nanoseconds: 1_000_000_000)
192+
let diff = try await measure {
193+
try await Task.sleep(nanoseconds: 100_000_000)
194+
}
195+
try expectGTE(diff, 100)
196+
}
197+
198+
#if compiler(>=5.7)
199+
try await asyncTest("ContinuousClock.sleep") {
200+
let diff = try await measure {
201+
let c = ContinuousClock()
202+
try await c.sleep(until: .now + .milliseconds(100))
203+
}
204+
try expectGTE(diff, 99)
205+
}
206+
try await asyncTest("SuspendingClock.sleep") {
207+
let diff = try await measure {
208+
let c = SuspendingClock()
209+
try await c.sleep(until: .now + .milliseconds(100))
210+
}
211+
try expectGTE(diff, 99)
186212
}
213+
#endif
187214
}
188215

189216

‎Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
102102
}
103103
swift_task_enqueueGlobalWithDelay_hook = unsafeBitCast(swift_task_enqueueGlobalWithDelay_hook_impl, to: UnsafeMutableRawPointer?.self)
104104

105+
#if compiler(>=5.7)
106+
typealias swift_task_enqueueGlobalWithDeadline_hook_Fn = @convention(thin) (Int64, Int64, Int64, Int64, Int32, UnownedJob, swift_task_enqueueGlobalWithDelay_original) -> Void
107+
let swift_task_enqueueGlobalWithDeadline_hook_impl: swift_task_enqueueGlobalWithDeadline_hook_Fn = { sec, nsec, tsec, tnsec, clock, job, original in
108+
JavaScriptEventLoop.shared.enqueue(job, withDelay: sec, nsec, tsec, tnsec, clock)
109+
}
110+
swift_task_enqueueGlobalWithDeadline_hook = unsafeBitCast(swift_task_enqueueGlobalWithDeadline_hook_impl, to: UnsafeMutableRawPointer?.self)
111+
#endif
112+
105113
typealias swift_task_enqueueMainExecutor_hook_Fn = @convention(thin) (UnownedJob, swift_task_enqueueMainExecutor_original) -> Void
106114
let swift_task_enqueueMainExecutor_hook_impl: swift_task_enqueueMainExecutor_hook_Fn = { job, original in
107115
JavaScriptEventLoop.shared.enqueue(job)
@@ -127,6 +135,30 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
127135
}
128136
}
129137

138+
#if compiler(>=5.7)
139+
/// Taken from https://github.com/apple/swift/blob/d375c972f12128ec6055ed5f5337bfcae3ec67d8/stdlib/public/Concurrency/Clock.swift#L84-L88
140+
@_silgen_name("swift_get_time")
141+
internal func swift_get_time(
142+
_ seconds: UnsafeMutablePointer<Int64>,
143+
_ nanoseconds: UnsafeMutablePointer<Int64>,
144+
_ clock: CInt)
145+
146+
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
147+
extension JavaScriptEventLoop {
148+
fileprivate func enqueue(
149+
_ job: UnownedJob, withDelay seconds: Int64, _ nanoseconds: Int64,
150+
_ toleranceSec: Int64, _ toleranceNSec: Int64,
151+
_ clock: Int32
152+
) {
153+
var nowSec: Int64 = 0
154+
var nowNSec: Int64 = 0
155+
swift_get_time(&nowSec, &nowNSec, clock)
156+
let delayNanosec = (seconds - nowSec) * 1_000_000_000 + (nanoseconds - nowNSec)
157+
enqueue(job, withDelay: delayNanosec <= 0 ? 0 : UInt64(delayNanosec))
158+
}
159+
}
160+
#endif
161+
130162
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
131163
public extension JSPromise {
132164
/// Wait for the promise to complete, returning (or throwing) its result.

‎Sources/_CJavaScriptEventLoop/include/_CJavaScriptEventLoop.h‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,14 @@ typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDelay_original)(
3535
SWIFT_EXPORT_FROM(swift_Concurrency)
3636
void *_Nullable swift_task_enqueueGlobalWithDelay_hook;
3737

38-
unsigned long long foo;
38+
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDeadline_original)(
39+
long long sec,
40+
long long nsec,
41+
long long tsec,
42+
long long tnsec,
43+
int clock, Job *_Nonnull job);
44+
SWIFT_EXPORT_FROM(swift_Concurrency)
45+
void *_Nullable swift_task_enqueueGlobalWithDeadline_hook;
3946

4047
/// A hook to take over main executor enqueueing.
4148
typedef SWIFT_CC(swift) void (*swift_task_enqueueMainExecutor_original)(

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /