Reputation: 1011
I found a data race in my Swift app by using the Thread Sanitizer, and so I made my first attempt to fix race conditions by converting the offending class
to an actor
. The crash that the race was causing seems to have gone away, but Thread Sanitizer is still saying there's a data race in the code, which I thought should be impossible in the actor
.
I can't post the entire actor
here, but here is the code where the race is occurring:
actor SampleActor {
private var things = Set<Int>()
func addThing(_ newThing: Int, seconds: Double) {
things.insert(newThing)
Task {
try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
self.things.remove(newThing)
}
}
}
// Code to cause the race
let sample = SampleActor()
for n in 0 ..< 1000 {
Task {
await sample.addThing(n, seconds: Double.random(in: 0...1.0))
}
}
I'm probably going about this the wrong way, but I need to add an object to the SampleActor
's set of objects, and have it be automatically removed after some amount of time.
Is there a better way to do this? And what am I missing about the use of actor
to avoid the data race in this case? Shouldn't SampleActor.things
be immune to races here because it's a property of an actor
?
Upvotes: 1
Views: 1436
Reputation: 437622
There is no data race here. This is a bug in TSAN. Reportedly they have been improving its interaction with actors, but as of Swift 5.7.2, at least, there are still issues. E.g., see https://github.com/apple/swift/issues/57902 or https://github.com/apple/swift/issues/57749.
As lorem ipsum said (+1), it is prudent to abandon the use of Task {…}
in addThing
, for more concise code that enjoys the benefits of structured concurrency, but that is not the issue here. For the time being, simply avoid using TSAN with Swift concurrency.
Upvotes: 1
Reputation: 29299
Remove the Task {
part in the actor
Add async
to the function
func addThing(_ newThing: Int, seconds: Double) async {
Upvotes: 5