diff options
Diffstat (limited to 'bindings/rs-timeout/src')
| -rw-r--r-- | bindings/rs-timeout/src/lib.rs | 4 | ||||
| -rw-r--r-- | bindings/rs-timeout/src/timeout.rs | 634 | ||||
| -rw-r--r-- | bindings/rs-timeout/src/timeout_bind.rs | 246 | ||||
| -rw-r--r-- | bindings/rs-timeout/src/timeout_bind_test.rs | 266 |
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) + ) + ); +} |
