use std::future::Future;
use std::io;
use std::num::NonZeroUsize;
use std::time::Duration;
pub const MAX_WORKER_THREADS: usize = 1024;
pub const MAX_THREAD_STACK_SIZE: usize = 256 * 1024 * 1024;
pub(crate) const DEFAULT_THREAD_NAME: &str = "lgwks-bot";
#[derive(Debug)]
pub struct Builder {
worker_threads: Option<NonZeroUsize>,
thread_name: String,
max_blocking_threads: Option<NonZeroUsize>,
thread_stack_size: Option<NonZeroUsize>,
}
impl Default for Builder {
fn default() -> Self {
Self {
worker_threads: None,
thread_name: String::from(DEFAULT_THREAD_NAME),
max_blocking_threads: None,
thread_stack_size: None,
}
}
}
impl Builder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn worker_threads(mut self, workers: Option<NonZeroUsize>) -> Self {
self.worker_threads = workers;
self
}
#[must_use]
pub fn thread_name(mut self, name: impl Into<String>) -> Self {
self.thread_name = name.into();
self
}
#[must_use]
pub fn max_blocking_threads(mut self, threads: Option<NonZeroUsize>) -> Self {
self.max_blocking_threads = threads;
self
}
#[must_use]
pub fn thread_stack_size(mut self, bytes: Option<NonZeroUsize>) -> Self {
self.thread_stack_size = bytes;
self
}
pub fn build(self) -> io::Result<Runtime> {
#[cfg(target_family = "wasm")]
if self.worker_threads.is_some() {
let refusal = Err(io::Error::new(
io::ErrorKind::Unsupported,
"lgwks_bot: worker_threads is unsupported on WASM",
));
lgwks_std::trace::debug!(error = ?refusal.as_ref().err(), "build: returning an error to the caller");
return refusal;
}
#[cfg(not(target_family = "wasm"))]
let workers = self.worker_threads.or_else(discover_workers);
#[cfg(not(target_family = "wasm"))]
let mut builder = lgwks_deps::tokio::runtime::Builder::new_multi_thread();
#[cfg(target_family = "wasm")]
let mut builder = lgwks_deps::tokio::runtime::Builder::new_current_thread();
#[cfg(not(target_family = "wasm"))]
if let Some(workers) = workers {
if self.worker_threads.is_some() && workers.get() > MAX_WORKER_THREADS {
let refusal = Err(io::Error::new(
io::ErrorKind::InvalidInput,
"lgwks_bot: worker_threads exceeds MAX_WORKER_THREADS",
));
lgwks_std::trace::debug!(error = ?refusal.as_ref().err(), "build: returning an error to the caller");
return refusal;
}
builder.worker_threads(workers.get().min(MAX_WORKER_THREADS));
}
#[cfg(not(target_family = "wasm"))]
builder.thread_name(&self.thread_name);
if let Some(max_blocking) = self.max_blocking_threads {
builder.max_blocking_threads(max_blocking.get());
}
#[cfg(not(target_family = "wasm"))]
if let Some(bytes) = self.thread_stack_size {
if bytes.get() > MAX_THREAD_STACK_SIZE {
let refusal = Err(io::Error::new(
io::ErrorKind::InvalidInput,
"lgwks_bot: thread_stack_size exceeds MAX_THREAD_STACK_SIZE",
));
lgwks_std::trace::debug!(error = ?refusal.as_ref().err(), "build: returning an error to the caller");
return refusal;
}
builder.thread_stack_size(bytes.get());
}
builder.enable_all();
builder.build().map(|inner| Runtime { inner })
}
}
#[cfg(not(target_family = "wasm"))]
fn discover_workers() -> Option<NonZeroUsize> {
std::thread::available_parallelism().ok()
}
#[derive(Debug)]
pub struct Runtime {
inner: lgwks_deps::tokio::runtime::Runtime,
}
impl Runtime {
pub fn new() -> io::Result<Self> {
Builder::new().build()
}
#[must_use]
pub fn handle(&self) -> Handle {
Handle {
inner: self.inner.handle().clone(),
}
}
pub fn block_on<F: Future>(&self, future: F) -> F::Output {
self.inner.block_on(future)
}
pub fn shutdown_timeout(self, timeout: Duration) {
self.inner.shutdown_timeout(timeout);
}
}
#[derive(Clone, Debug)]
pub struct Handle {
inner: lgwks_deps::tokio::runtime::Handle,
}
impl Handle {
pub fn block_on<F: Future>(&self, future: F) -> F::Output {
self.inner.block_on(future)
}
}
pub fn block_on<F: Future>(future: F) -> F::Output {
let mut builder = lgwks_deps::tokio::runtime::Builder::new_current_thread();
builder.enable_all();
let runtime = match builder.build() {
Ok(runtime) => runtime,
Err(error) => std::panic::resume_unwind(Box::new(format!(
"lgwks_bot::rt: the OS refused a current-thread runtime: {error}"
))),
};
runtime.block_on(future)
}