Skip to content

Commit 83d9b68

Browse files
committed
Separate out Duration-based APIs so they aren't in the back-deployment libs
The back-deployed Swift Concurrency library should not contain anything based on clocks or durations, which aren't always available in the underlying system. Move that functionality to separate files that are excluded from the back-deployed concurrency libraries. This is a partial step to better approximate the back-deployment libraries. At some point, we'll stop building the back-deployment libraries entirely and instead use the binaries provided by the toolchain. Fixes rdar://89237163.
1 parent 8484f39 commit 83d9b68

File tree

4 files changed

+162
-138
lines changed

4 files changed

+162
-138
lines changed

stdlib/public/BackDeployConcurrency/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,11 @@ set(swift_concurrency_extra_sources
5858
"../stubs/SwiftNativeNSObject.mm")
5959
set(swift_concurrency_async_fp_mode "never")
6060

61+
set(LLVM_OPTIONAL_SOURCES
62+
Clock.cpp
63+
Clock.swift
64+
ContinuousClock.swift
65+
SuspendingClock.swift
66+
TaskSleepDuration.swift)
67+
6168
add_subdirectory(../Concurrency stdlib/public/BackDeployConcurrency)

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@
1111
#===----------------------------------------------------------------------===#
1212

1313
if(NOT swift_concurrency_extra_sources)
14-
set(swift_concurrency_extra_sources)
14+
set(swift_concurrency_extra_sources
15+
Clock.cpp
16+
Clock.swift
17+
ContinuousClock.swift
18+
SuspendingClock.swift
19+
TaskSleepDuration.swift)
1520
endif()
1621

1722
if(NOT swift_concurrency_install_component)
@@ -113,10 +118,6 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
113118
AsyncThrowingStream.swift
114119
AsyncStream.cpp
115120
Deque.swift
116-
Clock.cpp
117-
Clock.swift
118-
ContinuousClock.swift
119-
SuspendingClock.swift
120121
${swift_concurrency_extra_sources}
121122
linker-support/magic-symbols-for-install-name.c
122123

stdlib/public/Concurrency/TaskSleep.swift

