nblf-queue 0.1.0__cp312-abi3-win_amd64.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.
nblf_queue/__init__.py
ADDED
nblf_queue/__init__.pyi
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
from typing import Callable, Generic, TypeVar
|
|
2
|
+
|
|
3
|
+
_T = TypeVar("_T")
|
|
4
|
+
|
|
5
|
+
class DynamicQueue(Generic[_T]):
|
|
6
|
+
"""
|
|
7
|
+
A dynamically growable, lock-free non-blocking MPMC queue.
|
|
8
|
+
|
|
9
|
+
Core operations detach from the python GIL, to ensure concurrent performance.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, size: int) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Constructs a new `DynamicQueue` with initial capacity `size`.
|
|
15
|
+
"""
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
def push(self, item: _T) -> _T | None:
|
|
19
|
+
"""
|
|
20
|
+
Attempts to push an element into the queue.
|
|
21
|
+
|
|
22
|
+
Returns the item, if the queue was full.
|
|
23
|
+
"""
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
def pop(self) -> _T | None:
|
|
27
|
+
"""
|
|
28
|
+
Attempts to pop an item from the queue.
|
|
29
|
+
|
|
30
|
+
Returns `None` if the queue was empty.
|
|
31
|
+
"""
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
def len(self) -> int:
|
|
35
|
+
"""
|
|
36
|
+
Returns the current length of the queue.
|
|
37
|
+
|
|
38
|
+
This method should not be used for synchronization.
|
|
39
|
+
"""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
def capacity(self) -> int:
|
|
43
|
+
"""
|
|
44
|
+
Returns the current capacity of the queue.
|
|
45
|
+
|
|
46
|
+
This method should not be used for synchronization.
|
|
47
|
+
"""
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
def is_empty(self) -> bool:
|
|
51
|
+
"""
|
|
52
|
+
Indicates whether the queue is currently empty.
|
|
53
|
+
|
|
54
|
+
This method should not be used for synchronization.
|
|
55
|
+
"""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
def is_full(self) -> bool:
|
|
59
|
+
"""
|
|
60
|
+
Indicates whether the queue is currently full.
|
|
61
|
+
|
|
62
|
+
This method should not be used for synchronization.
|
|
63
|
+
"""
|
|
64
|
+
...
|
|
65
|
+
|
|
66
|
+
def force_push(self, item: _T) -> _T | None:
|
|
67
|
+
"""
|
|
68
|
+
Pushes an item into the queue.
|
|
69
|
+
This method may take some time under heavy contention.
|
|
70
|
+
This method may pop an arbitrary amount of items from the queue.
|
|
71
|
+
|
|
72
|
+
Returns the last popped item, if the queue was full. All other items are dropped.
|
|
73
|
+
"""
|
|
74
|
+
...
|
|
75
|
+
|
|
76
|
+
def force_push_and_do(self, item: _T, f: Callable[[_T], None]) -> None:
|
|
77
|
+
"""
|
|
78
|
+
Pushes an item into the queue.
|
|
79
|
+
This method may take some time under heavy contention.
|
|
80
|
+
This method may pop an arbitrary amount of items from the queue.
|
|
81
|
+
Applies `f` to each popped item.
|
|
82
|
+
"""
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
def grow_by(self, by: int) -> bool:
|
|
86
|
+
"""
|
|
87
|
+
Attempts to grow the queue's capacity by `by` items.
|
|
88
|
+
|
|
89
|
+
This method may spuriously fail.
|
|
90
|
+
|
|
91
|
+
Returns `True` if the capacity was successfully grown.
|
|
92
|
+
"""
|
|
93
|
+
...
|
|
94
|
+
|
|
95
|
+
def grow(self) -> bool:
|
|
96
|
+
"""
|
|
97
|
+
Attempts to grow the queue's capacity using limited exponential growth.
|
|
98
|
+
|
|
99
|
+
This method may spuriously fail.
|
|
100
|
+
|
|
101
|
+
Returns `True` if the capacity was successfully grown.
|
|
102
|
+
"""
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
class Queue(Generic[_T]):
|
|
106
|
+
"""
|
|
107
|
+
A lock-free non-blocking MPMC queue.
|
|
108
|
+
|
|
109
|
+
Core operations detach from the python GIL, to ensure concurrent performance.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def __init__(self, size: int) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Constructs a new `Queue` with initial capacity `size`.
|
|
115
|
+
"""
|
|
116
|
+
...
|
|
117
|
+
|
|
118
|
+
def push(self, item: _T) -> _T | None:
|
|
119
|
+
"""
|
|
120
|
+
Attempts to push an element into the queue.
|
|
121
|
+
|
|
122
|
+
Returns the item, if the queue was full.
|
|
123
|
+
"""
|
|
124
|
+
...
|
|
125
|
+
|
|
126
|
+
def pop(self) -> _T | None:
|
|
127
|
+
"""
|
|
128
|
+
Attempts to pop an item from the queue.
|
|
129
|
+
|
|
130
|
+
Returns `None` if the queue was empty.
|
|
131
|
+
"""
|
|
132
|
+
...
|
|
133
|
+
|
|
134
|
+
def len(self) -> int:
|
|
135
|
+
"""
|
|
136
|
+
Returns the current length of the queue.
|
|
137
|
+
|
|
138
|
+
This method should not be used for synchronization.
|
|
139
|
+
"""
|
|
140
|
+
...
|
|
141
|
+
|
|
142
|
+
def capacity(self) -> int:
|
|
143
|
+
"""
|
|
144
|
+
Returns the current capacity of the queue.
|
|
145
|
+
|
|
146
|
+
This method should not be used for synchronization.
|
|
147
|
+
"""
|
|
148
|
+
...
|
|
149
|
+
|
|
150
|
+
def is_empty(self) -> bool:
|
|
151
|
+
"""
|
|
152
|
+
Indicates whether the queue is currently empty.
|
|
153
|
+
|
|
154
|
+
This method should not be used for synchronization.
|
|
155
|
+
"""
|
|
156
|
+
...
|
|
157
|
+
|
|
158
|
+
def is_full(self) -> bool:
|
|
159
|
+
"""
|
|
160
|
+
Indicates whether the queue is currently full.
|
|
161
|
+
|
|
162
|
+
This method should not be used for synchronization.
|
|
163
|
+
"""
|
|
164
|
+
...
|
|
165
|
+
|
|
166
|
+
def force_push(self, item: _T) -> _T | None:
|
|
167
|
+
"""
|
|
168
|
+
Pushes an item into the queue.
|
|
169
|
+
This method may take some time under heavy contention.
|
|
170
|
+
This method may pop an arbitrary amount of items from the queue.
|
|
171
|
+
|
|
172
|
+
Returns the last popped item, if the queue was full. All other items are dropped.
|
|
173
|
+
"""
|
|
174
|
+
...
|
|
175
|
+
|
|
176
|
+
def force_push_and_do(self, item: _T, f: Callable[[_T], None]) -> None:
|
|
177
|
+
"""
|
|
178
|
+
Pushes an item into the queue.
|
|
179
|
+
This method may take some time under heavy contention.
|
|
180
|
+
This method may pop an arbitrary amount of items from the queue.
|
|
181
|
+
Applies `f` to each popped item.
|
|
182
|
+
"""
|
|
183
|
+
...
|
|
Binary file
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nblf-queue
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
License-File: LICENSE
|
|
5
|
+
Summary: Lock-free, non-blocking MPMC queues.
|
|
6
|
+
Keywords: queue,mpmc,atomic,lock-free
|
|
7
|
+
Author-email: Louis Meller <louis.meller@icloud.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
11
|
+
Project-URL: Repository, https://github.com/lmeller-git/nblf-queue
|
|
12
|
+
Project-URL: Documentation, https://docs.rs/nblf-queue
|
|
13
|
+
|
|
14
|
+
[](https://codecov.io/gh/lmeller-git/nblf-queue)
|
|
15
|
+

|
|
16
|
+

|
|
17
|
+

|
|
18
|
+
[](https://crates.io/crates/nblf-queue)
|
|
19
|
+
[](https://docs.rs/nblf-queue)
|
|
20
|
+
|
|
21
|
+
# nblf-queue
|
|
22
|
+
|
|
23
|
+
> Non-Blocking Lock-Free Queue
|
|
24
|
+
|
|
25
|
+
An atomic lock-free MPMC queue based on the NBLFQ algorithm.
|
|
26
|
+
|
|
27
|
+
This repository provides multiple queue implementations with different storage and allocation strategies.
|
|
28
|
+
|
|
29
|
+
All queues in this repository are safe to use in a concurrent context and will never block the calling thread.
|
|
30
|
+
|
|
31
|
+
## Queue variants
|
|
32
|
+
|
|
33
|
+
- **Static queues**: fixed-capacity queues backed by static storage
|
|
34
|
+
- **Allocated queues**: fixed-capacity queues backed by dynamically allocated storage, only available on feature `alloc`
|
|
35
|
+
- **Dynamic queues**: dynamically growable queues, only available on feature `dynamic`
|
|
36
|
+
- **Pooled Queues**: variants of other queues, which may store arbitrary types, only available on feature `pool`
|
|
37
|
+
|
|
38
|
+
Non-pooled queues store items in atomically updated slots, restricting the stored items to small, pointer-like values.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
`nblf_queue::StaticQueue`:
|
|
44
|
+
|
|
45
|
+
```rust
|
|
46
|
+
use nblf_queue::{StaticQueue, MPMCQueue};
|
|
47
|
+
|
|
48
|
+
let q: StaticQueue<_, 2> = StaticQueue::new();
|
|
49
|
+
|
|
50
|
+
assert!(q.push(&42).is_ok());
|
|
51
|
+
assert!(q.push(&1).is_ok());
|
|
52
|
+
assert!(q.push(&4242).is_err());
|
|
53
|
+
|
|
54
|
+
assert_eq!(q.pop(), Some(&42));
|
|
55
|
+
assert_eq!(q.pop(), Some(&1));
|
|
56
|
+
assert!(q.pop().is_none());
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
`nblf_queue::PooledStaticQueue`:
|
|
61
|
+
|
|
62
|
+
```rust
|
|
63
|
+
#[cfg(feature = "pool")]
|
|
64
|
+
fn run() {
|
|
65
|
+
use nblf_queue::{PooledStaticQueue, MPMCQueue};
|
|
66
|
+
|
|
67
|
+
let q: PooledStaticQueue<_, 2> = PooledStaticQueue::new();
|
|
68
|
+
|
|
69
|
+
assert!(q.push(42).is_ok());
|
|
70
|
+
assert!(q.push(1).is_ok());
|
|
71
|
+
assert!(q.push(4242).is_err());
|
|
72
|
+
|
|
73
|
+
assert_eq!(q.pop(), Some(42));
|
|
74
|
+
assert_eq!(q.pop(), Some(1));
|
|
75
|
+
assert!(q.pop().is_none());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#[cfg(feature = "pool")]
|
|
79
|
+
run();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Choosing a queue type
|
|
84
|
+
|
|
85
|
+
`StaticQueue` and `Queue` may only store small values and are optimized for this use case.
|
|
86
|
+
|
|
87
|
+
`PooledStaticQueue` and `PooledQueue` may store arbitrary types, at the cost of higher memory usage and runtime cost.
|
|
88
|
+
|
|
89
|
+
`DynamicQueue` and `PooledDynamicQueue` may be grown dynamically, at the cost of higher total memory usage and runtime cost. This is cost is even higher for `PooledDynamicQueue`.
|
|
90
|
+
|
|
91
|
+
## Platform Support
|
|
92
|
+
|
|
93
|
+
Multiple storage types are available, dependent on platform:
|
|
94
|
+
|
|
95
|
+
- **Tagged64** - platforms with native 64-bit atomic operations or feature `atomic-fallback`
|
|
96
|
+
|
|
97
|
+
- **Tagged128** - platforms with native 128-bit atomic operations or feature `atomic-fallback`
|
|
98
|
+
|
|
99
|
+
Storage types will be chosen automatically, unless sepcified explicitly.
|
|
100
|
+
|
|
101
|
+
> [!NOTE]
|
|
102
|
+
> **ABA Safety & Storage Selection**
|
|
103
|
+
> If it is plausible that other threads could perform `(2^15 - 1) * queue_size`
|
|
104
|
+
> pop and push operations while a single thread is paused/preempted in pop/push, `Tagged128` slots should be used to ensure ABA safety.
|
|
105
|
+
|
|
106
|
+
## Feature Flags
|
|
107
|
+
|
|
108
|
+
- `std`: Enables `std` and `alloc` support
|
|
109
|
+
|
|
110
|
+
- `alloc`: Enables `alloc` support, allowing usage of some dynamically allocated queues
|
|
111
|
+
|
|
112
|
+
- `pool`: Enables pooled queues, which may store any type
|
|
113
|
+
|
|
114
|
+
- `dynamic`: Enables dynamic queues, which may dynamically grow
|
|
115
|
+
|
|
116
|
+
- `atomic-fallback`: Uses `portable-atomic` `fallback` feature for atomics if necessary. It is discouraged to use this feature, as `fallback` internally uses locks
|
|
117
|
+
|
|
118
|
+
- `default`: `pool`
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
## Python Bindings
|
|
122
|
+
|
|
123
|
+
Python bindings backed by `PooledQueue` and `PooledDynamicQueue` are available for concurrent applications.
|
|
124
|
+
Core operations detach from the GIL to allow parallel execution.
|
|
125
|
+
|
|
126
|
+
> [!NOTE]
|
|
127
|
+
> The Python bindings strictly use `Auto` slots without feature `atomic-fallback`.
|
|
128
|
+
> As a result, these bindings are only supported on platforms with native 64-bit or 128-bit atomic operations.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from nblf_queue import Queue, DynamicQueue
|
|
132
|
+
|
|
133
|
+
q: Queue[int] = Queue(10)
|
|
134
|
+
|
|
135
|
+
q.push(42)
|
|
136
|
+
item = q.pop()
|
|
137
|
+
assert item == 42
|
|
138
|
+
|
|
139
|
+
dq: DynamicQueue[str] = DynamicQueue(5)
|
|
140
|
+
dq.push("hello")
|
|
141
|
+
|
|
142
|
+
dq.grow()
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
## Testing
|
|
147
|
+
|
|
148
|
+
The core test-suite of this crate was adapted from [`crossbeam-queue`](https://github.com/crossbeam-rs/crossbeam/tree/main/crossbeam-queue).
|
|
149
|
+
|
|
150
|
+
Current testing is based on:
|
|
151
|
+
|
|
152
|
+
- **Miri** - to validate pointer arithmetic and catch UB
|
|
153
|
+
- **Loom and Shuttle** - to test for race conditions
|
|
154
|
+
- **ASan** - to check for memory corruption and leakage
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
## References
|
|
158
|
+
|
|
159
|
+
Alexandre Denis, Charles Goedefroit. NBLFQ: a lock-free MPMC queue optimized for low contention.
|
|
160
|
+
IPDPS 2025 - 39th International Parallel & Distributed Processing Symposium, IEEE, Jun 2025,
|
|
161
|
+
Milan, Italy. hal-04851700v2
|
|
162
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
nblf_queue-0.1.0.dist-info/METADATA,sha256=pvYPZmvwKvKzD9FKNOqlHHsqO9IbLMHqq0RQALEbRQg,5571
|
|
2
|
+
nblf_queue-0.1.0.dist-info/WHEEL,sha256=VRUGISFtmkUtogJQvMvdBlw5mQA2s0VSEJD4NOAK5wg,95
|
|
3
|
+
nblf_queue-0.1.0.dist-info/licenses/LICENSE,sha256=kQATdwljoFZxExTADvtRsqU58Wk4a4ivtDYG9kDyguY,1089
|
|
4
|
+
nblf_queue/__init__.py,sha256=gvsdy0JDHG8vHbCBhmUKk-9riZt91Wo4BwXp9MxM_40,88
|
|
5
|
+
nblf_queue/__init__.pyi,sha256=hEuiImBpxEF28eyXrDhih6FjUHrsG8_mC8i1cluvX4M,4902
|
|
6
|
+
nblf_queue/_nblf_queue_py.pyd,sha256=F2P5OE98RqegyHZU-ex9NH-gGZm1JdsUCKpg_9gHtCw,273408
|
|
7
|
+
nblf_queue-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 lmeller-git
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|