Skip to content

Commit 6ec1499

Browse files
authored
Merge pull request swiftlang#35054 from DougGregor/async-let-runtime
[Concurrency] Schedule "async let" child tasks.
2 parents e581602 + 79e6621 commit 6ec1499

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

stdlib/public/Concurrency/Task.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,10 @@ public func _runChildTask<T>(operation: @escaping () async throws -> T) async
428428
// Create the asynchronous task future.
429429
let (task, _) = Builtin.createAsyncTaskFuture(
430430
flags.bits, currentTask, operation)
431+
432+
// Enqueue the resulting job.
433+
_enqueueJobGlobal(Builtin.convertTaskToJob(task))
434+
431435
return task
432436
}
433437

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch)
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// REQUIRES: libdispatch
6+
7+
#if canImport(Darwin)
8+
import Darwin
9+
#elseif canImport(Glibc)
10+
import Glibc
11+
#endif
12+
13+
actor class Counter {
14+
private var value = 0
15+
private let scratchBuffer: UnsafeMutableBufferPointer<Int>
16+
17+
init(maxCount: Int) {
18+
scratchBuffer = .allocate(capacity: maxCount)
19+
}
20+
21+
func next() -> Int {
22+
let current = value
23+
24+
// Make sure we haven't produced this value before
25+
assert(scratchBuffer[current] == 0)
26+
scratchBuffer[current] = 1
27+
28+
value = value + 1
29+
return current
30+
}
31+
}
32+
33+
34+
func worker(
35+
identity: Int, counters: [Counter], numIterations: Int,
36+
scratchBuffer: UnsafeMutableBufferPointer<Int>
37+
) async {
38+
for i in 0..<numIterations {
39+
let counterIndex = Int.random(in: 0 ..< counters.count)
40+
let counter = counters[counterIndex]
41+
let nextValue = await counter.next()
42+
print("Worker \(identity) calling counter \(counterIndex) produced \(nextValue)")
43+
}
44+
}
45+
46+
func runTest(numCounters: Int, numWorkers: Int, numIterations: Int) async {
47+
let scratchBuffer = UnsafeMutableBufferPointer<Int>.allocate(
48+
capacity: numCounters * numWorkers * numIterations
49+
)
50+
51+
// Create counter actors.
52+
var counters: [Counter] = []
53+
for i in 0..<numCounters {
54+
counters.append(Counter(maxCount: numWorkers * numIterations))
55+
}
56+
57+
// Create a bunch of worker threads.
58+
var workers: [Task.Handle<Void>] = []
59+
for i in 0..<numWorkers {
60+
workers.append(
61+
Task.runDetached {
62+
usleep(UInt32.random(in: 0..<100) * 1000)
63+
await worker(
64+
identity: i, counters: counters, numIterations: numIterations,
65+
scratchBuffer: scratchBuffer
66+
)
67+
}
68+
)
69+
}
70+
71+
// Wait until all of the workers have finished.
72+
for worker in workers {
73+
await try! worker.get()
74+
}
75+
76+
// Clear out the scratch buffer.
77+
scratchBuffer.deallocate()
78+
print("DONE!")
79+
}
80+
81+
runAsyncAndBlock {
82+
await runTest(numCounters: 10, numWorkers: 100, numIterations: 1000)
83+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch)
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// REQUIRES: libdispatch
6+
7+
#if canImport(Darwin)
8+
import Darwin
9+
#elseif canImport(Glibc)
10+
import Glibc
11+
#endif
12+
13+
func fib(_ n: Int) -> Int {
14+
var first = 0
15+
var second = 1
16+
for _ in 0..<n {
17+
let temp = first
18+
first = second
19+
second = temp + first
20+
}
21+
return first
22+
}
23+
24+
func asyncFib(_ n: Int) async -> Int {
25+
if n == 0 || n == 1 {
26+
return n
27+
}
28+
29+
async let first = await asyncFib(n-2)
30+
async let second = await asyncFib(n-1)
31+
32+
// Sleep a random amount of time waiting on the result producing a result.
33+
usleep(UInt32.random(in: 0..<100) * 1000)
34+
35+
let result = await first + second
36+
37+
// Sleep a random amount of time before producing a result.
38+
usleep(UInt32.random(in: 0..<100) * 1000)
39+
40+
return result
41+
}
42+
43+
func runFibonacci(_ n: Int) async {
44+
let result = await asyncFib(n)
45+
46+
print()
47+
print("Async fib = \(result), sequential fib = \(fib(n))")
48+
assert(result == fib(n))
49+
}
50+
51+
runAsyncAndBlock {
52+
await runFibonacci(10)
53+
}

0 commit comments

Comments
 (0)