I am currently working with some legacy code that presents significant challenges in terms of refactoring. As a result, I am unable to implement asynchronous getters in certain actors. Consequently, I opted to make one of the actor's properties non-isolated, which has led to the emergence of a data race.
actor SomeActor {
// laziness is used to silence a warning
// Original warning "'nonisolated' can not be applied to stored properties"
nonisolated private(set) lazy var num: Int = 0
func updateNum() {
num += 1
}
}
NSLocks do not seem to have an impact here.
I would appreciate any insights or recommendations you might have regarding this issue.
You can opt-out from using actor and implement synchorization using NSLock within class that you will mark as @unchecked Sendable (so that you have to ensure this on your own), if the goal is to have synchronous access.
When you use an NSLock, the runtime knows what thread you used it from, so when a higher priority thread wants to use the same lock, it can bump up the priority of the original thread to get it unlocked faster. When you are in an async context, the runtime doesn't know which thread to boost. You can use withLock, which has a non-async block as an input, to avoid this error.
But the shortened version, is that because async functions can abandon thread, and locks essentially has to be locked and unlocked from the same thread. Given that async function can resume on a different thread, using separate lock/unlock is unsafe. You can however use withLock as locking around code that has no suspension points is clearly safe.