lukasa
(Cory Benfield)
2
So here’s my guess: you’re being bitten by Swift’s lifetime analysis in release mode, and the subscription is being cancelled.
Swift programmers are accustomed to the idea that variables stop being usable outside the scope in which they were defined. In this case, pipes is such a variable: it’ll stop being usable once the async block exits scope. This may lead you to assume that pipes will be be deallocated when the scope exits, as with C++ (this is a part of RAII). This assumption is wrong.
In Swift, a variable may be deallocated immediately after its last usage site. In this case, pipes is last used at the point of store(in: &pipes). The moment that line executes, Swift is free to deallocate the array pipes, calling deinit for everything inside it. This will drop the AnyCancellables, which will cancel their associated subscription. This can happen before the sema.wait, which will now never complete because the subscriber will never receive a value.
In debug mode this optimisation is not likely to be performed, but in release mode it can be. This would explain the behaviour you’re seeing.
To resolve this, you can use withExtendedLifetime to make pipes live until the end of whatever block you’re using. If you put sema.wait inside that block, that should do it.
7 Likes