-
Notifications
You must be signed in to change notification settings - Fork 62
Description
Windows Version
Windows 11 Pro 25H2, Build 26200.8037
Service Installation Method
In-box (retail Windows 11 25H2, not Insider, not GitHub install)
- MidiSrv version: 10.0.26100.7705
- MidiSrv MIDI 2.0 driver: 10.0.26100.1
SDK version, if appropriate
N/A
Location
Application using WinMM MIDI 1.0 (Classic API)
Type of bug
Application Crash, Application not working as expected
Steps to reproduce
Device: Akai Professional MPK Mini IV
USB HW ID: USB\VID_09E8&PID_005D&REV_0141&MI_01
Akai USB driver version: 7.0.0.3500
Affected DAWs: Mixcraft 10, Ableton Live Lite 12 (both hang identically)
Scenario A: DAW launch with MPK connected (most reliable repro)
- Clean boot Windows
- Connect Akai MPK Mini IV via USB (MidiSrv starts automatically on Manual startup type)
- Launch Mixcraft 10 (or Ableton Live Lite 12)
- Result: DAW window appears but immediately becomes unresponsive (
Responding = Falsein Task Manager). Main thread stuck onLpcReplywait reason (cross-process call to MidiSrv). - DAW remains hung indefinitely — must be force-killed.
Scenario B: Hot-plug MPK while DAW is running
- Clean boot Windows, MidiSrv set to Manual (stays Stopped)
- Launch Mixcraft 10 without MPK connected — loads normally
- Plug in MPK Mini IV — MidiSrv starts and begins MIDI 2.0 enumeration
- Result: Mixcraft becomes unresponsive. Same permanent hang.
Scenario C: Registry fix (discovery disabled) — still deadlocks
- Set
HKLM\Software\Microsoft\Windows MIDI Services\Midi2DiscoveryEnabled = 0 - Reboot, connect MPK Mini IV
- Launch Mixcraft
- Result: MidiSrv starts, creates all 10 MMDEVAPI bridge ports successfully (all show
OKstatus), but Mixcraft still hangs. Main thread blocked onLpcReply. MidiSrv has aSuspendedthread. - Conclusion: Discovery disable is insufficient — the deadlock is in core enumeration.
Scenario D: MidiSrv disabled entirely — works
sc.exe config MidiSrv start= disabled, reboot- Connect MPK Mini IV
- Launch Mixcraft — starts normally, no hang
- Conclusion: The deadlock is entirely within MidiSrv's code paths.
Runtime diagnostics when deadlock is active
- Mixcraft:
Responding = False, 35 threads, main thread inLpcReplywait state - MidiSrv:
Running, 9 threads (5 UserRequest, 3 EventPairLow, 1 Suspended), 508 handles Stop-Service MidiSrvloops forever on "Waiting for service to stop"taskkill /F /PID <pid>is required; service entersStopPendingand ghost devices remain until reboot
Expected behavior
- MidiSrv's device enumeration should not hold locks across synchronous waits on async operations (
FindAllAsync().get(),SwDeviceCreate().wait()) GetMidiDeviceCount()in the WinMM bridge should not deadlock with MidiSrv's endpoint activation- If MIDI 2.0 discovery fails or is disabled, endpoint activation should complete without blocking WinMM consumers
Stop-Serviceshould complete within a reasonable timeout even when devices are in a bad state
Additional notes
Root cause analysis (from source code review)
We identified a two-layer deadlock in the MidiSrv source:
Layer 1: MIDI 2.0 discovery worker blocks indefinitely
Files: MidiEndpointProtocolWorker.cpp, MidiEndpointProtocolManager.cpp
- KS transport
OnDeviceAdded()fires when MPK is plugged in ActivateEndpoint()creates a MIDI SWD (software device)DiscoverAndNegotiate()spawns aCMidiEndpointProtocolWorkerthread (detached)- Worker sends UMP discovery request via
RequestAllEndpointDiscoveryInformation() - MPK does NOT respond to MIDI-CI messages
m_allInitialDiscoveryAndNegotiationMessagesReceived.wait(5000ms)times out- Worker then blocks forever on
m_endProcessing.wait()— no timeout - Service shutdown calls
EndProcessing()→ signals event, butm_midiBidiDevice->Shutdown()blocks on stuck KS driver communication
Layer 2: Core enumeration deadlock (the real blocker)
Even with discovery disabled, MidiSrv deadlocks during device enumeration due to circular lock contention between the WinMM bridge and the service:
Thread 1 — WinMM app (e.g., Mixcraft calling midiInGetNumDevs()):
midiInGetNumDevs()
→ WinMM driver midMessage(MIDM_GETNUMDEVS)
→ CMidiPorts::GetMidiDeviceCount() [MidiSrvPorts.cpp]
→ Acquires m_Lock (critical_section)
→ SetupDiGetClassDevs() + SetupDiEnumDeviceInterfaces()
→ Queries device properties managed by MidiSrv
→ BLOCKS waiting for property update to complete
Thread 2 — MidiSrv service (device watcher callback):
OnDeviceAdded()
→ ActivateEndpointInternal() [MidiDeviceManager.cpp]
→ Acquires m_midiPortsLock
→ SwDeviceCreate() with 10-second synchronous wait
→ Needs to update device properties
→ But properties are being read by Thread 1's SetupDi call
→ DEADLOCK — circular wait
Additional synchronous blocking calls under m_midiPortsLock:
CompactPortNumbers()(line ~2083): holdsm_midiPortsLockwhile callingDeviceInformation::FindAllAsync().get()— synchronous wait on async WinRT operationAssignPortNumber()(line ~2200): same pattern,FindAllAsync().get()under lockSwDeviceCreate()(line ~1381): 10-secondCreationCompleted.wait(SWD_CREATION_TIMEOUT)per device
Additional bugs found
- Worker threads have no cancellation mechanism — only
m_endProcessingevent, but bidi device shutdown blocks RemoveWorkerIfPresent()triesThread->join()but threads are detached (joinable()returns false)Stop-Service MidiSrvnever completes because the SCM stop handler hits the same deadlocked paths
MIDI 2.0 endpoints registered
When MidiSrv runs with the MPK Mini IV connected, 9 MIDI 2.0 sub-ports are created under SWD\MMDEVAPI:
| Direction | Port Name |
|---|---|
| In (0_0) | MPK mini IV MIDI Port |
| In (0_1) | MPK mini IV DAW Port |
| In (0_2) | MPK mini IV Plugin Port |
| In (0_3) | MPK mini IV Software Control |
| Out (1_0) | MPK mini IV MIDI Port |
| Out (1_1) | MPK mini IV DAW Port |
| Out (1_2) | MPK mini IV Plugin Port |
| Out (1_3) | MPK mini IV Software Control |
| Out (1_4) | MPK mini IV Din Port |
Suggested fixes
- Break the circular lock: Don't hold
m_midiPortsLockacrossFindAllAsync().get()andSwDeviceCreate()— copy needed data, release the lock, perform the async operation, then re-acquire - Add timeouts to all waits:
m_endProcessing.wait()in the protocol worker needs a bounded timeout - Make CompactPortNumbers() async: Replace
FindAllAsync().get()with proper async/await or move to a background thread that doesn't hold the port lock - Add per-device disable: Allow users to block MIDI 2.0 enumeration for specific devices (by VID/PID) via registry, not just globally
Workaround
Disable MidiSrv: sc.exe config MidiSrv start= disabled
This forces MIDI apps to use the legacy WinMM → USB Audio Class driver path, which works correctly. To restore: sc.exe config MidiSrv start= demand
Questions for the MIDI team
- Is there a way to disable MIDI 2.0 enumeration for a specific device (by VID/PID) while keeping MidiSrv running for other devices?
- Has this circular lock pattern been identified in internal testing? It appears to affect any USB MIDI device that triggers MidiSrv endpoint activation while a WinMM app is enumerating.
- Are there plans to make
CompactPortNumbers()andAssignPortNumber()non-blocking? - Is there an ETW provider for MidiSrv that would show the exact lock contention in real-time?