timer-rs

Dual licensed crate.

- This crate i.e code is NOT an Open Source software. This is a FREE (gratis) software and follows the principle of Sources Available/Disclosed software which should be fairly used.
- It is published under FSF/OSI approved licenses however author does not follow/share OSI and FSF principles and phylosophy.
- License is subject to be changed in further versions without warning.
- If you are using code in non-free (in terms of gratis) software you MUST NEVER demand a development of any features which are missing and needed for your business if you are not sponsoring/contributing those changes.
- Access to the code can be limited by author to specific entities due to the local laws (not my bad or fault)(despite what is said in the license).
- AI generated sloppy code is prohibited. AI generates slop "a priori" (anyway).
- Licenses (thank you OSS sectarians ) do not anyhow limit AI training, but f^ck you all - ChatGPT, Co
ckPilot, especially Claude and rest unidentified cr@p.
- It is strongly discouraged from using the AI based tools to write or enhance the code. AI slope would 100% violate the license by introducing the 3rd party licensed code.
The pull requests are not supported. The patches can be sent over the email or using issues on gitlab.
- For each feature or fix, please send patches separatly.
- Please write what your patch is implementing or fixing.
- Please test your patch.
- I can read the code and I am able to understand it, so don't write a poem or essay in the description to the patches.
- Can I use the MPL-2.0 licensed code (crate) in larger project licensed with more permissive license like BSD or MIT.
I want to distribute (outside my organization) executable programs or libraries that I have compiled from someone else's unchanged MPL-licensed source code, either standalone or part of a larger work. What do I have to do?
You must inform the recipients where they can get the source for the MPLed code in the executable program or library you are distributing (i.e., you must comply with Section 3.2). You may distribute any executables you create under a license of your choosing, as long as that license does not interfere with the recipients' rights to the source under the terms of the MPL.
MPL2.0 FAQ
Yes, MPL- and Apache-licensed code can be used with an MIT codebase (so in that sense, they are "compatible").
However, the MPL- / Apache-licensed code remains under its original license. (So although compatible, you cannot relicense someone else's MPL or Apache code into the MIT license.) This means that your final codebase will contain a mix of MPL, Apache, and MIT licensed code.
As an example, MPL has weak copyleft, so if you modified an MPL file, that file (including your changes) must remain under the MPL license.
Answer1
You should use this license if you are located in the EU which gives you more advantages over GPL because
in case of any disputes, the license allows you to defend your rights in a European Union country, in this case it will be Spain. It has also been translated into all languages of the EU member states.
Matrix of EUPL compatible open source licences
EUPL-1.2 is incompatiable according to GNU ORG
This is a free software license. By itself, it has a copyleft comparable to the GPL's, and incompatible with it.
v 0.1.0-development
Rust edition 2024
A OS based timer and timer event dequeue which can be used to create a simple timeout scheduling. Can be used just as OS timer or use it together with the
timeout deque.
This crate is still incomplete and not properly tested. The proof of concept is now developed.
License:

Sources are available under: MPL-2.0 OR EUPL-1.2
Supports:
- GNU/Linux.
- Three dequeue types with the scheduling using absolute time.
- Async
ToDo
- BSD based systems (kqueue) i.e (FreeBSD, OSX)
- other timers types and queues.
Does not support:
Issues tracker:
Issues tracket is here
Usage:
see ./examples/ there
Examples
For every sync example, the event notification is used. In case of Linux, the EPoll is used.
Timer queue type consumer
This type of dequeue is consuming the instance.
use std::{fmt, os::fd::{AsFd, AsRawFd, RawFd}, sync::Arc};
use timer_deque_rs::
{
common,
timer_portable::TimerEventWatch,
OrderedTimerDeque,
TimerDequeueConsumer,
TimerReadRes
};
#[derive(Debug, PartialEq, Eq, Clone)]
struct TestItem(u64);
impl fmt::Display for TestItem
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "0 = {}", self.0)
}
}
fn main()
{
let mut ev_watch = TimerEventWatch::new().unwrap();
let mut time_list =
OrderedTimerDeque
::<TimerDequeueConsumer<Arc<TestItem>>>
::new("test_label".into(), 4, false).unwrap();
ev_watch.add(&time_list).unwrap();
let tss_set1 = common::get_current_timestamp().timestamp()+10;
let ent1 = Arc::new(TestItem(1));
let tss_set2 = common::get_current_timestamp().timestamp()+17;
let ent2 = Arc::new(TestItem(2));
let tss_set3 = common::get_current_timestamp().timestamp()+27;
let ent3 = Arc::new(TestItem(3));
time_list.add_to_timer(ent1.clone(), tss_set1, 0).unwrap();
time_list.add_to_timer(ent2.clone(), tss_set2, 0).unwrap();
time_list.add_to_timer(ent3.clone(), tss_set3, 0).unwrap();
let res = ev_watch.poll(Option::None).unwrap();
let poll_timeout = common::get_current_timestamp().timestamp();
assert_eq!(res.is_some(), true);
assert_eq!(res.as_ref().unwrap().len(), 1);
let timer_fd: RawFd = res.as_ref().unwrap()[0];
assert_eq!(timer_fd, time_list.as_fd().as_raw_fd());
let res = time_list.wait_for_event().unwrap();
println!("timer timeout with result: {}", res);
assert_eq!(TimerReadRes::Ok(1), res);
let mut timeout_items: Vec<Arc<TestItem>> = Vec::new();
time_list.timeout_event_handler(res, &mut timeout_items).unwrap();
assert_eq!(timeout_items.len(), 1); assert_eq!(timeout_items[0], ent1);
println!("timer item: {}, timeout: {}, curtime: {}", &timeout_items[0], tss_set1, poll_timeout);
assert_eq!(tss_set1, poll_timeout);
time_list.remove_from_sched_queue(&ent2).unwrap();
assert_eq!(time_list.timer_queue_len(), 1);
println!("item {} removed from shed. queue", ent2);
println!("queue len: {}", time_list.timer_queue_len());
let res = ev_watch.poll(Option::None).unwrap();
let poll_timeout = common::get_current_timestamp().timestamp();
assert_eq!(res.is_some(), true);
assert_eq!(res.as_ref().unwrap().len(), 1);
let timer_fd: RawFd = res.as_ref().unwrap()[0];
assert_eq!(timer_fd, time_list.as_fd().as_raw_fd());
let res = time_list.wait_for_event().unwrap();
println!("timer timeout with result: {}", res);
assert_eq!(TimerReadRes::Ok(1), res);
let mut timeout_items: Vec<Arc<TestItem>> = Vec::new();
time_list.timeout_event_handler(res, &mut timeout_items).unwrap();
assert_eq!(timeout_items.len(), 1); assert_eq!(timeout_items[0], ent3);
println!("timer item: {}, timeout: {}, curtime: {}", &timeout_items[0], tss_set3, poll_timeout);
assert_eq!(tss_set3, poll_timeout);
println!("timer queue len: {}", time_list.timer_queue_len());
assert_eq!(time_list.timer_queue_len(), 0);
return;
}
Timer queue type ticket issuer
This type of dequeue is issuing the ticket for an instance which can be cancelled (ticket).
use std::{fmt, os::fd::{AsFd, AsRawFd, RawFd}};
use timer_deque_rs::{common, timer_portable::linux::timer_poll::TimerEventWatch, OrderedTimerDeque, TimerDequeueId, TimerDequeueTicket, TimerDequeueTicketIssuer, TimerReadRes};
#[derive(Debug, PartialEq, Eq)]
struct TestItem(TimerDequeueTicket);
impl fmt::Display for TestItem
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "0 = {}", self.0)
}
}
fn main()
{
let mut ev_watch = TimerEventWatch::new().unwrap();
let mut time_list =
OrderedTimerDeque
::<TimerDequeueTicketIssuer>
::new("test_label".into(), 4, false).unwrap();
ev_watch.add(&time_list).unwrap();
let tss_set1 = common::get_current_timestamp().timestamp()+10;
let ticket1 = time_list.add_to_timer(tss_set1, 0).unwrap();
let ent1 = TestItem(ticket1);
let tss_set2 = common::get_current_timestamp().timestamp()+17;
let ticket2 = time_list.add_to_timer(tss_set2, 0).unwrap();
let ent2 = TestItem(ticket2);
let tss_set3 = common::get_current_timestamp().timestamp()+27;
let ticket3 = time_list.add_to_timer(tss_set3, 0).unwrap();
let ent3 = TestItem(ticket3);
let res = ev_watch.poll(Option::None).unwrap();
let poll_timeout = common::get_current_timestamp().timestamp();
assert_eq!(res.is_some(), true);
assert_eq!(res.as_ref().unwrap().len(), 1);
let timer_fd: RawFd = res.as_ref().unwrap()[0];
assert_eq!(timer_fd, time_list.as_fd().as_raw_fd());
let res = time_list.wait_for_event().unwrap();
println!("timer timeout with result: {}", res);
assert_eq!(TimerReadRes::Ok(1), res);
let mut timeout_items: Vec<TimerDequeueId> = Vec::new();
time_list.timeout_event_handler(res, &mut timeout_items).unwrap();
assert_eq!(timeout_items.len(), 1); assert_eq!(timeout_items[0], ent1.0);
assert_eq!(ent1.0, timeout_items[0]);
assert_eq!(ent1.0.is_queued(), false);
println!("timer item: {}, timeout: {}, curtime: {}", &timeout_items[0], tss_set1, poll_timeout);
assert_eq!(tss_set1, poll_timeout);
println!("item {} removing from shed. queue", ent2);
time_list.remove_from_sched_queue(ent2.0).unwrap();
assert_eq!(time_list.timer_queue_len(), 1); println!("queue len: {}", time_list.timer_queue_len());
let res = ev_watch.poll(Option::None).unwrap();
let poll_timeout = common::get_current_timestamp().timestamp();
assert_eq!(res.is_some(), true);
assert_eq!(res.as_ref().unwrap().len(), 1);
let timer_fd: RawFd = res.as_ref().unwrap()[0];
assert_eq!(timer_fd, time_list.as_fd().as_raw_fd());
let res = time_list.wait_for_event().unwrap();
println!("timer timeout with result: {}", res);
assert_eq!(TimerReadRes::Ok(1), res);
let mut timeout_items: Vec<TimerDequeueId> = Vec::new();
time_list.timeout_event_handler(res, &mut timeout_items).unwrap();
assert_eq!(timeout_items.len(), 1); assert_eq!(timeout_items[0], ent3.0);
println!("timer item: {}, timeout: {}, curtime: {}", &timeout_items[0], tss_set3, poll_timeout);
assert_eq!(tss_set3, poll_timeout);
println!("timer queue len: {}", time_list.timer_queue_len());
assert_eq!(time_list.timer_queue_len(), 0);
return;
}
Timer queue type notification (signal)
This type of dequeue is sending signal i.e notification using provided functionality.
use std::
{
fmt,
os::fd::RawFd,
sync::{atomic::{AtomicBool, Ordering}, mpsc::{self, SendError}, Arc},
time::Duration
};
use timer_deque_rs::
{
common,
timer_portable::TimerEventWatch,
timer_signal::TimerDequeueSignalTicket,
OrderedTimerDeque,
TimerDequeueSignal,
TimerReadRes
};
use timer_deque_rs::timer::{AsFd, AsRawFd};
#[derive(Debug)]
pub struct TestStruct
{
uniq_id: u64,
sig_chan_snd: mpsc::Sender<u64>,
}
impl Eq for TestStruct {}
impl PartialEq for TestStruct
{
fn eq(&self, other: &Self) -> bool
{
return self.uniq_id == other.uniq_id;
}
}
impl fmt::Display for TestStruct
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "uniq_id: {}", self.uniq_id)
}
}
impl TimerDequeueSignal for TestStruct
{
type TimerQueueID = u64;
type TimeoutErr = SendError<Self::TimerQueueID>;
fn get_id(&self) -> Self::TimerQueueID
{
return self.uniq_id;
}
fn sig_timeout(self) -> Result<(), Self::TimeoutErr>
{
return self.sig_chan_snd.send(self.uniq_id);
}
}
impl TestStruct
{
fn new(uniq_id: u64, snd: mpsc::Sender<u64>) -> Self
{
return Self{ uniq_id: uniq_id, sig_chan_snd: snd };
}
}
fn main()
{
let mut ev_watch = TimerEventWatch::new().unwrap();
let mut time_list =
OrderedTimerDeque
::<TimerDequeueSignalTicket<TestStruct>>
::new("test_label".into(), 4, false).unwrap();
ev_watch.add(&time_list).unwrap();
let (snd, rcv) = mpsc::channel::<u64>();
let exit_flag = Arc::new(AtomicBool::new(false));
let c_exit_flag = exit_flag.clone();
let thread_hndlr =
std::thread::spawn(move ||
{
let mut exp_ids = vec![300, 100];
loop
{
let Ok(uniq_id) = rcv.recv_timeout(Duration::from_millis(100))
else
{
if c_exit_flag.load(Ordering::Relaxed) == true
{
return;
}
else if exp_ids.len() == 0
{
return;
}
continue;
};
println!("received timeout signal from item: {}", uniq_id);
assert_eq!(exp_ids.pop().unwrap(), uniq_id);
}
}
);
let tss_set1 = common::get_current_timestamp().timestamp()+10;
let ent1 = TestStruct::new(100, snd.clone());
let tss_set2 = common::get_current_timestamp().timestamp()+17;
let ent2 = TestStruct::new(200, snd.clone());
let tss_set3 = common::get_current_timestamp().timestamp()+27;
let ent3 = TestStruct::new(300, snd.clone());
time_list.add_to_timer(ent1, tss_set1, 0).unwrap();
time_list.add_to_timer(ent2, tss_set2, 0).unwrap();
time_list.add_to_timer(ent3, tss_set3, 0).unwrap();
let res = ev_watch.poll(Option::None).unwrap();
let poll_timeout = common::get_current_timestamp().timestamp();
assert_eq!(res.is_some(), true);
assert_eq!(res.as_ref().unwrap().len(), 1);
let timer_fd: RawFd = res.as_ref().unwrap()[0];
assert_eq!(timer_fd, time_list.as_fd().as_raw_fd());
let res = time_list.wait_for_event().unwrap();
println!("timer timeout with result: {}, timeout: {}, curtime: {}", res, tss_set1, poll_timeout);
assert_eq!(TimerReadRes::Ok(1), res);
time_list.timeout_event_handler(res).unwrap();
time_list.remove_from_sched_queue(&200).unwrap();
let res = ev_watch.poll(Option::None).unwrap();
let poll_timeout = common::get_current_timestamp().timestamp();
assert_eq!(res.is_some(), true);
assert_eq!(res.as_ref().unwrap().len(), 1);
let timer_fd: RawFd = res.as_ref().unwrap()[0];
assert_eq!(timer_fd, time_list.as_fd().as_raw_fd());
let res = time_list.wait_for_event().unwrap();
println!("timer timeout with result: {}, timeout: {}, curtime: {}", res, tss_set3, poll_timeout);
assert_eq!(TimerReadRes::Ok(1), res);
time_list.timeout_event_handler(res).unwrap();
thread_hndlr.join().unwrap();
return;
}
Timer queue type consumer (async) polling future (less efficient)
In this example, the consumer type of deque is used in async context by polling the
timer. This is not efficient. In the next example, the AsyncFd is used it increase efficiency.
use std::{fmt, sync::Arc};
use timer_deque_rs::{common, OrderedTimerDeque, TimerDequeueConsumer, TimerReadRes};
#[derive(Debug, PartialEq, Eq, Clone)]
struct TestItem(u64);
impl fmt::Display for TestItem
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "0 = {}", self.0)
}
}
#[tokio::main]
async fn main()
{
let mut time_list =
OrderedTimerDeque
::<TimerDequeueConsumer<Arc<TestItem>>>
::new("test_label_async".into(), 4, false)
.unwrap();
let tss_set1 = common::get_current_timestamp().timestamp()+10;
let ent1 = Arc::new(TestItem(1));
let tss_set2 = common::get_current_timestamp().timestamp()+17;
let ent2 = Arc::new(TestItem(2));
let tss_set3 = common::get_current_timestamp().timestamp()+27;
let ent3 = Arc::new(TestItem(3));
time_list.add_to_timer(ent1.clone(), tss_set1, 0).unwrap();
time_list.add_to_timer(ent2.clone(), tss_set2, 0).unwrap();
time_list.add_to_timer(ent3.clone(), tss_set3, 0).unwrap();
let res = time_list.poll().await.unwrap();
let poll_timeout = common::get_current_timestamp().timestamp();
println!("timer timeout with result: {}", res);
assert_eq!(TimerReadRes::Ok(1), res);
let mut timeout_items: Vec<Arc<TestItem>> = Vec::new();
time_list.timeout_event_handler(res, &mut timeout_items).unwrap();
assert_eq!(timeout_items.len(), 1); assert_eq!(timeout_items[0], ent1);
println!("timer item: {}, timeout: {}, curtime: {}", &timeout_items[0], tss_set1, poll_timeout);
assert_eq!(tss_set1, poll_timeout);
time_list.remove_from_sched_queue(&ent2).unwrap();
assert_eq!(time_list.timer_queue_len(), 1);
println!("item {} removed from shed. queue", ent2);
println!("queue len: {}", time_list.timer_queue_len());
let res = time_list.poll().await.unwrap();
let poll_timeout = common::get_current_timestamp().timestamp();
println!("timer timeout with result: {}", res);
assert_eq!(TimerReadRes::Ok(1), res);
let mut timeout_items: Vec<Arc<TestItem>> = Vec::new();
time_list.timeout_event_handler(res, &mut timeout_items).unwrap();
assert_eq!(timeout_items.len(), 1); assert_eq!(timeout_items[0], ent3);
println!("timer item: {}, timeout: {}, curtime: {}", &timeout_items[0], tss_set3, poll_timeout);
assert_eq!(tss_set3, poll_timeout);
println!("timer queue len: {}", time_list.timer_queue_len());
assert_eq!(time_list.timer_queue_len(), 0);
return;
}
Timer queue type notifier (async) using more efficient AsyncFd
In this example, the notifier (signal) type of deque is used in async context by wrapping the timer deque in
the AsyncFd. This is more efficient than jus polling the FD for events like in previous example.
use std::{fmt, sync::{atomic::AtomicBool, Arc}};
use timer_deque_rs::
{
common,
timer_signal::TimerDequeueSignalTicket,
OrderedTimerDeque,
TimerDequeueSignal,
TimerReadRes
};
use tokio::
{
io::{unix::AsyncFd, Interest},
sync::mpsc::{self, error::SendError}
};
#[derive(Debug)]
pub struct TestStruct
{
uniq_id: u64,
sig_chan_snd: mpsc::UnboundedSender<u64>,
}
impl Eq for TestStruct {}
impl PartialEq for TestStruct
{
fn eq(&self, other: &Self) -> bool
{
return self.uniq_id == other.uniq_id;
}
}
impl fmt::Display for TestStruct
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "uniq_id: {}", self.uniq_id)
}
}
impl TimerDequeueSignal for TestStruct
{
type TimerQueueID = u64;
type TimeoutErr = SendError<Self::TimerQueueID>;
fn get_id(&self) -> Self::TimerQueueID
{
return self.uniq_id;
}
async
fn a_sig_timeout(self) -> Result<(), Self::TimeoutErr>
{
return self.sig_chan_snd.send(self.uniq_id);
}
}
impl TestStruct
{
fn new(uniq_id: u64, snd: mpsc::UnboundedSender<u64>) -> Self
{
return Self{ uniq_id: uniq_id, sig_chan_snd: snd };
}
}
#[tokio::main]
async fn main()
{
let time_list =
OrderedTimerDeque
::<TimerDequeueSignalTicket<TestStruct>>
::new("test_label".into(), 4, false).unwrap();
let mut async_time_list =
AsyncFd::new(time_list).unwrap();
let (snd, mut rcv) = mpsc::unbounded_channel::<u64>();
let exit_flag = Arc::new(AtomicBool::new(false));
let c_exit_flag = exit_flag.clone();
let hndl =
tokio::spawn(async move
{
let mut exp_ids = vec![300, 100];
loop
{
let Some(uniq_id) = rcv.recv().await
else
{
return;
};
println!("received timeout signal from item: {}", uniq_id);
assert_eq!(exp_ids.pop().unwrap(), uniq_id);
if exp_ids.len() == 0
{
return;
}
}
}
);
let tss_set1 = common::get_current_timestamp().timestamp()+5;
let ent1 = TestStruct::new(100, snd.clone());
let tss_set2 = common::get_current_timestamp().timestamp()+11;
let ent2 = TestStruct::new(200, snd.clone());
let tss_set3 = common::get_current_timestamp().timestamp()+15;
let ent3 = TestStruct::new(300, snd.clone());
async_time_list.get_mut().add_to_timer(ent1, tss_set1, 0).unwrap();
async_time_list.get_mut().add_to_timer(ent2, tss_set2, 0).unwrap();
async_time_list.get_mut().add_to_timer(ent3, tss_set3, 0).unwrap();
let mut read_guard =
async_time_list.ready_mut(Interest::READABLE).await.unwrap();
read_guard.clear_ready();
let poll_timeout = common::get_current_timestamp().timestamp();
let res = read_guard.get_inner().wait_for_event().unwrap();
println!("timer timeout with result: {}, timeout: {}, curtime: {}", res, tss_set1, poll_timeout);
assert_eq!(TimerReadRes::Ok(1), res);
read_guard.get_inner_mut().async_timeout_event_handler(res).await.unwrap();
async_time_list.get_mut().remove_from_sched_queue(&200).unwrap();
let mut read_guard =
async_time_list.ready_mut(Interest::READABLE).await.unwrap();
let poll_timeout = common::get_current_timestamp().timestamp();
let res = read_guard.get_inner().wait_for_event().unwrap();
println!("timer timeout with result: {}, timeout: {}, curtime: {}", res, tss_set1, poll_timeout);
assert_eq!(TimerReadRes::Ok(1), res);
read_guard.get_inner_mut().async_timeout_event_handler(res).await.unwrap();
let _ = hndl.await;
return;
}
Generic, OS based timer without queue
Just simple timer based on OS functionality.
use std::{borrow::Cow, time::Instant};
use timer_deque_rs::timer_portable::
{
timer::TimerFd,
FdTimerCom,
TimerExpMode,
TimerFlags,
TimerSetTimeFlags,
TimerType
};
fn main()
{
let timer =
TimerFd::new(Cow::Borrowed("test"), TimerType::CLOCK_REALTIME,
TimerFlags::empty()).unwrap();
let now = chrono::offset::Local::now().timestamp();
let snow = now + 3;
let s = Instant::now();
let res =
timer.set_time(TimerSetTimeFlags::TFD_TIMER_ABSTIME, TimerExpMode::OneShot{sec: snow, nsec: 0});
println!("timer was set: '{}' '{}'", now, snow);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let ovf = timer.read().unwrap().unwrap();
let ts = chrono::offset::Local::now().timestamp();
let e = s.elapsed();
assert_eq!(ovf, 1);
assert_eq!(ts, snow);
println!("elapsed: {:?}, ts: {}", e, ts);
assert_eq!((e.as_millis() <= 3100), true);
println!("Success");
return;
}