move slot mgr intoapp_state [refs #24]
This commit is contained in:
parent
a65f3187fe
commit
849318578f
5 changed files with 4 additions and 4 deletions
|
|
@ -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>;
|
||||
|
||||
|
|
|
|||
173
sslo_league/src/app_state/slot_mgr.rs
Normal file
173
sslo_league/src/app_state/slot_mgr.rs
Normal 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();
|
||||
}
|
||||
0
sslo_league/src/app_state/slot_mgr/ctrl_interface.rs
Normal file
0
sslo_league/src/app_state/slot_mgr/ctrl_interface.rs
Normal file
147
sslo_league/src/app_state/slot_mgr/slot_ctrl.rs
Normal file
147
sslo_league/src/app_state/slot_mgr/slot_ctrl.rs
Normal 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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue