Search guides and the APICtrl K
  1. API reference
  2. System, devices and network

apple_block.h

An Apple block, built by hand, for the APIs that take one.

#include <sonore/apple_block.h>

Design notes

Why this exists

A few of Apple's audio APIs take or hand out a BLOCK -- clang's closure extension, written ^(args) { ... } -- where everything else in them takes a function pointer and a context:

AudioComponentInstantiate                 its completion handler (host_au.h)
kAudioUnitProperty_RequestViewController  an AUv3's editor (host_au.h)
kAudioUnitProperty_RenderContextObserver  the render thread's workgroup
                                          (au_wrapper.h)

This SDK is plain C++ with no .mm file anywhere, and the ^ syntax needs -fblocks. Apple clang turns that on by default, but the only compiler that reads the Mac code in this loop -- MSVC, against tests/au_shim -- cannot parse it, and code no compiler reads is how au_wrapper.h spent weeks unbuildable. So a block is built here the way the compiler builds one, from the published ABI, exactly as the Cocoa files send messages through objc_msgSend rather than in Objective-C.

The ABI

clang's "Block Implementation Specification" (Block-ABI-Apple), unchanged since Mac OS X 10.6. Every block begins

void* isa;                 the block's class
int   flags;
int   reserved;
R   (*invoke)(block*, args...);  called with the block itself first
descriptor*;               { reserved, size } at minimum

and its captured variables follow. Calling a block is loading invoke and calling it with the block's own address as the first argument -- which is how the captures are reached.

Why every block here is GLOBAL, captures and all

A block handed to an API is copied, retained or released under rules that are documented unevenly: AudioUnitGetProperty's "Copy rule", ARC's objc_retainBlock, a plain Block_copy. A block whose flags carry BLOCK_IS_GLOBAL is immune to every one of them: _Block_copy returns the same pointer, _Block_release does nothing, and -[__NSGlobalBlock__ retain], -release and -copy are all identities. So nothing anybody does to it can free it, or copy it somewhere its captures are not.

The price is that the block lives where WE put it -- inside an object of ours -- and that object has to outlive every call the API can make. Each caller says how it guarantees that; host_au.h keeps a request alive until its completion has run, and au_wrapper.h keeps the observer inside the instance the OS stops rendering before it disposes.

What it cannot do

arm64e. Pointer authentication signs a block's invoke pointer with the block's address, and a hand-built block has no signature. Third-party code on macOS is arm64 or x86_64 -- arm64e is reserved for Apple's own binaries -- so this is refused at compile time rather than crashing at the first call.

Compiled, never run

There is no Mac in this loop. tests/mac_host_selftest.cpp is the program a Mac runs to check this file: it hands one of these to _Block_copy and calls the result.

Nothing public is declared here: the header supports the others in its area, and its notes above say how.

↑↓ to move↵ to openEvery public name in the headers, and every guide