use crate::rubysys::{
libc::timeval,
types::{c_int, c_void, Argc, BlockCallFunction, CallbackMutPtr, CallbackPtr, Id, Value},
};
#[cfg(any(unix, windows))]
use crate::rubysys::types::RawFd;
#[cfg_attr(rutie_dllimport, link(name = "rutie_ruby"))]
extern "C" {
pub fn rb_thread_call_without_gvl(
func: CallbackPtr,
args: *const c_void,
unblock_func: CallbackPtr,
unblock_args: *const c_void,
) -> *mut c_void;
pub fn rb_nogvl(
func: CallbackPtr,
data1: *mut c_void,
ubf: CallbackPtr,
data2: *mut c_void,
flags: c_int,
) -> *mut c_void;
pub fn rb_thread_call_without_gvl2(
func: CallbackPtr,
args: *const c_void,
unblock_func: CallbackPtr,
unblock_args: *const c_void,
) -> *mut c_void;
pub fn rb_thread_call_with_gvl(func: CallbackPtr, args: *const c_void) -> *mut c_void;
pub fn ruby_thread_has_gvl_p() -> c_int;
pub fn rb_thread_create(
function: rutie_callback!(type fn(*mut c_void) -> Value),
data: *mut c_void,
) -> Value;
#[cfg(any(unix, windows))]
pub fn rb_thread_wait_fd(fd: RawFd);
pub fn rb_thread_interrupted(thread: Value) -> c_int;
pub fn rb_fiber_alive_p(fiber: Value) -> Value;
pub fn rb_fiber_current() -> Value;
pub fn rb_fiber_new(func: BlockCallFunction, obj: Value) -> Value;
pub fn rb_fiber_new_storage(func: BlockCallFunction, obj: Value, storage: Value) -> Value;
pub fn rb_fiber_resume(fiber: Value, argc: Argc, argv: *const Value) -> Value;
pub fn rb_fiber_resume_kw(
fiber: Value,
argc: Argc,
argv: *const Value,
kw_splat: c_int,
) -> Value;
pub fn rb_fiber_yield(argc: Argc, argv: *const Value) -> Value;
pub fn rb_fiber_yield_kw(argc: Argc, argv: *const Value, kw_splat: c_int) -> Value;
pub fn rb_fiber_transfer(fiber: Value, argc: Argc, argv: *const Value) -> Value;
pub fn rb_fiber_transfer_kw(
fiber: Value,
argc: Argc,
argv: *const Value,
kw_splat: c_int,
) -> Value;
pub fn rb_fiber_raise(fiber: Value, argc: Argc, argv: *const Value) -> Value;
pub fn rb_obj_is_fiber(object: Value) -> Value;
pub fn rb_mutex_lock(mutex: Value) -> Value;
pub fn rb_mutex_locked_p(mutex: Value) -> Value;
pub fn rb_mutex_new() -> Value;
pub fn rb_mutex_sleep(mutex: Value, timeout: Value) -> Value;
pub fn rb_mutex_synchronize(
mutex: Value,
func: rutie_callback!(type fn(CallbackMutPtr) -> Value),
arg: CallbackMutPtr,
) -> Value;
pub fn rb_mutex_trylock(mutex: Value) -> Value;
pub fn rb_mutex_unlock(mutex: Value) -> Value;
pub fn rb_thread_alone() -> c_int;
pub fn rb_thread_atfork();
pub fn rb_thread_check_ints();
pub fn rb_thread_current() -> Value;
pub fn rb_thread_fd_writable(fd: c_int) -> c_int;
pub fn rb_thread_kill(thread: Value) -> Value;
pub fn rb_thread_local_aref(thread: Value, name: Id) -> Value;
pub fn rb_thread_local_aset(thread: Value, name: Id, value: Value) -> Value;
pub fn rb_thread_main() -> Value;
pub fn rb_thread_run(thread: Value) -> Value;
pub fn rb_thread_schedule();
pub fn rb_thread_sleep(seconds: c_int);
pub fn rb_thread_sleep_forever();
pub fn rb_thread_wait_for(time: timeval);
pub fn rb_thread_wakeup(thread: Value) -> Value;
}
pub type ExecRecursiveFunction =
rutie_callback!(type fn(obj: Value, arg: Value, recursive: c_int) -> Value);
#[cfg_attr(rutie_dllimport, link(name = "rutie_ruby"))]
extern "C" {
pub fn rb_exec_recursive(f: ExecRecursiveFunction, obj: Value, arg: Value) -> Value;
pub fn rb_exec_recursive_outer(f: ExecRecursiveFunction, obj: Value, arg: Value) -> Value;
pub fn rb_exec_recursive_paired(
f: ExecRecursiveFunction,
obj: Value,
paired: Value,
arg: Value,
) -> Value;
pub fn rb_exec_recursive_paired_outer(
f: ExecRecursiveFunction,
obj: Value,
paired: Value,
arg: Value,
) -> Value;
pub fn rb_thread_atfork_before_exec();
pub fn rb_thread_sleep_deadly();
pub fn rb_thread_stop() -> Value;
pub fn rb_thread_wakeup_alive(thread: Value) -> Value;
}
pub const RUBY_UBF_IO: usize = usize::MAX;
pub const RUBY_UBF_PROCESS: usize = usize::MAX;
pub const RUBY_INTERNAL_THREAD_EVENT_STARTED: u32 = 1 << 0;
pub const RUBY_INTERNAL_THREAD_EVENT_READY: u32 = 1 << 1;
pub const RUBY_INTERNAL_THREAD_EVENT_RESUMED: u32 = 1 << 2;
pub const RUBY_INTERNAL_THREAD_EVENT_SUSPENDED: u32 = 1 << 3;
pub const RUBY_INTERNAL_THREAD_EVENT_EXITED: u32 = 1 << 4;
pub const RUBY_INTERNAL_THREAD_EVENT_MASK: u32 = 0xff;
#[repr(C)]
pub struct InternalThreadEventData {
pub thread: Value,
}
pub type InternalThreadEventCallback = rutie_callback!(type fn(
event: u32,
event_data: *const InternalThreadEventData,
user_data: *mut c_void,
));
#[repr(C)]
pub struct InternalThreadEventHook {
_private: [u8; 0],
}
pub type InternalThreadSpecificKey = c_int;
pub const RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX: c_int = 8;
#[cfg_attr(rutie_dllimport, link(name = "rutie_ruby"))]
extern "C" {
pub fn rb_internal_thread_add_event_hook(
func: InternalThreadEventCallback,
events: u32,
data: *mut c_void,
) -> *mut InternalThreadEventHook;
pub fn rb_internal_thread_remove_event_hook(hook: *mut InternalThreadEventHook) -> bool;
pub fn rb_internal_thread_specific_key_create() -> InternalThreadSpecificKey;
pub fn rb_internal_thread_specific_get(
thread: Value,
key: InternalThreadSpecificKey,
) -> *mut c_void;
pub fn rb_internal_thread_specific_set(
thread: Value,
key: InternalThreadSpecificKey,
data: *mut c_void,
);
}
#[cfg_attr(rutie_dllimport, link(name = "rutie_ruby"))]
extern "C" {
pub fn rb_thread_lock_native_thread() -> bool;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binding::vm;
rutie_callback! {
fn report_recursion(_object: Value, _arg: Value, recursive: c_int) -> Value {
crate::util::bool_to_value(recursive != 0)
}
}
#[test]
fn test_sleep_deadly_and_recursion() {
crate::on_ruby_thread(|| unsafe {
vm::eval_string("main = Thread.current; $rutie_deadly = Thread.new { Thread.pass until main.stop?; main.wakeup }");
rb_thread_sleep_deadly();
vm::eval_string("$rutie_deadly.join");
let object = vm::eval_string("Object.new");
assert!(!rb_exec_recursive(report_recursion, object, object).is_true());
assert!(!rb_exec_recursive_outer(report_recursion, object, object).is_true());
assert!(!rb_exec_recursive_paired(report_recursion, object, object, object).is_true());
assert!(
!rb_exec_recursive_paired_outer(report_recursion, object, object, object).is_true()
);
assert!(rb_thread_wakeup_alive(rb_thread_current()).value == rb_thread_current().value);
let finished = vm::eval_string("Thread.new {}.tap(&:join)");
assert!(rb_thread_wakeup_alive(finished).is_nil());
assert!(vm::protect_value(|| rb_thread_stop()).is_err());
});
}
rutie_callback! {
fn add_one(data: *mut c_void) -> *mut c_void {
unsafe { *(data as *mut u32) += 1 };
data
}
}
#[test]
fn test_nogvl() {
crate::on_ruby_thread(|| unsafe {
let mut number = 41u32;
let data = &mut number as *mut u32 as *mut c_void;
let result = rb_nogvl(
add_one as CallbackPtr,
data,
std::ptr::null(),
std::ptr::null_mut(),
crate::rubysys::scheduler::RB_NOGVL_UBF_ASYNC_SAFE,
);
assert_eq!(result, data);
assert_eq!(number, 42);
});
}
}