Skip to content

Commit ecaf971

Browse files
committed
Use io-lifetimes types in API, instead of RawFd
Operating on `RawFd`s is unsafe, so this should be changed. Though some uses will be cleaner once `nix` is updated for IO safety (which seems to be ongoing, but complicated). The `Rc<RefCell<IoDispatcher>>` in `Async` is somewhat awkward here. For now `Dispatcher` still contains a `RawFd`. The only alternative I can think of with how the API works is to use `Rc<F>` and have `into_inner` call `Rc::try_unwrap(self.fd).unwrap()`.
1 parent 80819fa commit ecaf971

File tree

11 files changed

+144
-119
lines changed

11 files changed

+144
-119
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ members = [ "doc" ]
1818
codecov = { repository = "Smithay/calloop" }
1919

2020
[dependencies]
21+
io-lifetimes = "1.0.3"
2122
log = "0.4"
2223
nix = { version = "0.24", default-features = false, features = ["event", "fs", "signal", "socket", "time"] }
2324
futures-util = { version = "0.3.5", optional = true, default-features = false, features = ["std"]}

src/io.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
//! [`LoopHandle::adapt_io`]: crate::LoopHandle#method.adapt_io
88
99
use std::cell::RefCell;
10-
use std::os::unix::io::{AsRawFd, RawFd};
10+
use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
1111
use std::pin::Pin;
1212
use std::rc::Rc;
1313
use std::task::{Context, Poll as TaskPoll, Waker};
1414

15+
use io_lifetimes::AsFd;
1516
use nix::fcntl::{fcntl, FcntlArg, OFlag};
1617

1718
#[cfg(feature = "futures-io")]
@@ -31,30 +32,30 @@ use crate::{
3132
/// `AsyncWrite` if the underlying type implements `Read` and/or `Write`.
3233
///
3334
/// Note that this adapter and the futures procuded from it and *not* threadsafe.
34-
pub struct Async<'l, F: AsRawFd> {
35+
pub struct Async<'l, F: AsFd> {
3536
fd: Option<F>,
3637
dispatcher: Rc<RefCell<IoDispatcher>>,
3738
inner: Rc<dyn IoLoopInner + 'l>,
3839
old_flags: OFlag,
3940
}
4041

41-
impl<'l, F: AsRawFd + std::fmt::Debug> std::fmt::Debug for Async<'l, F> {
42+
impl<'l, F: AsFd + std::fmt::Debug> std::fmt::Debug for Async<'l, F> {
4243
#[cfg_attr(coverage, no_coverage)]
4344
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4445
f.debug_struct("Async").field("fd", &self.fd).finish()
4546
}
4647
}
4748

