How to properly deallocate an object that contains a DispatchSemaphore

When an object that contains a semaphore gets automatically deallocated by ARC in my app, I get an EXC_BREAKPOINT or an EXC_BAD_INSTRUCTION with the message "BUG IN CLIENT OF LIBDISPATCH: Semaphore object deallocated while in use". The call stack shows libdispatch.dylib`_dispatch_semaphore_dispose.cold.1 as the location of the crash. I noticed that this error doesn't occur when the semaphore's wait/signal calls are balanced such that the semaphore's counter is at the value I initially passed in (see code below). In my actual code (which is in Metal rendering code that's getting called every frame), I don't know what the semaphore's counter is at at any given moment (and because of the abstraction of a semaphore, I shouldn't have to know what the counter is at), so how do I make sure that the object that holds the semaphore (and the semaphore itself) is deallocated properly so I don't get this crash on deallocation?


class Test {
    var semaphore = DispatchSemaphore(value: 3)
}

var test = Test()

// will not crash
test.semaphore.wait(timeout: DispatchTime.distantFuture)
test.semaphore.signal()
        
test = Test()

// will crash
test.semaphore.wait(timeout: DispatchTime.distantFuture)
        
test = Test()
Answered by KMT in 397456022
Accepted Answer

I noticed that this error doesn't occur when the semaphore's wait/signal calls are balanced such that the semaphore's counter is at the value I initially passed in

Right. This is explicitly documented in the

dispatch_semaphore_wait
man page, which says:

Unbalanced dispatch semaphores cannot be released. For a given sema- phore, calls to

dispatch_semaphore_signal
and
dispatch_semaphore_wait
must be balanced before
dispatch_release
is called on it.

As to what you should do about this, that depends on what you’re using this semaphore for. I’m not a Metal expert but most of the folks I’ve seen using a Dispatch semaphore in a Metal context are using it to implement some sort of multi-buffering, which means this problem shows up when you want to shut down rendering. Is that the situation you’re seeing the problem?

But the workaround mentioned in there fixed my problem though.

Gotta say, I’m not a fan of the ‘call

dispatch_semaphore_signal
until things balance’ workaround. In my experience, folks hit this assert because of fuzzy thinking about their concurrency, and working around such problems tends to cause other, hard-to-debug problems showing up elsewhere.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes, the shutting down after multi-buffering is where I'm seeing the problem. This sample code is what I've been following for some of my implementation (https://developer.apple.com/library/archive/samplecode/AdoptingMetalII/Introduction/Intro.html), even though my implementation is more complex as depending on what I'm rendering, only some of the buffers are going to be in use, so their associated semaphores end up getting deallocated. I'm definitely open to a better solution to this semaphore problem if there's one that doesn't involve a workaround. Thanks!

How to properly deallocate an object that contains a DispatchSemaphore
 
 
Q