Skip to main content

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}