rloop 0.1.4__tar.gz → 0.1.6__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.
Files changed (34) hide show
  1. {rloop-0.1.4 → rloop-0.1.6}/Cargo.lock +11 -11
  2. {rloop-0.1.4 → rloop-0.1.6}/Cargo.toml +1 -1
  3. {rloop-0.1.4 → rloop-0.1.6}/PKG-INFO +2 -2
  4. {rloop-0.1.4 → rloop-0.1.6}/pyproject.toml +1 -0
  5. {rloop-0.1.4 → rloop-0.1.6}/rloop/loop.py +14 -13
  6. {rloop-0.1.4 → rloop-0.1.6}/src/event_loop.rs +57 -125
  7. {rloop-0.1.4 → rloop-0.1.6}/src/tcp.rs +4 -4
  8. {rloop-0.1.4 → rloop-0.1.6}/tests/test_handles.py +39 -0
  9. {rloop-0.1.4 → rloop-0.1.6}/LICENSE +0 -0
  10. {rloop-0.1.4 → rloop-0.1.6}/README.md +0 -0
  11. {rloop-0.1.4 → rloop-0.1.6}/rloop/__init__.py +0 -0
  12. {rloop-0.1.4 → rloop-0.1.6}/rloop/_compat.py +0 -0
  13. {rloop-0.1.4 → rloop-0.1.6}/rloop/_rloop.pyi +0 -0
  14. {rloop-0.1.4 → rloop-0.1.6}/rloop/exc.py +0 -0
  15. {rloop-0.1.4 → rloop-0.1.6}/rloop/futures.py +0 -0
  16. {rloop-0.1.4 → rloop-0.1.6}/rloop/server.py +0 -0
  17. {rloop-0.1.4 → rloop-0.1.6}/rloop/subprocess.py +0 -0
  18. {rloop-0.1.4 → rloop-0.1.6}/rloop/transports.py +0 -0
  19. {rloop-0.1.4 → rloop-0.1.6}/rloop/utils.py +0 -0
  20. {rloop-0.1.4 → rloop-0.1.6}/src/handles.rs +0 -0
  21. {rloop-0.1.4 → rloop-0.1.6}/src/io.rs +0 -0
  22. {rloop-0.1.4 → rloop-0.1.6}/src/lib.rs +0 -0
  23. {rloop-0.1.4 → rloop-0.1.6}/src/log.rs +0 -0
  24. {rloop-0.1.4 → rloop-0.1.6}/src/py.rs +0 -0
  25. {rloop-0.1.4 → rloop-0.1.6}/src/server.rs +0 -0
  26. {rloop-0.1.4 → rloop-0.1.6}/src/sock.rs +0 -0
  27. {rloop-0.1.4 → rloop-0.1.6}/src/time.rs +0 -0
  28. {rloop-0.1.4 → rloop-0.1.6}/src/utils.rs +0 -0
  29. {rloop-0.1.4 → rloop-0.1.6}/tests/conftest.py +0 -0
  30. {rloop-0.1.4 → rloop-0.1.6}/tests/tcp/__init__.py +0 -0
  31. {rloop-0.1.4 → rloop-0.1.6}/tests/tcp/test_tcp_conn.py +0 -0
  32. {rloop-0.1.4 → rloop-0.1.6}/tests/tcp/test_tcp_server.py +0 -0
  33. {rloop-0.1.4 → rloop-0.1.6}/tests/test_sockets.py +0 -0
  34. {rloop-0.1.4 → rloop-0.1.6}/tests/udp/test_udp.py +0 -0
@@ -4,9 +4,9 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "anyhow"
7
- version = "1.0.98"
7
+ version = "1.0.99"
8
8
  source = "registry+https://github.com/rust-lang/crates.io-index"
9
- checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
9
+ checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
10
10
 
11
11
  [[package]]
12
12
  name = "autocfg"
@@ -16,9 +16,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
16
16
 
17
17
  [[package]]
18
18
  name = "cc"
19
- version = "1.2.29"
19
+ version = "1.2.33"
20
20
  source = "registry+https://github.com/rust-lang/crates.io-index"
21
- checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
21
+ checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f"
22
22
  dependencies = [
23
23
  "shlex",
24
24
  ]
@@ -43,9 +43,9 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
43
43
 
44
44
  [[package]]
45
45
  name = "libc"
