Detailed steps on how to reproduce the bug
Since JUCE 9.0.0's new macOS CoreAudio implementation, my JUCE standalone app deadlocks at launch, before its first window appears. The identical codebase built against JUCE 8.0.13 launches fine on the same machine, same day, same devices. Reproduced twice in a row, sampled both times (full sample outputs attached).
Setup
- macOS Ventura 13.7.8, Intel Mac Pro (2019, 7,1)
- JUCE 9.0.0 stable tag, zero source changes to my tree (compiles clean with zero warnings)
- The app is a standalone that hosts an AudioProcessor via AudioProcessorPlayer + AudioDeviceManager, opened in a split configuration: input and output are two different physical devices (a loopback driver in, a Thunderbolt interface out)
- The machine has about 18 CoreAudio devices present: Apogee Symphony I/O (Thunderbolt), three loopback virtual devices, six Pro Tools Audio Bridge devices, NDI Audio, BlackHole 2ch, ZoomAudioDevice, TV/display audio, and two user aggregate devices
Steps
- Build the app (or plausibly any AudioDeviceManager client that queries latency at device setup) against 9.0.0
- Launch it on a machine with a large CoreAudio device list, with input and output on different physical devices
- During device setup, call AudioIODevice::getInputLatencyInSamples()
Result
The call never returns. The main thread sits forever in:
MyComponent (device setup)
juce::CoreAudioClasses::CoreAudioIODevice::getInputLatencyInSamples()
juce::AudioObject::getProperty<unsigned int>(...)
AudioObjectHasProperty (CoreAudio)
HALPlugIn::ObjectHasProperty
HALC_ShellObject::HasProperty
HALC_ProxyObject::HasProperty
mach_msg -> mach_msg2_trap (100% of samples, both runs)
The synchronous mach message to coreaudiod is never answered. No crash, no window, no dialog, 0.1 percent CPU, forever.
Second-order effect worth knowing
Force-killing the client while it is blocked in that proxy call left coreaudiod itself wedged: afterwards even a JUCE 8.0.13 client on the same machine blocked forever inside AudioDeviceStart (HALB_IOThread::StartAndWaitForState). Only killall coreaudiod recovered the system. So the failure compounds: the 9.0.0 hang invites a force quit, and the force quit degrades the whole audio stack.
What I ruled out
- Not my code: zero-change compile, my full DSP test suite passes 100 percent against 9.0.0, and pluginval strictness 10 passes on the 9.0.0-built VST3 and AU
- Not a one-off: reproduced on a second launch after a 12 second gap with a healthy coreaudiod
- Not a broken machine: 8.0.13 builds of the same app launch and run normally between the two 9.0.0 attempts
Suspicion
The new always-on private aggregate device path (aggregateDevicePrefix + pid) combined with a large device list. A dev machine with two devices likely never sees this; a pro-audio machine crowded with virtual devices and bridges hits it at launch.
Happy to run diagnostic builds or provide more samples.
What is the expected behaviour?
getInputLatencyInSamples() returns promptly (as it does on JUCE 8.0.13) and the app launches normally. On 8.0.13 the identical codebase, same machine, same 18-device list, launches and runs without issue.
Operating systems
macOS
What versions of the operating systems?
macOS Ventura 13.7.8 (22G921), Intel Mac Pro (2019, MacPro7,1)
Architectures
x86_64
Stacktrace
Run 1 (sample, 2416/2416 samples on the main thread):
2416 Thread DispatchQueue_1: com.apple.main-thread (serial)
+ 2416 start (in dyld)
+ 2416 juce::JUCEApplicationBase::main(int, char const**)
+ 2416 juce::JUCEApplicationBase::main()
+ 2416 juce::JUCEApplication::initialiseApp()
+ 2416 [our app] Application::initialise -> MainWindow -> component ctor
+ 2416 [our app] openAllDeviceChannels()
+ 2416 juce::CoreAudioClasses::CoreAudioIODevice::getInputLatencyInSamples()
+ 2416 juce::AudioObject::getProperty<unsigned int>(juce::PropertyAddress, juce::AudioObject::DataType) const
+ 2416 AudioObjectHasProperty (in CoreAudio)
+ 2416 HALPlugIn::ObjectHasProperty(HALObject const&, AudioObjectPropertyAddress const&) const
+ 2416 HAL_HardwarePlugIn_ObjectHasProperty(AudioHardwarePlugInInterface**, unsigned int, AudioObjectPropertyAddress const*)
+ 2416 HALC_ShellObject::HasProperty(unsigned int, AudioObjectPropertyAddress const&, bool&) const
+ 2416 HALC_ProxyObject::HasProperty(AudioObjectPropertyAddress const&, bool&) const
+ 2416 mach_msg (in libsystem_kernel.dylib)
+ 2416 mach_msg_overwrite
+ 2416 mach_msg2_internal
+ 2416 mach_msg2_trap
Run 2 (12 s later, fresh launch): identical deepest frames, 100% of samples in mach_msg2_trap under the same HALC_ProxyObject::HasProperty path.
Aftermath (JUCE 8.0.13 client on the same machine, AFTER force-killing the blocked 9.0.0 process, before killall coreaudiod):
juce::CoreAudioClasses::AudioIODeviceCombiner::start(juce::AudioIODeviceCallback*)
+ juce::CoreAudioClasses::CoreAudioInternal::start(juce::AudioIODeviceCallback*)
+ AudioDeviceStart (in CoreAudio)
+ HAL_HardwarePlugIn_DeviceStart
+ HALC_ProxyIOContext::_StartIO()
+ HALB_IOThread::StartAndWaitForState(unsigned int)
+ HALB_IOThread::_WaitForState(unsigned int)
Full sample outputs attached to the issue.
Plug-in formats (if applicable)
No response
Plug-in host applications (DAWs) (if applicable)
No response
Testing on the develop branch
I have not tested against the develop branch
Code of Conduct
Detailed steps on how to reproduce the bug
Since JUCE 9.0.0's new macOS CoreAudio implementation, my JUCE standalone app deadlocks at launch, before its first window appears. The identical codebase built against JUCE 8.0.13 launches fine on the same machine, same day, same devices. Reproduced twice in a row, sampled both times (full
sampleoutputs attached).Setup
Steps
Result
The call never returns. The main thread sits forever in:
The synchronous mach message to coreaudiod is never answered. No crash, no window, no dialog, 0.1 percent CPU, forever.
Second-order effect worth knowing
Force-killing the client while it is blocked in that proxy call left coreaudiod itself wedged: afterwards even a JUCE 8.0.13 client on the same machine blocked forever inside AudioDeviceStart (HALB_IOThread::StartAndWaitForState). Only
killall coreaudiodrecovered the system. So the failure compounds: the 9.0.0 hang invites a force quit, and the force quit degrades the whole audio stack.What I ruled out
Suspicion
The new always-on private aggregate device path (aggregateDevicePrefix + pid) combined with a large device list. A dev machine with two devices likely never sees this; a pro-audio machine crowded with virtual devices and bridges hits it at launch.
Happy to run diagnostic builds or provide more samples.
What is the expected behaviour?
getInputLatencyInSamples() returns promptly (as it does on JUCE 8.0.13) and the app launches normally. On 8.0.13 the identical codebase, same machine, same 18-device list, launches and runs without issue.
Operating systems
macOS
What versions of the operating systems?
macOS Ventura 13.7.8 (22G921), Intel Mac Pro (2019, MacPro7,1)
Architectures
x86_64
Stacktrace
Plug-in formats (if applicable)
No response
Plug-in host applications (DAWs) (if applicable)
No response
Testing on the
developbranchI have not tested against the
developbranchCode of Conduct