rsloop 0.1.5__tar.gz → 0.1.7__tar.gz
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.
- rsloop-0.1.7/.github/workflows/tests.yml +58 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/Cargo.lock +60 -3
- {rsloop-0.1.5 → rsloop-0.1.7}/Cargo.toml +3 -1
- {rsloop-0.1.5 → rsloop-0.1.7}/PKG-INFO +62 -32
- {rsloop-0.1.5 → rsloop-0.1.7}/README.md +60 -30
- {rsloop-0.1.5 → rsloop-0.1.7}/benchmarks/README.md +3 -1
- {rsloop-0.1.5 → rsloop-0.1.7}/benchmarks/compare_event_loops.py +110 -7
- {rsloop-0.1.5 → rsloop-0.1.7}/demo/README.md +2 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/justfile +3 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/pyproject.toml +2 -2
- rsloop-0.1.7/python/rsloop/__init__.py +957 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/scripts/build-wheels.sh +7 -74
- rsloop-0.1.7/scripts/python-versions.sh +74 -0
- rsloop-0.1.7/scripts/test-supported-pythons.sh +103 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/fd_ops.rs +78 -18
- {rsloop-0.1.5 → rsloop-0.1.7}/src/lib.rs +2 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/loop_core.rs +32 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/process_transport.rs +202 -56
- {rsloop-0.1.5 → rsloop-0.1.7}/src/python_api.rs +174 -22
- {rsloop-0.1.5 → rsloop-0.1.7}/src/python_names.rs +10 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/runtime.rs +222 -19
- {rsloop-0.1.5 → rsloop-0.1.7}/src/stream_transport.rs +505 -26
- {rsloop-0.1.5 → rsloop-0.1.7}/src/tls.rs +19 -0
- rsloop-0.1.7/src/windows_vibeio.rs +137 -0
- rsloop-0.1.7/tests/test_compat.py +217 -0
- rsloop-0.1.7/tests/test_run.py +366 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/tests/test_tls.py +22 -6
- {rsloop-0.1.5 → rsloop-0.1.7}/uv.lock +1 -1
- rsloop-0.1.5/python/rsloop/__init__.py +0 -202
- rsloop-0.1.5/tests/test_run.py +0 -181
- {rsloop-0.1.5 → rsloop-0.1.7}/.github/workflows/wheels.yml +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/.gitignore +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/LICENSE +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/build.rs +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/demo/fastapi_service.py +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/examples/01_basics.py +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/examples/02_fd_and_sockets.py +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/examples/03_streams.py +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/examples/04_unix_and_accepted_socket.py +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/examples/05_pipes_signals_subprocesses.py +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/rsloop.png +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/scripts/generate-test-tls-certs.sh +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/async_event.rs +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/blocking.rs +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/callbacks.rs +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/context.rs +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/errors.rs +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/fast_streams.rs +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/src/profiler.rs +0 -0
- {rsloop-0.1.5 → rsloop-0.1.7}/tests/packages/aiohttp_test.py +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
python_versions:
|
|
7
|
+
description: "Space-separated Python versions to test"
|
|
8
|
+
required: false
|
|
9
|
+
type: string
|
|
10
|
+
build_mode:
|
|
11
|
+
description: "Build profile to use with maturin develop"
|
|
12
|
+
required: true
|
|
13
|
+
default: release
|
|
14
|
+
type: choice
|
|
15
|
+
options:
|
|
16
|
+
- release
|
|
17
|
+
- debug
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
test-supported-pythons:
|
|
24
|
+
name: supported-pythons
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- name: Check out repository
|
|
29
|
+
uses: actions/checkout@v6
|
|
30
|
+
with:
|
|
31
|
+
persist-credentials: false
|
|
32
|
+
|
|
33
|
+
- name: Set up Python
|
|
34
|
+
uses: actions/setup-python@v6
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.13"
|
|
37
|
+
|
|
38
|
+
- name: Set up uv
|
|
39
|
+
uses: astral-sh/setup-uv@v6
|
|
40
|
+
|
|
41
|
+
- name: Set up Rust
|
|
42
|
+
uses: dtolnay/rust-toolchain@stable
|
|
43
|
+
with:
|
|
44
|
+
toolchain: 1.94.0
|
|
45
|
+
|
|
46
|
+
- name: Cache Cargo registry and target
|
|
47
|
+
uses: Swatinem/rust-cache@v2
|
|
48
|
+
|
|
49
|
+
- name: Run tests across supported Python versions
|
|
50
|
+
shell: bash
|
|
51
|
+
env:
|
|
52
|
+
RSLOOP_PYTHON_VERSIONS: ${{ inputs.python_versions }}
|
|
53
|
+
run: |
|
|
54
|
+
args=()
|
|
55
|
+
if [[ "${{ inputs.build_mode }}" == "debug" ]]; then
|
|
56
|
+
args+=(--debug)
|
|
57
|
+
fi
|
|
58
|
+
scripts/test-supported-pythons.sh "${args[@]}"
|
|
@@ -374,7 +374,7 @@ dependencies = [
|
|
|
374
374
|
"crossbeam-queue",
|
|
375
375
|
"flume",
|
|
376
376
|
"futures-util",
|
|
377
|
-
"io-uring",
|
|
377
|
+
"io-uring 0.7.11",
|
|
378
378
|
"io_uring_buf_ring",
|
|
379
379
|
"libc",
|
|
380
380
|
"once_cell",
|
|
@@ -878,6 +878,16 @@ dependencies = [
|
|
|
878
878
|
"str_stack",
|
|
879
879
|
]
|
|
880
880
|
|
|
881
|
+
[[package]]
|
|
882
|
+
name = "io-uring"
|
|
883
|
+
version = "0.6.4"
|
|
884
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
885
|
+
checksum = "595a0399f411a508feb2ec1e970a4a30c249351e30208960d58298de8660b0e5"
|
|
886
|
+
dependencies = [
|
|
887
|
+
"bitflags 1.3.2",
|
|
888
|
+
"libc",
|
|
889
|
+
]
|
|
890
|
+
|
|
881
891
|
[[package]]
|
|
882
892
|
name = "io-uring"
|
|
883
893
|
version = "0.7.11"
|
|
@@ -896,7 +906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
896
906
|
checksum = "1838759bb8c2f24cf05a35429d83145c4aa6af43f8ad38477295e12a7320a80e"
|
|
897
907
|
dependencies = [
|
|
898
908
|
"bytes",
|
|
899
|
-
"io-uring",
|
|
909
|
+
"io-uring 0.7.11",
|
|
900
910
|
"rustix",
|
|
901
911
|
]
|
|
902
912
|
|
|
@@ -1035,6 +1045,18 @@ dependencies = [
|
|
|
1035
1045
|
"adler2",
|
|
1036
1046
|
]
|
|
1037
1047
|
|
|
1048
|
+
[[package]]
|
|
1049
|
+
name = "mio"
|
|
1050
|
+
version = "1.1.1"
|
|
1051
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1052
|
+
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
|
|
1053
|
+
dependencies = [
|
|
1054
|
+
"libc",
|
|
1055
|
+
"log",
|
|
1056
|
+
"wasi",
|
|
1057
|
+
"windows-sys 0.61.2",
|
|
1058
|
+
]
|
|
1059
|
+
|
|
1038
1060
|
[[package]]
|
|
1039
1061
|
name = "nix"
|
|
1040
1062
|
version = "0.26.4"
|
|
@@ -1090,6 +1112,12 @@ version = "1.21.4"
|
|
|
1090
1112
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1091
1113
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
1092
1114
|
|
|
1115
|
+
[[package]]
|
|
1116
|
+
name = "oneshot"
|
|
1117
|
+
version = "0.2.1"
|
|
1118
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1119
|
+
checksum = "cfe21416a02c693fb9f980befcb230ecc70b0b3d1cc4abf88b9675c4c1457f0c"
|
|
1120
|
+
|
|
1093
1121
|
[[package]]
|
|
1094
1122
|
name = "openssl-probe"
|
|
1095
1123
|
version = "0.2.1"
|
|
@@ -1346,7 +1374,7 @@ dependencies = [
|
|
|
1346
1374
|
|
|
1347
1375
|
[[package]]
|
|
1348
1376
|
name = "rsloop"
|
|
1349
|
-
version = "0.1.
|
|
1377
|
+
version = "0.1.7"
|
|
1350
1378
|
dependencies = [
|
|
1351
1379
|
"async-std",
|
|
1352
1380
|
"compio",
|
|
@@ -1362,6 +1390,8 @@ dependencies = [
|
|
|
1362
1390
|
"rustls-pemfile",
|
|
1363
1391
|
"signal-hook",
|
|
1364
1392
|
"socket2",
|
|
1393
|
+
"tokio",
|
|
1394
|
+
"vibeio",
|
|
1365
1395
|
"windows-sys 0.61.2",
|
|
1366
1396
|
]
|
|
1367
1397
|
|
|
@@ -1729,6 +1759,16 @@ dependencies = [
|
|
|
1729
1759
|
"cfg-if",
|
|
1730
1760
|
]
|
|
1731
1761
|
|
|
1762
|
+
[[package]]
|
|
1763
|
+
name = "tokio"
|
|
1764
|
+
version = "1.50.0"
|
|
1765
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1766
|
+
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
|
|
1767
|
+
dependencies = [
|
|
1768
|
+
"bytes",
|
|
1769
|
+
"pin-project-lite",
|
|
1770
|
+
]
|
|
1771
|
+
|
|
1732
1772
|
[[package]]
|
|
1733
1773
|
name = "tracing"
|
|
1734
1774
|
version = "0.1.44"
|
|
@@ -1824,6 +1864,23 @@ version = "0.9.5"
|
|
|
1824
1864
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1825
1865
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
1826
1866
|
|
|
1867
|
+
[[package]]
|
|
1868
|
+
name = "vibeio"
|
|
1869
|
+
version = "0.2.5"
|
|
1870
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1871
|
+
checksum = "78dce705a708cacdd5822824342a8843abcceb9619e7079dc0e97b77dcc4ce1c"
|
|
1872
|
+
dependencies = [
|
|
1873
|
+
"crossbeam-queue",
|
|
1874
|
+
"futures-util",
|
|
1875
|
+
"io-uring 0.6.4",
|
|
1876
|
+
"libc",
|
|
1877
|
+
"mio",
|
|
1878
|
+
"oneshot",
|
|
1879
|
+
"slab",
|
|
1880
|
+
"tokio",
|
|
1881
|
+
"windows-sys 0.61.2",
|
|
1882
|
+
]
|
|
1883
|
+
|
|
1827
1884
|
[[package]]
|
|
1828
1885
|
name = "wasi"
|
|
1829
1886
|
version = "0.11.1+wasi-snapshot-preview1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rsloop"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.7"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "An event loop for asyncio written in Rust"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -33,6 +33,8 @@ signal-hook = "0.4"
|
|
|
33
33
|
socket2 = "0.6"
|
|
34
34
|
|
|
35
35
|
[target.'cfg(windows)'.dependencies]
|
|
36
|
+
tokio = { version = "1", default-features = false, features = ["io-util"] }
|
|
37
|
+
vibeio = { version = "0.2.5", default-features = false }
|
|
36
38
|
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Networking_WinSock", "Win32_System_Pipes", "Win32_System_Threading"] }
|
|
37
39
|
|
|
38
40
|
[build-dependencies]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rsloop
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -23,7 +23,7 @@ License-File: LICENSE
|
|
|
23
23
|
Summary: An event loop for asyncio written in Rust
|
|
24
24
|
Keywords: asyncio,event-loop,pyo3,python,rust
|
|
25
25
|
Home-Page: https://github.com/RustedBytes/rsloop
|
|
26
|
-
Author:
|
|
26
|
+
Author: Yehor Smoliakov <egorsmkv@gmail.com>
|
|
27
27
|
Requires-Python: >=3.8
|
|
28
28
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
29
29
|
Project-URL: Homepage, https://github.com/RustedBytes/rsloop
|
|
@@ -49,7 +49,7 @@ coroutines still run on the thread that calls `run_forever()` or
|
|
|
49
49
|
The package exposes:
|
|
50
50
|
|
|
51
51
|
- a native extension module at `rsloop._loop`
|
|
52
|
-
- a Python wrapper in `python/rsloop/__init__.py`
|
|
52
|
+
- a Python wrapper in [`python/rsloop/__init__.py`](./python/rsloop/__init__.py)
|
|
53
53
|
- `rsloop.Loop`, `rsloop.new_event_loop()`, and `rsloop.run(...)`
|
|
54
54
|
|
|
55
55
|
Repository metadata currently targets Python `>=3.8`. The packaged project now
|
|
@@ -100,7 +100,7 @@ finally:
|
|
|
100
100
|
|
|
101
101
|
Importing `rsloop` also patches `asyncio.set_event_loop()` so Python 3.8 can
|
|
102
102
|
accept an `rsloop.Loop` instance, matching the behavior exercised by
|
|
103
|
-
`tests/test_run.py
|
|
103
|
+
[`tests/test_run.py`](./tests/test_run.py).
|
|
104
104
|
|
|
105
105
|
## Verified Surface Area
|
|
106
106
|
|
|
@@ -177,8 +177,9 @@ The native fast-stream path is used only when:
|
|
|
177
177
|
|
|
178
178
|
Otherwise `rsloop` falls back to the stdlib `asyncio.streams` helpers.
|
|
179
179
|
|
|
180
|
-
The implementation lives in `src/fast_streams.rs` and
|
|
181
|
-
level transport code in
|
|
180
|
+
The implementation lives in [`src/fast_streams.rs`](./src/fast_streams.rs) and
|
|
181
|
+
is backed by the lower level transport code in
|
|
182
|
+
[`src/stream_transport.rs`](./src/stream_transport.rs).
|
|
182
183
|
|
|
183
184
|
## Runtime Model
|
|
184
185
|
|
|
@@ -197,29 +198,15 @@ model, but has not finished eliminating every helper thread yet.
|
|
|
197
198
|
|
|
198
199
|
These gaps are visible in the current implementation.
|
|
199
200
|
|
|
200
|
-
- Stream transport flow control is partial:
|
|
201
|
-
`pause_reading()` and `resume_reading()` work, but
|
|
202
|
-
`get_write_buffer_size()` returns `0`,
|
|
203
|
-
`get_write_buffer_limits()` returns `(0, 0)`, and
|
|
204
|
-
`set_write_buffer_limits()` is a no-op.
|
|
205
|
-
- Several compatibility parameters are currently accepted only to preserve API
|
|
206
|
-
shape, not to provide full behavior:
|
|
207
|
-
`happy_eyeballs_delay`, `interleave`, `all_errors`,
|
|
208
|
-
`ssl_shutdown_timeout`, and
|
|
209
|
-
`shutdown_default_executor(timeout=...)`.
|
|
210
201
|
- TLS uses a `rustls` backend with a narrower compatibility surface than
|
|
211
202
|
CPython's OpenSSL-backed `ssl` module. In particular, encrypted private keys
|
|
212
203
|
are not supported yet, and the fast-stream monkeypatch still falls back to
|
|
213
204
|
stdlib helpers whenever `ssl` is enabled. TLS transport internals also still
|
|
214
205
|
use helper-thread paths instead of the newer runtime-thread `compio` socket
|
|
215
206
|
path.
|
|
216
|
-
- Subprocess support
|
|
217
|
-
`preexec_fn`
|
|
218
|
-
`
|
|
219
|
-
`asyncio.create_subprocess_shell()` when using the stdlib subprocess stream
|
|
220
|
-
protocol.
|
|
221
|
-
- Low-level subprocess transport APIs do support text decoding when used with a
|
|
222
|
-
custom subprocess protocol.
|
|
207
|
+
- Subprocess support still has one notable gap:
|
|
208
|
+
`preexec_fn` remains unsupported because running arbitrary Python between
|
|
209
|
+
`fork()` and `exec()` is unsafe in this runtime model.
|
|
223
210
|
- Unix-specific APIs remain Unix-specific:
|
|
224
211
|
`create_unix_server`, `create_unix_connection`,
|
|
225
212
|
`add_signal_handler`, `remove_signal_handler`.
|
|
@@ -253,9 +240,9 @@ Build release wheels into `dist/wheels`:
|
|
|
253
240
|
scripts/build-wheels.sh
|
|
254
241
|
```
|
|
255
242
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
locate interpreters.
|
|
243
|
+
[`scripts/build-wheels.sh`](./scripts/build-wheels.sh) currently defaults to
|
|
244
|
+
CPython `3.8 3.9 3.10 3.11 3.12 3.13 3.14` plus free-threaded `3.14t`, and
|
|
245
|
+
uses `uv python install` / `uv python find` to locate interpreters.
|
|
259
246
|
|
|
260
247
|
## Profiling
|
|
261
248
|
|
|
@@ -304,12 +291,19 @@ uv run python examples/04_unix_and_accepted_socket.py
|
|
|
304
291
|
uv run python examples/05_pipes_signals_subprocesses.py
|
|
305
292
|
```
|
|
306
293
|
|
|
294
|
+
Example files:
|
|
295
|
+
[`examples/01_basics.py`](./examples/01_basics.py),
|
|
296
|
+
[`examples/02_fd_and_sockets.py`](./examples/02_fd_and_sockets.py),
|
|
297
|
+
[`examples/03_streams.py`](./examples/03_streams.py),
|
|
298
|
+
[`examples/04_unix_and_accepted_socket.py`](./examples/04_unix_and_accepted_socket.py),
|
|
299
|
+
[`examples/05_pipes_signals_subprocesses.py`](./examples/05_pipes_signals_subprocesses.py).
|
|
300
|
+
|
|
307
301
|
The repository also includes:
|
|
308
302
|
|
|
309
|
-
- `demo/fastapi_service.py` for running the same
|
|
310
|
-
`asyncio`, `uvloop`, or `rsloop`
|
|
311
|
-
- `benchmarks/compare_event_loops.py`
|
|
312
|
-
comparisons
|
|
303
|
+
- [`demo/fastapi_service.py`](./demo/fastapi_service.py) for running the same
|
|
304
|
+
FastAPI app on stdlib `asyncio`, `uvloop`, or `rsloop`
|
|
305
|
+
- [`benchmarks/compare_event_loops.py`](./benchmarks/compare_event_loops.py)
|
|
306
|
+
for callback, task, and TCP stream comparisons
|
|
313
307
|
|
|
314
308
|
## Benchmark
|
|
315
309
|
|
|
@@ -318,6 +312,42 @@ uv run --with maturin maturin develop --release
|
|
|
318
312
|
uv run --with uvloop python benchmarks/compare_event_loops.py
|
|
319
313
|
```
|
|
320
314
|
|
|
321
|
-
|
|
322
|
-
|
|
315
|
+
An example output from that script on Linux with CPython 3.14.0:
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
callbacks (200,000 ops)
|
|
319
|
+
loop median_s best_s ops_per_s vs_fastest
|
|
320
|
+
rsloop 0.041608 0.040585 4,806,807 1.00x
|
|
321
|
+
uvloop 0.087539 0.086690 2,284,707 2.10x
|
|
322
|
+
asyncio 0.229563 0.221348 871,222 5.52x
|
|
323
|
+
|
|
324
|
+
tasks (50,000 ops)
|
|
325
|
+
loop median_s best_s ops_per_s vs_fastest
|
|
326
|
+
uvloop 0.084425 0.083497 592,239 1.00x
|
|
327
|
+
rsloop 0.091845 0.090982 544,397 1.09x
|
|
328
|
+
asyncio 0.138782 0.137716 360,276 1.64x
|
|
329
|
+
tcp_streams mode: rsloop native fast streams
|
|
330
|
+
|
|
331
|
+
tcp_streams (5,000 ops)
|
|
332
|
+
loop median_s best_s ops_per_s vs_fastest
|
|
333
|
+
rsloop 0.119483 0.118451 41,847 1.00x
|
|
334
|
+
uvloop 0.119582 0.116446 41,812 1.00x
|
|
335
|
+
asyncio 0.138408 0.134438 36,125 1.16x
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
See [`benchmarks/README.md`](./benchmarks/README.md) for workload details and
|
|
339
|
+
extra flags, and [`demo/README.md`](./demo/README.md) for the FastAPI loop
|
|
340
|
+
comparison demo.
|
|
341
|
+
|
|
342
|
+
## Acknowledgements
|
|
343
|
+
|
|
344
|
+
`rsloop` builds on the Python `asyncio` model and is implemented with
|
|
345
|
+
[PyO3](https://pyo3.rs/) on the Rust side. The runtime and I/O work in the
|
|
346
|
+
current implementation rely in part on
|
|
347
|
+
[compio](https://github.com/compio-rs/compio).
|
|
348
|
+
|
|
349
|
+
## License
|
|
350
|
+
|
|
351
|
+
This project is licensed under the Apache License, Version 2.0. See
|
|
352
|
+
[`LICENSE`](./LICENSE) for the full text.
|
|
323
353
|
|
|
@@ -17,7 +17,7 @@ coroutines still run on the thread that calls `run_forever()` or
|
|
|
17
17
|
The package exposes:
|
|
18
18
|
|
|
19
19
|
- a native extension module at `rsloop._loop`
|
|
20
|
-
- a Python wrapper in `python/rsloop/__init__.py`
|
|
20
|
+
- a Python wrapper in [`python/rsloop/__init__.py`](./python/rsloop/__init__.py)
|
|
21
21
|
- `rsloop.Loop`, `rsloop.new_event_loop()`, and `rsloop.run(...)`
|
|
22
22
|
|
|
23
23
|
Repository metadata currently targets Python `>=3.8`. The packaged project now
|
|
@@ -68,7 +68,7 @@ finally:
|
|
|
68
68
|
|
|
69
69
|
Importing `rsloop` also patches `asyncio.set_event_loop()` so Python 3.8 can
|
|
70
70
|
accept an `rsloop.Loop` instance, matching the behavior exercised by
|
|
71
|
-
`tests/test_run.py
|
|
71
|
+
[`tests/test_run.py`](./tests/test_run.py).
|
|
72
72
|
|
|
73
73
|
## Verified Surface Area
|
|
74
74
|
|
|
@@ -145,8 +145,9 @@ The native fast-stream path is used only when:
|
|
|
145
145
|
|
|
146
146
|
Otherwise `rsloop` falls back to the stdlib `asyncio.streams` helpers.
|
|
147
147
|
|
|
148
|
-
The implementation lives in `src/fast_streams.rs` and
|
|
149
|
-
level transport code in
|
|
148
|
+
The implementation lives in [`src/fast_streams.rs`](./src/fast_streams.rs) and
|
|
149
|
+
is backed by the lower level transport code in
|
|
150
|
+
[`src/stream_transport.rs`](./src/stream_transport.rs).
|
|
150
151
|
|
|
151
152
|
## Runtime Model
|
|
152
153
|
|
|
@@ -165,29 +166,15 @@ model, but has not finished eliminating every helper thread yet.
|
|
|
165
166
|
|
|
166
167
|
These gaps are visible in the current implementation.
|
|
167
168
|
|
|
168
|
-
- Stream transport flow control is partial:
|
|
169
|
-
`pause_reading()` and `resume_reading()` work, but
|
|
170
|
-
`get_write_buffer_size()` returns `0`,
|
|
171
|
-
`get_write_buffer_limits()` returns `(0, 0)`, and
|
|
172
|
-
`set_write_buffer_limits()` is a no-op.
|
|
173
|
-
- Several compatibility parameters are currently accepted only to preserve API
|
|
174
|
-
shape, not to provide full behavior:
|
|
175
|
-
`happy_eyeballs_delay`, `interleave`, `all_errors`,
|
|
176
|
-
`ssl_shutdown_timeout`, and
|
|
177
|
-
`shutdown_default_executor(timeout=...)`.
|
|
178
169
|
- TLS uses a `rustls` backend with a narrower compatibility surface than
|
|
179
170
|
CPython's OpenSSL-backed `ssl` module. In particular, encrypted private keys
|
|
180
171
|
are not supported yet, and the fast-stream monkeypatch still falls back to
|
|
181
172
|
stdlib helpers whenever `ssl` is enabled. TLS transport internals also still
|
|
182
173
|
use helper-thread paths instead of the newer runtime-thread `compio` socket
|
|
183
174
|
path.
|
|
184
|
-
- Subprocess support
|
|
185
|
-
`preexec_fn`
|
|
186
|
-
`
|
|
187
|
-
`asyncio.create_subprocess_shell()` when using the stdlib subprocess stream
|
|
188
|
-
protocol.
|
|
189
|
-
- Low-level subprocess transport APIs do support text decoding when used with a
|
|
190
|
-
custom subprocess protocol.
|
|
175
|
+
- Subprocess support still has one notable gap:
|
|
176
|
+
`preexec_fn` remains unsupported because running arbitrary Python between
|
|
177
|
+
`fork()` and `exec()` is unsafe in this runtime model.
|
|
191
178
|
- Unix-specific APIs remain Unix-specific:
|
|
192
179
|
`create_unix_server`, `create_unix_connection`,
|
|
193
180
|
`add_signal_handler`, `remove_signal_handler`.
|
|
@@ -221,9 +208,9 @@ Build release wheels into `dist/wheels`:
|
|
|
221
208
|
scripts/build-wheels.sh
|
|
222
209
|
```
|
|
223
210
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
locate interpreters.
|
|
211
|
+
[`scripts/build-wheels.sh`](./scripts/build-wheels.sh) currently defaults to
|
|
212
|
+
CPython `3.8 3.9 3.10 3.11 3.12 3.13 3.14` plus free-threaded `3.14t`, and
|
|
213
|
+
uses `uv python install` / `uv python find` to locate interpreters.
|
|
227
214
|
|
|
228
215
|
## Profiling
|
|
229
216
|
|
|
@@ -272,12 +259,19 @@ uv run python examples/04_unix_and_accepted_socket.py
|
|
|
272
259
|
uv run python examples/05_pipes_signals_subprocesses.py
|
|
273
260
|
```
|
|
274
261
|
|
|
262
|
+
Example files:
|
|
263
|
+
[`examples/01_basics.py`](./examples/01_basics.py),
|
|
264
|
+
[`examples/02_fd_and_sockets.py`](./examples/02_fd_and_sockets.py),
|
|
265
|
+
[`examples/03_streams.py`](./examples/03_streams.py),
|
|
266
|
+
[`examples/04_unix_and_accepted_socket.py`](./examples/04_unix_and_accepted_socket.py),
|
|
267
|
+
[`examples/05_pipes_signals_subprocesses.py`](./examples/05_pipes_signals_subprocesses.py).
|
|
268
|
+
|
|
275
269
|
The repository also includes:
|
|
276
270
|
|
|
277
|
-
- `demo/fastapi_service.py` for running the same
|
|
278
|
-
`asyncio`, `uvloop`, or `rsloop`
|
|
279
|
-
- `benchmarks/compare_event_loops.py`
|
|
280
|
-
comparisons
|
|
271
|
+
- [`demo/fastapi_service.py`](./demo/fastapi_service.py) for running the same
|
|
272
|
+
FastAPI app on stdlib `asyncio`, `uvloop`, or `rsloop`
|
|
273
|
+
- [`benchmarks/compare_event_loops.py`](./benchmarks/compare_event_loops.py)
|
|
274
|
+
for callback, task, and TCP stream comparisons
|
|
281
275
|
|
|
282
276
|
## Benchmark
|
|
283
277
|
|
|
@@ -286,5 +280,41 @@ uv run --with maturin maturin develop --release
|
|
|
286
280
|
uv run --with uvloop python benchmarks/compare_event_loops.py
|
|
287
281
|
```
|
|
288
282
|
|
|
289
|
-
|
|
290
|
-
|
|
283
|
+
An example output from that script on Linux with CPython 3.14.0:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
callbacks (200,000 ops)
|
|
287
|
+
loop median_s best_s ops_per_s vs_fastest
|
|
288
|
+
rsloop 0.041608 0.040585 4,806,807 1.00x
|
|
289
|
+
uvloop 0.087539 0.086690 2,284,707 2.10x
|
|
290
|
+
asyncio 0.229563 0.221348 871,222 5.52x
|
|
291
|
+
|
|
292
|
+
tasks (50,000 ops)
|
|
293
|
+
loop median_s best_s ops_per_s vs_fastest
|
|
294
|
+
uvloop 0.084425 0.083497 592,239 1.00x
|
|
295
|
+
rsloop 0.091845 0.090982 544,397 1.09x
|
|
296
|
+
asyncio 0.138782 0.137716 360,276 1.64x
|
|
297
|
+
tcp_streams mode: rsloop native fast streams
|
|
298
|
+
|
|
299
|
+
tcp_streams (5,000 ops)
|
|
300
|
+
loop median_s best_s ops_per_s vs_fastest
|
|
301
|
+
rsloop 0.119483 0.118451 41,847 1.00x
|
|
302
|
+
uvloop 0.119582 0.116446 41,812 1.00x
|
|
303
|
+
asyncio 0.138408 0.134438 36,125 1.16x
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
See [`benchmarks/README.md`](./benchmarks/README.md) for workload details and
|
|
307
|
+
extra flags, and [`demo/README.md`](./demo/README.md) for the FastAPI loop
|
|
308
|
+
comparison demo.
|
|
309
|
+
|
|
310
|
+
## Acknowledgements
|
|
311
|
+
|
|
312
|
+
`rsloop` builds on the Python `asyncio` model and is implemented with
|
|
313
|
+
[PyO3](https://pyo3.rs/) on the Rust side. The runtime and I/O work in the
|
|
314
|
+
current implementation rely in part on
|
|
315
|
+
[compio](https://github.com/compio-rs/compio).
|
|
316
|
+
|
|
317
|
+
## License
|
|
318
|
+
|
|
319
|
+
This project is licensed under the Apache License, Version 2.0. See
|
|
320
|
+
[`LICENSE`](./LICENSE) for the full text.
|
|
@@ -13,6 +13,8 @@ uv run --with maturin maturin develop --release
|
|
|
13
13
|
uv run --with uvloop python benchmarks/compare_event_loops.py
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
Benchmark runner: [`benchmarks/compare_event_loops.py`](./compare_event_loops.py)
|
|
17
|
+
|
|
16
18
|
The Rust prototype should be installed in release mode before benchmarking.
|
|
17
19
|
Using the default debug build will heavily skew the comparison against
|
|
18
20
|
`asyncio` and `uvloop`.
|
|
@@ -42,7 +44,7 @@ uv run --with uvloop python benchmarks/compare_event_loops.py \
|
|
|
42
44
|
```
|
|
43
45
|
|
|
44
46
|
This writes one SVG per workload, such as
|
|
45
|
-
`benchmarks/profiles/rsloop-callbacks.svg
|
|
47
|
+
[`benchmarks/profiles/rsloop-callbacks.svg`](./profiles/rsloop-callbacks.svg), before the warmup and measured
|
|
46
48
|
passes.
|
|
47
49
|
|
|
48
50
|
The runner executes each loop/workload in a fresh subprocess and reports:
|