//! `memoryd`'s Honcho side at start (M5c spec, sections 5 and 10): the ledger read through, the //! sessions that wait again, the clients with their keys, and the worker's thread. use std::sync::{Arc, Mutex}; use bxcore::{SessionId, Timestamp}; use crate::config::Config; use crate::honcho::client::{Clients, Timeouts}; use crate::honcho::config::HonchoConfig; use crate::honcho::ledger::{Ledger, Record}; use crate::honcho::mirror::Mirror; use crate::honcho::state; use crate::honcho::turns::{LEDGER, TurnSync}; use crate::honcho::worker::{AskBox, Tuning, Wake, Worker}; use crate::serve::{Log, StartError}; /// With `[honcho]`, at start: the ledger read through, the sessions that wait again, and the /// clients with their keys read. Nothing is bound or started yet. pub struct Opened { pub sync: Arc>, clients: Clients, agent: String, owner: String, } impl Opened { /// The first time: sync begins now. The `Began` line is written and synced once `memory/` /// exists and before any socket is bound, so every turn reported later is after it. pub fn begin(&self) -> Result<(), StartError> { let sync = self.sync.lock().unwrap_or_else(|p| p.into_inner()); let mut ledger = sync.ledger(); if ledger.began().is_none() { let began = Record::Began { time: Timestamp::now(), }; ledger.append(began).map_err(StartError::Ledger)?; } Ok(()) } /// The memory writes' side of the ledger (task 12). pub fn mirror(&self) -> Mirror { let (ledger, status) = { let sync = self.sync.lock().unwrap_or_else(|p| p.into_inner()); (sync.shared_ledger(), sync.shared_status()) }; Mirror::new(ledger, &self.agent, &self.owner).with_status(status) } } /// Open the ledger (a cut-off last line is logged), resume the waiting sessions, and build the /// clients (their key files' warnings are logged). A key that cannot be read is `Secret`. pub fn open(config: &Config, honcho: &HonchoConfig, log: &Log) -> Result { let path = config.memory_dir().join(LEDGER); let ledger = Ledger::open(&path).map_err(StartError::Ledger)?; if ledger.repaired() { (log)(&format!( "memoryd: {}: a last line cut off by a crash was dropped", path.display() )); } let mut sync = TurnSync::new(ledger, &honcho.agent, &config.sessions_dir()); sync.resume(); let env = |name: &str| std::env::var_os(name); let (clients, warnings) = Clients::new(honcho, &env, Timeouts::WRITES).map_err(StartError::Secret)?; for warning in warnings { (log)(&warning); } Ok(Opened { sync: Arc::new(Mutex::new(sync)), clients, agent: honcho.agent.clone(), owner: honcho.owner.clone(), }) } /// Start the worker's thread, asking `brokerd` on `config`'s state socket. pub fn spawn(opened: Opened, config: &Config, wake: Arc, log: Log) -> Result<(), String> { let socket = config.state_socket(); let ask: AskBox = Box::new(move |session: &SessionId| state::ask(&socket, session, state::WAIT)); let worker = Worker::new( opened.sync, opened.clients, wake, ask, log, Tuning::DEFAULT, &opened.agent, ); std::thread::Builder::new() .name("honcho".to_string()) .spawn(move || worker.run()) .map(|_| ()) .map_err(|e| format!("cannot start the Honcho worker: {e}")) }