Skip to content

Commit 80ef80f

Browse files
authored
Merge pull request #41809 from DougGregor/back-deploy-without-duration
2 parents ae1ca94 + 4197128 commit 80ef80f

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 deadline: C.Instant,
140+
tolerance: C.Instant.Duration? = nil,
141+
clock: C
142+
) async throws {
143+
try await clock.sleep(until: deadline, tolerance: tolerance)
144+
}
145+
}

0 commit comments

Comments
 (0)