1use chrono::{DateTime, Utc};
2use errno::errno;
3use libc;
4use std::{mem::MaybeUninit, ptr};
5use thiserror;
6use tokio::signal::unix::{signal, SignalKind};
7
8#[derive(thiserror::Error, Debug)]
9pub enum Error {
10 #[error(transparent)]
11 Errno(errno::Errno),
12 #[error(transparent)]
13 Io(std::io::Error),
14}
15
16impl From<std::io::Error> for Error {
17 fn from(err: std::io::Error) -> Self {
18 Error::Io(err)
19 }
20}
21impl From<errno::Errno> for Error {
22 fn from(err: errno::Errno) -> Self {
23 Error::Errno(err)
24 }
25}
26
27type Result<T> = std::result::Result<T, Error>;
28
29unsafe fn arm_timer(duration: i64) -> Result<libc::timer_t> {
30 let mut timer: libc::timer_t = std::mem::zeroed();
32 let mut sev: libc::sigevent = std::mem::zeroed();
34 sev.sigev_notify = libc::SIGEV_SIGNAL;
35 sev.sigev_signo = SignalKind::alarm().as_raw_value();
36 if libc::timer_create(libc::CLOCK_REALTIME, &mut sev, &mut timer) != 0 {
37 return Err(Error::from(errno()));
38 }
39
40 let mut its = libc::itimerspec {
42 it_interval: libc::timespec {
43 tv_sec: 0,
44 tv_nsec: 0,
45 },
46 it_value: std::mem::zeroed(),
47 };
48 if libc::clock_gettime(libc::CLOCK_REALTIME, &mut its.it_value) != 0 {
50 let err = Err(Error::from(errno()));
51 disarm_timer(timer)?;
52 return err;
53 }
54
55 its.it_value.tv_sec += duration as libc::time_t;
57
58 if libc::timer_settime(timer, libc::TIMER_ABSTIME, &its, ptr::null_mut()) != 0 {
60 let err = Err(Error::from(errno()));
61 disarm_timer(timer)?;
62 return err;
63 }
64
65 Ok(timer)
66}
67unsafe fn disarm_timer(timer: libc::timer_t) -> Result<()> {
68 if libc::timer_delete(timer) != 0 {
69 return Err(Error::from(errno()));
70 }
71 Ok(())
72}
73
74#[cfg(unix)]
92pub async fn sleep_until<Tz: chrono::TimeZone>(time: DateTime<Tz>) -> Result<()> {
93 let time = time.with_timezone(&Utc);
94 let mut alarm = signal(SignalKind::alarm())?;
96 loop {
97 let currtime = Utc::now();
98 let seconds_to_sleep = (time - currtime).num_seconds();
99 if seconds_to_sleep < 0 {
100 break;
101 }
102 let timer = unsafe { arm_timer(seconds_to_sleep)? };
104 alarm.recv().await;
106 unsafe { disarm_timer(timer)? }
107 }
108 Ok(())
109}