#[cfg(feature = "async-rt")]
pub mod async_builder;
use std::{
ffi::CString,
path::{Path, PathBuf},
};
#[cfg(feature = "async-rt")]
pub use async_builder::*;
use jl_sys::jl_init;
#[cfg(any(julia_1_10, julia_1_11))]
use jl_sys::jl_init_with_image;
#[cfg(not(any(julia_1_10, julia_1_11)))]
use jl_sys::jl_init_with_image_file as jl_init_with_image;
use jlrs_sys::{jlrs_set_nthreadpools, jlrs_set_nthreads, jlrs_set_nthreads_per_pool};
#[cfg(any(feature = "multi-rt", feature = "local-rt"))]
use crate::error::JlrsResult;
#[cfg(feature = "async-rt")]
use crate::runtime::executor::Executor;
#[cfg(feature = "local-rt")]
use crate::runtime::handle::local_handle::LocalHandle;
#[cfg(feature = "multi-rt")]
use crate::runtime::handle::mt_handle::MtHandle;
use crate::{InstallJlrsCore, init_jlrs};
pub struct Builder {
pub(crate) image: Option<(PathBuf, PathBuf)>,
pub(crate) install_jlrs_core: InstallJlrsCore,
pub(crate) n_threads: usize,
pub(crate) n_threadsi: usize,
}
impl Builder {
pub const fn new() -> Self {
Builder {
image: None,
install_jlrs_core: InstallJlrsCore::Default,
n_threads: 0,
n_threadsi: 0,
}
}
#[cfg(feature = "local-rt")]
#[inline]
pub fn start_local(self) -> JlrsResult<LocalHandle> {
use crate::{error::RuntimeError, runtime::state::can_init};
if !can_init() {
Err(RuntimeError::AlreadyInitialized)?;
}
unsafe {
init_runtime(&self);
Ok(LocalHandle::new())
}
}
#[inline]
#[cfg(feature = "multi-rt")]
pub fn start_mt<'env, T: 'static + Send, F>(self, func: F) -> JlrsResult<T>
where
F: 'env + for<'scope> FnOnce(MtHandle<'scope, 'env>) -> T + Send,
{
mt_impl::sync_impl::start(self, func)
}
#[inline]
pub const fn n_threads(mut self, n: usize) -> Self {
self.n_threads = n;
self
}
#[inline]
pub const fn n_interactive_threads(mut self, n: usize) -> Self {
self.n_threadsi = n;
self
}
#[inline]
pub unsafe fn image<P, Q>(mut self, julia_bindir: P, image_path: Q) -> Result<Self, Self>
where
P: AsRef<Path> + Send + 'static,
Q: AsRef<Path> + Send + 'static,
{
if !julia_bindir.as_ref().exists() {
return Err(self);
}
if !image_path.as_ref().exists() {
return Err(self);
}
self.image = Some((
julia_bindir.as_ref().to_path_buf(),
image_path.as_ref().to_path_buf(),
));
Ok(self)
}
#[inline]
pub fn install_jlrs(mut self, install: InstallJlrsCore) -> Self {
self.install_jlrs_core = install;
self
}
#[cfg(feature = "async-rt")]
#[inline]
pub fn async_runtime<E: Executor<N>, const N: usize>(
self,
executor_opts: E,
) -> AsyncBuilder<E, N> {
let _: () = E::VALID;
AsyncBuilder::new(self, executor_opts)
}
}
#[cfg(feature = "multi-rt")]
mod mt_impl {
pub(super) mod sync_impl {
use std::thread;
use jl_sys::jl_atexit_hook;
use crate::{
error::{JlrsError, RuntimeError},
memory::gc::gc_safe,
prelude::JlrsResult,
runtime::{
builder::{Builder, init_runtime},
handle::{
mt_handle::{EXIT_LOCK, MtHandle, wait_loop},
wait,
},
state::{can_init, set_exit},
},
};
pub(crate) fn start<'env, T, F>(options: Builder, func: F) -> JlrsResult<T>
where
T: Send + 'static,
F: 'env + for<'scope> FnOnce(MtHandle<'scope, 'env>) -> T + Send,
{
if !can_init() {
Err(RuntimeError::AlreadyInitialized)?;
}
unsafe {
init_runtime(&options);
}
let ret = thread::scope(|scope| {
let handle = scope.spawn(|| unsafe {
thread::scope(|scope| {
let handle = MtHandle::new(scope);
func(handle)
})
});
unsafe {
wait_loop();
let th_res = handle.join();
gc_safe(|| wait(&EXIT_LOCK));
set_exit();
jl_atexit_hook(0);
th_res
}
});
match ret {
Ok(ret) => Ok(ret),
Err(e) => Err(JlrsError::exception(format!("{e:?}")))?,
}
}
}
}
unsafe fn init_runtime(options: &Builder) {
unsafe {
set_n_threads(options);
init_julia(options);
init_jlrs(&options.install_jlrs_core, true);
}
}
unsafe fn init_julia(options: &Builder) {
unsafe {
if let Some((bin_dir, image_path)) = options.image.as_ref() {
let julia_bindir_str = bin_dir.as_os_str().as_encoded_bytes();
let image_path_str = image_path.as_os_str().as_encoded_bytes();
let bindir = CString::new(julia_bindir_str).unwrap();
let im_rel_path = CString::new(image_path_str).unwrap();
jl_init_with_image(bindir.as_ptr(), im_rel_path.as_ptr())
} else {
jl_init();
}
}
}
unsafe fn set_n_threads(options: &Builder) {
unsafe {
if options.n_threadsi != 0 {
if options.n_threads == 0 {
jlrs_set_nthreads(-1);
jlrs_set_nthreadpools(2);
let perthread = Box::new([-1i16, options.n_threadsi as _]);
jlrs_set_nthreads_per_pool(Box::leak(perthread) as *const _);
} else {
let nthreads = options.n_threads as i16;
let nthreadsi = options.n_threadsi as i16;
jlrs_set_nthreads(nthreads + nthreadsi);
jlrs_set_nthreadpools(2);
let perthread = Box::new([nthreads, options.n_threadsi as _]);
jlrs_set_nthreads_per_pool(Box::leak(perthread) as *const _);
}
} else if options.n_threads == 0 {
jlrs_set_nthreads(-1);
jlrs_set_nthreadpools(1);
let perthread = Box::new(-1i16);
jlrs_set_nthreads_per_pool(Box::leak(perthread) as *const _);
} else {
let n_threads = options.n_threads as _;
jlrs_set_nthreads(n_threads);
jlrs_set_nthreadpools(1);
let perthread = Box::new(n_threads);
jlrs_set_nthreads_per_pool(Box::leak(perthread) as *const _);
}
}
}