Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 68 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,21 @@ Tasks sent from a synchronous context to an asynchronous context in Swift Concur

```swift
@MainActor
func test_mainActor_taskOrdering() async {
var counter = 0
func testMainActorTaskOrdering() async {
actor Counter {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need a Counter type in this example but the below examples do need it. For the sake of consistency with the below additions I made this example test a dash more complicated.

func increment() -> Int {
count += 1
return count
}
var count = 0
}

let counter = Counter()
var tasks = [Task<Void, Never>]()
for iteration in 1...100 {
tasks.append(Task {
counter += 1
XCTAssertEqual(counter, iteration) // often fails
let incrementedCount = await counter.increment()
XCTAssertEqual(incrementedCount, iteration) // often fails
})
}
for task in tasks {
Expand Down Expand Up @@ -51,14 +59,35 @@ queue.async {
This task begins execution once the above one-second sleep completes.
*/
}
Task {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Task was meant to show that we were entering an async context, but I'm realizing now that the inclusion of the Task is more confusing than not. Nothing within the Task closure changed – I just removed the Task enclosure. I did the same thing further down as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to me. The reader can infer from the await below that we're in an asynchronous context.

await queue.await {
/*
`async` context that can return a value or throw an error.
Executes after all other enqueued work is completed.
Work enqueued after this task will wait for this task to complete.
*/
await queue.await {
/*
`async` context that can return a value or throw an error.
Executes after all other enqueued work is completed.
Work enqueued after this task will wait for this task to complete.
*/
}
```

With a `FIFOQueue` you can easily execute asynchronous tasks from a nonisolated context in FIFO order:
```swift
func testFIFOQueueOrdering() async {
actor Counter {
func increment() -> Int {
count += 1
return count
}
var count = 0
}

let counter = Counter()
let queue = FIFOQueue()
for iteration in 1...100 {
queue.async {
let incrementedCount = await counter.increment()
XCTAssertEqual(incrementedCount, iteration) // always succeeds
}
}
await queue.await { }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe clarify with a comment that we're ensuring all tasks complete before we end the test?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix this in #9 which builds on top of this change.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
```

Expand All @@ -80,14 +109,35 @@ queue.async {
This task begins execution once the above task suspends due to the one-second sleep.
*/
}
Task {
await queue.await {
/*
`async` context that can return a value or throw an error.
Executes after all other enqueued work has begun executing.
Work enqueued after this task will wait for this task to complete or suspend.
*/
await queue.await {
/*
`async` context that can return a value or throw an error.
Executes after all other enqueued work has begun executing.
Work enqueued after this task will wait for this task to complete or suspend.
*/
}
```

With an `ActorQueue` you can easily begin execution of asynchronous tasks from a nonisolated context in order:
```swift
func testActorQueueOrdering() async {
actor Counter {
func increment() -> Int {
count += 1
return count
}
var count = 0
}

let counter = Counter()
let queue = ActorQueue()
for iteration in 1...100 {
queue.async {
let incrementedCount = await counter.increment()
XCTAssertEqual(incrementedCount, iteration) // always succeeds
}
}
await queue.await { }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we also clarify the purpose of this line.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix this in #9 which builds on top of this change.

}
```
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we'd explain better how the ActorQueue and FIFOQueue differ in the above example test, but I'm wary of trying to communicate too many ideas at once.


Expand Down