Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/build_wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
arch: [native]
python: ["{cp310,pp311}"]
python: ["{cp310,cp314t,cp315t,pp311}"]
# Split aarch64 across jobs because it uses emulation (slow)
include:
# DNS fails inside i686 containers on ubuntu-26.04 runners
# cffi has no free-threaded i686 wheels
- os: ubuntu-24.04
arch: i686
python: "{cp310,pp311}"
Expand All @@ -34,6 +35,9 @@ jobs:
- os: ubuntu-latest
arch: aarch64
python: "pp311"
- os: ubuntu-latest
arch: aarch64
python: "{cp314t,cp315t}"
steps:
- uses: actions/checkout@v7
with:
Expand Down
6 changes: 6 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Version 0.1.8 -- unreleased
* Fix possible crashes when a stream is garbage-collected while it is
running, or when an action is garbage-collected after being cancelled
* Update PortAudio submodule to the current development version
* Add wheels for free-threaded CPython 3.14t, and limited API wheels
(``abi3.abi3t``) for CPython 3.15 and 3.15t and newer
* Fix building from source on free-threaded CPython, which produced a
wheel wrongly tagged ``abi3`` that could not be installed
* Fix possible crashes when actions are started or waited for from
several threads at the same time

Version 0.1.7 -- 2024-07-02 -- PyPI__ -- diff__
* Restore the ``x86_64`` Linux wheels that were missing from 0.1.6
Expand Down
7 changes: 7 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ Common parameters that are shared by most commands:

All commands return a corresponding "action", which can be compared against the
active `actions`, and can be used as input for `cancel()` and `wait()`.
The commands, `actions` and `wait()` can be used from several threads at
once. The methods inherited from `sounddevice.Stream` (e.g.
:meth:`~sounddevice.Stream.start` and :meth:`~sounddevice.Stream.close`)
cannot, so they must be called from one thread or synchronized by the user,
as required by PortAudio. Likewise, a `RingBuffer` that is used for playback
(or recording) must only be written to (or read from) by one thread at a time.

The fields of action objects are defined in C but can be accessed with
Python (e.g. ``my_action.stats.min_blocksize``)
*after* the command is finished:
Expand Down
27 changes: 14 additions & 13 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from setuptools import setup, Extension
from wheel.bdist_wheel import bdist_wheel
import sys
import sysconfig

from setuptools import setup

__version__ = 'unknown'

Expand All @@ -12,22 +14,21 @@

# Adapted from
# https://github.com/joerick/python-abi3-package-sample/blob/main/setup.py
class bdist_wheel_abi3(bdist_wheel):
def get_tag(self):
python, abi, plat = super().get_tag()

if python.startswith("cp"):
# on CPython, our wheels are abi3 and compatible back to 3.2,
# but let's set it to our min version anyway
return "cp310", "abi3", plat

return python, abi, plat
# On CPython our wheels are abi3. Free-threaded builds support the limited API
# (as abi3t, PEP 803) only from 3.15 on, so 3.14t gets a version-specific
# wheel.
options = {}
if sys.implementation.name == 'cpython':
if not sysconfig.get_config_var('Py_GIL_DISABLED'):
options['bdist_wheel'] = {'py_limited_api': 'cp310'}
elif sys.version_info >= (3, 15):
options['bdist_wheel'] = {'py_limited_api': 'cp315'}


setup(
version=__version__,
package_dir={'': 'src'},
py_modules=['rtmixer'],
cffi_modules=['rtmixer_build.py:ffibuilder'], # sets Py_LIMITED_API for us
cmdclass={"bdist_wheel": bdist_wheel_abi3},
options=options,
)
32 changes: 21 additions & 11 deletions src/rtmixer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"""
__version__ = '0.1.7'

import threading as _threading

import sounddevice as _sd
from pa_ringbuffer import init as _init_ringbuffer
from _rtmixer import ffi as _ffi, lib as _lib
Expand Down Expand Up @@ -40,6 +42,8 @@ def __init__(self, kind, qsize=16, **kwargs):

self._actions = {}
self._temp_action_ptr = _ffi.new('struct action**')
# The queues allow only one reader and one writer at a time:
self._lock = _threading.Lock()

def __del__(self):
# The C callback uses memory owned by this object, so the stream
Expand All @@ -53,8 +57,9 @@ def __del__(self):
@property
def actions(self):
"""The set of active "actions"."""
self._drain_result_q()
return self._actions.keys()
with self._lock:
self._drain_result_q()
return set(self._actions)

@property
def stats(self):
Expand Down Expand Up @@ -135,17 +140,22 @@ def _check_channels(self, channels, kind):
return channels, mapping

def _enqueue(self, action, keep_alive=None):
self._drain_result_q()
self._temp_action_ptr[0] = action
ret = self._action_q.write(self._temp_action_ptr)
if ret != 1:
raise RuntimeError(
'Action queue is full, consider increasing qsize')
assert action not in self._actions
self._actions[action] = keep_alive
with self._lock:
self._drain_result_q()
self._temp_action_ptr[0] = action
ret = self._action_q.write(self._temp_action_ptr)
if ret != 1:
raise RuntimeError(
'Action queue is full, consider increasing qsize')
assert action not in self._actions
self._actions[action] = keep_alive

def _drain_result_q(self):
"""Get actions from the result queue and discard them."""
"""Get actions from the result queue and discard them.

Must be called with ``self._lock`` held.

"""
while self._result_q.readinto(self._temp_action_ptr):
# Each item is a linked list of actions
action = self._temp_action_ptr[0]
Expand Down
4 changes: 0 additions & 4 deletions tools/cibw_repair_wheel_command.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,3 @@ elif [[ "$RUNNER_OS" == "macOS" ]]; then
else
cp $WHEEL $DEST_DIR
fi

if [[ "$(python -c 'import platform; print(platform.python_implementation())')" == "CPython" ]]; then
pipx run abi3audit --strict --report $WHEEL
fi
Loading