Skip to content

Commit b7cdc2f

Browse files
amartini51etcwildektoso
committed
Various minor corrections.
Co-authored-by: Evan Wilde <[email protected]> Co-authored-by: Konrad Malawski <[email protected]>
1 parent a77615f commit b7cdc2f

File tree

1 file changed

+40
-43
lines changed

1 file changed

+40
-43
lines changed

TSPL.docc/LanguageGuide/Concurrency.md

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -321,42 +321,33 @@ by calling the [`Task.yield()`][] method.
321321

322322
```swift
323323
func generateSlideshow(forGallery gallery: String) async {
324-
let photos = await listPhotos(inGallery: gallery)
325-
for photo in photos {
326-
// ... render a few seconds of video for this photo ...
327-
Task.yield()
328-
}
324+
let photos = await listPhotos(inGallery: gallery)
325+
for photo in photos {
326+
// ... render a few seconds of video for this photo ...
327+
await Task.yield()
328+
}
329329
}
330330
```
331331

332-
Because `generateSlideshow(for:)` is a synchronous function,
333-
it can't contain any potential suspension points.
334-
However, calling it repeatedly for every photograph in a large gallery
335-
might take a long time.
336-
Explicitly inserting a possible suspension point with `Task.yield()`
337-
lets Swift balance between making progress on this task,
332+
Assuming the code that renders video is synchronous,
333+
it doesn't contain any suspension points.
334+
However, that work might take a long time.
335+
Periodically calling `Task.yield()` explicitly adds suspension points,
336+
which let Swift balance between making progress on this work,
338337
and letting other tasks make progress on other work.
339-
Without the suspension point,
340-
this task would render the entire slideshow without ever yielding its thread.
341-
342-
<!--
343-
TR: Do we have some general guidance about when you need to insert yield?
344-
SE-0304 says "if all are executing on a shared, limited-concurrency pool".
345-
-->
346338

347-
The [`Task.sleep(until:tolerance:clock:)`][] method
339+
The [`Task.sleep(for:tolerance:clock:)`][] method
348340
is useful when writing simple code
349341
to learn how concurrency works.
350-
This method does nothing,
351-
but waits at least the given number of nanoseconds before it returns.
342+
This method suspends the current task for at least the given amount of time.
352343
Here's a version of the `listPhotos(inGallery:)` function
353-
that uses `sleep(until:tolerance:clock:)` to simulate waiting for a network operation:
344+
that uses `sleep(for:tolerance:clock:)` to simulate waiting for a network operation:
354345

355-
[`Task.sleep(until:tolerance:clock:)`]: https://developer.apple.com/documentation/swift/task/sleep(until:tolerance:clock:)
346+
[`Task.sleep(for:tolerance:clock:)`]: https://developer.apple.com/documentation/swift/task/sleep(for:tolerance:clock:)
356347

