|
12 | 12 | # See the License for the specific language governing permissions and |
13 | 13 | # limitations under the License. |
14 | 14 |
|
15 | | -# With thanks to everyone I've ever discussed concurrency or datastructures with |
16 | | - |
17 | 15 | from __future__ import annotations |
18 | 16 |
|
19 | 17 | import asyncio |
| 18 | +import sys |
20 | 19 | import threading |
21 | 20 | from collections import deque |
22 | 21 | from collections.abc import Generator |
23 | | - |
24 | | -# PYUPDATE: 3.14 release + 3.14 minimum: reaudit |
25 | | -# heapq methods are not threadsafe pre 3.14 |
26 | | -# see: GH: cpython 135036 |
27 | 22 | from heapq import heappop, heappush |
28 | 23 |
|
29 | 24 | from . import _typings as t |
30 | 25 |
|
| 26 | +THREAD_SAFE_HEAPQ = sys.version_info[:2] >= (3, 14) |
| 27 | + |
31 | 28 | TYPE_CHECKING = False |
32 | 29 | if TYPE_CHECKING: |
33 | 30 | import typing |
@@ -604,30 +601,43 @@ def _get(self, /) -> T: |
604 | 601 | class PriorityQueue[T: HeapqOrderable](BaseQueue[T]): |
605 | 602 | """A thread-safe queue with both sync and async access methods.""" |
606 | 603 |
|
607 | | - __slots__ = ("_data", "_lock") |
| 604 | + __slots__ = ("_data",) if THREAD_SAFE_HEAPQ else ("_data", "_lock") |
608 | 605 |
|
609 | 606 | def __init_subclass__(cls) -> t.Never: |
610 | 607 | msg = "Don't subclass this" |
611 | 608 | raise RuntimeError(msg) |
612 | 609 |
|
613 | 610 | __final__ = True |
614 | 611 |
|
615 | | - def __init__(self, /, maxsize: int | None = None) -> None: |
616 | | - super().__init__(maxsize) |
617 | | - self._data: list[T] = [] |
618 | | - # heapq not threadsafe till 3.14 |
619 | | - self._lock = threading.RLock() |
| 612 | + if THREAD_SAFE_HEAPQ: |
| 613 | + |
| 614 | + def __init__(self, /, maxsize: int | None = None) -> None: |
| 615 | + super().__init__(maxsize) |
| 616 | + self._data: list[T] = [] |
| 617 | + |
| 618 | + def _put(self, item: T, /) -> None: |
| 619 | + heappush(self._data, item) |
| 620 | + |
| 621 | + def _get(self, /) -> T: |
| 622 | + return heappop(self._data) |
| 623 | + |
| 624 | + else: |
| 625 | + |
| 626 | + def __init__(self, /, maxsize: int | None = None) -> None: |
| 627 | + super().__init__(maxsize) |
| 628 | + self._data: list[T] = [] |
| 629 | + self._lock = threading.RLock() |
| 630 | + |
| 631 | + def _put(self, item: T, /) -> None: |
| 632 | + with self._lock: |
| 633 | + heappush(self._data, item) |
| 634 | + |
| 635 | + def _get(self, /) -> T: |
| 636 | + with self._lock: |
| 637 | + return heappop(self._data) |
620 | 638 |
|
621 | 639 | def _qsize(self, /) -> int: |
622 | 640 | return len(self._data) |
623 | 641 |
|
624 | 642 | def _items(self, /) -> list[T]: |
625 | 643 | return self._data.copy() |
626 | | - |
627 | | - def _put(self, item: T, /) -> None: |
628 | | - with self._lock: |
629 | | - heappush(self._data, item) |
630 | | - |
631 | | - def _get(self, /) -> T: |
632 | | - with self._lock: |
633 | | - return heappop(self._data) |
|
0 commit comments