ARTICLE AD BOX
I'm using Apple's private MultitouchSupport.framework to receive raw trackpad touch data on macOS. The basic pattern is:
MTDeviceCreateList() / MTDeviceCreateDefault() to get device references
MTRegisterContactFrameCallback() to register a callback
MTDeviceStart() to begin receiving touches
Later: MTUnregisterContactFrameCallback() then MTDeviceStop() to clean up
Issue: If I call MTDeviceStop() immediately after MTUnregisterContactFrameCallback(), the app intermittently crashes with CFRelease() called with NULL or EXC_BAD_ACCESS. The crash happens on a framework-internal thread named com.apple.MultitouchSupport.gesturestats or similar.
Adding a ~50ms delay between unregister and stop eliminates the crashes. But I found this value through trial and error: I don't understand why it's needed.
Minimal reproduction (Swift):
swift
// Bindings to private framework @_silgen_name("MTDeviceCreateDefault") func MTDeviceCreateDefault() -> UnsafeMutableRawPointer? @_silgen_name("MTDeviceStart") func MTDeviceStart(_ device: UnsafeMutableRawPointer, _ mode: Int32) @_silgen_name("MTDeviceStop") func MTDeviceStop(_ device: UnsafeMutableRawPointer) @_silgen_name("MTRegisterContactFrameCallback") func MTRegisterContactFrameCallback(_ device: UnsafeMutableRawPointer, _ callback: @convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?, Int32, Double, Int32) -> Int32) @_silgen_name("MTUnregisterContactFrameCallback") func MTUnregisterContactFrameCallback(_ device: UnsafeMutableRawPointer, _ callback: (@convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?, Int32, Double, Int32) -> Int32)?) // Callback let callback: @convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?, Int32, Double, Int32) -> Int32 = { _, _, _, _, _ in return 0 } // Start guard let device = MTDeviceCreateDefault() else { return } MTRegisterContactFrameCallback(device, callback) MTDeviceStart(device, 0) // ... later, stop (THIS CRASHES INTERMITTENTLY): MTUnregisterContactFrameCallback(device, callback) MTDeviceStop(device) // CFRelease(NULL) or EXC_BAD_ACCESSWorkaround that prevents crashes:
MTUnregisterContactFrameCallback(device, callback) Thread.sleep(forTimeInterval: 0.05) // 50ms delay - WHY IS THIS NEEDED? MTDeviceStop(device) // Now safeNotes:
The crash occurs on a background thread owned by the framework, not my code
Stack traces show CFRelease, __CFArrayReleaseValues, and sometimes IOHIDEventRelease
The crash is not deterministic, it depends on timing (whether a touch callback is in-flight)
50ms is sufficient on my machine; 10ms sometimes still crashes
The issue is worse during system wake from sleep (more timing sensitivity I assume)
Environment:
macOS 14/15
Apple Silicon and Intel
Built-in trackpad
What I'm looking for:
An explanation of what the framework's internal thread is doing between callback unregistration and device stop that causes this race condition, and ideally, a proper synchronization mechanism (API, notification, or signal) to wait for safe cleanup rather than an arbitrary sleep.
