rloop 0.1.4__tar.gz → 0.1.5__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.
- {rloop-0.1.4 → rloop-0.1.5}/Cargo.lock +3 -3
- {rloop-0.1.4 → rloop-0.1.5}/Cargo.toml +1 -1
- {rloop-0.1.4 → rloop-0.1.5}/PKG-INFO +2 -2
- {rloop-0.1.4 → rloop-0.1.5}/pyproject.toml +1 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/event_loop.rs +44 -111
- {rloop-0.1.4 → rloop-0.1.5}/tests/test_handles.py +39 -0
- {rloop-0.1.4 → rloop-0.1.5}/LICENSE +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/README.md +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/__init__.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/_compat.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/_rloop.pyi +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/exc.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/futures.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/loop.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/server.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/subprocess.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/transports.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/rloop/utils.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/handles.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/io.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/lib.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/log.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/py.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/server.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/sock.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/tcp.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/time.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/src/utils.rs +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/tests/conftest.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/tests/tcp/__init__.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/tests/tcp/test_tcp_conn.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/tests/tcp/test_tcp_server.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/tests/test_sockets.py +0 -0
- {rloop-0.1.4 → rloop-0.1.5}/tests/udp/test_udp.py +0 -0
|
@@ -16,9 +16,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
|
16
16
|
|
|
17
17
|
[[package]]
|
|
18
18
|
name = "cc"
|
|
19
|
-
version = "1.2.
|
|
19
|
+
version = "1.2.32"
|
|
20
20
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
-
checksum = "
|
|
21
|
+
checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e"
|
|
22
22
|
dependencies = [
|
|
23
23
|
"shlex",
|
|
24
24
|
]
|
|
@@ -189,7 +189,7 @@ dependencies = [
|
|
|
189
189
|
|
|
190
190
|
[[package]]
|
|
191
191
|
name = "rloop"
|
|
192
|
-
version = "0.1.
|
|
192
|
+
version = "0.1.5"
|
|
193
193
|
dependencies = [
|
|
194
194
|
"anyhow",
|
|
195
195
|
"libc",
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rloop
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: BSD License
|
|
7
7
|
Classifier: Operating System :: MacOS
|
|
8
8
|
Classifier: Operating System :: POSIX :: Linux
|
|
9
|
+
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
|
|
9
10
|
Classifier: Programming Language :: Python :: 3
|
|
10
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
11
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -20,7 +21,6 @@ License-File: LICENSE
|
|
|
20
21
|
Summary: An asyncio event loop implemented in Rust
|
|
21
22
|
Keywords: asyncio
|
|
22
23
|
Home-Page: https://github.com/gi0baro/rloop
|
|
23
|
-
Author: Giovanni Barillari <g@baro.dev>
|
|
24
24
|
Author-email: Giovanni Barillari <g@baro.dev>
|
|
25
25
|
License: BSD-3-Clause
|
|
26
26
|
Requires-Python: >=3.9
|
|
@@ -9,6 +9,7 @@ classifiers = [
|
|
|
9
9
|
'License :: OSI Approved :: BSD License',
|
|
10
10
|
'Operating System :: MacOS',
|
|
11
11
|
'Operating System :: POSIX :: Linux',
|
|
12
|
+
'Programming Language :: Python :: Free Threading :: 2 - Beta',
|
|
12
13
|
'Programming Language :: Python :: 3',
|
|
13
14
|
'Programming Language :: Python :: 3.9',
|
|
14
15
|
'Programming Language :: Python :: 3.10',
|
|
@@ -3,12 +3,12 @@ use std::{
|
|
|
3
3
|
io::Read,
|
|
4
4
|
mem,
|
|
5
5
|
os::fd::{AsRawFd, FromRawFd},
|
|
6
|
-
sync::{Mutex, RwLock, atomic},
|
|
6
|
+
sync::{Arc, Mutex, RwLock, atomic},
|
|
7
7
|
time::{Duration, Instant},
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
use anyhow::Result;
|
|
11
|
-
use mio::{Interest, Poll, Token, event, net::TcpListener};
|
|
11
|
+
use mio::{Interest, Poll, Token, Waker, event, net::TcpListener};
|
|
12
12
|
use pyo3::prelude::*;
|
|
13
13
|
|
|
14
14
|
use crate::{
|
|
@@ -19,13 +19,9 @@ use crate::{
|
|
|
19
19
|
server::Server,
|
|
20
20
|
tcp::{TCPReadHandle, TCPServer, TCPServerRef, TCPTransport, TCPWriteHandle},
|
|
21
21
|
time::Timer,
|
|
22
|
-
utils::syscall,
|
|
23
22
|
};
|
|
24
23
|
|
|
25
|
-
const WAKEB: &[u8; 1] = b"\0";
|
|
26
|
-
|
|
27
24
|
enum IOHandle {
|
|
28
|
-
Internal,
|
|
29
25
|
Py(PyHandleData),
|
|
30
26
|
Signals,
|
|
31
27
|
TCPListener(TCPListenerHandleData),
|
|
@@ -47,7 +43,6 @@ pub struct EventLoopRunState {
|
|
|
47
43
|
buf: Box<[u8]>,
|
|
48
44
|
events: event::Events,
|
|
49
45
|
pub read_buf: Box<[u8]>,
|
|
50
|
-
sock: socket2::Socket,
|
|
51
46
|
tick_last: u128,
|
|
52
47
|
}
|
|
53
48
|
|
|
@@ -55,14 +50,13 @@ pub struct EventLoopRunState {
|
|
|
55
50
|
pub struct EventLoop {
|
|
56
51
|
idle: atomic::AtomicBool,
|
|
57
52
|
io: Mutex<Poll>,
|
|
53
|
+
waker: Arc<Waker>,
|
|
58
54
|
handles_io: papaya::HashMap<Token, IOHandle>,
|
|
59
55
|
handles_ready: Mutex<VecDeque<BoxedHandle>>,
|
|
60
56
|
handles_sched: Mutex<BinaryHeap<Timer>>,
|
|
61
57
|
epoch: Instant,
|
|
62
58
|
counter_ready: atomic::AtomicUsize,
|
|
63
59
|
ssock: RwLock<Option<(socket2::Socket, socket2::Socket)>>,
|
|
64
|
-
wsock: Mutex<Option<socket2::Socket>>,
|
|
65
|
-
wsock_fd: atomic::AtomicI32,
|
|
66
60
|
closed: atomic::AtomicBool,
|
|
67
61
|
exc_handler: RwLock<PyObject>,
|
|
68
62
|
exception_handler: RwLock<PyObject>,
|
|
@@ -88,46 +82,6 @@ pub struct EventLoop {
|
|
|
88
82
|
}
|
|
89
83
|
|
|
90
84
|
impl EventLoop {
|
|
91
|
-
fn run_pre(&self) -> Result<EventLoopRunState> {
|
|
92
|
-
// wake sockets
|
|
93
|
-
let (sock_r, sock_w) = socket2::Socket::pair(socket2::Domain::UNIX, socket2::Type::STREAM, None)?;
|
|
94
|
-
sock_r.set_nonblocking(true)?;
|
|
95
|
-
sock_w.set_nonblocking(true)?;
|
|
96
|
-
let token = Token(sock_r.as_raw_fd() as usize);
|
|
97
|
-
let mut source = Source::FD(sock_r.as_raw_fd());
|
|
98
|
-
{
|
|
99
|
-
let guard = self.io.lock().unwrap();
|
|
100
|
-
guard.registry().register(&mut source, token, Interest::READABLE)?;
|
|
101
|
-
}
|
|
102
|
-
self.handles_io.pin().insert(token, IOHandle::Internal);
|
|
103
|
-
{
|
|
104
|
-
let mut guard = self.wsock.lock().unwrap();
|
|
105
|
-
self.wsock_fd.store(sock_w.as_raw_fd(), atomic::Ordering::Relaxed);
|
|
106
|
-
*guard = Some(sock_w);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
Ok(EventLoopRunState {
|
|
110
|
-
buf: vec![0; 4096].into_boxed_slice(),
|
|
111
|
-
events: event::Events::with_capacity(128),
|
|
112
|
-
read_buf: vec![0; 262_144].into_boxed_slice(),
|
|
113
|
-
tick_last: 0,
|
|
114
|
-
sock: sock_r,
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
fn run_post(&self, state: &mut EventLoopRunState) {
|
|
119
|
-
// cleanup wake sockets
|
|
120
|
-
self.wsock.lock().unwrap().take();
|
|
121
|
-
self.wsock_fd.store(-1, atomic::Ordering::Relaxed);
|
|
122
|
-
let token = Token(state.sock.as_raw_fd() as usize);
|
|
123
|
-
let mut source = Source::FD(state.sock.as_raw_fd());
|
|
124
|
-
{
|
|
125
|
-
let guard = self.io.lock().unwrap();
|
|
126
|
-
_ = guard.registry().deregister(&mut source);
|
|
127
|
-
}
|
|
128
|
-
self.handles_io.pin().remove(&token);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
85
|
#[inline]
|
|
132
86
|
fn step(&self, py: Python, state: &mut EventLoopRunState) -> std::result::Result<(), std::io::Error> {
|
|
133
87
|
let mut sched_time: Option<u64> = None;
|
|
@@ -154,33 +108,29 @@ impl EventLoop {
|
|
|
154
108
|
}
|
|
155
109
|
|
|
156
110
|
// I/O
|
|
157
|
-
let poll_result =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
111
|
+
let poll_result = if skip_poll {
|
|
112
|
+
Ok(())
|
|
113
|
+
} else {
|
|
114
|
+
let idle_swap = !matches!(sched_time, Some(0));
|
|
115
|
+
if idle_swap {
|
|
116
|
+
self.idle.store(true, atomic::Ordering::Release);
|
|
161
117
|
}
|
|
162
|
-
|
|
163
|
-
let
|
|
118
|
+
let res = py.allow_threads(|| {
|
|
119
|
+
let mut io = self.io.lock().unwrap();
|
|
120
|
+
let res = io.poll(&mut state.events, sched_time.map(Duration::from_micros));
|
|
164
121
|
if idle_swap {
|
|
165
|
-
self.idle.store(
|
|
122
|
+
self.idle.store(false, atomic::Ordering::Release);
|
|
166
123
|
}
|
|
167
|
-
let
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
self.idle.store(false, atomic::Ordering::Release);
|
|
172
|
-
}
|
|
173
|
-
if let Err(ref err) = res {
|
|
174
|
-
if err.kind() == std::io::ErrorKind::Interrupted {
|
|
175
|
-
// if we got an interrupt, we retry ready events (as we might need to process signals)
|
|
176
|
-
let _ = io.poll(&mut state.events, Some(Duration::from_millis(0)));
|
|
177
|
-
}
|
|
124
|
+
if let Err(ref err) = res {
|
|
125
|
+
if err.kind() == std::io::ErrorKind::Interrupted {
|
|
126
|
+
// if we got an interrupt, we retry ready events (as we might need to process signals)
|
|
127
|
+
let _ = io.poll(&mut state.events, Some(Duration::from_millis(0)));
|
|
178
128
|
}
|
|
179
|
-
|
|
180
|
-
});
|
|
181
|
-
state.tick_last = Instant::now().duration_since(self.epoch).as_micros();
|
|
129
|
+
}
|
|
182
130
|
res
|
|
183
|
-
}
|
|
131
|
+
});
|
|
132
|
+
state.tick_last = Instant::now().duration_since(self.epoch).as_micros();
|
|
133
|
+
res
|
|
184
134
|
};
|
|
185
135
|
|
|
186
136
|
let mut cb_handles = {
|
|
@@ -190,7 +140,7 @@ impl EventLoop {
|
|
|
190
140
|
self.counter_ready
|
|
191
141
|
.fetch_sub(cb_handles.len(), atomic::Ordering::Release);
|
|
192
142
|
|
|
193
|
-
{
|
|
143
|
+
if !skip_poll {
|
|
194
144
|
let io_handles = self.handles_io.pin();
|
|
195
145
|
for event in &state.events {
|
|
196
146
|
// NOTE: cancellation is not necessary as we have custom futures
|
|
@@ -199,7 +149,6 @@ impl EventLoop {
|
|
|
199
149
|
IOHandle::Py(handle) => self.handle_io_py(py, event, handle, &mut cb_handles),
|
|
200
150
|
IOHandle::TCPListener(handle) => self.handle_io_tcpl(py, handle, &io_handles, &mut cb_handles),
|
|
201
151
|
IOHandle::TCPStream(_) => self.handle_io_tcps(event, &mut cb_handles),
|
|
202
|
-
IOHandle::Internal => self.handle_io_internal(&mut state.sock, &mut state.buf),
|
|
203
152
|
IOHandle::Signals => self.handle_io_signals(py, &mut state.buf, &mut cb_handles),
|
|
204
153
|
}
|
|
205
154
|
}
|
|
@@ -245,11 +194,6 @@ impl EventLoop {
|
|
|
245
194
|
len
|
|
246
195
|
}
|
|
247
196
|
|
|
248
|
-
#[inline]
|
|
249
|
-
fn handle_io_internal(&self, socket: &mut socket2::Socket, buf: &mut [u8]) {
|
|
250
|
-
self.read_from_sock(socket, buf);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
197
|
#[inline]
|
|
254
198
|
fn handle_io_py(
|
|
255
199
|
&self,
|
|
@@ -338,8 +282,9 @@ impl EventLoop {
|
|
|
338
282
|
|
|
339
283
|
#[inline(always)]
|
|
340
284
|
fn wake(&self) {
|
|
341
|
-
|
|
342
|
-
|
|
285
|
+
if self.idle.load(atomic::Ordering::Acquire) {
|
|
286
|
+
_ = self.waker.wake();
|
|
287
|
+
}
|
|
343
288
|
}
|
|
344
289
|
|
|
345
290
|
pub(crate) fn tcp_listener_add(&self, listener: TcpListener, server: TCPServerRef) {
|
|
@@ -486,9 +431,7 @@ impl EventLoop {
|
|
|
486
431
|
guard.push_back(Box::new(handle));
|
|
487
432
|
}
|
|
488
433
|
self.counter_ready.fetch_add(1, atomic::Ordering::Release);
|
|
489
|
-
|
|
490
|
-
self.wake();
|
|
491
|
-
}
|
|
434
|
+
self.wake();
|
|
492
435
|
|
|
493
436
|
Ok(())
|
|
494
437
|
}
|
|
@@ -509,9 +452,7 @@ impl EventLoop {
|
|
|
509
452
|
guard.push_back(Box::new(handle));
|
|
510
453
|
}
|
|
511
454
|
self.counter_ready.fetch_add(1, atomic::Ordering::Release);
|
|
512
|
-
|
|
513
|
-
self.wake();
|
|
514
|
-
}
|
|
455
|
+
self.wake();
|
|
515
456
|
|
|
516
457
|
Ok(())
|
|
517
458
|
}
|
|
@@ -532,9 +473,7 @@ impl EventLoop {
|
|
|
532
473
|
guard.push_back(Box::new(handle));
|
|
533
474
|
}
|
|
534
475
|
self.counter_ready.fetch_add(1, atomic::Ordering::Release);
|
|
535
|
-
|
|
536
|
-
self.wake();
|
|
537
|
-
}
|
|
476
|
+
self.wake();
|
|
538
477
|
|
|
539
478
|
Ok(())
|
|
540
479
|
}
|
|
@@ -559,9 +498,7 @@ impl EventLoop {
|
|
|
559
498
|
.map_err(|_| anyhow::anyhow!("lock acquisition failed"))?;
|
|
560
499
|
guard.push(timer);
|
|
561
500
|
}
|
|
562
|
-
|
|
563
|
-
self.wake();
|
|
564
|
-
}
|
|
501
|
+
self.wake();
|
|
565
502
|
|
|
566
503
|
Ok(())
|
|
567
504
|
}
|
|
@@ -592,9 +529,7 @@ impl EventLoop {
|
|
|
592
529
|
.map_err(|_| anyhow::anyhow!("lock acquisition failed"))?;
|
|
593
530
|
guard.push(timer);
|
|
594
531
|
}
|
|
595
|
-
|
|
596
|
-
self.wake();
|
|
597
|
-
}
|
|
532
|
+
self.wake();
|
|
598
533
|
|
|
599
534
|
Ok(())
|
|
600
535
|
}
|
|
@@ -625,9 +560,7 @@ impl EventLoop {
|
|
|
625
560
|
.map_err(|_| anyhow::anyhow!("lock acquisition failed"))?;
|
|
626
561
|
guard.push(timer);
|
|
627
562
|
}
|
|
628
|
-
|
|
629
|
-
self.wake();
|
|
630
|
-
}
|
|
563
|
+
self.wake();
|
|
631
564
|
|
|
632
565
|
Ok(())
|
|
633
566
|
}
|
|
@@ -660,9 +593,7 @@ impl EventLoop {
|
|
|
660
593
|
self.counter_ready.fetch_add(1, atomic::Ordering::Release);
|
|
661
594
|
}
|
|
662
595
|
}
|
|
663
|
-
|
|
664
|
-
self.wake();
|
|
665
|
-
}
|
|
596
|
+
self.wake();
|
|
666
597
|
|
|
667
598
|
Ok(())
|
|
668
599
|
}
|
|
@@ -672,17 +603,19 @@ impl EventLoop {
|
|
|
672
603
|
impl EventLoop {
|
|
673
604
|
#[new]
|
|
674
605
|
fn new(py: Python) -> PyResult<Self> {
|
|
606
|
+
let poll = Poll::new()?;
|
|
607
|
+
let waker = Waker::new(poll.registry(), Token(0))?;
|
|
608
|
+
|
|
675
609
|
Ok(Self {
|
|
676
610
|
idle: atomic::AtomicBool::new(false),
|
|
677
|
-
io: Mutex::new(
|
|
611
|
+
io: Mutex::new(poll),
|
|
612
|
+
waker: Arc::new(waker),
|
|
678
613
|
handles_io: papaya::HashMap::with_capacity(128),
|
|
679
614
|
handles_ready: Mutex::new(VecDeque::with_capacity(128)),
|
|
680
615
|
handles_sched: Mutex::new(BinaryHeap::with_capacity(32)),
|
|
681
616
|
epoch: Instant::now(),
|
|
682
617
|
counter_ready: atomic::AtomicUsize::new(0),
|
|
683
618
|
ssock: RwLock::new(None),
|
|
684
|
-
wsock: Mutex::new(None),
|
|
685
|
-
wsock_fd: atomic::AtomicI32::new(-1),
|
|
686
619
|
closed: atomic::AtomicBool::new(false),
|
|
687
620
|
exc_handler: RwLock::new(py.None()),
|
|
688
621
|
exception_handler: RwLock::new(py.None()),
|
|
@@ -935,10 +868,7 @@ impl EventLoop {
|
|
|
935
868
|
guard.push_back(bhandle);
|
|
936
869
|
}
|
|
937
870
|
self.counter_ready.fetch_add(1, atomic::Ordering::Release);
|
|
938
|
-
|
|
939
|
-
if self.idle.load(atomic::Ordering::Acquire) {
|
|
940
|
-
self.wake();
|
|
941
|
-
}
|
|
871
|
+
self.wake();
|
|
942
872
|
|
|
943
873
|
handle
|
|
944
874
|
}
|
|
@@ -1195,7 +1125,12 @@ impl EventLoop {
|
|
|
1195
1125
|
}
|
|
1196
1126
|
|
|
1197
1127
|
fn _run(&self, py: Python) -> PyResult<()> {
|
|
1198
|
-
let mut state =
|
|
1128
|
+
let mut state = EventLoopRunState {
|
|
1129
|
+
buf: vec![0; 4096].into_boxed_slice(),
|
|
1130
|
+
events: event::Events::with_capacity(128),
|
|
1131
|
+
read_buf: vec![0; 262_144].into_boxed_slice(),
|
|
1132
|
+
tick_last: 0,
|
|
1133
|
+
};
|
|
1199
1134
|
|
|
1200
1135
|
loop {
|
|
1201
1136
|
if self.stopping.load(atomic::Ordering::Acquire) {
|
|
@@ -1208,12 +1143,10 @@ impl EventLoop {
|
|
|
1208
1143
|
}
|
|
1209
1144
|
break;
|
|
1210
1145
|
}
|
|
1211
|
-
self.run_post(&mut state);
|
|
1212
1146
|
return Err(err.into());
|
|
1213
1147
|
}
|
|
1214
1148
|
}
|
|
1215
1149
|
|
|
1216
|
-
self.run_post(&mut state);
|
|
1217
1150
|
Ok(())
|
|
1218
1151
|
}
|
|
1219
1152
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
def run_loop(loop):
|
|
2
5
|
async def run():
|
|
3
6
|
loop.stop()
|
|
@@ -57,3 +60,39 @@ def test_call_at(loop):
|
|
|
57
60
|
dt = loop.time() - t0
|
|
58
61
|
|
|
59
62
|
assert dt >= delay
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_call_soon_threadsafe(loop):
|
|
66
|
+
calls = []
|
|
67
|
+
|
|
68
|
+
def cb(arg):
|
|
69
|
+
calls.append(arg)
|
|
70
|
+
|
|
71
|
+
def wake(cond):
|
|
72
|
+
with cond:
|
|
73
|
+
cond.notify_all()
|
|
74
|
+
|
|
75
|
+
def stop():
|
|
76
|
+
loop.stop()
|
|
77
|
+
|
|
78
|
+
def trun(cond1, cond2, loop, cb):
|
|
79
|
+
with cond1:
|
|
80
|
+
cond1.wait()
|
|
81
|
+
loop.call_soon_threadsafe(cb, 2)
|
|
82
|
+
with cond2:
|
|
83
|
+
cond2.wait()
|
|
84
|
+
loop.call_soon_threadsafe(cb, 4)
|
|
85
|
+
|
|
86
|
+
cond1 = threading.Condition()
|
|
87
|
+
cond2 = threading.Condition()
|
|
88
|
+
t = threading.Thread(target=trun, args=(cond1, cond2, loop, cb))
|
|
89
|
+
t.start()
|
|
90
|
+
|
|
91
|
+
loop.call_soon(cb, 1)
|
|
92
|
+
loop.call_soon(wake, cond1)
|
|
93
|
+
loop.call_later(0.5, cb, 3)
|
|
94
|
+
loop.call_later(0.6, wake, cond2)
|
|
95
|
+
loop.call_later(1.0, stop)
|
|
96
|
+
loop.run_forever()
|
|
97
|
+
|
|
98
|
+
assert calls == [1, 2, 3, 4]
|
|
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
|
|
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
|