move slot mgr intoapp_state [refs #24]

This commit is contained in:
Thomas Weinhold 2026-01-16 22:55:46 +01:00
commit 849318578f
5 changed files with 4 additions and 4 deletions

View file

@ -11,10 +11,11 @@ use sslo_lib::sync::command::Commander;
use crate::db2;
use crate::db2::DatabaseManagerData;
use shutdown_control::ShutdownControl;
use crate::slot_mgr::SlotMgr;
use slot_mgr::SlotMgr;
use super::config::Config;
pub mod shutdown_control;
pub mod slot_mgr;
pub type AppState = SsloAppState<LeagueAppState>;

View file

@ -0,0 +1,173 @@
use std::sync::{Arc, Mutex};
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use sslo_lib::error::SsloError;
use sslo_lib::parameters::{LEAGUE_SLOT_CONTROL_KILL_TIMEOUT, LEAGUE_SLOT_CONTROL_SHUTDOWN_TIMEOUT};
use sslo_lib::sync;
use sslo_lib::sync::command::{Commander, Executor};
use sslo_lib::sync::state::{Reporter, Reviewer};
use crate::app_state::AppState;
use crate::db2::DatabaseManager;
use crate::ndpc::coll_preset::CollPreset;
use crate::app_state::shutdown_control::ShutdownStatus;
use crate::ShutdownControl;
use crate::app_state::slot_mgr::slot_ctrl::SlotCtrl;
mod slot_ctrl;
mod ctrl_interface;
/// possible commands for the slot manager
#[derive(Clone)]
pub enum SlotMgrCmd {
/// request to start a new server
StartServer(CollPreset),
/// request to gracefully end a server on a certain slot
CloseServer(usize),
/// forcefully kill a server on a certain slot
KillServer(usize),
}
#[derive(Clone)]
pub enum SlotMgrCmdRsp {
/// Command has been received and will be executed
Ok,
/// command cannot be executed for the specified reason
Rejected(String),
}
#[derive(Clone)]
pub enum SlotMgrStat {
Initialization,
Execution,
}
#[derive(Clone)]
pub struct SlotMgr {
thread_handle: Arc<Mutex<JoinHandle<()>>>,
commander: Commander<SlotMgrCmd, SlotMgrCmdRsp>,
reviewer: Reviewer<SlotMgrStat>
}
impl SlotMgr {
pub fn new(sc: ShutdownControl, db: DatabaseManager) -> Result<Self,SsloError> {
let (commander, executor) = sync::command::create_channel::<SlotMgrCmd, SlotMgrCmdRsp>();
let (reporter, reviewer) = sync::state::create_channel::<SlotMgrStat>();
let thread_handle = Arc::new(Mutex::new(match std::thread::Builder::new()
.name("Slot Manager".to_owned())
.spawn(move || {
worker(sc, app_state, executor, reporter);
}) {
Ok(handle) => handle,
Err(e) => {
log::error!("Failed to start Slot Manager: {}", e);
return Err(SsloError::ThreadSpawnError(e))
}
}));
Ok(Self {
thread_handle,
commander,
reviewer,
})
}
pub fn command(&self, cmd: SlotMgrCmd) -> Result<(),SsloError> {
self.commander.request(cmd)
}
pub fn join(&self) {
match self.thread_handle.join() {
Ok(_) => { },
Err(e) => {
log::error!("Failed to join Slot Manager: {}", e);
}
}
}
pub fn review(&self) -> Option<SlotMgrStat> {
self.reviewer.view()
}
}
struct Slot {
id: usize,
slot_ctrl: SlotCtrl,
}
fn worker(sc: ShutdownControl,
app_state: AppState,
executor: Executor<SlotMgrCmd, SlotMgrCmdRsp>,
reporter: Reporter<SlotMgrStat>,
) {
let mut app_state = app_state;
log::debug!("Starting slot manager");
// setup slots
let mut slots: Vec<Slot> = Vec::new();
let mut slot_mgr_cmds_tx: Vec<SlotMgrCmdTx> = Vec::new();
let mut slot_mgr_stats_rx: Vec<SlotMgrStatRx> = Vec::new();
for slot_id in 0..app_state.substate().config.slots.len() {
// create slot controller
let (ctrl_cmd_tx, ctrl_cmd_rx) = slot_ctrl_cmd::create_channel();
let (ctrl_stat_tx, ctrl_stat_rx) = slo_ctrl_stat::create_channel();
let slot_ctrl = match SlotCtrl::new(
slot_id,
sc.clone(),
app_state.clone(),
ctrl_cmd_rx,
ctrl_stat_tx,
) {
Ok(slot_ctrl) => slot_ctrl,
Err(e) => {
log::error!("Failed to create slot controller ID{}: {}", slot_id, e);
sc.shutdown_graceful();
break;
}
};
// create app-state communication channels
let (mgr_cmd_tx, mgr_cmd_rx) = slot_mgr_cmd::create_channel();
let (mgr_stat_tx, mgr_stat_rx) = slot_mgr_stat::create_channel();
slot_mgr_cmds_tx.push(mgr_cmd_tx);
slot_mgr_stats_rx.push(mgr_stat_rx);
// store slot
slots.push(Slot{
id: slot_id,
slot_ctrl,
cmd: ctrl_cmd_tx,
stat: ctrl_stat_rx,
});
}
// inform app state about slots
app_state.substate_mut().slot_command_init(slot_mgr_cmds_tx);
app_state.substate_mut().slot_states_init(slot_mgr_stats_rx);
// start managing
log::info!("Slot manager started");
loop {
// pause
std::thread::sleep(Duration::from_secs(1));
// finish manager when no slot is active anymore
let mut any_slot_active = false;
for slot in &slots {
if !slot.slot_ctrl.is_finished() { any_slot_active |= true; }
}
if !any_slot_active { break; }
}
// stop work
log::info!("Slot manager shut down");
sc.shutdown_graceful();
}

View file

@ -0,0 +1,147 @@
use std::thread::JoinHandle;
use sslo_lib::error::SsloError;
use sslo_lib::ndpc2::collection::NdpcCollection;
use crate::app_state::AppState;
use crate::ndpc::coll_preset::CollPreset;
use crate::app_state::shutdown_control::ShutdownControl;
pub struct SlotCtrl {
thread_handle: JoinHandle<()>,
}
impl SlotCtrl {
pub fn new(slot_id: usize,
sc: ShutdownControl,
app_state: AppState,
cmd_rx: SlotCtrlCmdRx,
stat_tx: SlotCtrlStatTx,) -> Result<Self, SsloError> {
let thread_handle = match std::thread::Builder::new()
.name(format!("Slot Controller {}", slot_id))
.spawn(move || {
worker(SlotControllerState {
slot_id,
sc,
app_state,
cmd_rx,
stat_tx,
session: SlotCtrlSessionStat::Idle,
});
}) {
Ok(handle) => handle,
Err(e) => {
log::error!("Failed to start Slot Controller SlotID{}: {}", slot_id, e);
return Err(SsloError::ThreadSpawnError(e))
}
};
Ok(Self{
thread_handle,
})
}
pub fn is_finished(&self) -> bool {
self.thread_handle.is_finished()
}
}
struct SlotControllerState {
slot_id: usize,
sc: ShutdownControl,
app_state: AppState,
session: SlotCtrlSessionStat,
cmd_rx: SlotCtrlCmdRx,
stat_tx: SlotCtrlStatTx,
}
fn worker(state: SlotControllerState) {
let mut state = state;
log::debug!("Starting slot controller for slot {}", state.slot_id);
log::debug!("Slot controller for slot {} started", state.slot_id);
loop {
// // pause
// std::thread::sleep(std::time::Duration::from_secs(1));
// check incoming requests
match state.cmd_rx.receive() {
None => std::thread::sleep(std::time::Duration::from_secs(1)),
Some(cmd) => match cmd {
SlotCtrlCmd::StartServer(preset) => {
load_session(&mut state, preset);
}
SlotCtrlCmd::CloseServer => {
end_session(&mut state);
}
SlotCtrlCmd::KillServer => {
end_session(&mut state);
}
}
}
}
// stop work
log::info!("Slot controller for slot {} shut down", state.slot_id);
}
fn report(state: &SlotControllerState) {
state.stat_tx.report(SlotCtrlStat {
slot_id: state.slot_id,
session: state.session.clone(),
online_drivers: 0,
driven_laps: 0,
});
}
fn load_session(state: &mut SlotControllerState, preset: CollPreset) {
// check correct state
match &state.session {
SlotCtrlSessionStat::Idle => {},
unexpected_state => {
log::warn!("Ignore session load, because of invalid state: {:?}", std::mem::discriminant(unexpected_state));
return;
}
};
// user info
log::info!("starting session '{}' in slot {}", preset.label(), state.slot_id);
state.session = SlotCtrlSessionStat::StartingSession(preset.clone());
report(&state);
// start server
// TODO
std::thread::sleep(std::time::Duration::from_secs(10));
// server started
state.session = SlotCtrlSessionStat::RunningSession(preset.clone());
report(&state);
}
fn end_session(state: &mut SlotControllerState) {
// check correct state
let preset = match &state.session {
SlotCtrlSessionStat::RunningSession(preset) => preset.clone(),
unexpected_state => {
log::warn!("Ignore ending session, because of invalid state: {:?}", std::mem::discriminant(unexpected_state));
return;
}
};
// user info
log::info!("ending session '{}' in slot {}", preset.label(), state.slot_id);
state.session = SlotCtrlSessionStat::EndingSession(preset.clone());
report(&state);
// stop server
// TODO
std::thread::sleep(std::time::Duration::from_secs(10));
// server stopped
state.session = SlotCtrlSessionStat::Idle;
report(&state);
}

View file

@ -3,7 +3,7 @@ use std::net::{Ipv6Addr, SocketAddr};
use app_state::LeagueAppState;
use sslo_lib::chrono::{TimeDeltaPrecision, TimeFormat};
use app_state::shutdown_control::ShutdownControl;
use crate::slot_mgr::SlotMgr;
use app_state::slot_mgr::SlotMgr;
mod http;
mod config;
@ -11,7 +11,6 @@ mod app_state;
mod helpers;
mod db2;
mod thread_lobby;
mod slot_mgr;
mod installer;
mod user_grade;
mod ndpc;