summaryrefslogtreecommitdiff
path: root/bindings/rs-timeout/src
diff options
context:
space:
mode:
author张杨 <[email protected]>2023-10-18 03:35:17 +0000
committer陆秋文 <[email protected]>2023-10-18 03:35:17 +0000
commitecc6d08170d6f4914fd90bf9fe574c657546cfae (patch)
treeb1a45b2f671d3213347be268c5787eebba1ff5b8 /bindings/rs-timeout/src
parent36450f5dfa230ef806151101ee8047375915ad73 (diff)
merge bind-rs-timeoutHEADmain
Diffstat (limited to 'bindings/rs-timeout/src')
-rw-r--r--bindings/rs-timeout/src/lib.rs4
-rw-r--r--bindings/rs-timeout/src/timeout.rs634
-rw-r--r--bindings/rs-timeout/src/timeout_bind.rs246
-rw-r--r--bindings/rs-timeout/src/timeout_bind_test.rs266
4 files changed, 1150 insertions, 0 deletions
diff --git a/bindings/rs-timeout/src/lib.rs b/bindings/rs-timeout/src/lib.rs
new file mode 100644
index 0000000..7c853bc
--- /dev/null
+++ b/bindings/rs-timeout/src/lib.rs
@@ -0,0 +1,4 @@
+pub mod timeout;
+pub mod timeout_bind;
+mod timeout_bind_test;
+// mod test_timeout; \ No newline at end of file
diff --git a/bindings/rs-timeout/src/timeout.rs b/bindings/rs-timeout/src/timeout.rs
new file mode 100644
index 0000000..d3382d4
--- /dev/null
+++ b/bindings/rs-timeout/src/timeout.rs
@@ -0,0 +1,634 @@
+use std::{panic, ptr::NonNull};
+
+use libc::c_void;
+
+use crate::timeout_bind::*;
+
+/// timeout type:
+/// - INT: Periodic timeout, start time only supports relative time.
+/// - ABS: Executed only once, start time is absolute time.
+/// - Default: Executed only once, start time is relative time
+/// ToType could use as i32
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum ToType {
+ INT = TIMEOUT_INT as isize, // periodic timeout, relative time
+ ABS = TIMEOUT_ABS as isize, // onetime timeout, absolute time
+ Default = TIMEOUT_DEFAULT as isize, // onetime timeout, relative time
+}
+// as i32
+impl From<ToType> for i32 {
+ fn from(timeout_type: ToType) -> Self {
+ timeout_type as i32
+ }
+}
+
+impl ToType {
+ // i32 -> ToType
+ fn new(flag: i32) -> Self {
+ match flag {
+ TIMEOUT_INT => ToType::INT,
+ TIMEOUT_ABS => ToType::ABS,
+ _ => ToType::Default,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct TimeoutCallBack<'a, T> {
+ pub fn_ptr: Option<extern "C" fn(arg: &mut T)>,
+ pub arg: &'a mut T,
+}
+
+/// TimeoutCallBack<T> -> timeout_cb
+impl<T> From<TimeoutCallBack<'_, T>> for timeout_cb {
+ fn from(timeout_cb: TimeoutCallBack<T>) -> Self {
+ timeout_cb {
+ fn_ptr: timeout_cb.fn_ptr.map(|f| unsafe { std::mem::transmute(f) }),
+ arg: timeout_cb.arg as *mut T as *mut c_void,
+ }
+ }
+}
+
+// type TimeoutCallBack = timeout_cb; // callback
+impl<'a, T> TimeoutCallBack<'a, T> {
+ pub fn new(callback: extern "C" fn(arg: &mut T), arg: &'a mut T) -> Self {
+ let fn_ptr = Some(callback);
+ TimeoutCallBack { fn_ptr, arg }
+ }
+}
+
+impl timeout_cb {
+ fn call(&self) -> bool {
+ if let Some(callback) = self.fn_ptr {
+ let result = panic::catch_unwind(|| unsafe {
+ callback(self.arg);
+ });
+ if result.is_ok() {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+/// on C side: callback function is hook<F>, arg is closure.
+/// run callback is run hook<F>, make hook like a exec for closure.
+unsafe extern "C" fn hook<F>(arg: *mut c_void)
+where
+ F: FnMut(),
+{
+ let closure = &mut *(arg as *mut F);
+ closure();
+}
+
+/// return callback exec function
+/// without the actual effect, only the compiler associated HOOK and F
+pub(crate) fn get_cb_exec<F>(_closure: &F) -> unsafe extern "C" fn(arg: *mut c_void)
+where
+ F: FnMut(), // closure
+{
+ hook::<F>
+}
+
+type Timeout = timeout;
+
+impl Timeout {
+ pub fn new(flags: ToType) -> Result<Timeout, &'static str> {
+ // timeout instantiated in rust rather than C side
+ let raw = Box::into_raw(Box::new(timeout::default()));
+ // Box::into_raw let instance ownership transfer to C
+ let raw = unsafe { timeout_init(raw, flags as i32) };
+ if raw.is_null() {
+ return Err("Failed to create Timeout");
+ }
+ // Box::from_raw: instance ownership from C side to rust.
+ Ok(*unsafe { Box::from_raw(raw) })
+ }
+ /// transfer *timeout to Timeout
+ /// instance ownership form C side to rust
+ pub(crate) fn transfer(to: *mut timeout) -> Timeout {
+ *unsafe { Box::from_raw(to) }
+ }
+ /// set callback
+ pub fn set_cb<T>(&mut self, cb: TimeoutCallBack<T>) {
+ self.callback = cb.into();
+ }
+
+ /// set callback by timeout_cb
+ /// for test
+ pub(crate) fn set_cb_raw(&mut self, cb: timeout_cb) {
+ self.callback = cb.into();
+ }
+
+ /// set closure
+ pub fn set_cb_closure<F>(&mut self, closure: &mut F)
+ where
+ F: FnMut(),
+ {
+ // let (cb_exec, mut cb) = make_cb!(closure);
+ let cb_exec = get_cb_exec(closure);
+ let exec_ptr = Box::into_raw(Box::new(cb_exec));
+
+ let to_cb = timeout_cb {
+ fn_ptr: Some(unsafe { *exec_ptr }),
+ arg: closure as *mut _ as *mut c_void,
+ };
+ self.callback = to_cb;
+ }
+
+ /// return true if timeout is registered and on timing wheel
+ pub fn is_pending(&self) -> bool {
+ //C code: return to->pending && to->pending != &to->timeouts->expired;
+ if !self.pending.is_null() // pending not null
+ && !self.timeouts.is_null() // timeouts not null
+ && self.pending != (unsafe { &mut (*self.timeouts).expired // pending not expired
+ })
+ {
+ return true;
+ }
+ false
+ }
+
+ /// return true if timeout is registered and on expired queue
+ pub fn is_expired(&self) -> bool {
+ //return to->pending && to->pending == &to->timeouts->expired;
+ if !self.pending.is_null() // pending not null
+ && !self.timeouts.is_null() // timeouts not null
+ && self.pending == (unsafe { &mut (*self.timeouts).expired // pending is expired
+ }) {
+ return true;
+ }
+ false
+ }
+ /// return true if timeout is periodic
+ pub fn is_periodic(&self) -> bool {
+ // if ((to->flags & TIMEOUT_INT) && to->interval > 0)
+ return (self.flags & TIMEOUT_INT != 0) && (self.interval > 0);
+ }
+
+ /// remove timeout from any timing wheel or expired queue (okay if on neither)
+ pub fn delete(&mut self) {
+ unsafe { timeout_del(self) };
+ }
+
+ /// return true if cb is not null and callback has executed
+ pub fn run_cb(&self) -> bool {
+ let cb = self.callback;
+ return cb.call();
+ }
+}
+
+impl Drop for Timeout {
+ fn drop(&mut self) {
+ self.delete(); // delete
+ }
+}
+
+impl Timeout {
+ /// create periodic timeout
+ pub fn new_int() -> Result<Timeout, &'static str> {
+ Timeout::new(ToType::INT)
+ }
+ // create onetime timeout with absolute time
+ pub fn new_abs() -> Result<Timeout, &'static str> {
+ Timeout::new(ToType::ABS)
+ }
+ /// create onetime timeout with relative time
+ pub fn new_default() -> Result<Timeout, &'static str> {
+ Timeout::new(ToType::Default)
+ }
+}
+
+/// delete periodic timeout
+/// No ownership of the Timeout object is passed in
+pub fn delete_to_periodic(to: &Timeout) {
+ if to.is_periodic() {
+ unsafe { timeout_del(to as *const _ as *mut timeout) };
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum TomItType {
+ PENDING = TIMEOUTS_PENDING as isize,
+ EXPIRED = TIMEOUTS_EXPIRED as isize,
+ ALL = TIMEOUTS_ALL as isize,
+ // CLEAR = TIMEOUTS_CLEAR as isize,
+}
+// as i32
+impl From<TomItType> for i32 {
+ fn from(flag: TomItType) -> Self {
+ flag as i32
+ }
+}
+
+impl TomItType {
+ pub fn new(flag: i32) -> Self {
+ match flag {
+ TIMEOUTS_PENDING => TomItType::PENDING,
+ TIMEOUTS_EXPIRED => TomItType::EXPIRED,
+ TIMEOUTS_ALL => TomItType::ALL,
+ // TIMEOUTS_CLEAR => TimeoutSItFlag::CLEAR, // CLEAR means clear all expired timeout on expire queue.
+ // this creates complications in ownership
+ _ => TomItType::ALL,
+ }
+ }
+}
+
+type TomIt = timeouts_it;
+
+impl TomIt {
+ /// flag has 3 value: PENDING, EXPIRED, ALL
+ fn new(flags: TomItType) -> TomIt {
+ let mut instance = timeouts_it::default();
+ TIMEOUTS_IT_INIT(&mut instance, flags as i32);
+ instance
+ }
+}
+
+/// expired timeout return type
+pub enum ToR {
+ OneTime(Timeout), // instance ownership from C side to rust
+ Periodic(&'static Timeout), // instance ownership still on C side
+}
+
+// TimeoutManager
+pub struct TimeoutManager {
+ tos: NonNull<timeouts>,
+}
+
+impl TimeoutManager {
+ /// TIMEOUT_mHZ 1000; TIMEOUT_uHZ 1000000; TIMEOUT_nHZ 1000000000;
+ /// if hz_set = 0, default hz_set = TIMEOUT_mHZ
+ pub fn new(hz_set: timeout_t) -> Result<TimeoutManager, &'static str> {
+ let mut err = 0 as usize;
+ // if hz_set = 0, default set to TIMEOUT_mHZ (timeouts_open)
+ let tos = unsafe { timeouts_open(hz_set, &mut err) };
+ if err != 0 {
+ return Err("Failed to create timeout manager, null");
+ }
+ let tos = NonNull::new(tos).ok_or("Failed to create timeout manager, null")?;
+ Ok(TimeoutManager { tos })
+ }
+
+ // get raw pointer
+ fn get_raw(&self) -> *const timeouts {
+ self.tos.as_ptr()
+ }
+
+ // get raw mut pointer
+ fn get_raw_mut(&self) -> *mut timeouts {
+ self.tos.as_ptr()
+ }
+
+ // close
+ pub fn close(&mut self) {
+ unsafe {
+ timeouts_close(self.get_raw_mut());
+ }
+ }
+
+ fn update_time(&mut self, time: timeout_t, timeout_type: ToType) {
+ match timeout_type {
+ ToType::INT => unsafe { timeouts_step(self.get_raw_mut(), time) },
+ ToType::ABS => unsafe { timeouts_update(self.get_raw_mut(), time) },
+ ToType::Default => unsafe { timeouts_step(self.get_raw_mut(), time) },
+ }
+ }
+ /// update time: relative time
+ pub fn update_time_rel(&mut self, time: timeout_t) {
+ self.update_time(time, ToType::Default);
+ }
+ /// update time: absolute time
+ pub fn update_time_abs(&mut self, current_time: timeout_t) {
+ self.update_time(current_time, ToType::ABS);
+ }
+ /// get tos hz
+ pub fn get_hz(&self) -> timeout_t {
+ unsafe { timeouts_hz(self.get_raw()) }
+ }
+ /// return interval to next required update.
+ ///
+ /// careful for two case:
+ /// - return value could be u64::MAX, it means no timeout on timing wheel
+ /// - return value will always be less than the next most recent timeout expiration time
+ pub fn get_next_wait_time(&self) -> timeout_t {
+ unsafe { timeouts_timeout(self.get_raw_mut()) }
+ }
+ /// return true if any timeouts pending on timing wheel
+ pub fn any_pending(&self) -> bool {
+ unsafe { timeouts_pending(self.get_raw()) }
+ }
+ /// return true if any timeouts on expired queue
+ pub fn any_expired(&self) -> bool {
+ unsafe { timeouts_expired(self.get_raw()) }
+ }
+ // return true if TimeoutManager is effective
+ pub fn check(&self) -> bool {
+ unsafe { timeouts_check(self.get_raw_mut(), stderr) }
+ }
+}
+
+impl TimeoutManager {
+ /// add Timeout to timing wheel
+ /// Timeout type:
+ /// - INT: first expired on now + timeout, then expired at now + timeout + timeout + ...
+ /// even if it's expired,TimeoutManger will not auto renew it. must consume it use expired_timeouts.
+ /// - ABS: expired on timeout, then expired only once.
+ /// - Default: first expired on now + timeout, then expired only once.
+ /// Pass in ownership of the Timeout object
+ pub fn add(&mut self, to: Timeout, timeout: timeout_t) {
+ let to_ptr = Box::into_raw(Box::new(to));
+ unsafe { timeouts_add(self.get_raw_mut(), to_ptr, timeout) };
+ }
+ /// remove Timeout from any timing wheel or expired queue (okay if on neither)
+ /// No ownership of the Timeout object is passed in
+ pub fn delete(&mut self, to: &mut Timeout) {
+ unsafe { timeouts_del(self.get_raw_mut(), to) };
+ }
+
+ /// consume expired timeout
+ /// return next expired timeout until the queue is empty, then returns NULL.
+ /// ToR::OneTime: Ownership of timeout objects moved from C to rust
+ /// all pending/expired flag has cleared.
+ /// ToR::Periodic: Ownership still on C side
+ pub(crate) fn expired_timeout<'a>(&'a mut self) -> Option<ToR> {
+ let to_ptr: *mut timeout = unsafe { timeouts_get(self.get_raw_mut()) };
+ if to_ptr.is_null() {
+ return None;
+ }
+ if unsafe { (*to_ptr).is_periodic() } {
+ return Some(ToR::Periodic(unsafe { &*to_ptr }));
+ }
+ return Some(ToR::OneTime(Timeout::transfer(to_ptr)));
+ }
+ /// return next expired timeout iterator
+ /// ToR::OneTime: Ownership of timeout objects moved from C to rust
+ /// all pending/expired flag has cleared.
+ /// ToR::Periodic: Ownership still on C side
+ pub fn expired_timeouts(&mut self) -> ExpireIter {
+ ExpireIter {
+ timeout_manager: self,
+ }
+ }
+ ///
+ /// return next quote of timeout as timeout_sit requested, or NULL if none
+ /// No ownership of the Timeout object is passed in from C to rust
+ /// No consume any timeout
+ pub(crate) fn next_timeout<'a>(&mut self, tos_it: &mut TomIt) -> Option<&'a Timeout> {
+ let to_ptr: *mut timeout = unsafe { timeouts_next(self.get_raw_mut(), tos_it) };
+ if to_ptr.is_null() {
+ return None;
+ }
+ return Some(unsafe { &*to_ptr });
+ }
+ /// return next timeout quote iterator(as requested by tos_it_type)
+ pub fn next_timeouts(&mut self, tos_it_type: TomItType) -> NextIter {
+ let tos_it = TomIt::new(tos_it_type);
+ NextIter {
+ timeout_manager: self,
+ timeout_sit: tos_it,
+ }
+ }
+}
+
+impl Drop for TimeoutManager {
+ fn drop(&mut self) {
+ self.close();
+ }
+}
+
+impl TimeoutManager {
+ pub fn next_timeouts_pending(&mut self) -> NextIter {
+ self.next_timeouts(TomItType::PENDING)
+ }
+ pub fn next_timeouts_expired(&mut self) -> NextIter {
+ self.next_timeouts(TomItType::EXPIRED)
+ }
+ pub fn next_timeouts_all(&mut self) -> NextIter {
+ self.next_timeouts(TomItType::ALL)
+ }
+}
+
+pub struct ExpireIter<'a> {
+ timeout_manager: &'a mut TimeoutManager,
+}
+
+impl<'a> Iterator for ExpireIter<'a> {
+ type Item = ToR;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.timeout_manager.expired_timeout()
+ }
+}
+
+pub struct NextIter<'a> {
+ timeout_manager: &'a mut TimeoutManager,
+ timeout_sit: TomIt,
+}
+
+impl<'a> Iterator for NextIter<'a> {
+ type Item = &'a Timeout;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.timeout_manager.next_timeout(&mut self.timeout_sit)
+ }
+}
+
+#[cfg(test)]
+#[allow(unused_variables)]
+mod tests {
+ use std::{cell::RefCell, rc::Rc};
+
+ use super::*;
+
+ #[test]
+ fn test_timeout_type() {
+ let int_type = ToType::INT;
+ let abs_type = ToType::ABS;
+ let default_type = ToType::Default;
+
+ assert_eq!(i32::from(int_type), TIMEOUT_INT);
+ assert_eq!(i32::from(abs_type), TIMEOUT_ABS);
+ assert_eq!(i32::from(default_type), TIMEOUT_DEFAULT);
+
+ assert_eq!(ToType::new(TIMEOUT_INT), int_type);
+ assert_eq!(ToType::new(TIMEOUT_ABS), abs_type);
+ assert_eq!(ToType::new(123), default_type);
+ }
+
+ #[test]
+ fn test_timeout() {
+ let to = Timeout::new(ToType::Default).unwrap(); // relative timeout
+ assert!(!to.is_pending());
+ assert!(!to.is_expired());
+
+ let to2 = Timeout::new(ToType::INT).unwrap(); // relative timeout
+ assert!(!to2.is_pending());
+ assert!(!to2.is_expired());
+
+ let mut tos = TimeoutManager::new(TIMEOUT_mHZ).unwrap();
+ tos.update_time_rel(0); // tos.now = 0
+ tos.add(to, 100); // onetime, expired time = tos.now + 100
+ tos.add(to2, 100); // periodic
+
+ let mut tos_it = TomIt::new(TomItType::PENDING);
+ let to = tos.next_timeout(&mut tos_it).unwrap();
+ let to2 = tos.next_timeout(&mut tos_it).unwrap();
+
+ tos.update_time_rel(1); // tos.now = 1
+ assert!(to.is_pending());
+ assert!(!to.is_expired());
+
+ tos.update_time_rel(98); // tos.now = 99
+ assert!(to.is_pending());
+ assert!(!to.is_expired());
+
+ tos.update_time_rel(10); // tos.now = 109
+
+ for to in tos.next_timeouts(TomItType::ALL) {
+ assert!(!to.is_pending());
+ assert!(to.is_expired());
+ }
+
+ for to in tos.expired_timeouts() {
+ match to {
+ ToR::OneTime(temp) => {
+ // all flag has cleared
+ // assert!(to.is_expired());
+ }
+ ToR::Periodic(temp) => {
+ assert!(temp.is_periodic()); // next expired time = 200
+ }
+ };
+ }
+
+ tos.update_time_rel(110); // tos.now = 219
+
+ let mut temp_flag = false;
+
+ for to in tos.expired_timeouts() {
+ match to {
+ ToR::OneTime(temp) => {
+ // all flag has cleared
+ // assert!(to.is_expired());
+ }
+ ToR::Periodic(temp) => {
+ assert!(temp.is_periodic());
+ temp_flag = true;
+ }
+ };
+ }
+
+ assert!(temp_flag);
+ }
+
+ #[test]
+ fn test_timeout_sit_flag_into_i32() {
+ let pending = TomItType::PENDING;
+ let expired = TomItType::EXPIRED;
+ let all = TomItType::ALL;
+
+ assert_eq!(i32::from(pending), TIMEOUTS_PENDING);
+ assert_eq!(i32::from(expired), TIMEOUTS_EXPIRED);
+ assert_eq!(i32::from(all), TIMEOUTS_ALL);
+
+ assert_eq!(TomItType::new(TIMEOUTS_PENDING), pending);
+ assert_eq!(TomItType::new(TIMEOUTS_EXPIRED), expired);
+ assert_eq!(TomItType::new(TIMEOUTS_ALL), all);
+ assert_eq!(TomItType::new(123), all);
+ }
+
+ #[test]
+ fn test_timeout_manger() {
+ let mut tos = TimeoutManager::new(TIMEOUT_mHZ).unwrap();
+ assert_eq!(tos.get_hz(), TIMEOUT_mHZ);
+ assert!(tos.check());
+
+ tos.update_time_abs(0);
+ assert_eq!(tos.get_next_wait_time(), u64::MAX); // no timeout wait, so wait time is u64::MAX
+
+ let timeout = Timeout::new(ToType::Default).unwrap(); // relative timeout
+ tos.add(timeout, 100); // expired time = tos.now + 100
+
+ let mut tos_it = TomIt::new(TomItType::PENDING);
+ let to = tos.next_timeout(&mut tos_it);
+ let to = to.unwrap();
+
+ tos.update_time_abs(30);
+ assert!(tos.any_pending());
+ assert!(!tos.any_expired());
+ assert!(tos.get_next_wait_time() < 70);
+
+ assert!(to.is_pending());
+ assert!(!to.is_expired());
+
+ tos.update_time_abs(100);
+ assert!(!tos.any_pending());
+ assert!(tos.any_expired());
+ assert_eq!(tos.get_next_wait_time(), 0);
+
+ tos.update_time_abs(150);
+
+ assert!(!to.is_pending());
+ assert!(to.is_expired());
+
+ let timeout2 = tos.expired_timeout();
+ assert!(timeout2.is_some());
+ }
+
+ // just for test
+ #[derive(Clone, Debug, PartialEq, Eq)]
+ pub struct Session {
+ pub session_id: String,
+ }
+ impl Drop for Session {
+ fn drop(&mut self) {
+ println!("drop session: {}", self.session_id);
+ }
+ }
+
+ #[test]
+ #[allow(unused_variables)]
+ fn test_cb() {
+ let mut timeout = Timeout::new(ToType::Default).unwrap();
+
+ extern "C" fn rust_callback2(arg: &mut i32) {
+ println!("Callback executed with arg: {}", arg);
+ }
+ let mut binding = 11;
+ let cb = TimeoutCallBack::new(rust_callback2, &mut binding);
+ timeout.set_cb(cb);
+ timeout.run_cb();
+
+ extern "C" fn rust_callback3(arg: &mut Rc<RefCell<Session>>) {
+ let value = arg.borrow();
+ println!("Callback executed with session_id: {}", value.session_id);
+ }
+
+ let session = Session {
+ session_id: "123".to_string(),
+ };
+ let session_ref = Rc::new(RefCell::new(session));
+ let mut binding = session_ref.clone();
+ let cb = TimeoutCallBack::new(rust_callback3, &mut binding);
+ timeout.set_cb(cb);
+ timeout.run_cb();
+ }
+
+ #[test]
+ #[allow(unused_variables)]
+ fn test_cb_closure() {
+ let mut timeout = Timeout::new(ToType::Default).unwrap();
+
+ let mut session = Session {
+ session_id: "123".to_string(),
+ };
+
+ timeout.set_cb_closure(&mut || {
+ println!("Callback executed with session_id: {}", session.session_id);
+ session.session_id = "456".to_string();
+ println!("Callback executed with session_id: {}", session.session_id);
+ });
+
+ timeout.run_cb();
+ }
+}
diff --git a/bindings/rs-timeout/src/timeout_bind.rs b/bindings/rs-timeout/src/timeout_bind.rs
new file mode 100644
index 0000000..bb65062
--- /dev/null
+++ b/bindings/rs-timeout/src/timeout_bind.rs
@@ -0,0 +1,246 @@
+#![allow(non_upper_case_globals)] // allow snake case just in this file
+use std::ptr::null_mut;
+
+use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, FILE};
+// TODO Check conditional compilation options
+
+/// V E R S I O N I N T E R F A C E S
+
+pub const TIMEOUT_VENDOR: &[u8; 26] = b"[email protected]";
+pub const TIMEOUT_V_REL: u32 = 538313254;
+pub const TIMEOUT_V_ABI: u32 = 538313252;
+pub const TIMEOUT_V_API: u32 = 538313254;
+
+extern "C" {
+ pub fn timeout_version() -> c_int;
+ pub fn timeout_vendor() -> *const c_char;
+ pub fn timeout_v_rel() -> c_int;
+ pub fn timeout_v_abi() -> c_int;
+ pub fn timeout_v_api() -> c_int;
+}
+
+/// I N T E G E R T Y P E I N T E R F A C E S
+
+// Originally a macro definition, use static variables instead
+// test-timeout.c pass test
+pub const TIMEOUT_PRIu: &[u8; 3] = b"lu\0"; // C 语言字符串结尾 `\0`, rust 没有
+pub const TIMEOUT_PRIx: &[u8; 3] = b"lx\0";
+pub const TIMEOUT_PRIX: &[u8; 3] = b"lX\0";
+
+pub const TIMEOUT_mHZ: u64 = 1000;
+pub const TIMEOUT_uHZ: u64 = 1000000;
+pub const TIMEOUT_nHZ: u64 = 1000000000;
+
+pub type timeout_t = u64;
+pub type timeout_error_t = usize;
+
+/// C A L L B A C K I N T E R F A C E
+///
+/// Callback function parameters unspecified to make embedding into existing
+/// applications easier.
+
+// #[cfg(TIMEOUT_CB_OVERRIDE)]
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct timeout_cb {
+ pub fn_ptr: Option<unsafe extern "C" fn(arg: *mut c_void)>,
+ pub arg: *mut c_void,
+}
+
+/// T I M E O U T I N T E R F A C E S
+
+// #[cfg(TIMEOUT_DISABLE_INTERVALS)]
+pub const TIMEOUT_DEFAULT: i32 = 0;
+pub const TIMEOUT_INT: i32 = 1;
+pub const TIMEOUT_ABS: i32 = 2;
+
+// macro TIMEOUT_INITIALIZER timeout_setcb
+
+// from TAILQ_ENTRY macro
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct timeout_TAILQ_ENTRY {
+ pub tqe_next: *mut timeout,
+ pub tqe_prev: *mut *mut timeout,
+}
+
+#[repr(C)]
+#[derive(Debug, Clone)]
+pub struct timeout {
+ pub flags: c_int,
+ pub expires: timeout_t, /* absolute expiration time */
+ pub pending: *mut timeout_list, /* timeout list if pending on wheel or expiry queue */
+ pub tqe: timeout_TAILQ_ENTRY, /* entry member for struct timeout_list lists */
+ // #[cfg(TIMEOUT_DISABLE_CALLBACKS)]
+ pub callback: timeout_cb, /* optional callback information */
+ // #[cfg(TIMEOUT_DISABLE_INTERVALS)]
+ pub interval: timeout_t, /* timeout interval if periodic */
+ // #[cfg(TIMEOUT_DISABLE_RELATIVE_ACCESS)]
+ pub timeouts: *mut timeouts, /* timeouts collection if member of */
+}
+
+impl Default for timeout {
+ fn default() -> Self {
+ timeout {
+ flags: 0,
+ expires: 0,
+ pending: null_mut(),
+ tqe: timeout_TAILQ_ENTRY {
+ tqe_next: null_mut(),
+ tqe_prev: null_mut(),
+ },
+ callback: timeout_cb {
+ fn_ptr: None,
+ arg: null_mut(),
+ },
+ interval: 0,
+ timeouts: null_mut(),
+ }
+ }
+}
+
+extern "C" {
+ /* initialize timeout structure (same as TIMEOUT_INITIALIZER) */
+ pub fn timeout_init(arg1: *mut timeout, arg2: c_int) -> *mut timeout;
+}
+// #[cfg(TIMEOUT_DISABLE_RELATIVE_ACCESS)]
+
+extern "C" {
+ /* true if on timing wheel, false otherwise */
+ pub fn timeout_pending(arg1: *mut timeout) -> bool;
+ /* true if on expired queue, false otherwise */
+ pub fn timeout_expired(arg1: *mut timeout) -> bool;
+ /* remove timeout from any timing wheel (okay if not member of any) */
+ pub fn timeout_del(arg1: *mut timeout);
+}
+
+/// T I M I N G W H E E L I N T E R F A C E S
+
+// 原定义在 timeout.c line 206 | TAILQ_HEAD(timeout_list, timeout);
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct timeout_list {
+ pub tqh_first: *mut timeout,
+ pub tqh_last: *mut *mut timeout,
+}
+
+// for timeouts
+const WHEEL_LEN: usize = 64; // timeout.c line 132 | (1U << WHEEL_BIT ) ,WHEEL_BIT = 6
+const WHEEL_NUM: usize = 4;
+pub type wheel_t = u64;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct timeouts {
+ pub wheel: [[timeout_list; WHEEL_LEN]; WHEEL_NUM],
+ pub expired: timeout_list,
+ pub pending: [wheel_t; WHEEL_NUM],
+ pub curtime: timeout_t,
+ pub hertz: timeout_t,
+}
+
+extern "C" {
+ /* open a new timing wheel, setting optional HZ (for float conversions) */
+ pub fn timeouts_open(arg1: timeout_t, arg2: *mut timeout_error_t) -> *mut timeouts;
+ /* destroy timing wheel */
+ pub fn timeouts_close(arg1: *mut timeouts);
+ /* return HZ setting (for float conversions) */
+ pub fn timeouts_hz(arg1: *const timeouts) -> timeout_t;
+ /* update timing wheel with current absolute time */
+ pub fn timeouts_update(arg1: *mut timeouts, arg2: timeout_t);
+ /* step timing wheel by relative time */
+ pub fn timeouts_step(arg1: *mut timeouts, arg2: timeout_t);
+ /* return interval to next required update */
+ pub fn timeouts_timeout(arg1: *mut timeouts) -> timeout_t;
+ /* add timeout to timing wheel */
+ pub fn timeouts_add(arg1: *mut timeouts, arg2: *mut timeout, arg3: timeout_t);
+ /* remove timeout from any timing wheel or expired queue (okay if on neither) */
+ pub fn timeouts_del(arg1: *mut timeouts, arg2: *mut timeout);
+ /* return any expired timeout (caller should loop until NULL-return) */
+ pub fn timeouts_get(arg1: *mut timeouts) -> *mut timeout;
+ /* return true if any timeouts pending on timing wheel */
+ pub fn timeouts_pending(arg1: *const timeouts) -> bool;
+ /* return true if any timeouts on expired queue */
+ pub fn timeouts_expired(arg1: *const timeouts) -> bool;
+ /* return true if invariants hold. describes failures to optional file handle. */
+ // arg2 use stderr is normal
+ pub fn timeouts_check(arg1: *mut timeouts, arg2: *mut FILE) -> bool;
+}
+
+extern "C" {
+ pub static mut stderr: *mut FILE;
+}
+
+pub const TIMEOUTS_PENDING: i32 = 16;
+pub const TIMEOUTS_EXPIRED: i32 = 32;
+pub const TIMEOUTS_ALL: i32 = 48;
+pub const TIMEOUTS_CLEAR: i32 = 64;
+
+// Macro definitions originally in C can be converted into C-compatible functions.
+#[no_mangle]
+pub extern "C" fn TIMEOUTS_IT_INITIALIZER(flags: c_int) -> timeouts_it {
+ timeouts_it {
+ flags: flags,
+ pc: 0,
+ i: 0,
+ j: 0,
+ to: null_mut(),
+ }
+}
+// Macro definitions originally in C can be converted into C-compatible functions.
+#[no_mangle]
+pub extern "C" fn TIMEOUTS_IT_INIT(cur: &mut timeouts_it, flags: i32) {
+ cur.flags = flags;
+ cur.pc = 0;
+}
+
+#[repr(C)]
+#[derive(Debug, Clone)]
+pub struct timeouts_it {
+ pub flags: c_int,
+ pub pc: c_uint,
+ pub i: c_uint,
+ pub j: c_uint,
+ pub to: *mut timeout,
+}
+
+impl Default for timeouts_it {
+ fn default() -> Self {
+ timeouts_it {
+ flags: 0,
+ pc: 0,
+ i: 0,
+ j: 0,
+ to: null_mut(),
+ }
+ }
+}
+
+/* return next timeout in pending wheel or expired queue. caller can delete
+ * the returned timeout, but should not otherwise manipulate the timing
+ * wheel. in particular, caller SHOULD NOT delete any other timeout as that
+ * could invalidate cursor state and trigger a use-after-free.
+ */
+// #[link(name = "timeout")]
+extern "C" {
+ pub fn timeouts_next(arg1: *mut timeouts, arg2: *mut timeouts_it) -> *mut timeout;
+}
+
+/*
+ * Calculate the interval our caller can wait before needing to process
+ * events.
+ */
+
+// extern "C" {
+// pub fn timeouts_int(T: *mut timeouts) -> timeout_t;
+// }
+// pub fn timeouts_timeout(t: *mut timeouts) -> timeout_t {
+// unsafe {
+// if !((*t).expired.tqh_first.is_null()) {
+// return 0;
+// }
+// timeouts_int(t)
+// }
+// }
+
+// B O N U S W H E E L I N T E R F A C E S
+// just use for lua, not use for rust.
diff --git a/bindings/rs-timeout/src/timeout_bind_test.rs b/bindings/rs-timeout/src/timeout_bind_test.rs
new file mode 100644
index 0000000..2438592
--- /dev/null
+++ b/bindings/rs-timeout/src/timeout_bind_test.rs
@@ -0,0 +1,266 @@
+use std::{
+ mem::{align_of, size_of, MaybeUninit},
+ ptr::addr_of,
+};
+
+use crate::timeout_bind::*;
+use std::ffi::CStr;
+
+#[cfg(test)]
+// V E R S I O N I N T E R F A C E S
+#[test]
+fn test_timeout_version() {
+ let version = unsafe { timeout_version() };
+ assert_eq!(version, TIMEOUT_V_REL as i32);
+}
+
+#[test]
+fn test_timeout_vendor() {
+ let vendor = unsafe { CStr::from_ptr(timeout_vendor()).to_bytes() };
+ assert_eq!(vendor, TIMEOUT_VENDOR);
+}
+
+#[test]
+fn test_timeout_v_rel() {
+ let v_rel = unsafe { timeout_v_rel() };
+ assert_eq!(v_rel, TIMEOUT_V_REL as i32);
+}
+
+#[test]
+fn test_timeout_v_abi() {
+ let v_abi = unsafe { timeout_v_abi() };
+ assert_eq!(v_abi, TIMEOUT_V_ABI as i32);
+}
+
+#[test]
+fn test_timeout_v_api() {
+ let v_api = unsafe { timeout_v_api() };
+ assert_eq!(v_api, TIMEOUT_V_API as i32);
+}
+
+// C A L L B A C K I N T E R F A C E
+
+#[test]
+fn bindgen_test_layout_timeout_cb() {
+ const UNINIT: MaybeUninit<timeout_cb> = MaybeUninit::uninit();
+ let ptr = UNINIT.as_ptr();
+ assert_eq!(
+ size_of::<timeout_cb>(),
+ 16usize,
+ concat!("Size of: ", stringify!(timeout_cb))
+ );
+ assert_eq!(
+ align_of::<timeout_cb>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(timeout_cb))
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).fn_ptr) as usize - ptr as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout_cb),
+ "::",
+ stringify!(fn_)
+ )
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).arg) as usize - ptr as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout_cb),
+ "::",
+ stringify!(arg)
+ )
+ );
+}
+
+/// T I M E O U T I N T E R F A C E S
+
+#[test]
+fn bindgen_test_layout_timeout_TAILQ_ENTRY() {
+ const UNINIT: MaybeUninit<timeout_TAILQ_ENTRY> = MaybeUninit::uninit();
+ let ptr = UNINIT.as_ptr();
+ assert_eq!(
+ size_of::<timeout_TAILQ_ENTRY>(),
+ 16usize,
+ concat!("Size of: ", stringify!(timeout__bindgen_ty_1))
+ );
+ assert_eq!(
+ align_of::<timeout_TAILQ_ENTRY>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(timeout__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).tqe_next) as usize - ptr as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout__bindgen_ty_1),
+ "::",
+ stringify!(tqe_next)
+ )
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).tqe_prev) as usize - ptr as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout__bindgen_ty_1),
+ "::",
+ stringify!(tqe_prev)
+ )
+ );
+}
+#[test]
+fn bindgen_test_layout_timeout() {
+ const UNINIT: MaybeUninit<timeout> = MaybeUninit::uninit();
+ let ptr = UNINIT.as_ptr();
+ assert_eq!(
+ size_of::<timeout>(),
+ 72usize,
+ concat!("Size of: ", stringify!(timeout))
+ );
+ assert_eq!(
+ align_of::<timeout>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(timeout))
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).flags) as usize - ptr as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).expires) as usize - ptr as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout),
+ "::",
+ stringify!(expires)
+ )
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).pending) as usize - ptr as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout),
+ "::",
+ stringify!(pending)
+ )
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).tqe) as usize - ptr as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout),
+ "::",
+ stringify!(tqe)
+ )
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).callback) as usize - ptr as usize },
+ 40usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout),
+ "::",
+ stringify!(callback)
+ )
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).interval) as usize - ptr as usize },
+ 56usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout),
+ "::",
+ stringify!(interval)
+ )
+ );
+ assert_eq!(
+ unsafe { addr_of!((*ptr).timeouts) as usize - ptr as usize },
+ 64usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeout),
+ "::",
+ stringify!(timeouts)
+ )
+ );
+}
+
+#[test]
+fn bindgen_test_layout_timeouts_it() {
+ const UNINIT: MaybeUninit<timeouts_it> = MaybeUninit::uninit();
+ let ptr = UNINIT.as_ptr();
+ assert_eq!(
+ size_of::<timeouts_it>(),
+ 24usize,
+ concat!("Size of: ", stringify!(timeouts_it))
+ );
+ assert_eq!(
+ align_of::<timeouts_it>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(timeouts_it))
+ );
+ assert_eq!(
+ unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeouts_it),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { ::std::ptr::addr_of!((*ptr).pc) as usize - ptr as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeouts_it),
+ "::",
+ stringify!(pc)
+ )
+ );
+ assert_eq!(
+ unsafe { ::std::ptr::addr_of!((*ptr).i) as usize - ptr as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeouts_it),
+ "::",
+ stringify!(i)
+ )
+ );
+ assert_eq!(
+ unsafe { ::std::ptr::addr_of!((*ptr).j) as usize - ptr as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeouts_it),
+ "::",
+ stringify!(j)
+ )
+ );
+ assert_eq!(
+ unsafe { ::std::ptr::addr_of!((*ptr).to) as usize - ptr as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeouts_it),
+ "::",
+ stringify!(to)
+ )
+ );
+}