loopmini 0.2.1__tar.gz → 0.2.2__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.
- {loopmini-0.2.1 → loopmini-0.2.2}/.github/workflows/ci.yml +6 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/Cargo.lock +1 -1
- {loopmini-0.2.1 → loopmini-0.2.2}/Cargo.toml +1 -2
- {loopmini-0.2.1 → loopmini-0.2.2}/PKG-INFO +1 -1
- {loopmini-0.2.1 → loopmini-0.2.2}/src/pyreactor.rs +10 -2
- {loopmini-0.2.1 → loopmini-0.2.2}/src/reactor.rs +19 -5
- {loopmini-0.2.1 → loopmini-0.2.2}/.gitignore +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/DEV.md +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/LICENSE +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/README.md +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/_config.yml +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/_layouts/default.html +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/pyproject.toml +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/python/loopmini/__init__.py +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/python/loopmini/loop.py +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/python/loopmini/subproc.py +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/python/loopmini/transports.py +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/rustfmt.toml +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/src/lib.rs +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/tests/oracle_util.py +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/tests/test_bench.py +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/tests/test_loop.py +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/tests/test_oracle.py +0 -0
- {loopmini-0.2.1 → loopmini-0.2.2}/tests/test_soak.py +0 -0
|
@@ -60,6 +60,12 @@ jobs:
|
|
|
60
60
|
contents: write
|
|
61
61
|
steps:
|
|
62
62
|
- uses: actions/checkout@v7
|
|
63
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
64
|
+
- id: crates-auth
|
|
65
|
+
uses: rust-lang/crates-io-auth-action@v1
|
|
66
|
+
- run: cargo publish
|
|
67
|
+
env:
|
|
68
|
+
CARGO_REGISTRY_TOKEN: ${{ steps.crates-auth.outputs.token }}
|
|
63
69
|
- uses: actions/download-artifact@v8
|
|
64
70
|
with:
|
|
65
71
|
path: dist
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "loopmini"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
description = "Rust-backed asyncio event loop"
|
|
7
7
|
repository = "https://github.com/AnswerDotAI/loopmini"
|
|
8
|
-
publish = false
|
|
9
8
|
readme = "README.md"
|
|
10
9
|
|
|
11
10
|
[lib]
|
|
@@ -44,14 +44,22 @@ impl PyReactor {
|
|
|
44
44
|
// An injected async exception (e.g. KeyboardInterrupt) can surface at either
|
|
45
45
|
// Python call; only a handle whose _run began may be dropped without requeue.
|
|
46
46
|
let cancelled = h.bind(py).getattr(intern!(py, "_cancelled")).and_then(|v| v.extract::<bool>());
|
|
47
|
-
match cancelled {
|
|
47
|
+
match cancelled {
|
|
48
|
+
Err(e) => {
|
|
49
|
+
self.core.requeue_front(std::iter::once(h).chain(it));
|
|
50
|
+
return Err(e);
|
|
51
|
+
}
|
|
52
|
+
Ok(true) => continue,
|
|
53
|
+
Ok(false) => {}
|
|
54
|
+
}
|
|
48
55
|
if let Err(e) = h.bind(py).call_method0(intern!(py, "_run")) {
|
|
49
56
|
// An injected exception surfacing at `_run`'s entry leaves a single-frame
|
|
50
57
|
// traceback: the callback never ran, so requeue the handle - dropping it
|
|
51
58
|
// would lose e.g. a task wakeup and orphan the task. A deeper traceback
|
|
52
59
|
// means the callback began, so the handle is consumed (CPython semantics).
|
|
53
60
|
let entry_only = e.traceback(py).map_or(true, |tb| tb.getattr("tb_next").ok().is_none_or(|n| n.is_none()));
|
|
54
|
-
if entry_only { self.core.requeue_front(std::iter::once(h).chain(it)) }
|
|
61
|
+
if entry_only { self.core.requeue_front(std::iter::once(h).chain(it)) }
|
|
62
|
+
else { self.core.requeue_front(it) }
|
|
55
63
|
return Err(e);
|
|
56
64
|
}
|
|
57
65
|
}
|
|
@@ -79,7 +79,8 @@ impl<H: Clone> Reactor<H> {
|
|
|
79
79
|
let mut inner = self.inner.lock().unwrap();
|
|
80
80
|
let fresh = !inner.fds.contains_key(&fd);
|
|
81
81
|
let e = inner.fds.entry(fd).or_default();
|
|
82
|
-
if write { e.writer = Some(h) }
|
|
82
|
+
if write { e.writer = Some(h) }
|
|
83
|
+
else { e.reader = Some(h) }
|
|
83
84
|
let ev = interest(fd, e);
|
|
84
85
|
if fresh { unsafe { self.poller.add(fd as i32, ev) } } else { self.poller.modify(borrowed(fd), ev) }
|
|
85
86
|
}
|
|
@@ -90,7 +91,11 @@ impl<H: Clone> Reactor<H> {
|
|
|
90
91
|
let had = if write { e.writer.take().is_some() } else { e.reader.take().is_some() };
|
|
91
92
|
let empty = e.reader.is_none() && e.writer.is_none();
|
|
92
93
|
let ev = interest(fd, e);
|
|
93
|
-
if empty {
|
|
94
|
+
if empty {
|
|
95
|
+
inner.fds.remove(&fd);
|
|
96
|
+
let _ = self.poller.delete(borrowed(fd));
|
|
97
|
+
}
|
|
98
|
+
else { self.poller.modify(borrowed(fd), ev)? }
|
|
94
99
|
Ok(had)
|
|
95
100
|
}
|
|
96
101
|
|
|
@@ -122,11 +127,17 @@ impl<H: Clone> Reactor<H> {
|
|
|
122
127
|
/// cross-thread handles, and say how long the driver may block in `poll`.
|
|
123
128
|
pub fn next_timeout(&self) -> ControlFlow<(), Option<Duration>> {
|
|
124
129
|
let mut inner = self.inner.lock().unwrap();
|
|
125
|
-
if inner.stop {
|
|
130
|
+
if inner.stop {
|
|
131
|
+
inner.stop = false;
|
|
132
|
+
return ControlFlow::Break(());
|
|
133
|
+
}
|
|
126
134
|
let Inner { ready, timers, .. } = &mut *inner;
|
|
127
135
|
self.drain_tsq(ready);
|
|
128
136
|
let now = self.now_us();
|
|
129
|
-
while timers.first_key_value().is_some_and(|((when, _), _)| *when <= now) {
|
|
137
|
+
while timers.first_key_value().is_some_and(|((when, _), _)| *when <= now) {
|
|
138
|
+
let (_, h) = timers.pop_first().unwrap();
|
|
139
|
+
ready.push_back(h);
|
|
140
|
+
}
|
|
130
141
|
// Clamped like CPython's MAXIMUM_SELECT_TIMEOUT: a sleep(inf) timer saturates
|
|
131
142
|
// to u64::MAX, and a pure-Rust consumer blocking in `poll` would hand that
|
|
132
143
|
// timespec to kqueue, which rejects it with EINVAL
|
|
@@ -175,7 +186,10 @@ mod tests {
|
|
|
175
186
|
fn cross_thread_schedule_wakes_poll() {
|
|
176
187
|
let reactor = Arc::new(Reactor::new().unwrap());
|
|
177
188
|
let sender = reactor.clone();
|
|
178
|
-
let thread = thread::spawn(move || {
|
|
189
|
+
let thread = thread::spawn(move || {
|
|
190
|
+
thread::sleep(Duration::from_millis(10));
|
|
191
|
+
sender.schedule_ts("ready").unwrap();
|
|
192
|
+
});
|
|
179
193
|
let events = reactor.poll(None).unwrap();
|
|
180
194
|
reactor.process(&events);
|
|
181
195
|
thread.join().unwrap();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|