use std::ffi::CString;
use std::marker::PhantomData;
use std::os::raw::c_void;
use va_list::VaList;
use crate::error::{Error, TarantoolError};
pub struct Fiber<'a, T: 'a> {
inner: *mut ffi::Fiber,
callback: *mut c_void,
phantom: PhantomData<&'a T>,
}
impl<'a, T> Fiber<'a, T> {
pub fn new<F>(name: &str, callback: &mut F) -> Self
where
F: FnMut(Box<T>) -> i32,
{
let (callback_ptr, trampoline) = unsafe { unpack_callback(callback) };
Self {
inner: unsafe { ffi::fiber_new(CString::new(name).unwrap().as_ptr(), trampoline) },
callback: callback_ptr,
phantom: PhantomData,
}
}
pub fn new_with_attr<F>(name: &str, attr: &FiberAttr, callback: &mut F) -> Self
where
F: FnMut(Box<T>) -> i32,
{
let (callback_ptr, trampoline) = unsafe { unpack_callback(callback) };
Self {
inner: unsafe {
ffi::fiber_new_ex(CString::new(name).unwrap().as_ptr(), attr.inner, trampoline)
},
callback: callback_ptr,
phantom: PhantomData,
}
}
pub fn start(&mut self, arg: T) {
unsafe {
ffi::fiber_start(self.inner, self.callback, Box::into_raw(Box::<T>::new(arg)));
}
}
pub fn wakeup(&self) {
unsafe { ffi::fiber_wakeup(self.inner) }
}
pub fn join(&self) -> i32 {
unsafe { ffi::fiber_join(self.inner) }
}
pub fn set_joinable(&mut self, is_joinable: bool) {
unsafe { ffi::fiber_set_joinable(self.inner, is_joinable) }
}
pub fn cancel(&mut self) {
unsafe { ffi::fiber_cancel(self.inner) }
}
}
pub fn set_cancellable(is_cancellable: bool) -> bool {
unsafe { ffi::fiber_set_cancellable(is_cancellable) }
}
pub fn is_cancelled() -> bool {
unsafe { ffi::fiber_is_cancelled() }
}
pub fn sleep(time: f64) {
unsafe { ffi::fiber_sleep(time) }
}
pub fn time() -> f64 {
unsafe { ffi::fiber_time() }
}
pub fn time64() -> u64 {
unsafe { ffi::fiber_time64() }
}
pub fn clock() -> f64 {
unsafe { ffi::fiber_clock() }
}
pub fn clock64() -> u64 {
unsafe { ffi::fiber_clock64() }
}
pub fn fiber_yield() {
unsafe { ffi::fiber_yield() }
}
pub fn reschedule() {
unsafe { ffi::fiber_reschedule() }
}
pub struct FiberAttr {
inner: *mut ffi::FiberAttr,
}
impl FiberAttr {
pub fn new() -> Self {
FiberAttr {
inner: unsafe { ffi::fiber_attr_new() },
}
}
pub fn stack_size(&self) -> usize {
unsafe { ffi::fiber_attr_getstacksize(self.inner) }
}
pub fn set_stack_size(&mut self, stack_size: usize) -> Result<(), Error> {
if unsafe { ffi::fiber_attr_setstacksize(self.inner, stack_size) } < 0 {
Err(TarantoolError::last().into())
} else {
Ok(())
}
}
}
impl Drop for FiberAttr {
fn drop(&mut self) {
unsafe { ffi::fiber_attr_delete(self.inner) }
}
}
pub struct Cond {
inner: *mut ffi::FiberCond,
}
impl Cond {
pub fn new() -> Self {
Cond {
inner: unsafe { ffi::fiber_cond_new() },
}
}
pub fn signal(&self) {
unsafe { ffi::fiber_cond_signal(self.inner) }
}
pub fn broadcast(&self) {
unsafe { ffi::fiber_cond_broadcast(self.inner) }
}
pub fn wait_timeout(&self, timeout: f64) -> bool {
!(unsafe { ffi::fiber_cond_wait_timeout(self.inner, timeout) } < 0)
}
pub fn wait(&self) -> bool {
!(unsafe { ffi::fiber_cond_wait(self.inner) } < 0)
}
}
impl Drop for Cond {
fn drop(&mut self) {
unsafe { ffi::fiber_cond_delete(self.inner) }
}
}
pub struct Latch {
inner: *mut ffi::Latch,
}
impl Latch {
pub fn new() -> Self {
Latch {
inner: unsafe { ffi::box_latch_new() },
}
}
pub fn lock(&self) -> LatchGuard {
unsafe { ffi::box_latch_lock(self.inner) };
LatchGuard { latch: self }
}
pub fn try_lock(&self) -> Option<LatchGuard> {
if unsafe { ffi::box_latch_trylock(self.inner) } == 0 {
Some(LatchGuard { latch: self })
} else {
None
}
}
}
impl Drop for Latch {
fn drop(&mut self) {
unsafe { ffi::box_latch_delete(self.inner) }
}
}
pub struct LatchGuard<'a> {
latch: &'a Latch,
}
impl<'a> Drop for LatchGuard<'a> {
fn drop(&mut self) {
unsafe { ffi::box_latch_unlock(self.latch.inner) }
}
}
pub(crate) unsafe fn unpack_callback<F, T>(callback: &mut F) -> (*mut c_void, ffi::FiberFunc)
where
F: FnMut(Box<T>) -> i32,
{
unsafe extern "C" fn trampoline<F, T>(mut args: VaList) -> i32
where
F: FnMut(Box<T>) -> i32,
{
let closure: &mut F = &mut *(args.get::<*const c_void>() as *mut F);
let arg = Box::from_raw(args.get::<*const c_void>() as *mut T);
(*closure)(arg)
}
(callback as *mut F as *mut c_void, Some(trampoline::<F, T>))
}
mod ffi {
use std::os::raw::{c_char, c_int};
use va_list::VaList;
#[repr(C)]
pub struct Fiber {
_unused: [u8; 0],
}
pub type FiberFunc = Option<unsafe extern "C" fn(VaList) -> c_int>;
extern "C" {
pub fn fiber_new(name: *const c_char, f: FiberFunc) -> *mut Fiber;
pub fn fiber_new_ex(
name: *const c_char,
fiber_attr: *const FiberAttr,
f: FiberFunc,
) -> *mut Fiber;
pub fn fiber_yield();
pub fn fiber_start(callee: *mut Fiber, ...);
pub fn fiber_wakeup(f: *mut Fiber);
pub fn fiber_cancel(f: *mut Fiber);
pub fn fiber_set_cancellable(yesno: bool) -> bool;
pub fn fiber_set_joinable(fiber: *mut Fiber, yesno: bool);
pub fn fiber_join(f: *mut Fiber) -> c_int;
pub fn fiber_sleep(s: f64);
pub fn fiber_is_cancelled() -> bool;
pub fn fiber_time() -> f64;
pub fn fiber_time64() -> u64;
pub fn fiber_clock() -> f64;
pub fn fiber_clock64() -> u64;
pub fn fiber_reschedule();
}
#[repr(C)]
pub struct FiberAttr {
_unused: [u8; 0],
}
extern "C" {
pub fn fiber_attr_new() -> *mut FiberAttr;
pub fn fiber_attr_delete(fiber_attr: *mut FiberAttr);
pub fn fiber_attr_setstacksize(fiber_attr: *mut FiberAttr, stack_size: usize) -> c_int;
pub fn fiber_attr_getstacksize(fiber_attr: *mut FiberAttr) -> usize;
}
#[repr(C)]
pub struct FiberCond {
_unused: [u8; 0],
}
extern "C" {
pub fn fiber_cond_new() -> *mut FiberCond;
pub fn fiber_cond_delete(cond: *mut FiberCond);
pub fn fiber_cond_signal(cond: *mut FiberCond);
pub fn fiber_cond_broadcast(cond: *mut FiberCond);
pub fn fiber_cond_wait_timeout(cond: *mut FiberCond, timeout: f64) -> c_int;
pub fn fiber_cond_wait(cond: *mut FiberCond) -> c_int;
}
#[repr(C)]
pub struct Latch {
_unused: [u8; 0],
}
extern "C" {
pub fn box_latch_new() -> *mut Latch;
pub fn box_latch_delete(latch: *mut Latch);
pub fn box_latch_lock(latch: *mut Latch);
pub fn box_latch_trylock(latch: *mut Latch) -> c_int;
pub fn box_latch_unlock(latch: *mut Latch);
}
}