ez-a-sync 0.33.4__cp313-cp313-musllinux_1_2_i686.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- a_sync/ENVIRONMENT_VARIABLES.py +42 -0
- a_sync/__init__.pxd +2 -0
- a_sync/__init__.py +145 -0
- a_sync/_smart.c +22830 -0
- a_sync/_smart.cpython-313-i386-linux-musl.so +0 -0
- a_sync/_smart.pxd +2 -0
- a_sync/_smart.pyi +202 -0
- a_sync/_smart.pyx +674 -0
- a_sync/_typing.py +258 -0
- a_sync/a_sync/__init__.py +60 -0
- a_sync/a_sync/_descriptor.c +20537 -0
- a_sync/a_sync/_descriptor.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/_descriptor.pyi +33 -0
- a_sync/a_sync/_descriptor.pyx +422 -0
- a_sync/a_sync/_flags.c +6082 -0
- a_sync/a_sync/_flags.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/_flags.pxd +3 -0
- a_sync/a_sync/_flags.pyx +92 -0
- a_sync/a_sync/_helpers.c +14529 -0
- a_sync/a_sync/_helpers.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/_helpers.pxd +3 -0
- a_sync/a_sync/_helpers.pyi +10 -0
- a_sync/a_sync/_helpers.pyx +167 -0
- a_sync/a_sync/_kwargs.c +12202 -0
- a_sync/a_sync/_kwargs.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/_kwargs.pxd +2 -0
- a_sync/a_sync/_kwargs.pyx +64 -0
- a_sync/a_sync/_meta.py +210 -0
- a_sync/a_sync/abstract.c +12420 -0
- a_sync/a_sync/abstract.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/abstract.pyi +141 -0
- a_sync/a_sync/abstract.pyx +221 -0
- a_sync/a_sync/base.c +14940 -0
- a_sync/a_sync/base.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/base.pyi +60 -0
- a_sync/a_sync/base.pyx +271 -0
- a_sync/a_sync/config.py +168 -0
- a_sync/a_sync/decorator.py +651 -0
- a_sync/a_sync/flags.c +5272 -0
- a_sync/a_sync/flags.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/flags.pxd +72 -0
- a_sync/a_sync/flags.pyi +74 -0
- a_sync/a_sync/flags.pyx +72 -0
- a_sync/a_sync/function.c +37856 -0
- a_sync/a_sync/function.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/function.pxd +28 -0
- a_sync/a_sync/function.pyi +571 -0
- a_sync/a_sync/function.pyx +1381 -0
- a_sync/a_sync/method.c +29662 -0
- a_sync/a_sync/method.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/method.pxd +9 -0
- a_sync/a_sync/method.pyi +523 -0
- a_sync/a_sync/method.pyx +1023 -0
- a_sync/a_sync/modifiers/__init__.pxd +1 -0
- a_sync/a_sync/modifiers/__init__.py +101 -0
- a_sync/a_sync/modifiers/cache/__init__.py +160 -0
- a_sync/a_sync/modifiers/cache/memory.py +165 -0
- a_sync/a_sync/modifiers/limiter.py +132 -0
- a_sync/a_sync/modifiers/manager.c +16157 -0
- a_sync/a_sync/modifiers/manager.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/modifiers/manager.pxd +5 -0
- a_sync/a_sync/modifiers/manager.pyi +219 -0
- a_sync/a_sync/modifiers/manager.pyx +299 -0
- a_sync/a_sync/modifiers/semaphores.py +173 -0
- a_sync/a_sync/property.c +27268 -0
- a_sync/a_sync/property.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/property.pyi +376 -0
- a_sync/a_sync/property.pyx +819 -0
- a_sync/a_sync/singleton.py +63 -0
- a_sync/aliases.py +3 -0
- a_sync/async_property/__init__.pxd +1 -0
- a_sync/async_property/__init__.py +1 -0
- a_sync/async_property/cached.c +20397 -0
- a_sync/async_property/cached.cpython-313-i386-linux-musl.so +0 -0
- a_sync/async_property/cached.pxd +10 -0
- a_sync/async_property/cached.pyi +45 -0
- a_sync/async_property/cached.pyx +178 -0
- a_sync/async_property/proxy.c +34662 -0
- a_sync/async_property/proxy.cpython-313-i386-linux-musl.so +0 -0
- a_sync/async_property/proxy.pxd +2 -0
- a_sync/async_property/proxy.pyi +124 -0
- a_sync/async_property/proxy.pyx +474 -0
- a_sync/asyncio/__init__.pxd +6 -0
- a_sync/asyncio/__init__.py +164 -0
- a_sync/asyncio/as_completed.c +18849 -0
- a_sync/asyncio/as_completed.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/as_completed.pxd +8 -0
- a_sync/asyncio/as_completed.pyi +109 -0
- a_sync/asyncio/as_completed.pyx +269 -0
- a_sync/asyncio/create_task.c +15912 -0
- a_sync/asyncio/create_task.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/create_task.pxd +2 -0
- a_sync/asyncio/create_task.pyi +51 -0
- a_sync/asyncio/create_task.pyx +271 -0
- a_sync/asyncio/gather.c +16687 -0
- a_sync/asyncio/gather.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/gather.pyi +107 -0
- a_sync/asyncio/gather.pyx +218 -0
- a_sync/asyncio/igather.c +13080 -0
- a_sync/asyncio/igather.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/igather.pxd +1 -0
- a_sync/asyncio/igather.pyi +8 -0
- a_sync/asyncio/igather.pyx +183 -0
- a_sync/asyncio/sleep.c +9601 -0
- a_sync/asyncio/sleep.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/sleep.pyi +14 -0
- a_sync/asyncio/sleep.pyx +49 -0
- a_sync/debugging.c +15370 -0
- a_sync/debugging.cpython-313-i386-linux-musl.so +0 -0
- a_sync/debugging.pyi +76 -0
- a_sync/debugging.pyx +107 -0
- a_sync/exceptions.c +13320 -0
- a_sync/exceptions.cpython-313-i386-linux-musl.so +0 -0
- a_sync/exceptions.pyi +376 -0
- a_sync/exceptions.pyx +446 -0
- a_sync/executor.py +619 -0
- a_sync/functools.c +12746 -0
- a_sync/functools.cpython-313-i386-linux-musl.so +0 -0
- a_sync/functools.pxd +7 -0
- a_sync/functools.pyi +33 -0
- a_sync/functools.pyx +139 -0
- a_sync/future.py +1497 -0
- a_sync/iter.c +37279 -0
- a_sync/iter.cpython-313-i386-linux-musl.so +0 -0
- a_sync/iter.pxd +11 -0
- a_sync/iter.pyi +370 -0
- a_sync/iter.pyx +981 -0
- a_sync/primitives/__init__.pxd +1 -0
- a_sync/primitives/__init__.py +53 -0
- a_sync/primitives/_debug.c +15765 -0
- a_sync/primitives/_debug.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/_debug.pxd +12 -0
- a_sync/primitives/_debug.pyi +52 -0
- a_sync/primitives/_debug.pyx +223 -0
- a_sync/primitives/_loggable.c +11538 -0
- a_sync/primitives/_loggable.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/_loggable.pxd +4 -0
- a_sync/primitives/_loggable.pyi +66 -0
- a_sync/primitives/_loggable.pyx +102 -0
- a_sync/primitives/locks/__init__.pxd +8 -0
- a_sync/primitives/locks/__init__.py +17 -0
- a_sync/primitives/locks/counter.c +17938 -0
- a_sync/primitives/locks/counter.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/locks/counter.pxd +12 -0
- a_sync/primitives/locks/counter.pyi +151 -0
- a_sync/primitives/locks/counter.pyx +267 -0
- a_sync/primitives/locks/event.c +17072 -0
- a_sync/primitives/locks/event.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/locks/event.pxd +22 -0
- a_sync/primitives/locks/event.pyi +43 -0
- a_sync/primitives/locks/event.pyx +185 -0
- a_sync/primitives/locks/prio_semaphore.c +25635 -0
- a_sync/primitives/locks/prio_semaphore.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/locks/prio_semaphore.pxd +25 -0
- a_sync/primitives/locks/prio_semaphore.pyi +217 -0
- a_sync/primitives/locks/prio_semaphore.pyx +597 -0
- a_sync/primitives/locks/semaphore.c +26553 -0
- a_sync/primitives/locks/semaphore.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/locks/semaphore.pxd +21 -0
- a_sync/primitives/locks/semaphore.pyi +197 -0
- a_sync/primitives/locks/semaphore.pyx +454 -0
- a_sync/primitives/queue.py +1026 -0
- a_sync/py.typed +0 -0
- a_sync/sphinx/__init__.py +3 -0
- a_sync/sphinx/ext.py +289 -0
- a_sync/task.py +934 -0
- a_sync/utils/__init__.py +105 -0
- a_sync/utils/iterators.py +297 -0
- a_sync/utils/repr.c +15866 -0
- a_sync/utils/repr.cpython-313-i386-linux-musl.so +0 -0
- a_sync/utils/repr.pyi +2 -0
- a_sync/utils/repr.pyx +73 -0
- ez_a_sync-0.33.4.dist-info/METADATA +368 -0
- ez_a_sync-0.33.4.dist-info/RECORD +177 -0
- ez_a_sync-0.33.4.dist-info/WHEEL +5 -0
- ez_a_sync-0.33.4.dist-info/licenses/LICENSE.txt +17 -0
- ez_a_sync-0.33.4.dist-info/top_level.txt +1 -0
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
from a_sync.primitives._debug cimport _DebugDaemonMixin
|
|
3
|
+
from a_sync.primitives.locks.event cimport CythonEvent as Event
|
|
4
|
+
|
|
5
|
+
cdef class CounterLock(_DebugDaemonMixin):
|
|
6
|
+
cdef char* __name
|
|
7
|
+
cdef long long _value
|
|
8
|
+
cdef list _heap
|
|
9
|
+
cdef dict[long long, Event] _events
|
|
10
|
+
cpdef bint is_ready(self, long long v)
|
|
11
|
+
cdef inline bint c_is_ready(self, long long v)
|
|
12
|
+
cpdef void set(self, long long value)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from a_sync.primitives._debug import _DebugDaemonMixin
|
|
3
|
+
from a_sync.primitives.locks import Event
|
|
4
|
+
from typing import Iterable, Optional
|
|
5
|
+
|
|
6
|
+
class CounterLock(_DebugDaemonMixin):
|
|
7
|
+
"""
|
|
8
|
+
An async primitive that uses an internal counter to manage task synchronization.
|
|
9
|
+
|
|
10
|
+
A coroutine can `await counter.wait_for(3)` and it will wait until the internal counter >= 3.
|
|
11
|
+
If some other task executes `counter.value = 5` or `counter.set(5)`, the first coroutine will proceed as 5 >= 3.
|
|
12
|
+
|
|
13
|
+
The internal counter can only be set to a value greater than the current value.
|
|
14
|
+
|
|
15
|
+
See Also:
|
|
16
|
+
:class:`CounterLockCluster` for managing multiple :class:`CounterLock` instances.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
is_ready: Incomplete
|
|
20
|
+
def __init__(self, start_value: int = 0, name: Optional[str] = None) -> None:
|
|
21
|
+
"""
|
|
22
|
+
Initializes the :class:`CounterLock` with a starting value and an optional name.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
start_value: The initial value of the counter.
|
|
26
|
+
name: An optional name for the counter, used in debug logs.
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
>>> counter = CounterLock(start_value=0, name="example_counter")
|
|
30
|
+
>>> counter.value
|
|
31
|
+
0
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
async def wait_for(self, value: int) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
Waits until the counter reaches or exceeds the specified value.
|
|
37
|
+
|
|
38
|
+
This method will ensure the debug daemon is running if the counter is not ready.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
value: The value to wait for.
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
>>> counter = CounterLock(start_value=0)
|
|
45
|
+
>>> await counter.wait_for(5) # This will block until counter.value >= 5
|
|
46
|
+
|
|
47
|
+
See Also:
|
|
48
|
+
:meth:`CounterLock.set` to set the counter value.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def set(self, value: int) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Sets the counter to the specified value.
|
|
54
|
+
|
|
55
|
+
This method internally uses the `value` property to enforce that the new value must be strictly greater than the current value.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
value: The value to set the counter to. Must be strictly greater than the current value.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
ValueError: If the new value is less than or equal to the current value.
|
|
62
|
+
|
|
63
|
+
Examples:
|
|
64
|
+
>>> counter = CounterLock(start_value=0)
|
|
65
|
+
>>> counter.set(5)
|
|
66
|
+
>>> counter.value
|
|
67
|
+
5
|
|
68
|
+
|
|
69
|
+
See Also:
|
|
70
|
+
:meth:`CounterLock.value` for direct value assignment.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def value(self) -> int:
|
|
75
|
+
"""
|
|
76
|
+
Gets the current value of the counter.
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
>>> counter = CounterLock(start_value=0)
|
|
80
|
+
>>> counter.value
|
|
81
|
+
0
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
@value.setter
|
|
85
|
+
def value(self, value: int) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Sets the counter to a new value, waking up any waiters if the value increases beyond the value they are awaiting.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
value: The new value of the counter.
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
ValueError: If the new value is less than the current value.
|
|
94
|
+
|
|
95
|
+
Examples:
|
|
96
|
+
>>> counter = CounterLock(start_value=0)
|
|
97
|
+
>>> counter.value = 5
|
|
98
|
+
>>> counter.value
|
|
99
|
+
5
|
|
100
|
+
>>> counter.value = 3
|
|
101
|
+
Traceback (most recent call last):
|
|
102
|
+
...
|
|
103
|
+
ValueError: You cannot decrease the value.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
async def _debug_daemon(self) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Periodically logs debug information about the counter state and waiters.
|
|
109
|
+
|
|
110
|
+
This method is used internally to provide debugging information when debug logging is enabled.
|
|
111
|
+
|
|
112
|
+
This code will only run if `self.logger.isEnabledFor(logging.DEBUG)` is True. You do not need to include any level checks in your custom implementations.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
class CounterLockCluster:
|
|
116
|
+
"""
|
|
117
|
+
An asyncio primitive that represents a collection of :class:`CounterLock` objects.
|
|
118
|
+
|
|
119
|
+
`wait_for(i)` will wait until the value of all :class:`CounterLock` objects is >= i.
|
|
120
|
+
|
|
121
|
+
See Also:
|
|
122
|
+
:class:`CounterLock` for managing individual counters.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
locks: Incomplete
|
|
126
|
+
def __init__(self, counter_locks: Iterable[CounterLock]) -> None:
|
|
127
|
+
"""
|
|
128
|
+
Initializes the :class:`CounterLockCluster` with a collection of :class:`CounterLock` objects.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
counter_locks: The :class:`CounterLock` objects to manage.
|
|
132
|
+
|
|
133
|
+
Examples:
|
|
134
|
+
>>> lock1 = CounterLock(start_value=0)
|
|
135
|
+
>>> lock2 = CounterLock(start_value=0)
|
|
136
|
+
>>> cluster = CounterLockCluster([lock1, lock2])
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
async def wait_for(self, value: int) -> bool:
|
|
140
|
+
"""
|
|
141
|
+
Waits until the value of all :class:`CounterLock` objects in the cluster reaches or exceeds the specified value.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
value: The value to wait for.
|
|
145
|
+
|
|
146
|
+
Examples:
|
|
147
|
+
>>> lock1 = CounterLock(start_value=0)
|
|
148
|
+
>>> lock2 = CounterLock(start_value=0)
|
|
149
|
+
>>> cluster = CounterLockCluster([lock1, lock2])
|
|
150
|
+
>>> await cluster.wait_for(5) # This will block until all locks have value >= 5
|
|
151
|
+
"""
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides two specialized async flow management classes, :class:`CounterLock` and :class:`CounterLockCluster`.
|
|
3
|
+
|
|
4
|
+
These primitives manage synchronization of tasks that must wait for an internal counter to reach a specific value.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from asyncio import sleep
|
|
8
|
+
from heapq import heapify, heappop, heappush
|
|
9
|
+
from libc.string cimport strcpy
|
|
10
|
+
from libc.stdlib cimport malloc, free
|
|
11
|
+
from libc.time cimport time
|
|
12
|
+
from typing import Iterable
|
|
13
|
+
|
|
14
|
+
from a_sync.asyncio cimport cigather
|
|
15
|
+
from a_sync.primitives._debug cimport _DebugDaemonMixin
|
|
16
|
+
from a_sync.primitives.locks.event cimport CythonEvent as Event
|
|
17
|
+
|
|
18
|
+
cdef extern from "time.h":
|
|
19
|
+
ctypedef long time_t
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
cdef class CounterLock(_DebugDaemonMixin):
|
|
23
|
+
"""
|
|
24
|
+
An async primitive that uses an internal counter to manage task synchronization.
|
|
25
|
+
|
|
26
|
+
A coroutine can `await counter.wait_for(3)` and it will wait until the internal counter >= 3.
|
|
27
|
+
If some other task executes `counter.value = 5` or `counter.set(5)`, the first coroutine will proceed as 5 >= 3.
|
|
28
|
+
|
|
29
|
+
The internal counter can only be set to a value greater than the current value.
|
|
30
|
+
|
|
31
|
+
See Also:
|
|
32
|
+
:class:`CounterLockCluster` for managing multiple :class:`CounterLock` instances.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __cinit__(self):
|
|
36
|
+
self._events = {}
|
|
37
|
+
"""A dictionary that maps each awaited value to an :class:`Event` that manages the waiters for that value."""
|
|
38
|
+
|
|
39
|
+
self._heap = []
|
|
40
|
+
|
|
41
|
+
def __init__(self, start_value: int = 0, str name = ""):
|
|
42
|
+
"""
|
|
43
|
+
Initializes the :class:`CounterLock` with a starting value and an optional name.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
start_value: The initial value of the counter.
|
|
47
|
+
name: An optional name for the counter, used in debug logs.
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
>>> counter = CounterLock(start_value=0, name="example_counter")
|
|
51
|
+
>>> counter.value
|
|
52
|
+
0
|
|
53
|
+
"""
|
|
54
|
+
# we need a constant to coerce to char*
|
|
55
|
+
cdef bytes encoded_name = name.encode("utf-8")
|
|
56
|
+
cdef Py_ssize_t length = len(encoded_name)
|
|
57
|
+
|
|
58
|
+
# Allocate memory for the char* and add 1 for the null character
|
|
59
|
+
self.__name = <char*>malloc(length + 1)
|
|
60
|
+
"""An optional name for the counter, used in debug logs."""
|
|
61
|
+
|
|
62
|
+
if self.__name == NULL:
|
|
63
|
+
raise MemoryError("Failed to allocate memory for __name.")
|
|
64
|
+
# Copy the bytes data into the char*
|
|
65
|
+
strcpy(self.__name, encoded_name)
|
|
66
|
+
|
|
67
|
+
self._value = start_value
|
|
68
|
+
"""The current value of the counter."""
|
|
69
|
+
|
|
70
|
+
def __dealloc__(self):
|
|
71
|
+
# Free the memory allocated for __name
|
|
72
|
+
if self.__name is not NULL:
|
|
73
|
+
free(self.__name)
|
|
74
|
+
|
|
75
|
+
def __repr__(self) -> str:
|
|
76
|
+
"""
|
|
77
|
+
Returns a string representation of the :class:`CounterLock` instance.
|
|
78
|
+
|
|
79
|
+
The representation includes the name, current value, and the number of waiters for each awaited value.
|
|
80
|
+
|
|
81
|
+
Examples:
|
|
82
|
+
>>> counter = CounterLock(start_value=0, name="example_counter")
|
|
83
|
+
>>> repr(counter)
|
|
84
|
+
'<CounterLock name=example_counter value=0 waiters={}>'
|
|
85
|
+
"""
|
|
86
|
+
cdef dict[long long, Event] events = self._events
|
|
87
|
+
cdef dict[long long, Py_ssize_t] waiters = {v: len((<Event>events[v])._waiters) for v in self._heap}
|
|
88
|
+
cdef str name = self.__name.decode("utf-8")
|
|
89
|
+
if name:
|
|
90
|
+
return "<CounterLock name={} value={} waiters={}>".format(self.__name.decode("utf-8"), self._value, waiters)
|
|
91
|
+
else:
|
|
92
|
+
return "<CounterLock value={} waiters={}>".format(self._value, waiters)
|
|
93
|
+
|
|
94
|
+
cpdef bint is_ready(self, long long v):
|
|
95
|
+
"""A function that indicates whether the current counter value is greater than or equal to a given value."""
|
|
96
|
+
return self._value >= v
|
|
97
|
+
|
|
98
|
+
cdef inline bint c_is_ready(self, long long v):
|
|
99
|
+
return self._value >= v
|
|
100
|
+
|
|
101
|
+
async def wait_for(self, long long value) -> bint:
|
|
102
|
+
"""
|
|
103
|
+
Waits until the counter reaches or exceeds the specified value.
|
|
104
|
+
|
|
105
|
+
This method will ensure the debug daemon is running if the counter is not ready.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
value: The value to wait for.
|
|
109
|
+
|
|
110
|
+
Examples:
|
|
111
|
+
>>> counter = CounterLock(start_value=0)
|
|
112
|
+
>>> await counter.wait_for(5) # This will block until counter.value >= 5
|
|
113
|
+
|
|
114
|
+
See Also:
|
|
115
|
+
:meth:`CounterLock.set` to set the counter value.
|
|
116
|
+
"""
|
|
117
|
+
cdef Event event
|
|
118
|
+
if not self.c_is_ready(value):
|
|
119
|
+
event = self._events.get(value)
|
|
120
|
+
if event is None:
|
|
121
|
+
event = Event()
|
|
122
|
+
self._events[value] = event
|
|
123
|
+
heappush(self._heap, value)
|
|
124
|
+
self._c_ensure_debug_daemon((),{})
|
|
125
|
+
try:
|
|
126
|
+
await event.c_wait()
|
|
127
|
+
except Exception:
|
|
128
|
+
if not event._waiters:
|
|
129
|
+
self._heap.remove(value)
|
|
130
|
+
heapify(self._heap)
|
|
131
|
+
self._events.pop(value, None)
|
|
132
|
+
raise
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
cpdef void set(self, long long value):
|
|
136
|
+
"""
|
|
137
|
+
Sets the counter to the specified value.
|
|
138
|
+
|
|
139
|
+
This method internally uses the `value` property to enforce that the new value must be strictly greater than the current value.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
value: The value to set the counter to. Must be strictly greater than the current value.
|
|
143
|
+
|
|
144
|
+
Raises:
|
|
145
|
+
ValueError: If the new value is less than or equal to the current value.
|
|
146
|
+
|
|
147
|
+
Examples:
|
|
148
|
+
>>> counter = CounterLock(start_value=0)
|
|
149
|
+
>>> counter.set(5)
|
|
150
|
+
>>> counter.value
|
|
151
|
+
5
|
|
152
|
+
|
|
153
|
+
See Also:
|
|
154
|
+
:meth:`CounterLock.value` for direct value assignment.
|
|
155
|
+
"""
|
|
156
|
+
cdef long long key
|
|
157
|
+
if value > self._value:
|
|
158
|
+
self._value = value
|
|
159
|
+
while self._heap:
|
|
160
|
+
key = heappop(self._heap)
|
|
161
|
+
if key <= self._value:
|
|
162
|
+
(<Event>self._events.pop(key)).set()
|
|
163
|
+
else:
|
|
164
|
+
heappush(self._heap, key)
|
|
165
|
+
return
|
|
166
|
+
elif value < self._value:
|
|
167
|
+
raise ValueError("You cannot decrease the value.")
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def value(self) -> int:
|
|
171
|
+
"""
|
|
172
|
+
Gets the current value of the counter.
|
|
173
|
+
|
|
174
|
+
Examples:
|
|
175
|
+
>>> counter = CounterLock(start_value=0)
|
|
176
|
+
>>> counter.value
|
|
177
|
+
0
|
|
178
|
+
"""
|
|
179
|
+
return self._value
|
|
180
|
+
|
|
181
|
+
@value.setter
|
|
182
|
+
def value(self, long long value) -> None:
|
|
183
|
+
"""
|
|
184
|
+
Sets the counter to a new value, waking up any waiters if the value increases beyond the value they are awaiting.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
value: The new value of the counter.
|
|
188
|
+
|
|
189
|
+
Raises:
|
|
190
|
+
ValueError: If the new value is less than the current value.
|
|
191
|
+
|
|
192
|
+
Examples:
|
|
193
|
+
>>> counter = CounterLock(start_value=0)
|
|
194
|
+
>>> counter.value = 5
|
|
195
|
+
>>> counter.value
|
|
196
|
+
5
|
|
197
|
+
>>> counter.value = 3
|
|
198
|
+
Traceback (most recent call last):
|
|
199
|
+
...
|
|
200
|
+
ValueError: You cannot decrease the value.
|
|
201
|
+
"""
|
|
202
|
+
self.set(value)
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def _name(self) -> str:
|
|
206
|
+
return self.__name.decode("utf-8")
|
|
207
|
+
|
|
208
|
+
async def _debug_daemon(self) -> None:
|
|
209
|
+
"""
|
|
210
|
+
Periodically logs debug information about the counter state and waiters.
|
|
211
|
+
|
|
212
|
+
This method is used internally to provide debugging information when debug logging is enabled.
|
|
213
|
+
|
|
214
|
+
This code will only run if `self.logger.isEnabledFor(logging.DEBUG)` is True. You do not need to include any level checks in your custom implementations.
|
|
215
|
+
"""
|
|
216
|
+
cdef time_t start, now
|
|
217
|
+
start = time(NULL)
|
|
218
|
+
await sleep(300)
|
|
219
|
+
while self._events:
|
|
220
|
+
now = time(NULL)
|
|
221
|
+
self.get_logger().debug(
|
|
222
|
+
"%s is still locked after %.2fm", self, (now - start) / 60
|
|
223
|
+
)
|
|
224
|
+
await sleep(300)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class CounterLockCluster:
|
|
228
|
+
"""
|
|
229
|
+
An asyncio primitive that represents a collection of :class:`CounterLock` objects.
|
|
230
|
+
|
|
231
|
+
`wait_for(i)` will wait until the value of all :class:`CounterLock` objects is >= i.
|
|
232
|
+
|
|
233
|
+
See Also:
|
|
234
|
+
:class:`CounterLock` for managing individual counters.
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
__slots__ = ("locks",)
|
|
238
|
+
|
|
239
|
+
def __init__(self, counter_locks: Iterable[CounterLock]) -> None:
|
|
240
|
+
"""
|
|
241
|
+
Initializes the :class:`CounterLockCluster` with a collection of :class:`CounterLock` objects.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
counter_locks: The :class:`CounterLock` objects to manage.
|
|
245
|
+
|
|
246
|
+
Examples:
|
|
247
|
+
>>> lock1 = CounterLock(start_value=0)
|
|
248
|
+
>>> lock2 = CounterLock(start_value=0)
|
|
249
|
+
>>> cluster = CounterLockCluster([lock1, lock2])
|
|
250
|
+
"""
|
|
251
|
+
self.locks = list(counter_locks)
|
|
252
|
+
|
|
253
|
+
async def wait_for(self, value: int) -> bint:
|
|
254
|
+
"""
|
|
255
|
+
Waits until the value of all :class:`CounterLock` objects in the cluster reaches or exceeds the specified value.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
value: The value to wait for.
|
|
259
|
+
|
|
260
|
+
Examples:
|
|
261
|
+
>>> lock1 = CounterLock(start_value=0)
|
|
262
|
+
>>> lock2 = CounterLock(start_value=0)
|
|
263
|
+
>>> cluster = CounterLockCluster([lock1, lock2])
|
|
264
|
+
>>> await cluster.wait_for(5) # This will block until all locks have value >= 5
|
|
265
|
+
"""
|
|
266
|
+
await cigather(counter_lock.wait_for(value) for counter_lock in self.locks)
|
|
267
|
+
return True
|