Skip to content

Instantly share code, notes, and snippets.

@manmal
Last active June 8, 2021 18:44
Show Gist options
  • Save manmal/caa6491e7981654604bbf4b96bc0427c to your computer and use it in GitHub Desktop.
Save manmal/caa6491e7981654604bbf4b96bc0427c to your computer and use it in GitHub Desktop.
Potential reentrancy problem in Swift Actors
import Foundation
// Crashes when count is set to 683. Works fine if count < 683.
// Usage:
// func testMessagePassing() throws {
// let coordinator = Coordinator()
// let expectation = XCTestExpectation()
// detach {
// await coordinator.simulateMessengers()
// expectation.fulfill()
// }
// XCTAssertEqual(XCTWaiter().wait(for: [expectation], timeout: 1), .completed)
// }
actor Coordinator {
private(set) var counter = 0
func shouldContinue(_ messenger: Messenger) -> Bool {
counter += 1
return messenger.recipient != nil
}
func simulateMessengers(count: Int = 682) async {
let messengers = generateMessengers(self, count: count)
await withTaskGroup(of: Void.self) { group in
group.spawn {
return await messengers[0].passMessageOn(())
}
}
print("shouldContinue was called " + String(counter) + " times")
}
}
actor Messenger {
let coordinator: Coordinator
let recipient: Messenger?
init(coordinator: Coordinator, recipient: Messenger?) {
self.coordinator = coordinator
self.recipient = recipient
}
func passMessageOn<T>(_ message: T) async {
guard
let recipient = recipient,
await coordinator.shouldContinue(self)
else {
return
}
await recipient.passMessageOn(message)
return
}
}
func generateMessengers(_ coordinator: Coordinator, count: Int) -> [Messenger] {
var actors = [Messenger]()
var previous: Messenger?
for _ in 0..<count {
let rambo = Messenger(coordinator: coordinator, recipient: previous)
previous = rambo
actors.append(rambo)
}
return actors.reversed()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment