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}