Skip to content

Commit 2a41920

Browse files
committed
[Concurrency] Implement basic runDetached on top of createAsyncTaskFuture.
1 parent 069dfad commit 2a41920

File tree

1 file changed

+171
-15
lines changed

1 file changed

+171
-15
lines changed

stdlib/public/Concurrency/Task.swift

Lines changed: 171 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,32 +79,32 @@ extension Task {
7979
/// TODO: Define the details of task priority; It is likely to be a concept
8080
/// similar to Darwin Dispatch's QoS; bearing in mind that priority is not as
8181
/// much of a thing on other platforms (i.e. server side Linux systems).
82-
public struct Priority: Comparable {
83-
public static let `default`: Task.Priority = .init() // TODO: replace with actual values
82+
public enum Priority: Int, Comparable {
83+
case userInteractive = 0x21
84+
case userInitiated = 0x19
85+
case `default` = 0x15
86+
case utility = 0x11
87+
case background = 0x09
88+
case unspecified = 0x00
8489

85-
// TODO: specifics of implementation are not decided yet
86-
private let __value: Int = 0
87-
88-
public static func < (lhs: Self, rhs: Self) -> Bool {
89-
lhs.__value < rhs.__value
90+
public static func < (lhs: Priority, rhs: Priority) -> Bool {
91+
lhs.rawValue < rhs.rawValue
9092
}
9193
}
9294
}
9395

9496
// ==== Task Handle ------------------------------------------------------------
9597

9698
extension Task {
97-
9899
/// A task handle refers to an in-flight `Task`,
99100
/// allowing for potentially awaiting for its result or canceling it.
100101
///
101102
/// It is not a programming error to drop a handle without awaiting or canceling it,
102103
/// i.e. the task will run regardless of the handle still being present or not.
103104
/// Dropping a handle however means losing the ability to await on the task's result
104105
/// and losing the ability to cancel it.
105-
@_frozen
106106
public struct Handle<Success> {
107-
private let task: Builtin.NativeObject
107+
let task: Builtin.NativeObject
108108

109109
/// Wait for the task to complete, returning (or throwing) its result.
110110
///
@@ -121,7 +121,18 @@ extension Task {
121121
/// and throwing a specific error or using `checkCancellation` the error
122122
/// thrown out of the task will be re-thrown here.
123123
public func get() async throws -> Success {
124-
fatalError("\(#function) not implemented yet.")
124+
let rawResult = taskFutureWait(
125+
on: task, waiting: Builtin.getCurrentAsyncTask())
126+
switch TaskFutureWaitResult<Success>(raw: rawResult) {
127+
case .executing:
128+
fatalError("don't know how to synchronously return")
129+
130+
case .success(let result):
131+
return result
132+
133+
case .failure(let error):
134+
throw error
135+
}
125136
}
126137

127138
/// Attempt to cancel the task.
@@ -138,6 +149,78 @@ extension Task {
138149
}
139150
}
140151

152+
// ==== Job Flags --------------------------------------------------------------
153+
154+
extension Task {
155+
/// Flags for schedulable jobs.
156+
///
157+
/// This is a port of the C++ FlagSet.
158+
struct JobFlags {
159+
/// Kinds of schedulable jobs.
160+
enum Kind : Int {
161+
case task = 0
162+
};
163+
164+
/// The actual bit representation of these flags.
165+
var bits: Int = 0
166+
167+
/// The kind of job described by these flags.
168+
var kind: Kind {
169+
get {
170+
Kind(rawValue: bits & 0xFF)!
171+
}
172+
173+
set {
174+
bits = (bits & ~0xFF) | newValue.rawValue
175+
}
176+
}
177+
178+
/// Whether this is an asynchronous task.
179+
var isAsyncTask: Bool { kind == .task }
180+
181+
/// The priority given to the job.
182+
var priority: Priority {
183+
get {
184+
Priority(rawValue: (bits & 0xFF00) >> 8)!
185+
}
186+
187+
set {
188+
bits = (bits & ~0xFF00) | (newValue.rawValue << 8)
189+
}
190+
}
191+
192+
/// Whether this is a child task.
193+
var isChildTask: Bool {
194+
get {
195+
(bits & (1 << 24)) != 0
196+
}
197+
198+
set {
199+
if newValue {
200+
bits = bits | 1 << 24
201+
} else {
202+
bits = (bits & ~(1 << 24))
203+
}
204+
}
205+
}
206+
207+
/// Whether this is a future.
208+
var isFuture: Bool {
209+
get {
210+
(bits & (1 << 25)) != 0
211+
}
212+
213+
set {
214+
if newValue {
215+
bits = bits | 1 << 25
216+
} else {
217+
bits = (bits & ~(1 << 25))
218+
}
219+
}
220+
}
221+
}
222+
}
223+
141224
// ==== Detached Tasks ---------------------------------------------------------
142225

143226
extension Task {
@@ -172,9 +255,22 @@ extension Task {
172255
/// tasks result or `cancel` it.
173256
public static func runDetached<T>(
174257
priority: Priority = .default,
175-
operation: () async -> T
258+
operation: @escaping () async -> T
176259
) -> Handle<T> {
177-
fatalError("\(#function) not implemented yet.")
260+
// Set up the job flags for a new task.
261+
var flags = JobFlags()
262+
flags.kind = .task
263+
flags.priority = priority
264+
flags.isFuture = true
265+
266+
// Create the asynchronous task future.
267+
let (task, context) =
268+
Builtin.createAsyncTaskFuture(flags.bits, nil, operation)
269+
270+
// FIXME: Launch the task on an executor... somewhere....
271+
runTask(task)
272+
273+
return Handle<T>(task: task)
178274
}
179275

180276
/// Run given throwing `operation` as part of a new top-level task.
@@ -209,9 +305,24 @@ extension Task {
209305
/// throw the error the operation has thrown when awaited on.
210306
public static func runDetached<T>(
211307
priority: Priority = .default,
212-
operation: () async throws -> T
308+
operation: @escaping () async throws -> T
213309
) -> Handle<T> {
214-
fatalError("\(#function) not implemented yet.")
310+
// Set up the job flags for a new task.
311+
var flags = JobFlags()
312+
flags.kind = .task
313+
flags.priority = priority
314+
flags.isFuture = true
315+
316+
// Create the asynchronous task future.
317+
let (task, context) =
318+
Builtin.createAsyncTaskFuture(flags.bits, nil, operation)
319+
320+
print(task)
321+
322+
// FIXME: Launch the task on an executor... somewhere....
323+
runTask(task)
324+
325+
return Handle<T>(task: task)
215326
}
216327
}
217328

@@ -314,3 +425,48 @@ public func runAsync(_ asyncFun: @escaping () async -> ()) {
314425
let childTask = Builtin.createAsyncTask(0, nil, asyncFun)
315426
runTask(childTask.0)
316427
}
428+
429+
/// Describes the result of waiting for a future.
430+
enum TaskFutureWaitResult<T> {
431+
/// The future is still executing, and our waiting task has been placed
432+
/// on its queue for when the future completes.
433+
case executing
434+
435+
/// The future has succeeded with the given value.
436+
case success(T)
437+
438+
/// The future has thrown the given error.
439+
case failure(Error)
440+
441+
/// Initialize this instance from a raw result, taking any instance within
442+
/// that result.
443+
init(raw: RawTaskFutureWaitResult) {
444+
switch raw.kind {
445+
case 0:
446+
self = .executing
447+
448+
case 1:
449+
// Take the value on success
450+
let storagePtr = raw.storage.bindMemory(to: T.self, capacity: 1)
451+
self = .success(UnsafeMutablePointer<T>(mutating: storagePtr).move())
452+
453+
case 2:
454+
// Take the error on error.
455+
self = .failure(unsafeBitCast(raw.storage, to: Error.self))
456+
457+
default:
458+
assert(false)
459+
self = .executing
460+
}
461+
}
462+
}
463+
464+
struct RawTaskFutureWaitResult {
465+
let kind: Int
466+
let storage: UnsafeRawPointer
467+
}
468+
469+
@_silgen_name("swift_task_future_wait")
470+
func taskFutureWait(
471+
on task: Builtin.NativeObject, waiting waitingTask: Builtin.NativeObject
472+
) -> RawTaskFutureWaitResult

0 commit comments

Comments
 (0)