46
- version = "0.2.174"
46
+ version = "0.2.175"
47
47
  source = "registry+https://github.com/rust-lang/crates.io-index"
48
- checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
48
+ checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
49
49
 
50
50
  [[package]]
51
51
  name = "log"
@@ -98,9 +98,9 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
98
98
 
99
99
  [[package]]
100
100
  name = "proc-macro2"
101
- version = "1.0.95"
101
+ version = "1.0.101"
102
102
  source = "registry+https://github.com/rust-lang/crates.io-index"
103
- checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
103
+ checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
104
104
  dependencies = [
105
105
  "unicode-ident",
106
106
  ]
@@ -189,7 +189,7 @@ dependencies = [
189
189
 
190
190
  [[package]]
191
191
  name = "rloop"
192
- version = "0.1.4"
192
+ version = "0.1.6"
193
193
  dependencies = [
194
194
  "anyhow",
195
195
  "libc",
@@ -228,9 +228,9 @@ dependencies = [
228
228
 
229
229
  [[package]]
230
230
  name = "syn"
231
- version = "2.0.104"
231
+ version = "2.0.106"
232
232
  source = "registry+https://github.com/rust-lang/crates.io-index"
233
- checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
233
+ checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
234
234
  dependencies = [
235
235
  "proc-macro2",
236
236
  "quote",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rloop"
3
- version = "0.1.4"
3
+ version = "0.1.6"
4
4
  description = "An asyncio event loop implemented in Rust"
5
5
  authors = ["Giovanni Barillari <g@baro.dev>"]
6
6
  license = "BSD-3-Clause"
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rloop
3
- Version: 0.1.4
3
+ Version: 0.1.6
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',
@@ -816,22 +816,22 @@ class RLoop(__BaseLoop, __asyncio.AbstractEventLoop):
816
816
  try:
817
817
  n = sock.send(data)
818
818
  except (BlockingIOError, InterruptedError):
819
- pass
820
- else:
821
- if n == len(data):
822
- return
823
- data = memoryview(data)
824
- data = data[n:]
819
+ n = 0
820
+
821
+ if n == len(data):
822
+ return
825
823
 
826
824
  fd = sock.fileno()
827
825
  self._ensure_fd_no_transport(fd)
828
826
  future = _SyncSockWriterFuture(sock, self)
829
- self.add_writer(fd, self._sock_sendall, future, sock, data)
827
+ self.add_writer(fd, self._sock_sendall, future, sock, memoryview(data), [n])
830
828
  return await future
831
829
 
832
- def _sock_sendall(self, fut, sock, data):
830
+ def _sock_sendall(self, fut, sock, data, pos):
831
+ start = pos[0]
832
+
833
833
  try:
834
- n = sock.send(data)
834
+ n = sock.send(data[start:])
835
835
  except (BlockingIOError, InterruptedError):
836
836
  return
837
837
  except (SystemExit, KeyboardInterrupt):
@@ -841,12 +841,13 @@ class RLoop(__BaseLoop, __asyncio.AbstractEventLoop):
841
841
  self.remove_writer(sock.fileno())
842
842
  return
843
843
 
844
- self.remove_writer(sock.fileno())
845
- if n == len(data):
844
+ start += n
845
+
846
+ if start == len(data):
846
847
  fut.set_result(None)
848
+ self.remove_writer(sock.fileno())
847
849
  else:
848
- data = data[n:]
849
- self.add_writer(sock.fileno(), self._sock_sendall, fut, sock, data)
850
+ pos[0] = start
850
851
 
851
852
  # async def sock_sendto(self, sock, data, address):
852
853
  # raise NotImplementedError
@@ -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 = match skip_poll {
158
- true => {
159
- state.events.clear();
160
- Ok(())
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
- false => {
163
- let idle_swap = !matches!(sched_time, Some(0));
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(true, atomic::Ordering::Release);
122
+ self.idle.store(false, atomic::Ordering::Release);
123
+ }
124
+ if let Err(ref err) = res
125
+ && err.kind() == std::io::ErrorKind::Interrupted
126
+ {
127
+ // if we got an interrupt, we retry ready events (as we might need to process signals)
128
+ let _ = io.poll(&mut state.events, Some(Duration::from_millis(0)));
166
129
  }
167
- let res = py.allow_threads(|| {
168
- let mut io = self.io.lock().unwrap();
169
- let res = io.poll(&mut state.events, sched_time.map(Duration::from_micros));
170
- if idle_swap {
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
- }
178
- }
179
- res
180
- });
181
- state.tick_last = Instant::now().duration_since(self.epoch).as_micros();
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,
@@ -258,15 +202,15 @@ impl EventLoop {
258
202
  handle: &PyHandleData,
259
203
  handles: &mut VecDeque<BoxedHandle>,
260
204
  ) {
261
- if let Some(cbr) = &handle.cbr {
262
- if event.is_readable() {
263
- handles.push_back(Box::new(cbr.clone_ref(py)));
264
- }
205
+ if let Some(cbr) = &handle.cbr
206
+ && event.is_readable()
207
+ {
208
+ handles.push_back(Box::new(cbr.clone_ref(py)));
265
209
  }
266
- if let Some(cbw) = &handle.cbw {
267
- if event.is_writable() {
268
- handles.push_back(Box::new(cbw.clone_ref(py)));
269
- }
210
+ if let Some(cbw) = &handle.cbw
211
+ && event.is_writable()
212
+ {
213
+ handles.push_back(Box::new(cbw.clone_ref(py)));
270
214
  }
271
215
  }
272
216
 
@@ -338,8 +282,9 @@ impl EventLoop {
338
282
 
339
283
  #[inline(always)]
340
284
  fn wake(&self) {
341
- let fd = self.wsock_fd.load(atomic::Ordering::Relaxed);
342
- _ = syscall!(write(fd, WAKEB.as_ptr().cast(), 1));
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) {
@@ -437,11 +382,10 @@ impl EventLoop {
437
382
 
438
383
  #[inline(always)]
439
384
  pub(crate) fn tcp_stream_close(&self, py: Python, fd: usize) {
440
- if let Some(transport) = self.tcp_transports.pin().remove(&fd) {
441
- if let Some(lfd) = transport.borrow(py).lfd {
442
- self.tcp_lstreams.pin().get(&lfd).map(|v| v.pin().remove(&fd));
443
- }
444
- // transport.drop_ref(py);
385
+ if let Some(transport) = self.tcp_transports.pin().remove(&fd)
386
+ && let Some(lfd) = transport.borrow(py).lfd
387
+ {
388
+ self.tcp_lstreams.pin().get(&lfd).map(|v| v.pin().remove(&fd));
445
389
  }
446
390
  }
447
391
 
@@ -486,9 +430,7 @@ impl EventLoop {
486
430
  guard.push_back(Box::new(handle));
487
431
  }
488
432
  self.counter_ready.fetch_add(1, atomic::Ordering::Release);
489
- if self.idle.load(atomic::Ordering::Acquire) {
490
- self.wake();
491
- }
433
+ self.wake();
492
434
 
493
435
  Ok(())
494
436
  }
@@ -509,9 +451,7 @@ impl EventLoop {
509
451
  guard.push_back(Box::new(handle));
510
452
  }
511
453
  self.counter_ready.fetch_add(1, atomic::Ordering::Release);
512
- if self.idle.load(atomic::Ordering::Acquire) {
513
- self.wake();
514
- }
454
+ self.wake();
515
455
 
516
456
  Ok(())
517
457
  }
@@ -532,9 +472,7 @@ impl EventLoop {
532
472
  guard.push_back(Box::new(handle));
533
473
  }
534
474
  self.counter_ready.fetch_add(1, atomic::Ordering::Release);
535
- if self.idle.load(atomic::Ordering::Acquire) {
536
- self.wake();
537
- }
475
+ self.wake();
538
476
 
539
477
  Ok(())
540
478
  }
@@ -559,9 +497,7 @@ impl EventLoop {
559
497
  .map_err(|_| anyhow::anyhow!("lock acquisition failed"))?;
560
498
  guard.push(timer);
561
499
  }
562
- if self.idle.load(atomic::Ordering::Acquire) {
563
- self.wake();
564
- }
500
+ self.wake();
565
501
 
566
502
  Ok(())
567
503
  }
@@ -592,9 +528,7 @@ impl EventLoop {
592
528
  .map_err(|_| anyhow::anyhow!("lock acquisition failed"))?;
593
529
  guard.push(timer);
594
530
  }
595
- if self.idle.load(atomic::Ordering::Acquire) {
596
- self.wake();
597
- }
531
+ self.wake();
598
532
 
599
533
  Ok(())
600
534
  }
@@ -625,9 +559,7 @@ impl EventLoop {
625
559
  .map_err(|_| anyhow::anyhow!("lock acquisition failed"))?;
626
560
  guard.push(timer);
627
561
  }
628
- if self.idle.load(atomic::Ordering::Acquire) {
629
- self.wake();
630
- }
562
+ self.wake();
631
563
 
632
564
  Ok(())
633
565
  }
@@ -660,9 +592,7 @@ impl EventLoop {
660
592
  self.counter_ready.fetch_add(1, atomic::Ordering::Release);
661
593
  }
662
594
  }
663
- if self.idle.load(atomic::Ordering::Acquire) {
664
- self.wake();
665
- }
595
+ self.wake();
666
596
 
667
597
  Ok(())
668
598
  }
@@ -672,17 +602,19 @@ impl EventLoop {
672
602
  impl EventLoop {
673
603
  #[new]
674
604
  fn new(py: Python) -> PyResult<Self> {
605
+ let poll = Poll::new()?;
606
+ let waker = Waker::new(poll.registry(), Token(0))?;
607
+
675
608
  Ok(Self {
676
609
  idle: atomic::AtomicBool::new(false),
677
- io: Mutex::new(Poll::new()?),
610
+ io: Mutex::new(poll),
611
+ waker: Arc::new(waker),
678
612
  handles_io: papaya::HashMap::with_capacity(128),
679
613
  handles_ready: Mutex::new(VecDeque::with_capacity(128)),
680
614
  handles_sched: Mutex::new(BinaryHeap::with_capacity(32)),
681
615
  epoch: Instant::now(),
682
616
  counter_ready: atomic::AtomicUsize::new(0),
683
617
  ssock: RwLock::new(None),
684
- wsock: Mutex::new(None),
685
- wsock_fd: atomic::AtomicI32::new(-1),
686
618
  closed: atomic::AtomicBool::new(false),
687
619
  exc_handler: RwLock::new(py.None()),
688
620
  exception_handler: RwLock::new(py.None()),
@@ -935,10 +867,7 @@ impl EventLoop {
935
867
  guard.push_back(bhandle);
936
868
  }
937
869
  self.counter_ready.fetch_add(1, atomic::Ordering::Release);
938
- // wake when necessary
939
- if self.idle.load(atomic::Ordering::Acquire) {
940
- self.wake();
941
- }
870
+ self.wake();
942
871
 
943
872
  handle
944
873
  }
@@ -1195,7 +1124,12 @@ impl EventLoop {
1195
1124
  }
1196
1125
 
1197
1126
  fn _run(&self, py: Python) -> PyResult<()> {
1198
- let mut state = self.run_pre()?;
1127
+ let mut state = EventLoopRunState {
1128
+ buf: vec![0; 4096].into_boxed_slice(),
1129
+ events: event::Events::with_capacity(128),
1130
+ read_buf: vec![0; 262_144].into_boxed_slice(),
1131
+ tick_last: 0,
1132
+ };
1199
1133
 
1200
1134
  loop {
1201
1135
  if self.stopping.load(atomic::Ordering::Acquire) {
@@ -1208,12 +1142,10 @@ impl EventLoop {
1208
1142
  }
1209
1143
  break;
1210
1144
  }
1211
- self.run_post(&mut state);
1212
1145
  return Err(err.into());
1213
1146
  }
1214
1147
  }
1215
1148
 
1216
- self.run_post(&mut state);
1217
1149
  Ok(())
1218
1150
  }
1219
1151
  }
@@ -605,10 +605,10 @@ impl TCPReadHandle {
605
605
  #[inline]
606
606
  fn recv_eof(&self, py: Python, event_loop: &EventLoop, transport: &TCPTransport) -> bool {
607
607
  event_loop.tcp_stream_rem(self.fd, Interest::READABLE);
608
- if let Ok(pyr) = transport.proto.call_method0(py, pyo3::intern!(py, "eof_received")) {
609
- if let Ok(true) = pyr.is_truthy(py) {
610
- return false;
611
- }
608
+ if let Ok(pyr) = transport.proto.call_method0(py, pyo3::intern!(py, "eof_received"))
609
+ && let Ok(true) = pyr.is_truthy(py)
610
+ {
611
+ return false;
612
612
  }
613
613
  transport.close_from_read_handle(py, event_loop)
614
614
  }
@@ -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