Repository navigation
MIDI: Fix input queue overflow hang and broken real-time bytes - #7940
Merged
Merged
Conversation
MPU401_RecQueueBuffer only advanced its counter inside the branch that writes a byte, so once the 1024-byte record queue was full the loop could never finish and the caller never returned. The only drain points are the guest reading the data port and the tail of this same function, neither of which the spin reaches, and two of the call sites are reachable from the emulation thread. It now stops when the queue is full and drops the rest of the event, which is what MPU401_InputSysex already does in that case. midi_raw_out_rt_byte checked midi_in but then called play_msg, which dereferences midi_out without a check, so it crashed when no MIDI output device was open. It also placed the status byte at offset 3 of an int, so the RtMidi backend read msg[0] as zero, picked length 3 from midi_lengths and sent three zero bytes instead of the single F8, FA, FB or FC. It now guards and formats the message the same way midi_raw_out_byte does.
Member
|
Merged, thanks! Also, the IRQ 2 problems are due to some machines' BIOS'es not doing the IRQ 9 to 2 redirect correctly and now that you made me think, it may be due to the machine assigning IRQ 9 to something else. So that's not a bug on our side. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two defects in the MPU-401 MIDI input path. One can hang the emulator, the other sends the wrong bytes to the MIDI output port and can crash 86Box when no output device is open.
MPU401_RecQueueBuffer (src/sound/snd_mpu401.c:196) never returns once the record queue is full:
cntis only incremented insideif (mpu->rec_queue_used < MPU401_INPUT_QUEUE), so when that test failscnt < lencan never become false. Only the guest reading the data port (:769) and the block at the end of this same function drain the queue, and the spin reaches neither. MPU401_ClrQueue (:235) and the SysEx abort path (:1391) do zerorec_queue_used, but two call sites are reachable from the emulation thread (:482 and :490, in the command write handler), where the spinning thread is the one that would have to issue that reset.Filling the queue does not need an unusual guest. In intelligent mode any real-time byte above 0xF8 is queued as two bytes with no room check (:1555-1560), and RtMidi is deliberately configured not to ignore active sensing (src/sound/midi_rtmidi.cpp:188). With the guest not reading the port, a controller sending active sensing every 300 ms fills the 1024-byte queue on message 512, about two and a half minutes, and the call that queues the next message never returns.
The loop now stops when there is no room and drops the rest of the event, which is what MPU401_InputSysex already does when the queue is full (:1401-1409), and dropping is the only alternative to blocking here. InputSysex itself is unchanged, since it already clamps to the free space. This does not conflict with the MIDI input queuing work mentioned in #6266: the loop is unbounded whatever the threading, and a rework of the input queue would replace it along with the queue.
The second fix is in midi_raw_out_rt_byte (src/sound/midi.c:310). It checks
midi_in, then calls play_msg, which dereferencesmidi_outwith no check (midi.c:219).midi_outis NULL whenever MIDI Out is None or the host port failed to open, and the settings dialog allows an MPU-401 with MIDI In and no MIDI Out. The configuration attached to #6266 is exactly that,midi_in_device = midi_inunder[Sound]and nomidi_devicekey.The formatting is wrong too, whenever the device's Real time option is on.
val << 24puts the status byte at offset 3 of the int on a little-endian host, so rtmidi_play_msg readsmsg[0]as zero, takesmidi_lengths[0]which is 3, and sends three zero bytes instead of the single F8, FA, FB or FC (src/sound/midi_rtmidi.cpp:51 and :59-64). Anything slaved to the emulated MPU-401 clock never starts, stops or syncs. midi_raw_out_byte (midi.c:334) already gets both parts right, so this now guardsmidi_outand usesmidi_out->midi_rt_bufthe same way, leaving the MIDI In policy checks above it alone. That removes the only write tomidi_t.midi_cmd_r(src/include/86box/midi.h:61), which was already write-only. I left the field in place to keep the diff to the bug, happy to drop it if you would rather.Checked with two standalone programs rather than a full build, since Qt is not available here. The first runs the fill loop verbatim with the real MPU401_INPUT_QUEUE and the real call shape from the active sensing path, nothing draining the queue, with an iteration cap so it reports instead of hanging: current code reaches
rec_queue_used=1024on message 512 and does not terminate on 513, the patched loop returns from every call. The second prints what play_msg receives,00 00 00 f8at length 3 before andf8 00 00 00at length 1 after. Both files compile withclang -fsyntax-only -Wall -Wextrawith exactly the same warnings as before, and the changed lines are clang-format clean.Neither fix closes #6266. The IRQ 2 crash there is the ACPI SCI redirect already identified in the thread, and that report has
realtime = 0. The overflow spin is a plausible cause of the "MIDI input simply ceases" behaviour described in the same report, but I have not reproduced that here.Checklist
References