Commit 4ca81a60 authored by SamwiseFilmore's avatar SamwiseFilmore

Refactor; Add CHashmap for better concurrency

Currently using `DepGraph::grouped_resolve` in order to ease into naive
parallel service startup, also changed the way things are done inside
ServiceGraph, and added commented out code for rayon integration
(currently rayon is having all kinds of low-level issues).
parent 72791b4d
Pipeline #2705 passed with stage
in 4 minutes and 17 seconds
This diff is collapsed.
[package]
name = "init"
description = "Init system for redox, includes service management"
version = "0.1.0"
edition = "2018"
name = "init"
version = "0.1.0"
[dependencies]
chashmap = "2.2"
failure = "0.1"
generational-arena = "0.2"
log = "0.4"
......
#![allow(dead_code)]
use std::collections::HashSet;
use generational_arena::{Arena, Index};
......@@ -128,6 +130,7 @@ impl<T> DepGraph<T> {
/// let groups = dep_graph.grouped_resolve();
/// # groups[0] contains no Ts that are dependent upon each other
/// ```
// Don't ask how this works, I have no idea
pub fn grouped_resolve(&self) -> Vec<Vec<Index>> {
let arena_len = self.graph.len();
let mut groups = vec![vec![]];
......
......@@ -15,9 +15,10 @@ use log::error;
use syscall::flag::{O_RDONLY, O_WRONLY};
use crate::service::Service;
use crate::service_tree::ServiceTree;
use crate::service_tree::ServiceGraph;
const INITFS_SERVICE_DIR: &str = "initfs:/etc/init.d";
const FS_SERVICE_DIR: &str = "file:/etc/init.d";
fn switch_stdio(stdio: &str) -> Result<()> {
let stdin = unsafe { File::from_raw_fd(
......@@ -67,14 +68,30 @@ pub fn main() {
error!("failed to run initfs:/etc/init.rc: {}", err);
}
} else {
let service_list = Service::from_dir(INITFS_SERVICE_DIR)
let initfs_services = Service::from_dir(INITFS_SERVICE_DIR)
.unwrap_or_else(|err| {
error!("error parsing service directory '{}': {}", INITFS_SERVICE_DIR, err);
vec![]
});
let mut service_graph = ServiceTree::new();
service_graph.push_services(service_list);
let mut service_graph = ServiceGraph::new();
service_graph.push_services(initfs_services);
service_graph.start_services();
//*
crate::switch_stdio("display:1")
.unwrap_or_else(|err| {
error!("error switching stdio: {}", err);
});
// */
let fs_services = Service::from_dir(FS_SERVICE_DIR)
.unwrap_or_else(|err| {
error!("error parsing service directory '{}': {}", FS_SERVICE_DIR, err);
vec![]
});
service_graph.push_services(fs_services);
service_graph.start_services();
}
......
......@@ -15,30 +15,17 @@ use toml;
use crate::PathExt;
use self::ServiceState::*;
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub enum ServiceState {
Offline,
// This might be surperfluous, I included it for
// a dumb debugging reason, might take it out later.
Starting,
Online,
Failed
}
impl ServiceState {
pub fn is_starting(&self) -> bool {
match self {
Offline => false,
Starting => true,
Online => false,
Failed => false
}
}
pub fn is_online(&self) -> bool {
match self {
Offline => false,
Starting => false,
Online => true,
Failed => false
}
......@@ -114,9 +101,6 @@ pub struct Service {
pub vars: Option<HashMap<String, String>>,
pub cwd: Option<PathBuf>,
#[serde(skip)]
pub state: ServiceState
}
impl Service {
......@@ -184,14 +168,12 @@ impl Service {
}
/// Spawn the process indicated by a method on this service and `wait()` on it.
pub fn wait_method(&mut self, method_name: &String) -> Result<(), Error> {
pub fn wait_method(&self, method_name: &String) -> Result<(), Error> {
match self.methods.get(method_name) {
Some(method) => {
info!("running method '{}' for service '{}'", method_name, self.name);
self.state = ServiceState::Starting;
method.wait(&self.vars, &self.cwd)?;
self.state = ServiceState::Online; //TODO: Transition statemap out of metadata graph
Ok(())
},
None => {
......
use std::collections::HashMap;
use std::ops::Deref;
use chashmap::CHashMap;
use failure::{err_msg, Error};
use generational_arena::Index;
use log::{error, warn};
//use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use crate::dep_graph::DepGraph;
use crate::service::Service;
const FS_SERVICE_DIR: &str = "file:/etc/init.d";
use crate::service::{Service, ServiceState};
/// Main data structure for init, containing the main interface
/// for dealing with services
pub struct ServiceTree {
pub struct ServiceGraph {
graph: DepGraph<Service>,
// Must be sorta global so that dependencies across `push_services`
// boundaries link up correctly.
redirect_map: HashMap<String, Index>
redirect_map: HashMap<String, Index>,
state_map: CHashMap<Index, ServiceState>
}
impl ServiceTree {
pub fn new() -> ServiceTree {
ServiceTree {
impl ServiceGraph {
pub fn new() -> ServiceGraph {
ServiceGraph {
graph: DepGraph::new(),
redirect_map: HashMap::new()
redirect_map: HashMap::new(),
state_map: CHashMap::new()
}
}
......@@ -31,6 +36,7 @@ impl ServiceTree {
/// is inserted into the graph. Metadata for services is not manipulated
pub fn push_services(&mut self, mut services: Vec<Service>) {
self.graph.reserve(services.len());
self.state_map.reserve(services.len());
// This is sorta ugly, but provides have to work
for service in services.drain(..) {
......@@ -46,6 +52,7 @@ impl ServiceTree {
}
self.redirect_map.insert(name, index);
self.state_map.insert(index, ServiceState::default());
}
//TODO: Only iterate over the services that are being added
......@@ -63,51 +70,42 @@ impl ServiceTree {
// I mean, it is, but IDK what to do in that situation
// It's really a pkg problem at that point
//TODO: The dep really needs to be invalidated in some way
None => warn!("dependency not found: {}", dependency)
None => warn!("dependency not found: '{}'", dependency)
}
}
}
}
}
/// WIP: This function attempts to run the start method on each service in the graph
/// if it is not already running or starting.
pub fn start_services(&mut self) {
let resolved = self.graph.linear_resolve();
/// Runs the `start` method of a service if the service referenced by
/// `index` exists, and the service is not already running.
pub fn start_service(&self, index: Index) -> Result<(), Error> {
let service = self.graph.get(index)
.ok_or(err_msg("service not found"))?;
for index /*group*/ in resolved.iter() {
//for index in group.iter() {
let service = self.graph.get_mut(*index)
// These should all exist, the resolver can only
// return indexes that are in the graph anyway
.expect("resolved service index did not exist");
if !(service.state.is_starting() || service.state.is_online()) {
service.wait_method(&"start".to_string())
.unwrap_or_else(|err| { error!("error starting service '{}': {}", service.name, err) });
if let Some(provides) = &service.provides {
/*
if provides.contains(&"display:".to_string()) {
crate::switch_stdio("display:1")
.unwrap_or_else(|err| {
warn!("{}", err);
});
}*/
if provides.contains(&"file:".to_string()) {
let fs_services = Service::from_dir(FS_SERVICE_DIR)
.unwrap_or_else(|err| {
error!("error parsing service directory '{}': {}", FS_SERVICE_DIR, err);
vec![]
});
self.push_services(fs_services);
self.start_services();
}
}
}
//}
let service_state = self.state_map.get(&index)
.map(|guard| *guard.deref() )
.unwrap_or(ServiceState::Offline);
if !service_state.is_online() {
service.wait_method(&"start".to_string())?;
self.state_map.insert(index, ServiceState::Online);
}
Ok(())
}
/// Find a solution to the dependency tree and run `ServiceGraph::start_service`
/// on each index in the tree.
pub fn start_services(&self) {
let resolved = self.graph.grouped_resolve();
for group in resolved.iter() {
//TODO: Use par_iter() if rayon will work on redox
group.iter().for_each(|index| {
self.start_service(*index)
.unwrap_or_else(|err| { error!("error starting service: {}", err) });
});
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment