# Audio Device Manager bug MacOS

**URL:** <https://forum.juce.com/t/audio-device-manager-bug-macos/67804>\
**Category:** MacOSX and iOS\
**Created:** [December 15, 2025, 2:39pm UTC](https://forum.juce.com/t/audio-device-manager-bug-macos/67804 "2025-12-15T14:39:27Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Frenchjucer](https://avatars.discourse-cdn.com/v4/letter/f/13edae/32.png) [@Frenchjucer](https://forum.juce.com/u/Frenchjucer)\
**Post date:** [December 15, 2025, 2:39pm UTC](https://forum.juce.com/t/audio-device-manager-bug-macos/67804/1 "2025-12-15T14:39:27Z")

</div>

Hello everyone, I am trying to test `juce::AudioDeviceManager` on MacOS and check whether connections with Bluetooth devices are working correctly.

Here, my application crashes when I select my Bluetooth device as the input and output device.  
Sometimes when I want to connect my Airpods, for example, to another input or output device, the application crashes.

  
I received this explanation using AI:

> The core issue is a critical **Thread Safety / Deadlock** problem within the `AudioIODeviceCombinerclass` in JUCE’s CoreAudio backend.
> 
> Here is the global explanation of why it crashes and the fix:
> 
> **1. The Problem:**  
> When you use separate input and output devices (e.g., AirPods + Built-in Mic), JUCE creates a virtual `AudioIODeviceCombiner`
> 
> 1. **The Trigger:** When a device sends a notification (like AirPods connecting or changing sample rate), CoreAudio fires a callback (on a high-priority **HALC\_ShellPlugIn** thread).
> 
> 2. **The Trap:** This callback eventually calls `AudioIODeviceCombiner::restartAsync()`
> 
> 3. **The Crash:** In the original code,
> 
> ### **2. The Solution: Make it Truly “Async”**
> 
> The fix forces the restart logic to happen _only_ on the main thread, where it is safe to close and open devices.
> 
> **The Modification:** We remove the synchronous `close()` call from **`restartAsync()`**. The method now simply “schedules” the work by starting a timer. The actual closing happens millieconds later inside **`timerCallback()`**, which runs safely on the Message Thread.
> 
> **Corrected Code:**
> 
> ```cpp
> void restartAsync() override
> {
> {
> const ScopedLock sl (closeLock);
> if (active && callback != nullptr)
> previousCallback = callback;
> }
> // "Please restart me later" - safe!
> startTimer (100); 
> }
> 
> ```
> 
> This prevents the CoreAudio thread from ever being blocked or destroyed while it’s executing code.

If any JUCE admins can test the solution or find a more robust backup, I’m interested.

Cheers.
