use std::fmt;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use crate::atom::AtomTable;
use crate::io::{IoSink, NullSink};
use crate::native::stdlib_stubs::{lists_bifs::ListsMapState, maps_bifs::MapsHofState};
use crate::term::Term;
use crate::timer::{TimerRef, TimerWheel};
use super::code_management_bifs::CodeManagementFacility;
use super::links::LinkFacility;
use super::registry::RegistryFacility;
use super::select::SelectFacility;
use super::spawn::SpawnFacility;
use super::supervision::SupervisionFacility;
#[derive(Clone, Debug)]
pub struct TrampolineRequest {
pub fun: Term,
pub args: Vec<Term>,
pub continuation: Option<NativeContinuation>,
}
#[derive(Clone, Debug)]
pub enum NativeContinuation {
Maps(MapsHofState),
ListsMap(ListsMapState),
GleamResultTry,
}
#[derive(Copy, Clone, Debug)]
pub struct SuspendRequest {
pub timeout_ms: Option<u64>,
}
pub struct ProcessContext {
pid: Option<u64>,
timers: Option<Arc<Mutex<TimerWheel>>>,
atom_table: Option<Arc<AtomTable>>,
spawn_facility: Option<Arc<dyn SpawnFacility>>,
link_facility: Option<Arc<dyn LinkFacility>>,
supervision_facility: Option<Arc<dyn SupervisionFacility>>,
code_management_facility: Option<Arc<dyn CodeManagementFacility>>,
registry_facility: Option<Arc<dyn RegistryFacility>>,
select_facility: Option<Arc<dyn SelectFacility>>,
io_sink: Arc<dyn IoSink>,
shutdown_requested: bool,
trampoline: Option<TrampolineRequest>,
suspend: Option<SuspendRequest>,
}
impl fmt::Debug for ProcessContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ProcessContext")
.field("pid", &self.pid)
.field("timers", &self.timers)
.field("atom_table", &self.atom_table.as_ref().map(|_| ".."))
.field(
"spawn_facility",
&self.spawn_facility.as_ref().map(|_| ".."),
)
.field("link_facility", &self.link_facility.as_ref().map(|_| ".."))
.field(
"supervision_facility",
&self.supervision_facility.as_ref().map(|_| ".."),
)
.field(
"code_management_facility",
&self.code_management_facility.as_ref().map(|_| ".."),
)
.field(
"registry_facility",
&self.registry_facility.as_ref().map(|_| ".."),
)
.field(
"select_facility",
&self.select_facility.as_ref().map(|_| ".."),
)
.field("io_sink", &"..")
.field("shutdown_requested", &self.shutdown_requested)
.field("trampoline", &self.trampoline)
.field("suspend", &self.suspend)
.finish()
}
}
impl Default for ProcessContext {
fn default() -> Self {
Self::new()
}
}
impl ProcessContext {
#[must_use]
pub fn new() -> Self {
Self {
pid: None,
timers: None,
atom_table: None,
spawn_facility: None,
link_facility: None,
supervision_facility: None,
code_management_facility: None,
registry_facility: None,
select_facility: None,
io_sink: Arc::new(NullSink),
trampoline: None,
suspend: None,
shutdown_requested: false,
}
}
#[must_use]
pub fn with_timer_services(pid: u64, timers: Arc<Mutex<TimerWheel>>) -> Self {
Self {
pid: Some(pid),
timers: Some(timers),
atom_table: None,
spawn_facility: None,
link_facility: None,
supervision_facility: None,
code_management_facility: None,
registry_facility: None,
select_facility: None,
io_sink: Arc::new(NullSink),
trampoline: None,
suspend: None,
shutdown_requested: false,
}
}
#[must_use]
pub fn pid(&self) -> Option<u64> {
self.pid
}
pub fn set_pid(&mut self, pid: Option<u64>) {
self.pid = pid;
}
#[must_use]
pub fn spawn_facility(&self) -> Option<&dyn SpawnFacility> {
self.spawn_facility.as_deref()
}
pub fn set_spawn_facility(&mut self, facility: Option<Arc<dyn SpawnFacility>>) {
self.spawn_facility = facility;
}
#[must_use]
pub fn link_facility(&self) -> Option<&dyn LinkFacility> {
self.link_facility.as_deref()
}
pub fn set_link_facility(&mut self, facility: Option<Arc<dyn LinkFacility>>) {
self.link_facility = facility;
}
#[must_use]
pub fn supervision_facility(&self) -> Option<&dyn SupervisionFacility> {
self.supervision_facility.as_deref()
}
pub fn set_supervision_facility(&mut self, facility: Option<Arc<dyn SupervisionFacility>>) {
self.supervision_facility = facility;
}
#[must_use]
pub fn code_management_facility(&self) -> Option<&dyn CodeManagementFacility> {
self.code_management_facility.as_deref()
}
pub fn set_code_management_facility(
&mut self,
facility: Option<Arc<dyn CodeManagementFacility>>,
) {
self.code_management_facility = facility;
}
#[must_use]
pub fn atom_table(&self) -> Option<&AtomTable> {
self.atom_table.as_deref()
}
pub fn set_atom_table(&mut self, table: Option<Arc<AtomTable>>) {
self.atom_table = table;
}
#[must_use]
pub fn registry_facility(&self) -> Option<&dyn RegistryFacility> {
self.registry_facility.as_deref()
}
pub fn set_registry_facility(&mut self, facility: Option<Arc<dyn RegistryFacility>>) {
self.registry_facility = facility;
}
pub fn schedule_timer(
&mut self,
delay: Duration,
target_pid: u64,
message: Term,
) -> Option<TimerRef> {
let timers = self.timers.as_ref()?;
Some(
timers
.lock()
.unwrap_or_else(|error| error.into_inner())
.schedule(delay, target_pid, message),
)
}
pub fn schedule_timer_with_reference<F>(
&mut self,
delay: Duration,
target_pid: u64,
message: F,
) -> Option<TimerRef>
where
F: FnOnce(TimerRef) -> Term,
{
let timers = self.timers.as_ref()?;
let mut timers = timers.lock().unwrap_or_else(|error| error.into_inner());
let reference = timers.reserve_reference();
timers.schedule_reserved(reference, delay, target_pid, message(reference))
}
pub fn cancel_timer(&mut self, reference: TimerRef) -> Option<Duration> {
let timers = self.timers.as_ref()?;
timers
.lock()
.unwrap_or_else(|error| error.into_inner())
.cancel(reference)
}
pub const fn allocate_term(&mut self, term: Term) -> Term {
term
}
#[must_use]
pub fn select_facility(&self) -> Option<&dyn SelectFacility> {
self.select_facility.as_deref()
}
pub fn set_select_facility(&mut self, facility: Option<Arc<dyn SelectFacility>>) {
self.select_facility = facility;
}
#[must_use]
pub fn io_sink(&self) -> &dyn IoSink {
self.io_sink.as_ref()
}
pub fn set_io_sink(&mut self, sink: Arc<dyn IoSink>) {
self.io_sink = sink;
}
pub fn request_shutdown(&mut self) {
self.shutdown_requested = true;
}
pub fn take_shutdown_request(&mut self) -> bool {
let requested = self.shutdown_requested;
self.shutdown_requested = false;
requested
}
pub fn set_trampoline(&mut self, fun: Term, args: Vec<Term>) {
self.trampoline = Some(TrampolineRequest {
fun,
args,
continuation: None,
});
}
pub fn set_continuation_trampoline(
&mut self,
fun: Term,
args: Vec<Term>,
continuation: NativeContinuation,
) {
self.trampoline = Some(TrampolineRequest {
fun,
args,
continuation: Some(continuation),
});
}
pub fn take_trampoline(&mut self) -> Option<TrampolineRequest> {
self.trampoline.take()
}
#[must_use]
pub fn has_trampoline(&self) -> bool {
self.trampoline.is_some()
}
pub fn request_suspend(&mut self, timeout_ms: Option<u64>) {
self.suspend = Some(SuspendRequest { timeout_ms });
}
pub fn take_suspend(&mut self) -> Option<SuspendRequest> {
self.suspend.take()
}
pub fn alloc_tuple(&mut self, elements: &[Term]) -> Result<Term, Term> {
let words = 1 + elements.len();
let heap: &mut [u64] = Box::leak(vec![0u64; words].into_boxed_slice());
crate::term::boxed::write_tuple(heap, elements)
.ok_or_else(|| Term::atom(crate::atom::Atom::BADARG))
}
pub fn alloc_cons(&mut self, head: Term, tail: Term) -> Result<Term, Term> {
let heap: &mut [u64] = Box::leak(Box::new([0u64; 2]));
crate::term::boxed::write_cons(heap, head, tail)
.ok_or_else(|| Term::atom(crate::atom::Atom::BADARG))
}
}