Commit 7ea54437 authored by SamwiseFilmore's avatar SamwiseFilmore

WIP: Use sync primitives; Better Path::scheme impl

ServiceGraph is now a thread-safe data structure, using an RwLock for
the DepGraph and CHashMaps for everything else. That also means it has
complete interior mutability, so it should be much easier to share
across threads.

Disclaimer: I haven't been able to test this commit because it triggers
an invalid opcode fault without panicking. Currently working on
debugging this.
parent 4ca81a60
Pipeline #2717 passed with stage
in 2 minutes and 45 seconds
......@@ -43,16 +43,13 @@ trait PathExt {
}
impl PathExt for Path {
//TODO: Could be better written, gross indexing
// Credit to @stratact for this implemenation
fn scheme(&self) -> Option<PathBuf> {
let path = self.as_os_str()
.to_string_lossy();
if let Some(indx) = path.find(':') {
Some(PathBuf::from(&path[..indx + 1]))
} else {
None
}
path.find(':')
.map(|i| path[..i + 1].into())
}
}
......@@ -68,13 +65,15 @@ pub fn main() {
error!("failed to run initfs:/etc/init.rc: {}", err);
}
} else {
//std::env::set_var("RUST_BACKTRACE", "1");
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 = ServiceGraph::new();
let service_graph = ServiceGraph::new();
service_graph.push_services(initfs_services);
service_graph.start_services();
......
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::RwLock;
use chashmap::CHashMap;
use failure::{err_msg, Error};
......@@ -11,13 +11,15 @@ use crate::dep_graph::DepGraph;
use crate::service::{Service, ServiceState};
/// Main data structure for init, containing the main interface
/// for dealing with services
/// for dealing with services.
/// This is implemented entirely with thread-safe interiorly mutable
/// structures.
pub struct ServiceGraph {
graph: DepGraph<Service>,
graph: RwLock<DepGraph<Service>>,
// Must be sorta global so that dependencies across `push_services`
// boundaries link up correctly.
redirect_map: HashMap<String, Index>,
redirect_map: CHashMap<String, Index>,
state_map: CHashMap<Index, ServiceState>
}
......@@ -25,8 +27,8 @@ pub struct ServiceGraph {
impl ServiceGraph {
pub fn new() -> ServiceGraph {
ServiceGraph {
graph: DepGraph::new(),
redirect_map: HashMap::new(),
graph: RwLock::new(DepGraph::new()),
redirect_map: CHashMap::new(),
state_map: CHashMap::new()
}
}
......@@ -34,15 +36,20 @@ impl ServiceGraph {
/// Push some services into the graph, and add their dependency nodes.
/// Note that this does not start any services, only their metadata
/// 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());
pub fn push_services(&self, mut services: Vec<Service>) {
let mut graph = self.graph.write()
.expect("service graph mutex poisoned");
graph.reserve(services.len());
self.state_map.reserve(services.len());
let mut service_indexes = vec![];
// This is sorta ugly, but provides have to work
for service in services.drain(..) {
let name = service.name.clone();
let provides = service.provides.clone();
let index = self.graph.insert(service);
let index = graph.insert(service);
if let Some(mut provides) = provides {
self.redirect_map.reserve(provides.len() + 1);
......@@ -51,20 +58,21 @@ impl ServiceGraph {
}
}
service_indexes.push(index);
self.redirect_map.insert(name, index);
self.state_map.insert(index, ServiceState::default());
}
//TODO: Only iterate over the services that are being added
for parent in self.redirect_map.values() {
let dependencies = self.graph.get(*parent)
for parent in service_indexes {
let dependencies = graph.get(parent)
.expect("services were just added")
.dependencies.clone();
if let Some(ref dependencies) = dependencies {
for dependency in dependencies.iter() {
match self.redirect_map.get(dependency) {
Some(child) => self.graph.dependency(*parent, *child)
Some(child) => graph.dependency(parent, *child)
.unwrap_or_else(|()| warn!("failed to add dependency") ),
// It's not a super big deal if a dependency doesn't exist
// I mean, it is, but IDK what to do in that situation
......@@ -80,7 +88,14 @@ impl ServiceGraph {
/// 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)
let graph = self.graph.read()
.expect("service graph lock poisoned");
self.start_service_with_graph(&graph, index)
}
// Allow for less time spent locking and unlocking the graph
fn start_service_with_graph(&self, graph: &DepGraph<Service>, index: Index) -> Result<(), Error> {
let service = graph.get(index)
.ok_or(err_msg("service not found"))?;
let service_state = self.state_map.get(&index)
......@@ -98,12 +113,14 @@ impl ServiceGraph {
/// 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();
let graph = self.graph.read()
.expect("service graph lock poisoned");
let resolved = 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)
self.start_service_with_graph(&graph, *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