From beeea9a0c10e9e0480ed51cd44ba9f440ce56682 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 2 Jan 2020 00:11:13 +0200 Subject: [PATCH] ui: implement PosixTimer Add interface for posix timers timer_create(2) time(7) --- src/bin.rs | 9 ++- ui/src/lib.rs | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+), 1 deletion(-) diff --git a/src/bin.rs b/src/bin.rs index 187d1d28..b41f6825 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -226,6 +226,7 @@ fn run_app() -> Result<()> { /* Catch SIGWINCH to handle terminal resizing */ let signals = &[ + signal_hook::SIGALRM, /* Catch SIGWINCH to handle terminal resizing */ signal_hook::SIGWINCH, /* Catch SIGCHLD to handle embed applications status change */ @@ -387,7 +388,13 @@ fn run_app() -> Result<()> { state.redraw(); } }, - _ => {} + signal_hook::SIGALRM => { + state.rcv_event(UIEvent::Timer); + state.redraw(); + }, + other => { + debug!("got other signal: {:?}", other); + } } }, } diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 51465bd1..8b109388 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -183,3 +183,205 @@ pub mod username { ptr_to_string(pwent.pw_name) } } + +pub mod timer { + use super::{MeliError, Result}; + use libc::clockid_t; + use libc::sigevent; + use libc::{itimerspec, timespec}; + use nix::sys::signal::{SigEvent, SigevNotify}; + use std::convert::TryInto; + use std::time::Duration; + + #[allow(non_camel_case_types)] + pub type timer_t = libc::intptr_t; + + #[link(name = "rt")] + extern "C" { + fn timer_create(clockid: clockid_t, sevp: *const sigevent, timerid: *mut timer_t) -> i32; + fn timer_settime( + timerid: timer_t, + flags: i32, + new_value: *const itimerspec, + old_value: *const itimerspec, + ) -> i32; + + fn timer_delete(timerid: timer_t) -> i32; + } + + #[derive(Debug)] + pub struct PosixTimer { + timer_id: timer_t, + interval: Duration, + value: Duration, + } + + impl Drop for PosixTimer { + fn drop(&mut self) { + unsafe { + timer_delete(self.timer_id); + } + } + } + + impl PosixTimer { + pub fn rearm(&mut self) { + debug!("posixtimer rearm"); + let spec = itimerspec { + it_interval: timespec { + tv_sec: self.interval.as_secs().try_into().unwrap_or(0), + tv_nsec: self.interval.subsec_nanos().try_into().unwrap_or(0), + }, + it_value: timespec { + tv_sec: self.value.as_secs().try_into().unwrap_or(0), + tv_nsec: self.value.subsec_nanos().try_into().unwrap_or(0), + }, + }; + let ret = + unsafe { timer_settime(self.timer_id, 0, &spec as *const _, std::ptr::null_mut()) }; + if ret != 0 { + match ret { + libc::EFAULT => { + panic!( + "EFAULT: new_value, old_value, or curr_value is not a valid pointer." + ); + } + libc::EINVAL => { + panic!("EINVAL: timerid is invalid."); + } + _ => {} + } + } + } + + pub fn set_interval(&mut self, interval: Duration) { + let spec = itimerspec { + it_interval: timespec { + tv_sec: interval.as_secs().try_into().unwrap_or(0), + tv_nsec: interval.subsec_nanos().try_into().unwrap_or(0), + }, + it_value: timespec { + tv_sec: self.value.as_secs().try_into().unwrap_or(0), + tv_nsec: self.value.subsec_nanos().try_into().unwrap_or(0), + }, + }; + let ret = + unsafe { timer_settime(self.timer_id, 0, &spec as *const _, std::ptr::null_mut()) }; + if ret != 0 { + match ret { + libc::EFAULT => { + panic!( + "EFAULT: new_value, old_value, or curr_value is not a valid pointer." + ); + } + libc::EINVAL => { + panic!("EINVAL: timerid is invalid."); + } + _ => {} + } + } + self.interval = interval; + } + + pub fn arm(&mut self, value: Duration) { + let spec = itimerspec { + it_interval: timespec { + tv_sec: self.interval.as_secs().try_into().unwrap_or(0), + tv_nsec: self.interval.subsec_nanos().try_into().unwrap_or(0), + }, + it_value: timespec { + tv_sec: value.as_secs().try_into().unwrap_or(0), + tv_nsec: value.subsec_nanos().try_into().unwrap_or(0), + }, + }; + let ret = + unsafe { timer_settime(self.timer_id, 0, &spec as *const _, std::ptr::null_mut()) }; + if ret != 0 { + match ret { + libc::EFAULT => { + panic!( + "EFAULT: new_value, old_value, or curr_value is not a valid pointer." + ); + } + libc::EINVAL => { + panic!("EINVAL: timerid is invalid."); + } + _ => {} + } + } + self.value = value; + } + + pub fn new_with_signal( + interval: Duration, + value: Duration, + signal: nix::sys::signal::Signal, + ) -> Result { + let mut timer_id = Default::default(); + + let sigev_notify = SigevNotify::SigevSignal { + signal, + si_value: 0, + }; + let event = SigEvent::new(sigev_notify); + + let ret = unsafe { + timer_create( + libc::CLOCK_MONOTONIC, + &event.sigevent() as *const _, + &mut timer_id as *mut _, + ) + }; + + if ret != 0 { + match ret { + libc::EAGAIN => { + return Err(MeliError::new( + "Temporary error during kernel allocation of timer", + )); + } + libc::EINVAL => { + panic!("Clock ID, sigev_notify, sigev_signo, or sigev_notify_thread_id is invalid."); + } + libc::ENOMEM => { + return Err(MeliError::new("Could not allocate memory.")); + } + _ => {} + } + } + + let spec = itimerspec { + it_interval: timespec { + tv_sec: interval.as_secs().try_into().unwrap_or(0), + tv_nsec: interval.subsec_nanos().try_into().unwrap_or(0), + }, + it_value: timespec { + tv_sec: value.as_secs().try_into().unwrap_or(0), + tv_nsec: value.subsec_nanos().try_into().unwrap_or(0), + }, + }; + + let ret = + unsafe { timer_settime(timer_id, 0, &spec as *const _, std::ptr::null_mut()) }; + if ret != 0 { + match ret { + libc::EFAULT => { + panic!( + "EFAULT: new_value, old_value, or curr_value is not a valid pointer." + ); + } + libc::EINVAL => { + panic!("EINVAL: timerid is invalid."); + } + _ => {} + } + } + + Ok(PosixTimer { + timer_id, + interval, + value, + }) + } + } +}