You should probably nil out the variable before resuming the continuing, to reduce the window in which a second invocation of doSomething can happen:
switch result {
case .success:
if let continuation = nillableContinuation {
nillableContinuation = nil
continuation.resume()
}
case .failure(let error):
if let continuation = nillableContinuation {
nillableContinuation = nil
continuation.resume(throwing: error)
}
}
However, if doSomething can be invoked simultaneously from multiple threads, that still won't avoid race conditions. You'd need to add some additional synchronization around the check of the continuation.
Sorry to hijack the thread, but I don’t understand what’s wrong with the original code. Is the continuation called multiple times because of re-entrancy? Where? I can see in general how resuming a continuation while the reference is around can result in subsequent invocations but here the reference is inside a function and resumption happens inside a closure so for the life of me I cannot explain how this code fails.
in this case everything is ok, first callback will resume continuation and set it to nil, so second one will do nothing. But function doSomething may call this callback concurrently, like so:
func doSomething(callback: @escaping (Result<Void, Error>) -> Void) {
for _ in 0..<10 {
DispatchQueue.global().async {
callback(.success(()))
}
}
}
in this case it may happen that two or more execution of callback will resume continuation before any of them set it to nil
Ah, that makes sense. I did not think much about doSomething, so I though the issue was in the surrounding code, and missed the point. Thanks for clarifying!