I have a process which runs for a long time and which I would like the ability to interrupt.
func longProcess (shouldAbort: @escaping ()->Bool) {
// Runs a long loop and periodically checks shouldAbort(),
// returning early if shouldAbort() returns true
}
Here's my class which uses it:
class Example {
private var abortFlag: NSObject? = .init()
private var dispatchQueue: DispatchQueue = .init(label: "Example")
func startProcess () {
let shouldAbort: ()->Bool = { [weak abortFlag] in
return abortFlag == nil
}
dispatchQueue.async {
longProcess(shouldAbort: shouldAbort)
}
}
func abortProcess () {
self.abortFlag = nil
}
}
The shouldAbort
closure captures a weak
reference to abortFlag
, and checks whether that reference points to nil
or to an NSObject
. Since the reference is weak
, if the original NSObject
is deallocated then the reference that is captured by the closure will suddenly be nil
and the closure will start returning true
. The closure will be called repeatedly during the longProcess
function, which is occurring on the private dispatchQueue
. The abortProcess
method on the Example
class will be externally called from some other queue. What if someone calls abortProcess()
, thereby deallocating abortFlag
, at the exact same time that longProcess
is trying to perform the check to see if abortFlag
has been deallocated yet? Is checking myWeakReference == nil
a thread-safe operation?
I saw this bug (Weak properties are not thread safe when reading SR-192) indicating that weak reference reads weren't thread safe, but it has been fixed, which suggests that (absent any bugs in the runtime), weak reference reads are intended to be thread safe.
Also interesting: Friday Q&A 2017-09-22: Swift 4 Weak References by Mike Ash