mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Skip a change-event slot whose handle is not stored yet when a channel change signals the list.
The review of the one-event-per-kind change (E-11) found a narrow window kept from before: change_event_list counts a slot (atomic_fetch_add on n_change_events) before it stores the handle there. audio_channels_live on the present path running between the two read the slot's 0 and passed it to kern_event_signal, which ends the process with "not a writable event handle". It needs a kind's first request to meet a channel-setting change. change_event_list now stores the handle with release order, and audio_channels_live loads each slot with acquire order and skips a slot that still reads 0; that slot's event is signalled by the next channel change. Not reproduced before the change in a game run. tests/audio_mixer_check.c counts a slot without storing it and changes the channels: no signal may reach handle 0, and the listed events are signalled again. Against the previous audio_mixer.c it fails with handle 0 signalled once. The check passes, and the compile check of audio_mixer.c (also with -Wall -Wextra) adds no warnings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1354,13 +1354,14 @@ static const char device_name[] = "AudioTvOutput";
|
||||
|
||||
// The events the game took to hear of a change of the output device (IAudioDevice's, its two notifications' and the
|
||||
// virtual device manager's). audio_channels_live signals them all: the game then asks for the channel count again, as
|
||||
// it does on a console whose output moves from the TV to headphones.
|
||||
// it does on a console whose output moves from the TV to headphones. A slot is counted before its handle is stored, so
|
||||
// audio_channels_live skips a slot that still reads 0 (kern_event_signal ends the process on a bad handle).
|
||||
static uint32_t change_events[16];
|
||||
static _Atomic int n_change_events;
|
||||
|
||||
static void change_event_list(uint32_t ev) {
|
||||
int i = atomic_fetch_add(&n_change_events, 1);
|
||||
if (i < 16) change_events[i] = ev;
|
||||
if (i < 16) __atomic_store_n(&change_events[i], ev, __ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
// One event per kind, made at the kind's first request; every request gets its own readable handle to it. Made per
|
||||
@@ -1396,7 +1397,10 @@ void audio_channels_live(void) {
|
||||
if (now == was) return;
|
||||
int n = atomic_load(&n_change_events);
|
||||
if (n > 16) n = 16;
|
||||
for (int i = 0; i < n; i++) kern_event_signal(change_events[i]);
|
||||
for (int i = 0; i < n; i++) {
|
||||
uint32_t e = __atomic_load_n(&change_events[i], __ATOMIC_ACQUIRE);
|
||||
if (e) kern_event_signal(e);
|
||||
}
|
||||
rt_log("audio: NX2NAVI_AUDIO_CHANNELS=%s: the output goes from %s to %s; the game is told the device changed "
|
||||
"(%d events)", want, was == 6 ? "5.1" : "stereo", now == 6 ? "5.1" : "stereo", n);
|
||||
}
|
||||
|
||||
@@ -413,6 +413,14 @@ static void change_events_once(void) {
|
||||
for (int i = 0; i < n && i < 16; i++) once += signalled[change_events[i]] == 1;
|
||||
CHECK(once == n, "a channel change signals each listed event once (%d of %d)", once, n);
|
||||
CHECK(!signal_closed, "no closed event is signalled");
|
||||
/* a slot counted whose handle is not stored yet (a kind's first request on another thread): skipped, never
|
||||
signalled as handle 0, which the kernel answers with a fatal error */
|
||||
atomic_fetch_add(&n_change_events, 1);
|
||||
channels_setting = "stereo";
|
||||
audio_channels_live();
|
||||
CHECK(output_channels() == 2, "back to stereo");
|
||||
CHECK(!signalled[0], "a slot not stored yet is skipped (handle 0 signalled %d times)", signalled[0]);
|
||||
CHECK(signalled[change_events[0]] == 2, "the listed events are signalled again (%d)", signalled[change_events[0]]);
|
||||
audio_mode = "none", channels_setting = "stereo";
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user