48-
impl<'l, F: AsRawFd> Async<'l, F> {
49+
impl<'l, F: AsFd> Async<'l, F> {
4950
pub(crate) fn new<Data>(inner: Rc<LoopInner<'l, Data>>, fd: F) -> crate::Result<Async<'l, F>> {
50-
let rawfd = fd.as_raw_fd();
51+
let rawfd = fd.as_fd().as_raw_fd();
5152
// set non-blocking
5253
let old_flags = fcntl(rawfd, FcntlArg::F_GETFL)?;
5354
let old_flags = unsafe { OFlag::from_bits_unchecked(old_flags) };
5455
fcntl(rawfd, FcntlArg::F_SETFL(old_flags | OFlag::O_NONBLOCK))?;
5556
// register in the loop
5657
let dispatcher = Rc::new(RefCell::new(IoDispatcher {
57-
fd: rawfd,
58+
fd: fd.as_fd().as_raw_fd(),
5859
token: None,
5960
waker: None,
6061
is_registered: false,
@@ -114,11 +115,11 @@ impl<'l, F: AsRawFd> Async<'l, F> {
114115

115116
/// A future that resolves once the associated object becomes ready for reading
116117
#[derive(Debug)]
117-
pub struct Readable<'s, 'l, F: AsRawFd> {
118+
pub struct Readable<'s, 'l, F: AsFd> {
118119
io: &'s mut Async<'l, F>,
119120
}
120121

121-
impl<'s, 'l, F: AsRawFd> std::future::Future for Readable<'s, 'l, F> {
122+
impl<'s, 'l, F: AsFd> std::future::Future for Readable<'s, 'l, F> {
122123
type Output = ();
123124
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<()> {
124125
let io = &mut self.as_mut().io;
@@ -134,11 +135,11 @@ impl<'s, 'l, F: AsRawFd> std::future::Future for Readable<'s, 'l, F> {
134135

135136
/// A future that resolves once the associated object becomes ready for writing
136137
#[derive(Debug)]
137-
pub struct Writable<'s, 'l, F: AsRawFd> {
138+
pub struct Writable<'s, 'l, F: AsFd> {
138139
io: &'s mut Async<'l, F>,
139140
}
140141

141-
impl<'s, 'l, F: AsRawFd> std::future::Future for Writable<'s, 'l, F> {
142+
impl<'s, 'l, F: AsFd> std::future::Future for Writable<'s, 'l, F> {
142143
type Output = ();
143144
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<()> {
144145
let io = &mut self.as_mut().io;
@@ -152,7 +153,7 @@ impl<'s, 'l, F: AsRawFd> std::future::Future for Writable<'s, 'l, F> {
152153
}
153154
}
154155

155-
impl<'l, F: AsRawFd> Drop for Async<'l, F> {
156+
impl<'l, F: AsFd> Drop for Async<'l, F> {
156157
fn drop(&mut self) {
157158
self.inner.kill(&self.dispatcher);
158159
// restore flags
@@ -163,7 +164,7 @@ impl<'l, F: AsRawFd> Drop for Async<'l, F> {
163164
}
164165
}
165166

166-
impl<'l, F: AsRawFd> Unpin for Async<'l, F> {}
167+
impl<'l, F: AsFd> Unpin for Async<'l, F> {}
167168

168169
trait IoLoopInner {
169170
fn register(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()>;
@@ -175,7 +176,7 @@ impl<'l, Data> IoLoopInner for LoopInner<'l, Data> {
175176
fn register(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()> {
176177
let disp = dispatcher.borrow();
177178
self.poll.borrow_mut().register(
178-
disp.fd,
179+
unsafe { BorrowedFd::borrow_raw(disp.fd) },
179180
Interest::EMPTY,
180181
Mode::OneShot,
181182
disp.token.expect("No token for IO dispatcher"),
@@ -185,7 +186,7 @@ impl<'l, Data> IoLoopInner for LoopInner<'l, Data> {
185186
fn reregister(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()> {
186187
let disp = dispatcher.borrow();
187188
self.poll.borrow_mut().reregister(
188-
disp.fd,
189+
unsafe { BorrowedFd::borrow_raw(disp.fd) },
189190
disp.interest,
190191
Mode::OneShot,
191192
disp.token.expect("No token for IO dispatcher"),
@@ -207,7 +208,7 @@ impl<'l, Data> IoLoopInner for LoopInner<'l, Data> {
207208
}
208209

209210
struct IoDispatcher {
210-
fd: RawFd,
211+
fd: RawFd, // FIXME: `BorrowedFd`? How to statically verify it doesn't outlive file?
211212
token: Option<Token>,
212213
waker: Option<Waker>,
213214
is_registered: bool,
@@ -249,7 +250,7 @@ impl<Data> EventDispatcher<Data> for RefCell<IoDispatcher> {
249250
fn unregister(&self, poll: &mut Poll) -> crate::Result<bool> {
250251
let disp = self.borrow();
251252
if disp.is_registered {
252-
poll.unregister(disp.fd)?;
253+
poll.unregister(unsafe { BorrowedFd::borrow_raw(disp.fd) })?;
253254
}
254255
Ok(true)
255256
}
@@ -268,7 +269,7 @@ impl<Data> EventDispatcher<Data> for RefCell<IoDispatcher> {
268269

269270
#[cfg(feature = "futures-io")]
270271
#[cfg_attr(docsrs, doc(cfg(feature = "futures-io")))]
271-
impl<'l, F: AsRawFd + std::io::Read> AsyncRead for Async<'l, F> {
272+
impl<'l, F: AsFd + std::io::Read> AsyncRead for Async<'l, F> {
272273
fn poll_read(
273274
mut self: Pin<&mut Self>,
274275
cx: &mut Context<'_>,
@@ -298,7 +299,7 @@ impl<'l, F: AsRawFd + std::io::Read> AsyncRead for Async<'l, F> {
298299

299300
#[cfg(feature = "futures-io")]
300301
#[cfg_attr(docsrs, doc(cfg(feature = "futures-io")))]
301-
impl<'l, F: AsRawFd + std::io::Write> AsyncWrite for Async<'l, F> {
302+
impl<'l, F: AsFd + std::io::Write> AsyncWrite for Async<'l, F> {
302303
fn poll_write(
303304
mut self: Pin<&mut Self>,
304305
cx: &mut Context<'_>,

src/loop_logic.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::cell::{Cell, RefCell};
22
use std::fmt::Debug;
33
use std::io;
4-
use std::os::unix::io::AsRawFd;
54
use std::rc::Rc;
65
use std::sync::atomic::{AtomicBool, Ordering};
76
use std::sync::Arc;
87
use std::time::{Duration, Instant};
98

9+
use io_lifetimes::AsFd;
1010
use slotmap::SlotMap;
1111

1212
use crate::sources::{Dispatcher, EventSource, Idle, IdleDispatcher};
@@ -194,7 +194,7 @@ impl<'l, Data> LoopHandle<'l, Data> {
194194
///
195195
/// The produced futures can be polled in any executor, and notably the one provided by
196196
/// calloop.
197-
pub fn adapt_io<F: AsRawFd>(&self, fd: F) -> crate::Result<crate::io::Async<'l, F>> {
197+
pub fn adapt_io<F: AsFd>(&self, fd: F) -> crate::Result<crate::io::Async<'l, F>> {
198198
crate::io::Async::new(self.inner.clone(), fd)
199199
}
200200
}
@@ -578,9 +578,12 @@ mod tests {
578578

579579
#[test]
580580
fn insert_bad_source() {
581+
use std::os::unix::io::FromRawFd;
582+
581583
let event_loop = EventLoop::<()>::try_new().unwrap();
584+
let fd = unsafe { io_lifetimes::OwnedFd::from_raw_fd(420) };
582585
let ret = event_loop.handle().insert_source(
583-
crate::sources::generic::Generic::new(420, Interest::READ, Mode::Level),
586+
crate::sources::generic::Generic::new(fd, Interest::READ, Mode::Level),
584587
|_, _, _| Ok(PostAction::Continue),
585588
);
586589
assert!(ret.is_err());
@@ -589,9 +592,11 @@ mod tests {
589592
#[test]
590593
fn insert_source_no_interest() {
591594
use nix::unistd::{close, pipe};
595+
use std::os::unix::io::FromRawFd;
592596

593597
// Create a pipe to get an arbitrary fd.
594598
let (read, write) = pipe().unwrap();
599+
let read = unsafe { io_lifetimes::OwnedFd::from_raw_fd(read) };
595600
// We don't need the write end.
596601
close(write).unwrap();
597602

@@ -605,7 +610,6 @@ mod tests {
605610
if let Ok(token) = ret {
606611
// Unwrap the dispatcher+source and close the read end.
607612
handle.remove(token);
608-
close(dispatcher.into_source_inner().unwrap()).unwrap();
609613
} else {
610614
// Fail the test.
611615
panic!();
@@ -753,6 +757,7 @@ mod tests {
753757
fn change_interests() {
754758
use nix::sys::socket::{recv, socketpair, AddressFamily, MsgFlags, SockFlag, SockType};
755759
use nix::unistd::write;
760+
use std::os::unix::io::{AsRawFd, FromRawFd};
756761
let mut event_loop = EventLoop::<bool>::try_new().unwrap();
757762

758763
let (sock1, sock2) = socketpair(
@@ -762,14 +767,15 @@ mod tests {
762767
SockFlag::empty(), // recv with DONTWAIT will suffice for platforms without SockFlag::SOCK_NONBLOCKING such as macOS
763768
)
764769
.unwrap();
770+
let sock1 = unsafe { io_lifetimes::OwnedFd::from_raw_fd(sock1) };
765771

766772
let source = Generic::new(sock1, Interest::READ, Mode::Level);
767-
let dispatcher = Dispatcher::new(source, |_, &mut fd, dispatched| {
773+
let dispatcher = Dispatcher::new(source, |_, fd, dispatched| {
768774
*dispatched = true;
769775
// read all contents available to drain the socket
770776
let mut buf = [0u8; 32];
771777
loop {
772-
match recv(fd, &mut buf, MsgFlags::MSG_DONTWAIT) {
778+
match recv(fd.as_raw_fd(), &mut buf, MsgFlags::MSG_DONTWAIT) {
773779
Ok(0) => break, // closed pipe, we are now inert
774780
Ok(_) => {}
775781
Err(e) => {

src/sources/generic.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//! # let mut event_loop = calloop::EventLoop::<()>::try_new()
1616
//! # .expect("Failed to initialize the event loop!");
1717
//! # let handle = event_loop.handle();
18-
//! # let io_object = 0;
18+
//! # let io_object = std::io::stdin();
1919
//! handle.insert_source(
2020
//! // wrap your IO object in a Generic, here we register for read readiness
2121
//! // in level-triggering mode
@@ -36,17 +36,15 @@
3636
//! It can also help you implementing your own event sources: just have
3737
//! these `Generic<_>` as fields of your event source, and delegate the
3838
//! [`EventSource`](crate::EventSource) implementation to them.
39-
//!
40-
//! If you need to directly work with a [`RawFd`](std::os::unix::io::RawFd), rather than an
41-
//! FD-backed object, see [`Generic::from_fd`](Generic#method.from_fd).
4239
43-
use std::{marker::PhantomData, os::unix::io::AsRawFd};
40+
use io_lifetimes::AsFd;
41+
use std::marker::PhantomData;
4442

4543
use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
4644

4745
/// A generic event source wrapping a FD-backed type
4846
#[derive(Debug)]
49-
pub struct Generic<F: AsRawFd, E = std::io::Error> {
47+
pub struct Generic<F: AsFd, E = std::io::Error> {
5048
/// The wrapped FD-backed type
5149
pub file: F,
5250
/// The programmed interest
@@ -62,7 +60,7 @@ pub struct Generic<F: AsRawFd, E = std::io::Error> {
6260
_error_type: PhantomData<E>,
6361
}
6462

65-
impl<F: AsRawFd> Generic<F, std::io::Error> {
63+
impl<F: AsFd> Generic<F, std::io::Error> {
6664
/// Wrap a FD-backed type into a `Generic` event source that uses
6765
/// [`std::io::Error`] as its error type.
6866
pub fn new(file: F, interest: Interest, mode: Mode) -> Generic<F, std::io::Error> {
@@ -87,7 +85,7 @@ impl<F: AsRawFd> Generic<F, std::io::Error> {
8785
}
8886
}
8987

90-
impl<F: AsRawFd, E> Generic<F, E> {
88+
impl<F: AsFd, E> Generic<F, E> {
9189
/// Unwrap the `Generic` source to retrieve the underlying type
9290
pub fn unwrap(self) -> F {
9391
self.file
@@ -96,7 +94,7 @@ impl<F: AsRawFd, E> Generic<F, E> {
9694

9795
impl<F, E> EventSource for Generic<F, E>
9896
where
99-
F: AsRawFd,
97+
F: AsFd,
10098
E: Into<Box<dyn std::error::Error + Send + Sync>>,
10199
{
102100
type Event = Readiness;
@@ -124,7 +122,7 @@ where
124122
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
125123
let token = token_factory.token();
126124

127-
poll.register(self.file.as_raw_fd(), self.interest, self.mode, token)?;
125+
poll.register(self.file.as_fd(), self.interest, self.mode, token)?;
128126

129127
self.token = Some(token);
130128
Ok(())
@@ -137,14 +135,14 @@ where
137135
) -> crate::Result<()> {
138136
let token = token_factory.token();
139137

140-
poll.reregister(self.file.as_raw_fd(), self.interest, self.mode, token)?;
138+
poll.reregister(self.file.as_fd(), self.interest, self.mode, token)?;
141139

142140
self.token = Some(token);
143141
Ok(())
144142
}
145143

146144
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
147-
poll.unregister(self.file.as_raw_fd())?;
145+
poll.unregister(self.file.as_fd())?;
148146
self.token = None;
149147
Ok(())
150148
}

src/sources/ping.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
//! block to construct event sources whose source of event is not file descriptor, but rather an
1010
//! userspace source (like an other thread).
1111
12-
use nix::unistd::close;
13-
use std::os::unix::io::RawFd;
14-
1512
// The ping source has platform-dependent implementations provided by modules
1613
// under this one. These modules should expose:
1714
// - a make_ping() function
@@ -58,17 +55,6 @@ pub type PingSource = platform::PingSource;
5855
#[error(transparent)]
5956
pub struct PingError(Box<dyn std::error::Error + Sync + Send>);
6057

61-
#[derive(Debug)]
62-
struct CloseOnDrop(RawFd);
63-
64-
impl Drop for CloseOnDrop {
65-
fn drop(&mut self) {
66-
if let Err(e) = close(self.0) {
67-
log::warn!("[calloop] Failed to close ping fd: {:?}", e);
68-
}
69-
}
70-
}
71-
7258
#[cfg(test)]
7359
mod tests {
7460
use crate::transient::TransientSource;

0 commit comments

Comments
 (0)