Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dd08e01
Make pybind11 modules GIL-free
ndgrigorian Apr 23, 2025
5f4e279
Declare each Cython module free-threading compatible
ndgrigorian Apr 23, 2025
54aafb2
add lock to warning check in onetrace_enabled context manager
ndgrigorian Feb 16, 2026
245b42c
Make ordermanager free-threading safe
ndgrigorian Feb 16, 2026
52a8039
adds warning to syclinterface_diagnostics
ndgrigorian Feb 16, 2026
3f97aab
update caching for free-threaded python compatibility
ndgrigorian Feb 16, 2026
05e0b80
remove python-gil as a requirement
ndgrigorian Feb 16, 2026
4ecd31d
remove pytest-cov as test dependencies
ndgrigorian Feb 17, 2026
c5d69b8
update test_memory_create for free-threaded Python
ndgrigorian Feb 17, 2026
2823108
test dpctl built with and without free-threaded Python 3.14 in public CI
ndgrigorian Feb 18, 2026
ccb1bfe
adds trove classifier for Python free-threading status
ndgrigorian Feb 18, 2026
2794463
fix missing parts of build/test matrices
ndgrigorian Feb 19, 2026
e9c20d5
make SequentialOrderManager thread-local and cached queues, devices g…
ndgrigorian Feb 23, 2026
30a50e5
make __copy__ methods in cache classes hold locks
ndgrigorian Feb 26, 2026
24ade80
use Parameter.empty instead of _empty
ndgrigorian Apr 19, 2026
75c691c
add order manager example
ndgrigorian Apr 19, 2026
df6f452
fix potential hang in capi initialization
ndgrigorian Apr 21, 2026
a0d7b1b
Merge branch 'master' into feature/enable-free-threaded-python
ndgrigorian May 26, 2026
1c61a70
Merge branch 'master' into feature/enable-free-threaded-python
ndgrigorian May 26, 2026
3a9ce96
correct name in example
ndgrigorian May 26, 2026
a28065e
address PR comments
ndgrigorian Jun 2, 2026
31f0dbb
run examples on free-threaded and GIL-enabled Python
ndgrigorian Jun 2, 2026
3b675fc
make examples free-threading compatible
ndgrigorian Jun 2, 2026
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
146 changes: 113 additions & 33 deletions .github/workflows/conda-package.yml

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions .github/workflows/run-tests-from-dppy-bits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ jobs:
strategy:
matrix:
python: ['3.10', '3.11', '3.12', '3.13', '3.14']
python_spec: ['']
include:
- python: '3.14'
python_spec: '3.14.* *_cp314'
experimental: [false]
runner: [ubuntu-22.04, ubuntu-24.04]
continue-on-error: ${{ matrix.experimental }}
Expand All @@ -47,7 +51,12 @@ jobs:

- name: Install dpctl
run: |
conda create -n "${{ env.TEST_ENV_NAME }}" -c dppy/label/dev "${{ env.CHANNELS }}" dpctl pytest pytest-cov cython setuptools c-compiler cxx-compiler
PYTHON_SPEC="${{ matrix.python_spec }}"
if [ -z "${PYTHON_SPEC}" ]; then
PYTHON_SPEC="${{ matrix.python }}"
fi
export PYTHON_SPEC
conda create -n "${{ env.TEST_ENV_NAME }}" -c dppy/label/dev "${{ env.CHANNELS }}" dpctl pytest cython setuptools c-compiler cxx-compiler python="${PYTHON_SPEC}"

- name: Smoke test
run: |
Expand Down Expand Up @@ -79,6 +88,10 @@ jobs:
strategy:
matrix:
python: ['3.10', '3.11', '3.12', '3.13', '3.14']
python_spec: ['']
include:
- python: '3.14'
python_spec: '3.14.* *_cp314'
experimental: [false]
runner: [windows-latest]

Expand Down Expand Up @@ -106,7 +119,9 @@ jobs:

