Skip to content

Commit f4874f2

Browse files
Add a check that a single Waker is active per Poll instance (#1329)
1 parent 95393bc commit f4874f2

File tree

7 files changed

+76
-7
lines changed

7 files changed

+76
-7
lines changed

src/poll.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,15 @@ impl Registry {
613613
.try_clone()
614614
.map(|selector| Registry { selector })
615615
}
616+
617+
/// Internal check to ensure only a single `Waker` is active per [`Poll`]
618+
/// instance.
619+
#[cfg(debug_assertions)]
620+
pub(crate) fn register_waker(&self) {
621+
if self.selector.register_waker() {
622+
panic!("Only a single `Waker` can be active per `Poll` instance");
623+
}
624+
}
616625
}
617626

618627
impl fmt::Debug for Registry {

src/sys/shell/selector.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ impl Selector {
1818
pub fn select(&self, _: &mut Events, _: Option<Duration>) -> io::Result<()> {
1919
os_required!();
2020
}
21+
22+
#[cfg(debug_assertions)]
23+
pub fn register_waker(&self) -> bool {
24+
os_required!();
25+
}
2126
}
2227

2328
#[cfg(unix)]

src/sys/unix/selector/epoll.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use libc::{EPOLLET, EPOLLIN, EPOLLOUT, EPOLLRDHUP};
44
use log::error;
55
use std::os::unix::io::{AsRawFd, RawFd};
66
#[cfg(debug_assertions)]
7-
use std::sync::atomic::{AtomicUsize, Ordering};
7+
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
88
use std::time::Duration;
99
use std::{cmp, i32, io, ptr};
1010

@@ -17,6 +17,8 @@ pub struct Selector {
1717
#[cfg(debug_assertions)]
1818
id: usize,
1919
ep: RawFd,
20+
#[cfg(debug_assertions)]
21+
has_waker: AtomicBool,
2022
}
2123

2224
impl Selector {
@@ -33,6 +35,8 @@ impl Selector {
3335
#[cfg(debug_assertions)]
3436
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
3537
ep,
38+
#[cfg(debug_assertions)]
39+
has_waker: AtomicBool::new(false),
3640
})
3741
}
3842

@@ -42,6 +46,8 @@ impl Selector {
4246
#[cfg(debug_assertions)]
4347
id: self.id,
4448
ep,
49+
#[cfg(debug_assertions)]
50+
has_waker: AtomicBool::new(self.has_waker.load(Ordering::Acquire)),
4551
})
4652
}
4753

@@ -93,6 +99,11 @@ impl Selector {
9399
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
94100
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_DEL, fd, ptr::null_mut())).map(|_| ())
95101
}
102+
103+
#[cfg(debug_assertions)]
104+
pub fn register_waker(&self) -> bool {
105+
self.has_waker.swap(true, Ordering::AcqRel)
106+
}
96107
}
97108

98109
cfg_net! {

src/sys/unix/selector/kqueue.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::mem::MaybeUninit;
44
use std::ops::{Deref, DerefMut};
55
use std::os::unix::io::{AsRawFd, RawFd};
66
#[cfg(debug_assertions)]
7-
use std::sync::atomic::{AtomicUsize, Ordering};
7+
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
88
use std::time::Duration;
99
use std::{cmp, io, ptr, slice};
1010

@@ -69,6 +69,8 @@ pub struct Selector {
6969
#[cfg(debug_assertions)]
7070
id: usize,
7171
kq: RawFd,
72+
#[cfg(debug_assertions)]
73+
has_waker: AtomicBool,
7274
}
7375

7476
impl Selector {
@@ -79,6 +81,8 @@ impl Selector {
7981
#[cfg(debug_assertions)]
8082
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
8183
kq,
84+
#[cfg(debug_assertions)]
85+
has_waker: AtomicBool::new(false),
8286
})
8387
}
8488

@@ -88,6 +92,8 @@ impl Selector {
8892
#[cfg(debug_assertions)]
8993
id: self.id,
9094
kq,
95+
#[cfg(debug_assertions)]
96+
has_waker: AtomicBool::new(self.has_waker.load(Ordering::Acquire)),
9197
})
9298
}
9399

@@ -208,6 +214,11 @@ impl Selector {
208214
kevent_register(self.kq, &mut changes, &[libc::ENOENT as Data])
209215
}
210216

