use hydra_dashmap::mapref::entry::Entry;
use hydra_dashmap::DashMap;
use once_cell::sync::Lazy;
use crate::ArgumentError;
use crate::ExitReason;
use crate::Pid;
use crate::ProcessFlags;
use crate::ProcessItem;
use crate::ProcessRegistration;
use crate::ProcessSend;
use crate::SystemMessage;
static PROCESS_REGISTRY: Lazy<DashMap<u64, ProcessRegistration>> = Lazy::new(DashMap::new);
static PROCESS_NAMES: Lazy<DashMap<String, u64>> = Lazy::new(DashMap::new);
pub fn process_exists_lock<C: FnOnce(bool) -> R, R>(pid: Pid, callback: C) -> R {
if PROCESS_REGISTRY.get(&pid.id()).is_some() {
callback(true)
} else {
callback(false)
}
}
pub fn process_drop(pid: Pid) -> Option<ProcessRegistration> {
PROCESS_REGISTRY
.remove(&pid.id())
.map(|(_, process)| process)
}
pub fn process_sender(pid: Pid) -> Option<ProcessSend> {
PROCESS_REGISTRY
.get(&pid.id())
.map(|process| process.sender.clone())
}
pub fn process_name_lookup(name: &str) -> Option<Pid> {
PROCESS_NAMES
.get(name)
.map(|process_id| Pid::local(*process_id))
}
pub fn process_insert(id: u64, registration: ProcessRegistration) {
PROCESS_REGISTRY.insert(id, registration);
}
pub fn process_name_remove(name: &str) {
PROCESS_NAMES.remove(name);
}
pub fn process_alive(pid: Pid) -> bool {
if pid.is_remote() {
panic!("Expected a local pid!");
}
PROCESS_REGISTRY
.get(&pid.id())
.map(|process| process.exit_reason.is_none())
.unwrap_or_default()
}
pub fn process_register(pid: Pid, name: String) -> Result<(), ArgumentError> {
if pid.is_remote() {
return Err(ArgumentError::from("Expected local pid for register!"));
}
let entry = PROCESS_NAMES.entry(name.clone());
let entry = match entry {
Entry::Occupied(entry) => {
if *entry.get() == pid.id() {
return Ok(());
} else {
return Err(ArgumentError::from(format!(
"Name {:?} registered to another process!",
name
)));
}
}
Entry::Vacant(entry) => entry,
};
let mut updated = false;
let mut found = false;
PROCESS_REGISTRY.alter(&pid.id(), |_, mut process| {
found = true;
if process.name.is_none() {
process.name = Some(name);
updated = true;
}
process
});
if !found {
return Err(ArgumentError::from("Process does not exist!"));
}
if !updated {
return Err(ArgumentError::from(format!(
"Process {:?} was already registered!",
pid
)));
}
entry.insert(pid.id());
Ok(())
}
pub fn process_unregister(name: &str) {
let Some((_, pid)) = PROCESS_NAMES.remove(name) else {
panic!("Name {:?} was not registered!", name);
};
PROCESS_REGISTRY.alter(&pid, |_, mut process| {
process.name = None;
process
});
}
pub fn process_exit(pid: Pid, from: Pid, exit_reason: ExitReason) {
PROCESS_REGISTRY.alter(&pid.id(), |_, mut process| {
let trapping_exits = process.flags.contains(ProcessFlags::TRAP_EXIT);
match exit_reason {
ExitReason::Normal | ExitReason::Ignore => {
if pid == from {
process.exit_reason = Some(exit_reason);
process.handle.abort();
} else if trapping_exits {
process
.sender
.send(ProcessItem::SystemMessage(SystemMessage::Exit(
from,
exit_reason,
)))
.unwrap();
}
}
ExitReason::Kill => {
process.exit_reason = Some(exit_reason);
process.handle.abort();
}
ExitReason::Custom(_) => {
if pid == from || !trapping_exits {
process.exit_reason = Some(exit_reason);
process.handle.abort();
} else {
process
.sender
.send(ProcessItem::SystemMessage(SystemMessage::Exit(
from,
exit_reason,
)))
.unwrap();
}
}
}
process
});
}
pub fn process_exit_signal_linked(pid: Pid, from: Pid, exit_reason: ExitReason) {
PROCESS_REGISTRY.alter(&pid.id(), |_, mut process| {
if process.flags.contains(ProcessFlags::TRAP_EXIT) {
process
.sender
.send(ProcessItem::SystemMessage(SystemMessage::Exit(
from,
exit_reason.clone(),
)))
.unwrap();
} else if !exit_reason.is_normal() {
process.exit_reason = Some(exit_reason);
process.handle.abort();
}
process
});
}
pub fn process_flags(pid: Pid) -> Option<ProcessFlags> {
PROCESS_REGISTRY.get(&pid.id()).map(|process| process.flags)
}
pub fn process_set_flags(pid: Pid, flags: ProcessFlags) {
PROCESS_REGISTRY.alter(&pid.id(), |_, mut process| {
process.flags = flags;
process
});
}
pub fn process_set_exit_reason(pid: Pid, exit_reason: ExitReason) {
PROCESS_REGISTRY.alter(&pid.id(), |_, mut process| {
process.exit_reason = Some(exit_reason);
process
});
}
pub fn process_list() -> Vec<Pid> {
PROCESS_REGISTRY
.iter()
.map(|process| Pid::local(*process.key()))
.collect()
}
pub fn process_name_list() -> Vec<String> {
PROCESS_NAMES
.iter()
.map(|value| value.key().to_owned())
.collect()
}