- name: Install dpctl
run: |
conda install -n ${{ env.TEST_ENV_NAME }} -c dppy/label/dev ${{ env.CHANNELS }} dpctl pytest pytest-cov cython setuptools c-compiler cxx-compiler
SET "PYTHON_SPEC=${{ matrix.python_spec }}"
IF "%PYTHON_SPEC%"=="" SET "PYTHON_SPEC=${{ matrix.python }}"
conda install -n ${{ env.TEST_ENV_NAME }} -c dppy/label/dev ${{ env.CHANNELS }} dpctl pytest cython setuptools c-compiler cxx-compiler python="%PYTHON_SPEC%"

# intel-opencl-rt is not being installed when running conda install dpctl, so do it manually
- name: Install intel-opencl-rt
Expand Down
3 changes: 0 additions & 3 deletions conda-recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ requirements:
- {{ compiler('dpcpp') }} >={{ required_compiler_version }}
host:
- python
- python-gil # [py>=314]
- pip >=24.0
- level-zero-devel >=1.16
- pybind11 >=2.12
Expand All @@ -52,7 +51,6 @@ requirements:
- tomli # [py<311]
run:
- python
- python-gil # [py>=314]
- {{ pin_compatible('intel-sycl-rt', min_pin='x.x', max_pin='x') }}
- {{ pin_compatible('intel-cmplr-lib-rt', min_pin='x.x', max_pin='x') }}
- numpy
Expand All @@ -65,7 +63,6 @@ test:
- cython
- setuptools
- pytest
- pytest-cov

about:
home: https://github.com/IntelPython/dpctl.git
Expand Down
9 changes: 1 addition & 8 deletions conda-recipe/run_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,4 @@ set -e

${PYTHON} -c "import dpctl; print(dpctl.__version__)"
${PYTHON} -m dpctl -f
# don't use coverage for Python 3.13 due to crashes related to
# Cython >= 3.1.0 and Python >= 3.13
# TODO: remove if crash is triaged
if ${PYTHON} --version 2>&1 | grep -q '^Python 3\.13'; then
${PYTHON} -m pytest -q -ra --disable-warnings --pyargs dpctl -vv
else
${PYTHON} -m pytest -q -ra --disable-warnings --cov dpctl --cov-report term-missing --pyargs dpctl -vv
fi
${PYTHON} -m pytest -q -ra --disable-warnings --pyargs dpctl -vv
5 changes: 5 additions & 0 deletions dpctl/_diagnostics.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# distutils: language = c++
# cython: language_level=3
# cython: linetrace=True
# cython: freethreading_compatible = True

""" Implements developer utilities.
"""
Expand Down Expand Up @@ -60,6 +61,10 @@ def _shutdown_logger():
def syclinterface_diagnostics(verbosity="warning", log_dir=None):
"""Context manager that activate verbosity of DPCTLSyclInterface
function calls.

.. warning::
This context manager modifies the ``DPCTL_VERBOSITY`` environment
variable and should only be used from a single thread.
"""
_allowed_verbosity = ["warning", "error"]
if verbosity not in _allowed_verbosity:
Expand Down
1 change: 1 addition & 0 deletions dpctl/_sycl_context.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# distutils: language = c++
# cython: language_level=3
# cython: linetrace=True
# cython: freethreading_compatible = True

""" Implements SyclContext Cython extension type.
"""
Expand Down
1 change: 1 addition & 0 deletions dpctl/_sycl_device.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# distutils: language = c++
# cython: language_level=3
# cython: linetrace=True
# cython: freethreading_compatible = True

""" Implements SyclDevice Cython extension type.
"""
Expand Down
72 changes: 40 additions & 32 deletions dpctl/_sycl_device_factory.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
# distutils: language = c++
# cython: language_level=3
# cython: linetrace=True
# cython: freethreading_compatible = True

""" This module implements several device creation helper functions:
"""
This module implements several device creation helper functions:

- wrapper functions to create a SyclDevice from the standard SYCL
device selector classes.
Expand Down Expand Up @@ -46,7 +48,7 @@ from ._backend cimport ( # noqa: E211
_device_type,
)

from contextvars import ContextVar
import threading