357348
```swift
358349
func listPhotos(inGallery name: String) async throws -> [String] {
359-
try await Task.sleep(until: .now + .seconds(2), clock: .continuous)
350+
try await Task.sleep(for: .seconds(2))
360351
return ["IMG001", "IMG99", "IMG0404"]
361352
}
362353
```
@@ -367,7 +358,7 @@ func listPhotos(inGallery name: String) async throws -> [String] {
367358
```swifttest
368359
>> struct Data {} // Instead of actually importing Foundation
369360
-> func listPhotos(inGallery name: String) async throws -> [String] {
370-
try await Task.sleep(until: .now + .seconds(2), clock: .continuous)
361+
try await Task.sleep(for: .seconds(2))
371362
return ["IMG001", "IMG99", "IMG0404"]
372363
}
373364
```
@@ -398,9 +389,9 @@ from nonthrowing code.
398389
For example:
399390

400391
```swift
401-
func getRainyWeekendPhotos() await -> Result<[String]> {
392+
func getRainyWeekendPhotos() async -> Result<[String]> {
402393
return Result {
403-
photos = try await listPhotos(inGallery: "A Rainy Weekend")
394+
try await listPhotos(inGallery: "A Rainy Weekend")
404395
}
405396
}
406397
```
@@ -409,8 +400,8 @@ In contrast,
409400
there's no safe way to wrap asynchronous code
410401
so you can call it from synchronous code and wait for the result.
411402
The Swift standard library intentionally omits this unsafe functionality,
412-
and trying to implement it yourself introduces
413-
problems like memory races, threading bugs, and deadlocks.
403+
and trying to implement it yourself can lead to
404+
problems like subtle races, threading issues, and deadlocks.
414405

415406
<!--
416407
OUTLINE
@@ -634,7 +625,7 @@ Because of the explicit relationship between tasks and task groups,
634625
this approach is called *structured concurrency*.
635626
Although you take on some of the responsibility for correctness,
636627
the explicit parent-child relationships between tasks
637-
let Swift handle some behaviors like propagating cancellation for you,
628+
lets Swift handle some behaviors like propagating cancellation for you,
638629
and lets Swift detect some errors at compile time.
639630

640631
[`TaskGroup`]: https://developer.apple.com/documentation/swift/taskgroup
@@ -645,17 +636,23 @@ Here's another version of the code to download photos
645636
that handles any number of photos:
646637

647638
```swift
648-
await withTaskGroup(of: Data.self) { taskGroup in
639+
await withTaskGroup(of: Data.self) { group in
649640
let photoNames = await listPhotos(inGallery: "Summer Vacation")
650641
for name in photoNames {
651-
taskGroup.addTask {
642+
group.addTask {
652643
let photo = downloadPhoto(named: name)
653644
show(photo)
654645
}
655646
}
656647
}
657648
```
658649

650+
<!-- XXX
651+
The above listing uses TaskGroup<Data> but doesn't return Data,
652+
and its child tasks also don't return Data.
653+
I think that's a mistake.
654+
-->
655+
659656
The code above creates a new task group,
660657
and then creates child tasks of that task group
661658
to download each photo is the gallery.
@@ -679,14 +676,14 @@ you add code that accumulates its result
679676
inside the closure you pass to `withTaskGroup(of:returning:body:)`.
680677

681678
```
682-
let photos = await withTaskGroup(of: Data.self) { taskGroup in
679+
let photos = await withTaskGroup(of: Data.self) { group in
683680
let photoNames = await listPhotos(inGallery: "Summer Vacation")
684681
for name in photoNames {
685-
taskGroup.addTask { await downloadPhoto(named: name) }
682+
group.addTask { await downloadPhoto(named: name) }
686683
}
687684
688685
var results: [Data] = []
689-
for await photo in taskGroup {
686+
for await photo in group {
690687
results.append(photo)
691688
}
692689
return results
@@ -726,7 +723,7 @@ There are two ways a task can do this:
726723
by calling the [`Task.checkCancellation()`][] method,
727724
or by reading the [`Task.isCancelled`][] property.
728725
Calling `checkCancellation()` throws an error if the task is canceled;
729-
a throwing task can let that error propagate out of the task,
726+
a throwing task can propagate the error out of the task,
730727
stopping all of the task's work.
731728
This has the advantage of being simple to implement and understand.
732729
For more flexibility, use the `isCancelled` property,
@@ -737,25 +734,25 @@ like closing network connections and deleting temporary files.
737734
[`Task.isCancelled`]: https://developer.apple.com/documentation/swift/task/3814832-iscancelled
738735

739736
```
740-
let photos = await withTaskGroup(of: Data.self) { taskGroup in
737+
let photos = await withTaskGroup(of: Optional<Data>.self) { group in
741738
let photoNames = await listPhotos(inGallery: "Summer Vacation")
742739
for name in photoNames {
743-
taskGroup.addTaskUnlessCancelled {
740+
group.addTaskUnlessCancelled {
744741
guard isCancelled == false else { return nil }
745742
return await downloadPhoto(named: name)
746743
}
747744
}
748745
749746
var results: [Data] = []
750-
for await photo in taskGroup {
747+
for await photo in group {
751748
if let photo { results.append(photo) }
752749
}
753750
return results
754751
}
755752
```
756753

757754
<!-- XXX TR:
758-
Should the listing above add a call to taskGroup.cancelAll()
755+
Should the listing above add a call to group.cancelAll()
759756
to show that part of cancellation?
760757
If so, what's the best practice for where to put that?
761758
-->
@@ -783,7 +780,7 @@ A full version of this code would include clean-up work
783780
as part of cancellation,
784781
like deleting partial downloads and closing network connections.
785782

786-
An task that isn't part of a task group can handle cancellation
783+
A task that isn't part of a task group can handle cancellation
787784
using the [`Task.withTaskCancellationHandler(operation:onCancel:)`][] method.
788785
For example:
789786

@@ -806,11 +803,11 @@ let video = await Task.withTaskCancellationHandler {
806803
807804
::
808805
809-
let handle = spawnDetached {
806+
let handle = Task.detached {
810807
await withTaskGroup(of: Bool.self) { group in
811808
var done = false
812809
while done {
813-
await group.spawn { Task.isCancelled } // is this child task canceled?
810+
await group.addTask { Task.isCancelled } // is this child task canceled?
814811
done = try await group.next() ?? false
815812
}
816813
print("done!") // <1>

0 commit comments

Comments
 (0)