makepad_platform/os/linux/
select_timer.rs

1use std::{collections::VecDeque, mem, os::raw::c_int, ptr, time::Instant};
2
3
4use self::super::libc_sys;
5
6
7#[derive(Clone, Copy)]
8pub struct SelectTimer {
9    id: u64,
10    timeout: f64,
11    repeats: bool,
12    delta_timeout: f64,
13}
14
15pub struct SelectTimers {
16    //pub signal_fds: [c_int; 2],
17    pub timers: VecDeque<SelectTimer>,
18    pub time_start: Instant,
19    pub select_time: f64,
20}
21
22impl SelectTimers {
23    pub fn new() -> Self {
24        Self {
25            timers: Default::default(),
26            time_start: Instant::now(),
27            select_time: 0.0
28        }
29    }
30    
31    pub fn select(&mut self, fd: c_int) {
32        let mut fds = mem::MaybeUninit::uninit();
33        unsafe {
34            libc_sys::FD_ZERO(fds.as_mut_ptr());
35            libc_sys::FD_SET(0, fds.as_mut_ptr());
36            libc_sys::FD_SET(fd, fds.as_mut_ptr()); 
37        }
38        //libc_sys::FD_SET(self.signal_fds[0], fds.as_mut_ptr());
39        // If there are any timers, we set the timeout for select to the `delta_timeout`
40        // of the first timer that should be fired. Otherwise, we set the timeout to
41        // None, so that select will block indefinitely.
42        let mut timeout = self.timers.front().map(|timer| 
43            libc_sys::timeval {
44                // `tv_sec` is in seconds, so take the integer part of `delta_timeout`
45                tv_sec: timer.delta_timeout.trunc() as libc_sys::time_t,
46                // `tv_usec` is in microseconds, so take the fractional part of
47                // `delta_timeout` 1000000.0.
48                tv_usec: (timer.delta_timeout.fract() * 1000_000.0) as libc_sys::time_t,
49            });
50        let _nfds = unsafe {libc_sys::select(
51            fd+1,
52            fds.as_mut_ptr(),
53            ptr::null_mut(),
54            ptr::null_mut(),
55            timeout.as_mut().map(|t| t as *mut _).unwrap_or(ptr::null_mut())
56        )};  
57    }
58    
59    pub fn time_now(&self) -> f64 {
60        let time_now = Instant::now(); //unsafe {mach_absolute_time()};
61        (time_now.duration_since(self.time_start)).as_secs_f64() 
62    }
63    
64    pub fn update_timers(&mut self, out: &mut Vec<u64>) {
65        out.clear();
66        let last_select_time = self.select_time;
67        self.select_time = self.time_now();
68        let mut select_time_used = self.select_time - last_select_time;
69        //println!("{}", self.timers.len());
70        while let Some(timer) = self.timers.front_mut() {
71            // If the amount of time that elapsed is less than `delta_timeout` for the
72            // next timer, then no more timers need to be fired.
73            //  println!("TIMER COMPARE {} {}", select_time_used, timer.delta_timeout);
74            if select_time_used < timer.delta_timeout {
75                timer.delta_timeout -= select_time_used;
76                break;
77            }
78            
79            let timer = *self.timers.front().unwrap();
80            select_time_used -= timer.delta_timeout;
81            
82            // Stop the timer to remove it from the list.
83            self.stop_timer(timer.id);
84            // If the timer is repeating, simply start it again.
85            if timer.repeats {
86                self.start_timer(timer.id, timer.timeout, timer.repeats);
87            }
88            out.push(timer.id);
89        }
90    }
91    
92    
93    pub fn start_timer(&mut self, id: u64, timeout: f64, repeats: bool) {
94        //println!("STARTING TIMER {:?} {:?} {:?}", id, timeout, repeats);
95        
96        // Timers are stored in an ordered list. Each timer stores the amount of time between
97        // when its predecessor in the list should fire and when the timer itself should fire
98        // in `delta_timeout`.
99        
100        // Since we are starting a new timer, our first step is to find where in the list this
101        // new timer should be inserted. `delta_timeout` is initially set to `timeout`. As we move
102        // through the list, we subtract the `delta_timeout` of the timers preceding the new timer
103        // in the list. Once this subtraction would cause an overflow, we have found the correct
104        // position in the list. The timer should fire after the one preceding it in the list, and
105        // before the one succeeding it in the list. Moreover `delta_timeout` is now set to the
106        // correct value.
107        let mut delta_timeout = timeout;
108        let index = self.timers.iter().position( | timer | {
109            if delta_timeout < timer.delta_timeout {
110                return true;
111            }
112            delta_timeout -= timer.delta_timeout;
113            false
114        }).unwrap_or(self.timers.len());
115        
116        // Insert the timer in the list.
117        //
118        // We also store the original `timeout` with each timer. This is necessary if the timer is
119        // repeatable and we want to restart it later on.
120        self.timers.insert(
121            index,
122            SelectTimer {
123                id,
124                timeout,
125                repeats,
126                delta_timeout,
127            },
128        );
129        
130        // The timer succeeding the newly inserted timer now has a new timer preceding it, so we
131        // need to adjust its `delta_timeout`.
132        //
133        // Note that by construction, `timer.delta_timeout < delta_timeout`. Otherwise, the newly
134        // inserted timer would have been inserted *after* the timer succeeding it, not before it.
135        if index < self.timers.len() - 1 {
136            let timer = &mut self.timers[index + 1];
137            // This computation should never underflow (see above)
138            timer.delta_timeout -= delta_timeout;
139        }
140    }
141    
142    pub fn stop_timer(&mut self, id: u64) {
143        //println!("STOPPING TIMER {:?}", id);
144        
145        // Since we are stopping an existing timer, our first step is to find where in the list this
146        // timer should be removed.
147        let index = if let Some(index) = self.timers.iter().position( | timer | timer.id == id) {
148            index
149        } else {
150            return;
151        };
152        
153        // Remove the timer from the list. 
154        // The timer being removed is always the first one in the queue, so it can just be removed directly.
155        self.timers.remove(index);
156    }
157    
158}