mirror of
https://github.com/got-feedBack/feedBack-desktop.git
synced 2026-09-11 02:54:10 +00:00
fix(audio): route device probing through the message thread; document timeout desync
Review follow-up (PR #113, finding 1): ProbeDeviceOptions constructs and destroys short-lived ASIO device objects (DeviceSetup::probeDual / rateSupportedBy createDevice) on the Node thread - the same create/destroy-off-the-message-thread pattern as the lifecycle ops, and a probe ASIOAudioIODevice owns the same reset-timer machinery. Wrap it in runDeviceLifecycleOp for consistency. Finding 2 (timeout desync - op reported failed may still complete late) is documented as a known limitation on the helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
aa0bb9d173
commit
895d0af969
@@ -80,6 +80,14 @@ inline bool dispatchOnMessageThread(Func&& func)
|
|||||||
// closure must own everything it touches — capture by value (engine
|
// closure must own everything it touches — capture by value (engine
|
||||||
// snapshot, args) and write results through a shared_ptr, never through
|
// snapshot, args) and write results through a shared_ptr, never through
|
||||||
// references to the caller's stack.
|
// references to the caller's stack.
|
||||||
|
//
|
||||||
|
// KNOWN LIMITATION (timeout desync): when the 15 s wait expires the binding
|
||||||
|
// reports failure to JS, but the closure may still complete afterwards —
|
||||||
|
// e.g. addSource returns -1 yet the source gets created, holding its input
|
||||||
|
// device until the next reconfigure. Memory-safe by the capture rule above,
|
||||||
|
// just not logically reconciled; only reachable with the message thread
|
||||||
|
// jammed > 15 s. Callers that care can re-query engine state (listSources,
|
||||||
|
// getCurrentDevice) after a reported failure.
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
inline bool runDeviceLifecycleOp(Func&& func)
|
inline bool runDeviceLifecycleOp(Func&& func)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -126,9 +126,24 @@ Napi::Value ProbeDeviceOptions(const Napi::CallbackInfo& info)
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto options = liveEngine->probeDeviceOptionsDual(
|
// Probing constructs + destroys short-lived ASIO device objects
|
||||||
juce::String(inputType), juce::String(inputName),
|
// (DeviceSetup::probeDual / rateSupportedBy createDevice) — same
|
||||||
juce::String(outputType), juce::String(outputName));
|
// create/destroy-off-the-message-thread pattern as the lifecycle ops, so
|
||||||
|
// it takes the same hop (PR #113 review finding 1). Query-only (never
|
||||||
|
// open()), but a probe ASIOAudioIODevice owns the same reset-timer
|
||||||
|
// machinery as the live one.
|
||||||
|
auto optionsPtr = std::make_shared<AudioEngine::DeviceOptions>();
|
||||||
|
if (!runDeviceLifecycleOp([liveEngine, inputType, inputName, outputType, outputName, optionsPtr] {
|
||||||
|
*optionsPtr = liveEngine->probeDeviceOptionsDual(
|
||||||
|
juce::String(inputType), juce::String(inputName),
|
||||||
|
juce::String(outputType), juce::String(outputName));
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
obj.Set("error", "device probe did not complete (message thread unavailable or timed out)");
|
||||||
|
obj.Set("compatible", false);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
const auto& options = *optionsPtr;
|
||||||
obj.Set("type", options.inputType.toStdString()); // legacy alias
|
obj.Set("type", options.inputType.toStdString()); // legacy alias
|
||||||
obj.Set("inputType", options.inputType.toStdString());
|
obj.Set("inputType", options.inputType.toStdString());
|
||||||
obj.Set("outputType", options.outputType.toStdString());
|
obj.Set("outputType", options.outputType.toStdString());
|
||||||
|
|||||||
Reference in New Issue
Block a user