ev3dev_lang_rust/wait.rs
1//! Utility functions for cpu efficient `wait` commands.
2//! Uses the `libc::epoll_wait` that only works on linux systems.
3
4#[cfg(target_os = "linux")]
5use libc;
6use std::os::unix::io::RawFd;
7use std::time::{Duration, Instant};
8
9/// Wait for until a condition `cond` is `true` or the `timeout` is reached.
10/// If the `timeout` is `None` it will wait an infinite time.
11/// The condition is checked when the `file` has changed.
12///
13/// # Arguments
14/// * `file` - Listen to changes in this file
15/// * `cond` - Condition that should become true
16/// * `timeout` - Maximal timeout to wait for the condition or file changes
17///
18/// # Example
19/// ```
20/// use std::fs::File;
21/// use std::os::unix::io::AsRawFd;
22/// use std::time::Duration;
23///
24/// use ev3dev_lang_rust::wait;
25///
26/// if let Ok(file) = File::open("...") {
27/// let cond = || {
28/// // ...
29/// true
30/// };
31/// let timeout = Duration::from_millis(2000);
32///
33/// wait::wait(file.as_raw_fd(), cond, Some(timeout));
34/// }
35/// ```
36pub fn wait<F>(fd: RawFd, cond: F, timeout: Option<Duration>) -> bool
37where
38 F: Fn() -> bool,
39{
40 if cond() {
41 return true;
42 }
43
44 let start = Instant::now();
45
46 let mut t = timeout;
47
48 loop {
49 let wait_timeout = match t {
50 Some(duration) => duration.as_millis() as i32,
51 None => -1,
52 };
53 wait_file_changes(fd, wait_timeout);
54
55 if let Some(duration) = timeout {
56 let elapsed = start.elapsed();
57 if elapsed >= duration {
58 return false;
59 }
60 t = Some(duration - elapsed);
61 }
62
63 if cond() {
64 return true;
65 }
66 }
67}
68
69/// Wrapper for `libc::epoll_wait`
70#[cfg(target_os = "linux")]
71fn wait_file_changes(fd: RawFd, timeout: i32) -> bool {
72 let epfd = unsafe { libc::epoll_create(1) as i32 };
73 let mut ep_event = libc::epoll_event {
74 events: 0,
75 u64: fd as u64,
76 };
77
78 let result = unsafe {
79 libc::epoll_ctl(
80 epfd,
81 libc::EPOLL_CTL_ADD,
82 fd,
83 &mut ep_event as *mut libc::epoll_event,
84 ) as i32
85 };
86
87 if result == -1 {
88 // cannot register fd as epoll fd
89 // just wait for 100ms
90 std::thread::sleep(Duration::from_millis(100));
91 return false;
92 }
93
94 let mut buf: [libc::epoll_event; 1] = [ep_event];
95
96 // number of file descriptors ready for the requested I/O operation
97 let result =
98 unsafe { libc::epoll_wait(epfd, buf.as_mut_ptr(), buf.len() as i32, timeout) as i32 };
99
100 let err = unsafe { libc::close(epfd) as i32 };
101 if err == -1 {
102 // epfd cannot be closed, at least we tried
103 }
104
105 result > 0
106}
107
108/// Stub method for non linux os's
109#[cfg(not(target_os = "linux"))]
110fn wait_file_changes(_fd: RawFd, _timeout: i32) -> bool {
111 std::thread::sleep(Duration::from_millis(100));
112 false
113}