Skip to content

Commit a8fcbb5

Browse files
authored
Add an example and testcases for the event::port API. (#1363)
Add an example of how to use `event::port` on Solarish platforms, and add a test as well.
1 parent 6cc0166 commit a8fcbb5

3 files changed

Lines changed: 238 additions & 0 deletions

File tree

examples/port.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//! A simple example of the Solarish `port` API.
2+
//!
3+
//! This creates a fifo and then monitors it for data. To use it,
4+
//! run the example, note the path it prints out, and then in
5+
//! another window, run `cat <path>` and type some text.
6+
7+
#[cfg(all(solarish, feature = "event", feature = "fs"))]
8+
fn main() -> std::io::Result<()> {
9+
use rustix::buffer::spare_capacity;
10+
use rustix::event::port;
11+
use rustix::fd::AsRawFd;
12+
use rustix::fs;
13+
use std::ptr::null_mut;
14+
15+
let port = port::create()?;
16+
let mut out = Vec::with_capacity(10);
17+
18+
let tmpdir = tempfile::tempdir()?;
19+
let fifo_path = tmpdir.path().join("fifo");
20+
fs::mknodat(
21+
fs::CWD,
22+
&fifo_path,
23+
fs::FileType::Fifo,
24+
fs::Mode::RUSR | fs::Mode::WUSR,
25+
0,
26+
)?;
27+
28+
eprintln!("Listening for data on fifo {}", fifo_path.display());
29+
30+
let fifo = fs::openat(fs::CWD, &fifo_path, fs::OFlags::RDONLY, fs::Mode::empty())?;
31+
32+
loop {
33+
// Associate `some_fd` with the port.
34+
unsafe {
35+
port::associate_fd(&port, fifo.as_raw_fd(), port::PollFlags::IN, null_mut())?;
36+
}
37+
38+
port::getn(&port, spare_capacity(&mut out), 1, None)?;
39+
40+
for event in out.drain(..) {
41+
dbg!(event.events(), event.object(), event.userdata());
42+
43+
let mut buf = [0_u8; 32];
44+
loop {
45+
match rustix::io::read(&fifo, &mut buf) {
46+
Ok(0) => return Ok(()),
47+
Ok(n) => {
48+
dbg!(&buf[..n]);
49+
break;
50+
}
51+
Err(rustix::io::Errno::INTR) => continue,
52+
Err(err) => Err(err).unwrap(),
53+
}
54+
}
55+
}
56+
}
57+
}
58+
59+
#[cfg(not(all(solarish, feature = "event", feature = "fs")))]
60+
fn main() -> Result<(), &'static str> {
61+
Err("This example requires --features=event,fs and is only supported on Solarish.")
62+
}

tests/event/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ mod epoll_timeout;
1414
#[cfg(not(target_os = "wasi"))]
1515
mod eventfd;
1616
mod poll;
17+
#[cfg(solarish)]
18+
mod port;
1719
#[cfg(any(bsd, linux_kernel, windows, target_os = "wasi"))]
1820
mod select;
1921

