Skip to content

Commit d468cc9

Browse files
authored
Port to rustix from nix (Smithay#131)
* Begin porting to rustix * Cut down nix features Signed-off-by: John Nunley <dev@notgull.net> * Make signals an optional feature Signed-off-by: John Nunley <dev@notgull.net> * Alphabetize dependencies Signed-off-by: John Nunley <dev@notgull.net> * Fix some test errors Signed-off-by: John Nunley <dev@notgull.net> * Fix some CI errors Signed-off-by: John Nunley <dev@notgull.net> * Don't drop sock2 by accident Signed-off-by: John Nunley <dev@notgull.net> * Bump MSRV to 1.63 Signed-off-by: John Nunley <dev@notgull.net> * Change CI to use 1.63 Signed-off-by: John Nunley <dev@notgull.net> * Fix macos Signed-off-by: John Nunley <dev@notgull.net> --------- Signed-off-by: John Nunley <dev@notgull.net>
1 parent d6bba12 commit d468cc9

File tree

11 files changed

+80
-97
lines changed

11 files changed

+80
-97
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ jobs:
8686
strategy:
8787
fail-fast: false
8888
matrix:
89-
rust: ['1.56.0', 'stable', 'beta']
89+
rust: ['1.63.0', 'stable', 'beta']
9090

9191
runs-on: 'ubuntu-latest'
9292

@@ -126,7 +126,7 @@ jobs:
126126

127127
- name: Downgrade log
128128
uses: actions-rs/cargo@v1
129-
if: ${{ matrix.rust == '1.56.0' }}
129+
if: ${{ matrix.rust == '1.63.0' }}
130130
with:
131131
command: update
132132
args: --package log --precise 0.4.16
@@ -137,6 +137,12 @@ jobs:
137137
command: test
138138
args: --features "block_on executor"
139139

140+
- name: Run tests with signals
141+
uses: actions-rs/cargo@v1
142+
with:
143+
command: test
144+
args: --features "block_on executor signals"
145+
140146
- name: Run book tests
141147
uses: actions-rs/cargo@v1
142148
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## Unreleased
44

5+
#### Breaking changes
6+
7+
- Bump MSRV to 1.63
8+
- Make signals an optional feature under the `signals` features.
9+
- Replace the `nix` crate with standard library I/O errors and the `rustix`
10+
crate.
11+
512
## 0.11.0 -- 2023-06-05
613

714
#### Bugfixes

Cargo.toml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ keywords = [ "events", "loop", "callback", "eventloop", "unix" ]
1010
autotests = false
1111
edition = "2018"
1212
readme = "README.md"
13-
rust-version = "1.56.0"
13+
rust-version = "1.63.0"
1414

1515
[workspace]
1616
members = [ "doc" ]
@@ -19,24 +19,27 @@ members = [ "doc" ]
1919
codecov = { repository = "Smithay/calloop" }
2020

2121
[dependencies]
22+
async-task = { version = "4.4.0", optional = true }
2223
bitflags = "1.2"
24+
futures-io = { version = "0.3.5", optional = true }
2325
io-lifetimes = "1.0.3"
2426
log = "0.4"
25-
nix = { version = "0.26", default-features = false, features = ["event", "fs", "signal", "socket", "time"] }
26-
async-task = { version = "4.4.0", optional = true }
27-
futures-io = { version = "0.3.5", optional = true }
28-
thiserror = "1.0"
27+
nix = { version = "0.26", default-features = false, features = ["signal"], optional = true }
2928
pin-utils = { version = "0.1.0", optional = true }
30-
slab = "0.4.8"
3129
polling = "2.6.0"
30+
rustix = { version = "0.38", default-features = false, features = ["event", "fs", "pipe", "std"] }
31+
slab = "0.4.8"
32+
thiserror = "1.0"
3233

3334
[dev-dependencies]
3435
futures = "0.3.5"
36+
rustix = { version = "0.38", default-features = false, features = ["net"] }
3537

3638
[features]
3739
block_on = ["pin-utils"]
3840
executor = ["async-task"]
3941
nightly_coverage = []
42+
signals = ["nix"]
4043

4144
[package.metadata.docs.rs]
4245
features = ["block_on", "executor"]

src/error.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,6 @@ pub enum Error {
4141
OtherError(#[from] Box<dyn std::error::Error + Sync + Send>),
4242
}
4343

44-
impl From<nix::errno::Errno> for Error {
45-
/// Converts a [`nix::Error`] into a wrapped version of the equivalent
46-
/// [`std::io::Error`].
47-
#[cfg_attr(feature = "nightly_coverage", no_coverage)]
48-
fn from(err: nix::errno::Errno) -> Self {
49-
Into::<std::io::Error>::into(err).into()
50-
}
51-
}
52-
5344
impl From<Error> for std::io::Error {
5445
/// Converts Calloop's error type into a [`std::io::Error`].
5546
fn from(err: Error) -> Self {

src/io.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::rc::Rc;
1313
use std::task::{Context, Poll as TaskPoll, Waker};
1414

1515
use io_lifetimes::{AsFd, BorrowedFd};
16-
use nix::fcntl::{fcntl, FcntlArg, OFlag};
16+
use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags};
1717

1818
#[cfg(feature = "futures-io")]
1919
use futures_io::{AsyncRead, AsyncWrite, IoSlice, IoSliceMut};
@@ -37,7 +37,7 @@ pub struct Async<'l, F: AsFd> {
3737
fd: Option<F>,
3838
dispatcher: Rc<RefCell<IoDispatcher>>,
3939
inner: Rc<dyn IoLoopInner + 'l>,
40-
old_flags: OFlag,
40+
old_flags: OFlags,
4141
}
4242

4343
impl<'l, F: AsFd + std::fmt::Debug> std::fmt::Debug for Async<'l, F> {
@@ -49,11 +49,9 @@ impl<'l, F: AsFd + std::fmt::Debug> std::fmt::Debug for Async<'l, F> {
4949

5050
impl<'l, F: AsFd> Async<'l, F> {
5151
pub(crate) fn new<Data>(inner: Rc<LoopInner<'l, Data>>, fd: F) -> crate::Result<Async<'l, F>> {
52-
let rawfd = fd.as_fd().as_raw_fd();
5352
// set non-blocking
54-
let old_flags = fcntl(rawfd, FcntlArg::F_GETFL)?;
55-
let old_flags = unsafe { OFlag::from_bits_unchecked(old_flags) };
56-
fcntl(rawfd, FcntlArg::F_SETFL(old_flags | OFlag::O_NONBLOCK))?;
53+
let old_flags = fcntl_getfl(&fd).map_err(std::io::Error::from)?;
54+
fcntl_setfl(&fd, old_flags | OFlags::NONBLOCK).map_err(std::io::Error::from)?;
5755
// register in the loop
5856
let dispatcher = Rc::new(RefCell::new(IoDispatcher {
5957
fd: fd.as_fd().as_raw_fd(),
@@ -158,9 +156,9 @@ impl<'l, F: AsFd> Drop for Async<'l, F> {
158156
fn drop(&mut self) {
159157
self.inner.kill(&self.dispatcher);
160158
// restore flags
161-
let _ = fcntl(
162-
self.dispatcher.borrow().fd,
163-
FcntlArg::F_SETFL(self.old_flags),
159+
let _ = fcntl_setfl(
160+
unsafe { BorrowedFd::borrow_raw(self.dispatcher.borrow().fd) },
161+
self.old_flags,
164162
);
165163
}
166164
}

src/loop_logic.rs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -695,14 +695,10 @@ mod tests {
695695

696696
#[test]
697697
fn insert_source_no_interest() {
698-
use nix::unistd::{close, pipe};
699-
use std::os::unix::io::FromRawFd;
698+
use rustix::pipe::pipe;
700699

701700
// Create a pipe to get an arbitrary fd.
702-
let (read, write) = pipe().unwrap();
703-
let read = unsafe { io_lifetimes::OwnedFd::from_raw_fd(read) };
704-
// We don't need the write end.
705-
close(write).unwrap();
701+
let (read, _write) = pipe().unwrap();
706702

707703
let source = crate::sources::generic::Generic::new(read, Interest::EMPTY, Mode::Level);
708704
let dispatcher = Dispatcher::new(source, |_, _, _| Ok(PostAction::Continue));
@@ -856,27 +852,25 @@ mod tests {
856852

857853
#[test]
858854
fn change_interests() {
859-
use nix::sys::socket::{recv, socketpair, AddressFamily, MsgFlags, SockFlag, SockType};
860-
use nix::unistd::write;
861-
use std::os::unix::io::{AsRawFd, FromRawFd};
855+
use rustix::io::write;
856+
use rustix::net::{recv, socketpair, AddressFamily, RecvFlags, SocketFlags, SocketType};
862857
let mut event_loop = EventLoop::<bool>::try_new().unwrap();
863858

864859
let (sock1, sock2) = socketpair(
865-
AddressFamily::Unix,
866-
SockType::Stream,
867-
None,
868-
SockFlag::empty(), // recv with DONTWAIT will suffice for platforms without SockFlag::SOCK_NONBLOCKING such as macOS
860+
AddressFamily::UNIX,
861+
SocketType::STREAM,
862+
SocketFlags::empty(),
863+
None, // recv with DONTWAIT will suffice for platforms without SockFlag::SOCK_NONBLOCKING such as macOS
869864
)
870865
.unwrap();
871-
let sock1 = unsafe { io_lifetimes::OwnedFd::from_raw_fd(sock1) };
872866

873867
let source = Generic::new(sock1, Interest::READ, Mode::Level);
874868
let dispatcher = Dispatcher::new(source, |_, fd, dispatched| {
875869
*dispatched = true;
876870
// read all contents available to drain the socket
877871
let mut buf = [0u8; 32];
878872
loop {
879-
match recv(fd.as_raw_fd(), &mut buf, MsgFlags::MSG_DONTWAIT) {
873+
match recv(&*fd, &mut buf, RecvFlags::DONTWAIT) {
880874
Ok(0) => break, // closed pipe, we are now inert
881875
Ok(_) => {}
882876
Err(e) => {
@@ -907,7 +901,7 @@ mod tests {
907901
assert!(!dispatched);
908902

909903
// write something, the socket becomes readable
910-
write(sock2, &[1, 2, 3]).unwrap();
904+
write(&sock2, &[1, 2, 3]).unwrap();
911905
dispatched = false;
912906
event_loop
913907
.dispatch(Duration::ZERO, &mut dispatched)

src/sources/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub mod channel;
1212
pub mod futures;
1313
pub mod generic;
1414
pub mod ping;
15-
#[cfg(target_os = "linux")]
15+
#[cfg(all(target_os = "linux", feature = "signals"))]
1616
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
1717
pub mod signals;
1818
pub mod timer;

src/sources/ping/eventfd.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,11 @@
1818
//! can then check the LSB and if it's set, we know it was a close event. This
1919
//! only works if a close event never fires more than once.
2020
21-
use std::{
22-
os::unix::io::{AsRawFd, FromRawFd},
23-
sync::Arc,
24-
};
21+
use std::sync::Arc;
2522

2623
use io_lifetimes::{AsFd, BorrowedFd, OwnedFd};
27-
use nix::sys::eventfd::{eventfd, EfdFlags};
28-
use nix::unistd::{read, write};
24+
use rustix::event::{eventfd, EventfdFlags};
25+
use rustix::io::{read, write, Errno};
2926

3027
use super::PingError;
3128
use crate::{
@@ -40,14 +37,14 @@ const INCREMENT_CLOSE: u64 = 0x1;
4037

4138
#[inline]
4239
pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
43-
let read = eventfd(0, EfdFlags::EFD_CLOEXEC | EfdFlags::EFD_NONBLOCK)?;
40+
let read = eventfd(0, EventfdFlags::CLOEXEC | EventfdFlags::NONBLOCK)?;
4441

4542
// We only have one fd for the eventfd. If the sending end closes it when
4643
// all copies are dropped, the receiving end will be closed as well. We need
4744
// to make sure the fd is not closed until all holders of it have dropped
4845
// it.
4946

50-
let fd = Arc::new(unsafe { OwnedFd::from_raw_fd(read) });
47+
let fd = Arc::new(read);
5148

5249
let ping = Ping {
5350
event: Arc::new(FlagOnDrop(Arc::clone(&fd))),
@@ -65,13 +62,13 @@ pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
6562
#[inline]
6663
fn send_ping(fd: BorrowedFd<'_>, count: u64) -> std::io::Result<()> {
6764
assert!(count > 0);
68-
match write(fd.as_raw_fd(), &count.to_ne_bytes()) {
65+
match write(fd, &count.to_ne_bytes()) {
6966
// The write succeeded, the ping will wake up the loop.
7067
Ok(_) => Ok(()),
7168

7269
// The counter hit its cap, which means previous calls to write() will
7370
// wake up the loop.
74-
Err(nix::errno::Errno::EAGAIN) => Ok(()),
71+
Err(Errno::AGAIN) => Ok(()),
7572

7673
// Anything else is a real error.
7774
Err(e) => Err(e.into()),
@@ -84,7 +81,7 @@ fn drain_ping(fd: BorrowedFd<'_>) -> std::io::Result<u64> {
8481
const NBYTES: usize = 8;
8582
let mut buf = [0u8; NBYTES];
8683

87-
match read(fd.as_raw_fd(), &mut buf) {
84+
match read(fd, &mut buf) {
8885
// Reading from an eventfd should only ever produce 8 bytes. No looping
8986
// is required.
9087
Ok(NBYTES) => Ok(u64::from_ne_bytes(buf)),

src/sources/ping/pipe.rs

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@
22
//! syscall. Sending a ping involves writing to one end of a pipe, and the other
33
//! end becoming readable is what wakes up the event loop.
44
5-
use std::{
6-
os::unix::io::{AsRawFd, FromRawFd, RawFd},
7-
sync::Arc,
8-
};
5+
use std::sync::Arc;
96

10-
use io_lifetimes::OwnedFd;
11-
use nix::fcntl::OFlag;
12-
use nix::unistd::{read, write};
7+
use io_lifetimes::{AsFd, BorrowedFd, OwnedFd};
8+
use rustix::io::{read, write, Errno};
139

1410
use super::PingError;
1511
use crate::{
@@ -18,42 +14,31 @@ use crate::{
1814

1915
#[cfg(target_os = "macos")]
2016
#[inline]
21-
fn make_ends() -> std::io::Result<(RawFd, RawFd)> {
22-
// macOS does not have pipe2, but we can emulate the behavior of pipe2 by
23-
// setting the flags after calling pipe.
24-
use nix::{
25-
fcntl::{fcntl, FcntlArg},
26-
unistd::pipe,
27-
};
17+
fn make_ends() -> std::io::Result<(OwnedFd, OwnedFd)> {
18+
use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags};
19+
use rustix::pipe::pipe;
2820

2921
let (read, write) = pipe()?;
3022

31-
let read_flags = OFlag::from_bits_truncate(fcntl(read, FcntlArg::F_GETFD)?)
32-
| OFlag::O_CLOEXEC
33-
| OFlag::O_NONBLOCK;
34-
let write_flags = OFlag::from_bits_truncate(fcntl(write, FcntlArg::F_GETFD)?)
35-
| OFlag::O_CLOEXEC
36-
| OFlag::O_NONBLOCK;
23+
let set_flags = |fd| fcntl_setfl(fd, fcntl_getfl(fd)? | OFlags::CLOEXEC | OFlags::NONBLOCK);
3724

38-
fcntl(read, FcntlArg::F_SETFL(read_flags))?;
39-
fcntl(write, FcntlArg::F_SETFL(write_flags))?;
25+
set_flags(&read)?;
26+
set_flags(&write)?;
4027

4128
Ok((read, write))
4229
}
4330

4431
#[cfg(not(target_os = "macos"))]
4532
#[inline]
46-
fn make_ends() -> std::io::Result<(RawFd, RawFd)> {
47-
Ok(nix::unistd::pipe2(OFlag::O_CLOEXEC | OFlag::O_NONBLOCK)?)
33+
fn make_ends() -> std::io::Result<(OwnedFd, OwnedFd)> {
34+
use rustix::pipe::{pipe_with, PipeFlags};
35+
Ok(pipe_with(PipeFlags::CLOEXEC | PipeFlags::NONBLOCK)?)
4836
}
4937

5038
#[inline]
5139
pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
5240
let (read, write) = make_ends()?;
5341

54-
let read = unsafe { OwnedFd::from_raw_fd(read) };
55-
let write = unsafe { OwnedFd::from_raw_fd(write) };
56-
5742
let source = PingSource {
5843
pipe: Generic::new(read, Interest::READ, Mode::Level),
5944
};
@@ -66,7 +51,7 @@ pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
6651
// Helper functions for the event source IO.
6752

6853
#[inline]
69-
fn send_ping(fd: RawFd) -> std::io::Result<()> {
54+
fn send_ping(fd: BorrowedFd<'_>) -> std::io::Result<()> {
7055
write(fd, &[0u8])?;
7156
Ok(())
7257
}
@@ -100,7 +85,7 @@ impl EventSource for PingSource {
10085
let mut action = PostAction::Continue;
10186

10287
loop {
103-
match read(fd.as_raw_fd(), &mut buf) {
88+
match read(&fd, &mut buf) {
10489
Ok(0) => {
10590
// The other end of the pipe was closed, mark ourselves
10691
// for removal.
@@ -112,7 +97,7 @@ impl EventSource for PingSource {
11297
Ok(_) => read_something = true,
11398

11499
// Nothing more to read.
115-
Err(nix::errno::Errno::EAGAIN) => break,
100+
Err(Errno::AGAIN) => break,
116101

117102
// Propagate error.
118103
Err(e) => return Err(e.into()),
@@ -154,7 +139,7 @@ pub struct Ping {
154139
impl Ping {
155140
/// Send a ping to the `PingSource`
156141
pub fn ping(&self) {
157-
if let Err(e) = send_ping(self.pipe.as_raw_fd()) {
142+
if let Err(e) = send_ping(self.pipe.as_fd()) {
158143
log::warn!("[calloop] Failed to write a ping: {:?}", e);
159144
}
160145
}

0 commit comments

Comments
 (0)