from ._sycl_device import SyclDeviceCreationError
from .enum_types import backend_type
Expand Down Expand Up @@ -286,7 +288,8 @@ cpdef int get_num_devices(


cpdef cpp_bool has_cpu_devices():
""" A helper function to check if there are any SYCL CPU devices available.
"""
A helper function to check if there are any SYCL CPU devices available.

Returns:
bool:
Expand All @@ -298,7 +301,8 @@ cpdef cpp_bool has_cpu_devices():


cpdef cpp_bool has_gpu_devices():
""" A helper function to check if there are any SYCL GPU devices available.
"""
A helper function to check if there are any SYCL GPU devices available.

Returns:
bool:
Expand All @@ -310,7 +314,8 @@ cpdef cpp_bool has_gpu_devices():


cpdef cpp_bool has_accelerator_devices():
""" A helper function to check if there are any SYCL Accelerator devices
"""
A helper function to check if there are any SYCL Accelerator devices
available.

Returns:
Expand All @@ -325,7 +330,8 @@ cpdef cpp_bool has_accelerator_devices():


cpdef SyclDevice select_accelerator_device():
"""A wrapper for ``sycl::device{sycl::accelerator_selector_v}`` constructor.
"""
A wrapper for ``sycl::device{sycl::accelerator_selector_v}`` constructor.

Returns:
dpctl.SyclDevice:
Expand All @@ -347,7 +353,8 @@ cpdef SyclDevice select_accelerator_device():


cpdef SyclDevice select_cpu_device():
"""A wrapper for ``sycl::device{sycl::cpu_selector_v}`` constructor.
"""
A wrapper for ``sycl::device{sycl::cpu_selector_v}`` constructor.

Returns:
dpctl.SyclDevice:
Expand All @@ -369,7 +376,8 @@ cpdef SyclDevice select_cpu_device():


cpdef SyclDevice select_default_device():
"""A wrapper for ``sycl::device{sycl::default_selector_v}`` constructor.
"""
A wrapper for ``sycl::device{sycl::default_selector_v}`` constructor.

Returns:
dpctl.SyclDevice:
Expand All @@ -391,7 +399,8 @@ cpdef SyclDevice select_default_device():


cpdef SyclDevice select_gpu_device():
"""A wrapper for ``sycl::device{sycl::gpu_selector_v}`` constructor.
"""
A wrapper for ``sycl::device{sycl::gpu_selector_v}`` constructor.

Returns:
dpctl.SyclDevice:
Expand All @@ -414,46 +423,45 @@ cpdef SyclDevice select_gpu_device():

cdef class _DefaultDeviceCache:
cdef dict __device_map__
cdef object _cache_lock

def __cinit__(self):
self.__device_map__ = {}

cdef get_or_create(self):
"""Return instance of SyclDevice and indicator if cache
has been modified"""
key = 0
if key in self.__device_map__:
return self.__device_map__[key], False
dev = select_default_device()
self.__device_map__[key] = dev
return dev, True

cdef _update_map(self, dev_map):
self._cache_lock = threading.Lock()

def get_or_create(self):
"""Return cached default SyclDevice, creating it if needed."""
with self._cache_lock:
key = 0
if key in self.__device_map__:
return self.__device_map__[key]
dev = select_default_device()
self.__device_map__[key] = dev
return dev

def _update_map(self, dev_map):
self.__device_map__.update(dev_map)

def __copy__(self):
cdef _DefaultDeviceCache _copy = _DefaultDeviceCache.__new__(
_DefaultDeviceCache)
_copy._update_map(self.__device_map__)
# lock must be held to avoid race conditions on map state
with self._cache_lock:
_copy._update_map(self.__device_map__.copy())
return _copy


_global_default_device_cache = ContextVar(
"global_default_device_cache",
default=_DefaultDeviceCache()
)
# all threads share the same cached default
_global_default_device_cache = _DefaultDeviceCache()


cpdef SyclDevice _cached_default_device():
"""Returns a cached device selected by default selector.
"""
Returns a cached device selected by default selector.

Returns:
dpctl.SyclDevice:
A cached default-selected SYCL device.

"""
cdef _DefaultDeviceCache _cache = _global_default_device_cache.get()
d_, changed_ = _cache.get_or_create()
if changed_:
_global_default_device_cache.set(_cache)
return d_
return _global_default_device_cache.get_or_create()
1 change: 1 addition & 0 deletions dpctl/_sycl_event.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# distutils: language = c++
# cython: language_level=3
# cython: linetrace=True
# cython: freethreading_compatible = True

""" Implements SyclEvent Cython extension type.
"""
Expand Down
1 change: 1 addition & 0 deletions dpctl/_sycl_platform.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# distutils: language = c++
# cython: language_level=3
# cython: linetrace=True
# cython: freethreading_compatible = True

""" Implements SyclPlatform Cython extension type.
"""
Expand Down
1 change: 1 addition & 0 deletions dpctl/_sycl_queue.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# distutils: language = c++
# cython: language_level=3
# cython: linetrace=True
# cython: freethreading_compatible = True

""" Implements SyclQueue Cython extension type.
"""
Expand Down
44 changes: 22 additions & 22 deletions dpctl/_sycl_queue_manager.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
# distutils: language = c++
# cython: language_level=3
# cython: linetrace=True
# cython: freethreading_compatible = True

import logging
from contextvars import ContextVar
import threading
from ._sycl_context cimport SyclContext
from ._sycl_device cimport SyclDevice

Expand All @@ -33,13 +34,14 @@ _logger = logging.getLogger(__name__)

cdef class _DeviceDefaultQueueCache:
cdef dict __device_queue_map__
cdef object _cache_lock

def __cinit__(self):
self.__device_queue_map__ = {}
self._cache_lock = threading.Lock()

def get_or_create(self, key):
"""Return instance of SyclQueue and indicator if cache
has been modified"""
"""Return cached SyclQueue for given key, creating it if needed."""
if (
isinstance(key, tuple)
and len(key) == 2
Expand All @@ -56,32 +58,34 @@ cdef class _DeviceDefaultQueueCache:
ctx_dev = q.sycl_context, q.sycl_device
else:
raise TypeError
if ctx_dev in self.__device_queue_map__:
return self.__device_queue_map__[ctx_dev], False
if q is None:
q = SyclQueue(*ctx_dev)
self.__device_queue_map__[ctx_dev] = q
return q, True

cdef _update_map(self, dev_queue_map):
with self._cache_lock:
Comment thread
ndgrigorian marked this conversation as resolved.
if ctx_dev in self.__device_queue_map__:
return self.__device_queue_map__[ctx_dev]
if q is None:
q = SyclQueue(*ctx_dev)
self.__device_queue_map__[ctx_dev] = q
return q

def _update_map(self, dev_queue_map):
self.__device_queue_map__.update(dev_queue_map)

def __copy__(self):
cdef _DeviceDefaultQueueCache _copy = _DeviceDefaultQueueCache.__new__(
_DeviceDefaultQueueCache
)
_copy._update_map(self.__device_queue_map__)
# lock must be held to avoid race conditions on map state
with self._cache_lock:
_copy._update_map(self.__device_queue_map__.copy())
return _copy


_global_device_queue_cache = ContextVar(
"global_device_queue_cache",
default=_DeviceDefaultQueueCache()
)
# all threads share the same cached default
_global_device_queue_cache = _DeviceDefaultQueueCache()


cpdef object get_device_cached_queue(object key):
"""Returns a cached queue associated with given device.
"""
Returns a cached queue associated with given device.

Args:
key : Either a 2-tuple consisting of a :class:`dpctl.SyclContext` and
Expand All @@ -96,8 +100,4 @@ cpdef object get_device_cached_queue(object key):
TypeError: If the input key is not one of the accepted types.

"""
_cache = _global_device_queue_cache.get()
q_, changed_ = _cache.get_or_create(key)
if changed_:
_global_device_queue_cache.set(_cache)
return q_
return _global_device_queue_cache.get_or_create(key)
Loading
Loading