Documentation
¶
Overview ¶
escaping-block-regression demonstrates an ObjC API pattern that broke before applegen learned to skip `defer block.Release()` for escaping blocks.
MTLCommandBuffer.AddCompletedHandler retains the supplied block and invokes it on GPU completion, possibly after the calling Go function has returned. The pre-fix generator emitted:
func (o MTLCommandBufferObject) AddCompletedHandler(block ...) {
_block0 := objc.NewBlock(...)
defer _block0.Release() // <-- decrements refcount before Metal fires the block
objc.Send(...)
}
In practice Metal performs a synchronous `[block copy]` inside the addCompletedHandler: call so the defer leaves refcount >= 1 and the handler fires. But the pattern was a latent use-after-free for any framework that defers the copy (e.g. AVAudioEngine.installTapOnBus:), and even for Metal the dispose-on-zero-refcount removes the Go closure from purego's cache, leaving a window for crashes under GC pressure.
Post-fix (appledocs c09228c541) the generator omits the defer for selectors matching install*/set*Block:/set*Handler:/add*Handler:/ subscribe*/register*/observe*. This program submits many command buffers with completion handlers, forces GC between submission and completion, and asserts that every handler fired.