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