This commit adds logic to release the GIL for these PortAudio routines:
Pa_IsStreamStopped
Pa_IsStreamActive
Pa_GetStreamTime
Pa_GetStreamCpuLoad
Pa_GetStreamWriteAvailable
Pa_GetStreamReadAvailable
Pa_Initialize
Pa_Terminate
On macOS/OSX, CoreAudio may require a device-level lock for certain
operations, e.g., opening a stream or delivering audio data to a
callback. If the lock acquisition order between the device lock and the
Python GIL is inconsistent, deadlock might occur. Michael Graczyk
identified one such instance, where:
- an input stream, running in thread A, acquires the device lock,
followed by the GIL, in order to call the user's Python input callback
handler;
- that callback makes a call that releases the GIL (e.g., I/O)
- thread B acquires the GIL and opens a PyAudio stream, eventually
invoking Pa_OpenStream. Note that prior to this patch, calling
Pa_OpenStream did _not_ release the GIL.
- While holding the GIL, Pa_OpenStream attempts to acquire the device
lock.
- Meanwhile, thread A (still holding the device lock) attempts to
reacquire the GIL (e.g., once the I/O call is complete).
This commit:
(a) updates the implementation to release the GIL before calling
Pa_OpenStream and Pa_StartStream. Hopefully this change will bring
the code closer to the discipline of always acquiring the device
lock before the GIL. Thanks Michael for the patch!
(b) adds a unit test to reproduce the problem (on CoreAudio systems).
Many thanks again to Michael Graczyk for identifying the issue and
contributing the fix!
This adds an optional parameter specifying whether an input overflow
exception should be raised (or ignored). Previously, a code comment
conflicted with actual implementation, by suggesting that overflows
are ignored when in fact they are always surfaced. Furthermore,
detecting an overflow condition was flawed. This change allows
the user to decide whether exceptions are thrown, for API parity with
pa_write_stream.
Thanks to Tony Jacobson for the report and patch!
Deadlock due to callback thread waiting for the GIL while the
GIL is still held by thread calling Pa_StopStream. The fix is
to release the GIL before calling Pa_StopStream.
Much gratitude to Jason Roehm for the patch!