tests/event/port.rs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
//! We assume that `port_get` etc. don't mutate the timestamp.
2+
//!
3+
//! illumos and Solaris document that the timestamp is `const`, but it's
4+
//! `mut` in the Rust libc bindings. Test that it isn't actually mutated
5+
//! in practice.
6+
7+
// Test that the timeout isn't mutated on a timeout.
8+
#[test]
9+
fn test_port_timeout_assumption() {
10+
unsafe {
11+
use rustix::fd::AsRawFd;
12+
use std::ffi::c_void;
13+
14+
let port = libc::port_create();
15+
assert_ne!(port, -1);
16+
17+
let file = std::fs::File::open("Cargo.toml").unwrap();
18+
let fd = file.as_raw_fd();
19+
20+
let r = libc::port_associate(
21+
port,
22+
libc::PORT_SOURCE_FD,
23+
fd as _,
24+
libc::POLLERR.into(),
25+
7 as *mut c_void,
26+
);
27+
assert_ne!(r, -1);
28+
29+
let mut event = std::mem::zeroed::<libc::port_event>();
30+
let orig_timeout = libc::timespec {
31+
tv_sec: 0,
32+
tv_nsec: 500_000_000,
33+
};
34+
let mut timeout = orig_timeout.clone();
35+
libc_errno::set_errno(libc_errno::Errno(0));
36+
let r = libc::port_get(port, &mut event, &mut timeout);
37+
assert_eq!(libc_errno::errno().0, libc::ETIME);
38+
assert_eq!(r, -1);
39+
40+
assert_eq!(timeout, orig_timeout);
41+
}
42+
}
43+
44+
// Test that the timeout isn't mutated on an immediate wake.
45+
#[test]
46+
fn test_port_event_assumption() {
47+
unsafe {
48+
use rustix::fd::AsRawFd;
49+
use std::ffi::c_void;
50+
51+
let port = libc::port_create();
52+
assert_ne!(port, -1);
53+
54+
let file = std::fs::File::open("Cargo.toml").unwrap();
55+
let fd = file.as_raw_fd();
56+
57+
let r = libc::port_associate(
58+
port,
59+
libc::PORT_SOURCE_FD,
60+
fd as _,
61+
libc::POLLIN.into(),
62+
7 as *mut c_void,
63+
);
64+
assert_ne!(r, -1);
65+
66+
let mut event = std::mem::zeroed::<libc::port_event>();
67+
let orig_timeout = libc::timespec {
68+
tv_sec: 1,
69+
tv_nsec: 5678,
70+
};
71+
let mut timeout = orig_timeout.clone();
72+
let r = libc::port_get(port, &mut event, &mut timeout);
73+
assert_ne!(r, -1);
74+
75+
assert_eq!(timeout, orig_timeout);
76+
assert_eq!(event.portev_user, 7 as *mut c_void);
77+
}
78+
}
79+
80+
// Test that the timeout isn't mutated when data arrives midway
81+
// through a timeout.
82+
#[cfg(feature = "fs")]
83+
#[test]
84+
fn test_port_delay_assumption() {
85+
use rustix::fs;
86+
87+
let tmpdir = tempfile::tempdir().unwrap();
88+
let fifo_path = tmpdir.path().join("fifo");
89+
fs::mknodat(
90+
fs::CWD,
91+
&fifo_path,
92+
fs::FileType::Fifo,
93+
fs::Mode::RUSR
94+
| fs::Mode::WUSR
95+
| fs::Mode::RGRP
96+
| fs::Mode::WGRP
97+
| fs::Mode::ROTH
98+
| fs::Mode::WOTH,
99+
0,
100+
)
101+
.unwrap();
102+
103+
let fifo_path_clone = fifo_path.clone();
104+
let _mutater = std::thread::Builder::new()
105+
.name("mutater".to_string())
106+
.spawn(|| {
107+
let fifo = fs::openat(
108+
fs::CWD,
109+
fifo_path_clone,
110+
fs::OFlags::WRONLY,
111+
fs::Mode::empty(),
112+
)
113+
.unwrap();
114+
115+
for i in 0..10 {
116+
let buf = [b'A' + (i % 26)];
117+
match rustix::io::write(&fifo, &buf) {
118+
Ok(1) => {}
119+
Ok(n) => panic!("unexpected write of length {}", n),
120+
Err(rustix::io::Errno::PIPE) => return,
121+
Err(err) => Err(err).unwrap(),
122+
}
123+
std::thread::sleep(std::time::Duration::new(0, 4_000_000));
124+
}
125+
panic!("Loop iterated too many times without completing!");
126+
})
127+
.unwrap();
128+
129+
let fifo = fs::openat(fs::CWD, &fifo_path, fs::OFlags::RDONLY, fs::Mode::empty()).unwrap();
130+
131+
unsafe {
132+
use rustix::fd::AsRawFd;
133+
use std::ffi::c_void;
134+
135+
let port = libc::port_create();
136+
assert_ne!(port, -1);
137+
138+
for i in 0..5 {
139+
let r = libc::port_associate(
140+
port,
141+
libc::PORT_SOURCE_FD,
142+
fifo.as_raw_fd() as _,
143+
libc::POLLIN.into(),
144+
(9 + i) as *mut c_void,
145+
);
146+
assert_ne!(r, -1);
147+
148+
let mut event = std::mem::zeroed::<libc::port_event>();
149+
let orig_timeout = libc::timespec {
150+
tv_sec: 5,
151+
tv_nsec: 5678,
152+
};
153+
let mut timeout = orig_timeout.clone();
154+
let r = libc::port_get(port, &mut event, &mut timeout);
155+
assert_ne!(r, -1, "port_get: {:?}", std::io::Error::last_os_error());
156+
157+
assert_eq!(timeout, orig_timeout);
158+
assert_eq!(event.portev_user, (9 + i) as *mut c_void);
159+
160+
let mut buf = [0_u8; 1];
161+
loop {
162+
match rustix::io::read(&fifo, &mut buf) {
163+
Ok(1) => {
164+
assert_eq!(buf[0], b'A' + i);
165+
break;
166+
}
167+
Ok(n) => panic!("unexpected read of length {}", n),
168+
Err(rustix::io::Errno::INTR) => continue,
169+
Err(err) => Err(err).unwrap(),
170+
}
171+
}
172+
}
173+
}
174+
}

0 commit comments

Comments
 (0)