217+
#[cfg(debug_assertions)]
218+
pub fn register_waker(&self) -> bool {
219+
self.has_waker.swap(true, Ordering::AcqRel)
220+
}
221+
211222
// Used by `Waker`.
212223
#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
213224
pub fn setup_waker(&self, token: Token) -> io::Result<()> {

src/sys/windows/selector.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ cfg_net! {
1212

1313
use miow::iocp::{CompletionPort, CompletionStatus};
1414
use std::collections::VecDeque;
15+
use std::io;
1516
use std::marker::PhantomPinned;
1617
use std::os::windows::io::RawSocket;
1718
use std::pin::Pin;
@@ -20,7 +21,6 @@ use std::sync::atomic::AtomicUsize;
2021
use std::sync::atomic::{AtomicBool, Ordering};
2122
use std::sync::{Arc, Mutex};
2223
use std::time::Duration;
23-
use std::io;
2424
use winapi::shared::ntdef::NT_SUCCESS;
2525
use winapi::shared::ntdef::{HANDLE, PVOID};
2626
use winapi::shared::ntstatus::STATUS_CANCELLED;
@@ -327,8 +327,9 @@ static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
327327
pub struct Selector {
328328
#[cfg(debug_assertions)]
329329
id: usize,
330-
331330
pub(super) inner: Arc<SelectorInner>,
331+
#[cfg(debug_assertions)]
332+
has_waker: AtomicBool,
332333
}
333334

334335
impl Selector {
@@ -340,6 +341,8 @@ impl Selector {
340341
#[cfg(debug_assertions)]
341342
id,
342343
inner: Arc::new(inner),
344+
#[cfg(debug_assertions)]
345+
has_waker: AtomicBool::new(false),
343346
}
344347
})
345348
}
@@ -349,6 +352,8 @@ impl Selector {
349352
#[cfg(debug_assertions)]
350353
id: self.id,
351354
inner: Arc::clone(&self.inner),
355+
#[cfg(debug_assertions)]
356+
has_waker: AtomicBool::new(self.has_waker.load(Ordering::Acquire)),
352357
})
353358
}
354359

@@ -360,6 +365,11 @@ impl Selector {
360365
self.inner.select(events, timeout)
361366
}
362367

368+
#[cfg(debug_assertions)]
369+
pub fn register_waker(&self) -> bool {
370+
self.has_waker.swap(true, Ordering::AcqRel)
371+
}
372+
363373
pub(super) fn clone_port(&self) -> Arc<CompletionPort> {
364374
self.inner.cp.clone()
365375
}
@@ -499,7 +509,7 @@ impl SelectorInner {
499509
} else if iocp_event.token() % 2 == 1 {
500510
// Handle is a named pipe. This could be extended to be any non-AFD event.
501511
let callback = (*(iocp_event.overlapped() as *mut super::Overlapped)).callback;
502-
512+
503513
let len = events.len();
504514
callback(iocp_event.entry(), Some(events));
505515
n += events.len() - len;
@@ -701,7 +711,7 @@ impl Drop for SelectorInner {
701711
let callback = unsafe {
702712
(*(iocp_event.overlapped() as *mut super::Overlapped)).callback
703713
};
704-
714+
705715
callback(iocp_event.entry(), None);
706716
} else {
707717
// drain sock state to release memory of Arc reference

src/waker.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::io;
1616
/// `Waker` events are only guaranteed to be delivered while the `Waker` value
1717
/// is alive.
1818
///
19-
/// Only a single `Waker` should active per [`Poll`], if multiple threads need
19+
/// Only a single `Waker` can be active per [`Poll`], if multiple threads need
2020
/// access to the `Waker` it can be shared via for example an `Arc`. What
2121
/// happens if multiple `Waker`s are registered with the same `Poll` is
2222
/// undefined.
@@ -81,6 +81,8 @@ pub struct Waker {
8181
impl Waker {
8282
/// Create a new `Waker`.
8383
pub fn new(registry: &Registry, token: Token) -> io::Result<Waker> {
84+
#[cfg(debug_assertions)]
85+
registry.register_waker();
8486
sys::Waker::new(poll::selector(&registry), token).map(|inner| Waker { inner })
8587
}
8688

tests/waker.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,27 @@ fn waker_multiple_wakeups_different_thread() {
106106
handle2.join().unwrap();
107107
}
108108

109+
#[test]
110+
#[cfg_attr(
111+
not(debug_assertions),
112+
ignore = "only works with debug_assertions enabled"
113+
)]
114+
#[should_panic = "Only a single `Waker` can be active per `Poll` instance"]
115+
fn using_multiple_wakers_panics() {
116+
init();
117+
118+
let poll = Poll::new().expect("unable to create new Poll instance");
119+
let token1 = Token(10);
120+
let token2 = Token(11);
121+
122+
let waker1 = Waker::new(poll.registry(), token1).expect("unable to first waker");
123+
// This should panic.
124+
let waker2 = Waker::new(poll.registry(), token2).unwrap();
125+
126+
drop(waker1);
127+
drop(waker2);
128+
}
129+
109130
fn expect_waker_event(poll: &mut Poll, events: &mut Events, token: Token) {
110131
poll.poll(events, Some(Duration::from_millis(100))).unwrap();
111132
assert!(!events.is_empty());

0 commit comments

Comments
 (0)