Lines changed: 4 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ extension Task where Success == Never, Failure == Never {
3131

3232
/// The type of continuation used in the implementation of
3333
/// sleep(nanoseconds:).
34-
private typealias SleepContinuation = UnsafeContinuation<(), Error>
34+
typealias SleepContinuation = UnsafeContinuation<(), Error>
3535

3636
/// Describes the state of a sleep() operation.
37-
private enum SleepState {
37+
enum SleepState {
3838
/// The sleep continuation has not yet begun.
3939
case notStarted
4040

@@ -106,7 +106,7 @@ extension Task where Success == Never, Failure == Never {
106106

107107
/// Called when the sleep(nanoseconds:) operation woke up without being
108108
/// canceled.
109-
private static func onSleepWake(
109+
static func onSleepWake(
110110
_ wordPtr: UnsafeMutablePointer<Builtin.Word>
111111
) {
112112
while true {
@@ -150,7 +150,7 @@ extension Task where Success == Never, Failure == Never {
150150

151151
/// Called when the sleep(nanoseconds:) operation has been canceled before
152152
/// the sleep completed.
153-
private static func onSleepCancel(
153+
static func onSleepCancel(
154154
_ wordPtr: UnsafeMutablePointer<Builtin.Word>
155155
) {
156156
while true {
@@ -294,133 +294,4 @@ extension Task where Success == Never, Failure == Never {
294294
throw error
295295
}
296296
}
297-
298-
@available(SwiftStdlib 5.7, *)
299-
internal static func _sleep(
300-
until seconds: Int64, _ nanoseconds: Int64,
301-
tolerance: Duration?,
302-
clock: _ClockID
303-
) async throws {
304-
// Allocate storage for the storage word.
305-
let wordPtr = UnsafeMutablePointer<Builtin.Word>.allocate(capacity: 1)
306-
307-
// Initialize the flag word to "not started", which means the continuation
308-
// has neither been created nor completed.
309-
Builtin.atomicstore_seqcst_Word(
310-
wordPtr._rawValue, SleepState.notStarted.word._builtinWordValue)
311-
312-
do {
313-
// Install a cancellation handler to resume the continuation by
314-
// throwing CancellationError.
315-
try await withTaskCancellationHandler {
316-
let _: () = try await withUnsafeThrowingContinuation { continuation in
317-
while true {
318-
let state = SleepState(loading: wordPtr)
319-
switch state {
320-
case .notStarted:
321-
// The word that describes the active continuation state.
322-
let continuationWord =
323-
SleepState.activeContinuation(continuation).word
324-
325-
// Try to swap in the continuation word.
326-
let (_, won) = Builtin.cmpxchg_seqcst_seqcst_Word(
327-
wordPtr._rawValue,
328-
state.word._builtinWordValue,
329-
continuationWord._builtinWordValue)
330-
if !Bool(_builtinBooleanLiteral: won) {
331-
// Keep trying!
332-
continue
333-
}
334-
335-
// Create a task that resumes the continuation normally if it
336-
// finishes first. Enqueue it directly with the delay, so it fires
337-
// when we're done sleeping.
338-
let sleepTaskFlags = taskCreateFlags(
339-
priority: nil, isChildTask: false, copyTaskLocals: false,
340-
inheritContext: false, enqueueJob: false,
341-
addPendingGroupTaskUnconditionally: false)
342-
let (sleepTask, _) = Builtin.createAsyncTask(sleepTaskFlags) {
343-
onSleepWake(wordPtr)
344-
}
345-
let toleranceSeconds: Int64
346-
let toleranceNanoseconds: Int64
347-
if let components = tolerance?.components {
348-
toleranceSeconds = components.seconds
349-
toleranceNanoseconds = components.attoseconds / 1_000_000_000
350-
} else {
351-
toleranceSeconds = 0
352-
toleranceNanoseconds = -1
353-
}
354-
355-
_enqueueJobGlobalWithDeadline(
356-
seconds, nanoseconds,
357-
toleranceSeconds, toleranceNanoseconds,
358-
clock.rawValue, Builtin.convertTaskToJob(sleepTask))
359-
return
360-
361-
case .activeContinuation, .finished:
362-
fatalError("Impossible to have multiple active continuations")
363-
364-
case .cancelled:
365-
fatalError("Impossible to have cancelled before we began")
366-
367-
case .cancelledBeforeStarted:
368-
// Finish the continuation normally. We'll throw later, after
369-
// we clean up.
370-
continuation.resume()
371-
return
372-
}
373-
}
374-
}
375-
} onCancel: {
376-
onSleepCancel(wordPtr)
377-
}
378-
379-
// Determine whether we got cancelled before we even started.
380-
let cancelledBeforeStarted: Bool
381-
switch SleepState(loading: wordPtr) {
382-
case .notStarted, .activeContinuation, .cancelled:
383-
fatalError("Invalid state for non-cancelled sleep task")
384-
385-
case .cancelledBeforeStarted:
386-
cancelledBeforeStarted = true
387-
388-
case .finished:
389-
cancelledBeforeStarted = false
390-
}
391-
392-
// We got here without being cancelled, so deallocate the storage for
393-
// the flag word and continuation.
394-
wordPtr.deallocate()
395-
396-
// If we got cancelled before we even started, through the cancellation
397-
// error now.
398-
if cancelledBeforeStarted {
399-
throw _Concurrency.CancellationError()
400-
}
401-
} catch {
402-
// The task was cancelled; propagate the error. The "on wake" task is
403-
// responsible for deallocating the flag word and continuation, if it's
404-
// still running.
405-
throw error
406-
}
407-
}
408-
409-
/// Suspends the current task until the given deadline within a tolerance.
410-
///
411-
/// If the task is canceled before the time ends, this function throws
412-
/// `CancellationError`.
413-
///
414-
/// This function doesn't block the underlying thread.
415-
///
416-
/// try await Task.sleep(until: .now + .seconds(3), clock: .continuous)
417-
///
418-
@available(SwiftStdlib 5.7, *)
419-
public static func sleep<C: Clock>(
420-
until deadline: C.Instant,
421-
tolerance: C.Instant.Duration? = nil,
422-
clock: C
423-
) async throws {
424-
try await clock.sleep(until: deadline, tolerance: tolerance)
425-
}
426297
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
import Swift
13+
@_implementationOnly import _SwiftConcurrencyShims
14+
15+
@available(SwiftStdlib 5.7, *)
16+
extension Task where Success == Never, Failure == Never {
17+
@available(SwiftStdlib 5.7, *)
18+
internal static func _sleep(
19+
until seconds: Int64, _ nanoseconds: Int64,
20+
tolerance: Duration?,
21+
clock: _ClockID
22+
) async throws {
23+
// Allocate storage for the storage word.
24+
let wordPtr = UnsafeMutablePointer<Builtin.Word>.allocate(capacity: 1)
25+
26+
// Initialize the flag word to "not started", which means the continuation
27+
// has neither been created nor completed.
28+
Builtin.atomicstore_seqcst_Word(
29+
wordPtr._rawValue, SleepState.notStarted.word._builtinWordValue)
30+
31+
do {
32+
// Install a cancellation handler to resume the continuation by
33+
// throwing CancellationError.
34+
try await withTaskCancellationHandler {
35+
let _: () = try await withUnsafeThrowingContinuation { continuation in
36+
while true {
37+
let state = SleepState(loading: wordPtr)
38+
switch state {
39+
case .notStarted:
40+
// The word that describes the active continuation state.
41+
let continuationWord =
42+
SleepState.activeContinuation(continuation).word
43+
44+
// Try to swap in the continuation word.
45+
let (_, won) = Builtin.cmpxchg_seqcst_seqcst_Word(
46+
wordPtr._rawValue,
47+
state.word._builtinWordValue,
48+
continuationWord._builtinWordValue)
49+
if !Bool(_builtinBooleanLiteral: won) {
50+
// Keep trying!
51+
continue
52+
}
53+
54+
// Create a task that resumes the continuation normally if it
55+
// finishes first. Enqueue it directly with the delay, so it fires
56+
// when we're done sleeping.
57+
let sleepTaskFlags = taskCreateFlags(
58+
priority: nil, isChildTask: false, copyTaskLocals: false,
59+
inheritContext: false, enqueueJob: false,
60+
addPendingGroupTaskUnconditionally: false)
61+
let (sleepTask, _) = Builtin.createAsyncTask(sleepTaskFlags) {
62+
onSleepWake(wordPtr)
63+
}
64+
let toleranceSeconds: Int64
65+
let toleranceNanoseconds: Int64
66+
if let components = tolerance?.components {
67+
toleranceSeconds = components.seconds
68+
toleranceNanoseconds = components.attoseconds / 1_000_000_000
69+
} else {
70+
toleranceSeconds = 0
71+
toleranceNanoseconds = -1
72+
}
73+
74+
_enqueueJobGlobalWithDeadline(
75+
seconds, nanoseconds,
76+
toleranceSeconds, toleranceNanoseconds,
77+
clock.rawValue, Builtin.convertTaskToJob(sleepTask))
78+
return
79+
80+
case .activeContinuation, .finished:
81+
fatalError("Impossible to have multiple active continuations")
82+
83+
case .cancelled:
84+
fatalError("Impossible to have cancelled before we began")
85+
86+
case .cancelledBeforeStarted:
87+
// Finish the continuation normally. We'll throw later, after
88+
// we clean up.
89+
continuation.resume()
90+
return
91+
}
92+
}
93+
}
94+
} onCancel: {
95+
onSleepCancel(wordPtr)
96+
}
97+
98+
// Determine whether we got cancelled before we even started.
99+
let cancelledBeforeStarted: Bool
100+
switch SleepState(loading: wordPtr) {
101+
case .notStarted, .activeContinuation, .cancelled:
102+
fatalError("Invalid state for non-cancelled sleep task")
103+
104+
case .cancelledBeforeStarted:
105+
cancelledBeforeStarted = true
106+
107+
case .finished:
108+
cancelledBeforeStarted = false
109+
}
110+
111+
// We got here without being cancelled, so deallocate the storage for
112+
// the flag word and continuation.
113+
wordPtr.deallocate()
114+
115+
// If we got cancelled before we even started, through the cancellation
116+
// error now.
117+
if cancelledBeforeStarted {
118+
throw _Concurrency.CancellationError()
119+
}
120+
} catch {
121+
// The task was cancelled; propagate the error. The "on wake" task is
122+
// responsible for deallocating the flag word and continuation, if it's
123+
// still running.
124+
throw error
125+
}
126+
}
127+
128+
/// Suspends the current task until the given deadline within a tolerance.
129+
///
130+
/// If the task is canceled before the time ends, this function throws
131+
/// `CancellationError`.
132+
///
133+
/// This function doesn't block the underlying thread.
134+
///
135+
/// try await Task.sleep(until: .now + .seconds(3), clock: .continuous)
136+
///
137+
@available(SwiftStdlib 5.7, *)
138+
public static func sleep<C: Clock>(
139+
until deadine: C.Instant,
140+
tolerance: C.Instant.Duration? = nil,
141+
clock: C
142+
) async throws {
143+
try await clock.sleep(until: deadine, tolerance: tolerance)
144+
}
145+
}

0 commit comments

Comments
 (0)