diff --git a/Cargo.lock b/Cargo.lock
index 509e60aa4..eef290a2d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,7 @@
name = "parity"
version = "1.1.0"
dependencies = [
+ "bincode 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.63 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 1.1.1 (git+https://github.com/tomusdrw/rust-ctrlc.git)",
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -9,6 +10,9 @@ dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.1.0",
"ethcore-devtools 1.1.0",
+ "ethcore-ipc 1.1.0",
+ "ethcore-ipc-codegen 1.1.0",
+ "ethcore-ipc-nano 1.1.0",
"ethcore-rpc 1.1.0",
"ethcore-util 1.1.0",
"ethcore-webapp 1.1.0",
@@ -23,6 +27,9 @@ dependencies = [
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_codegen 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -63,6 +70,17 @@ dependencies = [
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "bincode"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "bitflags"
version = "0.3.3"
@@ -78,6 +96,11 @@ name = "blastfig"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "byteorder"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "bytes"
version = "0.3.0"
@@ -254,6 +277,36 @@ dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "ethcore-ipc"
+version = "1.1.0"
+dependencies = [
+ "ethcore-devtools 1.1.0",
+ "nanomsg 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)",
+ "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ethcore-ipc-codegen"
+version = "1.1.0"
+dependencies = [
+ "aster 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ethcore-ipc 1.1.0",
+ "quasi 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quasi_codegen 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_syntax 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ethcore-ipc-nano"
+version = "1.1.0"
+dependencies = [
+ "ethcore-ipc 1.1.0",
+ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "nanomsg 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)",
+]
+
[[package]]
name = "ethcore-rpc"
version = "1.1.0"
@@ -659,6 +712,24 @@ name = "modifier"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "nanomsg"
+version = "0.5.0"
+source = "git+https://github.com/ethcore/nanomsg.rs.git#26449b15f29b850bcf62a577f1ee3a56474a0bc9"
+dependencies = [
+ "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)",
+]
+
+[[package]]
+name = "nanomsg-sys"
+version = "0.5.0"
+source = "git+https://github.com/ethcore/nanomsg.rs.git#26449b15f29b850bcf62a577f1ee3a56474a0bc9"
+dependencies = [
+ "gcc 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "net2"
version = "0.2.23"
diff --git a/Cargo.toml b/Cargo.toml
index 6f1a70d76..5dc5afcb3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,9 @@ build = "build.rs"
[build-dependencies]
rustc_version = "0.1"
+syntex = "*"
+serde_codegen = "0.7.0"
+"ethcore-ipc-codegen" = { path = "ipc/codegen" }
[dependencies]
log = "0.3"
@@ -30,6 +33,10 @@ ethcore-devtools = { path = "devtools" }
ethcore-rpc = { path = "rpc", optional = true }
ethcore-webapp = { path = "webapp", optional = true }
semver = "0.2"
+ethcore-ipc-nano = { path = "ipc/nano" }
+"ethcore-ipc" = { path = "ipc/rpc" }
+bincode = "*"
+serde = "0.7.0"
[dependencies.hyper]
version = "0.8"
diff --git a/build.rs b/build.rs
index 41b9a1b3e..9bb9ec249 100644
--- a/build.rs
+++ b/build.rs
@@ -15,11 +15,36 @@
// along with Parity. If not, see .
extern crate rustc_version;
+extern crate syntex;
+extern crate ethcore_ipc_codegen as codegen;
+extern crate serde_codegen;
+use std::env;
+use std::path::Path;
use rustc_version::{version_meta, Channel};
fn main() {
if let Channel::Nightly = version_meta().channel {
println!("cargo:rustc-cfg=nightly");
}
+
+ let out_dir = env::var_os("OUT_DIR").unwrap();
+
+ // ipc pass
+ {
+ let src = Path::new("parity/hypervisor/service.rs.in");
+ let dst = Path::new(&out_dir).join("hypervisor_service_ipc.rs");
+ let mut registry = syntex::Registry::new();
+ codegen::register(&mut registry);
+ registry.expand("", &src, &dst).unwrap();
+ }
+
+ // serde pass
+ {
+ let src = Path::new(&out_dir).join("hypervisor_service_ipc.rs");
+ let dst = Path::new(&out_dir).join("hypervisor_service_cg.rs");
+ let mut registry = syntex::Registry::new();
+ serde_codegen::register(&mut registry);
+ registry.expand("", &src, &dst).unwrap();
+ }
}
diff --git a/ipc/codegen/src/codegen.rs b/ipc/codegen/src/codegen.rs
index 85ea86f16..c20514097 100644
--- a/ipc/codegen/src/codegen.rs
+++ b/ipc/codegen/src/codegen.rs
@@ -188,7 +188,6 @@ fn implement_dispatch_arm_invoke_stmt(
) -> ast::Stmt
{
let function_name = builder.id(dispatch.function_name.as_str());
- let output_type_id = builder.id(dispatch.return_type_name.clone().unwrap().as_str());
let input_args_exprs = dispatch.input_arg_names.iter().enumerate().map(|(arg_index, arg_name)| {
let arg_ident = builder.id(arg_name);
@@ -216,10 +215,6 @@ fn implement_dispatch_arm_invoke_stmt(
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
- tt.extend(::quasi::ToTokens::to_tokens(&output_type_id, ext_cx).into_iter());
- tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
- tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("payload"), ::syntax::parse::token::Plain)));
- tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Colon));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter());
@@ -231,7 +226,6 @@ fn implement_dispatch_arm_invoke_stmt(
}
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
- tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("bincode"), ::syntax::parse::token::ModName)));
@@ -729,6 +723,7 @@ fn implement_interface(
Err(e) => { panic!("ipc read error: {:?}, aborting", e); }
_ => { }
}
+
// method_num is a 16-bit little-endian unsigned number
match method_num[1] as u16 + (method_num[0] as u16)*256 {
// handshake
diff --git a/ipc/nano/src/lib.rs b/ipc/nano/src/lib.rs
index 18a5b0ffe..624de776b 100644
--- a/ipc/nano/src/lib.rs
+++ b/ipc/nano/src/lib.rs
@@ -54,7 +54,7 @@ impl Deref for GuardedSocket where S: WithSocket {
/// Spawns client <`S`> over specified address
/// creates socket and connects endpoint to it
/// for duplex (paired) connections with the service
-pub fn init_client(socket_addr: &str) -> Result, SocketError> where S: WithSocket {
+pub fn init_duplex_client(socket_addr: &str) -> Result, SocketError> where S: WithSocket {
let mut socket = try!(Socket::new(Protocol::Pair).map_err(|e| {
warn!(target: "ipc", "Failed to create ipc socket: {:?}", e);
SocketError::DuplexLink
@@ -71,16 +71,38 @@ pub fn init_client(socket_addr: &str) -> Result, SocketError
})
}
+/// Spawns client <`S`> over specified address
+/// creates socket and connects endpoint to it
+/// for request-reply connections to the service
+pub fn init_client(socket_addr: &str) -> Result, SocketError> where S: WithSocket {
+ let mut socket = try!(Socket::new(Protocol::Req).map_err(|e| {
+ warn!(target: "ipc", "Failed to create ipc socket: {:?}", e);
+ SocketError::RequestLink
+ }));
+
+ let endpoint = try!(socket.connect(socket_addr).map_err(|e| {
+ warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e);
+ SocketError::RequestLink
+ }));
+
+ Ok(GuardedSocket {
+ client: Arc::new(S::init(socket)),
+ _endpoint: endpoint,
+ })
+}
+
/// Error occured while establising socket or endpoint
#[derive(Debug)]
pub enum SocketError {
/// Error establising duplex (paired) socket and/or endpoint
- DuplexLink
+ DuplexLink,
+ /// Error establising duplex (paired) socket and/or endpoint
+ RequestLink,
}
impl Worker where S: IpcInterface {
/// New worker over specified `service`
- pub fn new(service: Arc) -> Worker {
+ pub fn new(service: &Arc) -> Worker {
Worker:: {
service: service.clone(),
sockets: Vec::new(),
@@ -103,7 +125,7 @@ impl Worker where S: IpcInterface {
if method_sign_len >= 2 {
// method_num
- let method_num = self.buf[1] as u16 * 256 + self.buf[0] as u16;
+ let method_num = self.buf[0] as u16 * 256 + self.buf[1] as u16;
// payload
let payload = &self.buf[2..];
@@ -155,6 +177,26 @@ impl Worker where S: IpcInterface {
Ok(())
}
+
+ /// Add generic socket for request-reply style communications
+ /// with multiple clients
+ pub fn add_reqrep(&mut self, addr: &str) -> Result<(), SocketError> {
+ let mut socket = try!(Socket::new(Protocol::Rep).map_err(|e| {
+ warn!(target: "ipc", "Failed to create ipc socket: {:?}", e);
+ SocketError::DuplexLink
+ }));
+
+ let endpoint = try!(socket.bind(addr).map_err(|e| {
+ warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", addr, e);
+ SocketError::DuplexLink
+ }));
+
+ self.sockets.push((socket, endpoint));
+
+ self.rebuild_poll_request();
+
+ Ok(())
+ }
}
#[cfg(test)]
@@ -206,13 +248,13 @@ mod service_tests {
#[test]
fn can_create_worker() {
- let worker = Worker::::new(Arc::new(DummyService::new()));
+ let worker = Worker::::new(&Arc::new(DummyService::new()));
assert_eq!(0, worker.sockets.len());
}
#[test]
fn can_add_duplex_socket_to_worker() {
- let mut worker = Worker::::new(Arc::new(DummyService::new()));
+ let mut worker = Worker::::new(&Arc::new(DummyService::new()));
worker.add_duplex("ipc:///tmp/parity-test10.ipc").unwrap();
assert_eq!(1, worker.sockets.len());
}
@@ -220,7 +262,7 @@ mod service_tests {
#[test]
fn worker_can_poll_empty() {
let service = Arc::new(DummyService::new());
- let mut worker = Worker::::new(service.clone());
+ let mut worker = Worker::::new(&service);
worker.add_duplex("ipc:///tmp/parity-test20.ipc").unwrap();
worker.poll();
assert_eq!(0, service.methods_stack.read().unwrap().len());
@@ -230,7 +272,7 @@ mod service_tests {
fn worker_can_poll() {
let url = "ipc:///tmp/parity-test30.ipc";
- let mut worker = Worker::::new(Arc::new(DummyService::new()));
+ let mut worker = Worker::::new(&Arc::new(DummyService::new()));
worker.add_duplex(url).unwrap();
let (_socket, _endpoint) = dummy_write(url, &vec![0, 0, 7, 7, 6, 6]);
@@ -245,7 +287,7 @@ mod service_tests {
fn worker_can_poll_long() {
let url = "ipc:///tmp/parity-test40.ipc";
- let mut worker = Worker::::new(Arc::new(DummyService::new()));
+ let mut worker = Worker::::new(&Arc::new(DummyService::new()));
worker.add_duplex(url).unwrap();
let message = [0u8; 1024*1024];
diff --git a/ipc/tests/over_nano.rs b/ipc/tests/over_nano.rs
index 720dff81d..0c0d40372 100644
--- a/ipc/tests/over_nano.rs
+++ b/ipc/tests/over_nano.rs
@@ -32,14 +32,14 @@ mod tests {
fn init_worker(addr: &str) -> nanoipc::Worker {
- let mut worker = nanoipc::Worker::::new(Arc::new(Service::new()));
+ let mut worker = nanoipc::Worker::::new(&Arc::new(Service::new()));
worker.add_duplex(addr).unwrap();
worker
}
#[test]
fn can_create_client() {
- let client = nanoipc::init_client::>("ipc:///tmp/parity-nano-test10.ipc");
+ let client = nanoipc::init_duplex_client::>("ipc:///tmp/parity-nano-test10.ipc");
assert!(client.is_ok());
}
@@ -60,7 +60,7 @@ mod tests {
});
while !worker_is_ready.load(Ordering::Relaxed) { }
- let client = nanoipc::init_client::>(url).unwrap();
+ let client = nanoipc::init_duplex_client::>(url).unwrap();
let hs = client.handshake();
@@ -105,5 +105,4 @@ mod tests {
worker_should_exit.store(true, Ordering::Relaxed);
}
-
}
diff --git a/parity/hypervisor/mod.rs b/parity/hypervisor/mod.rs
new file mode 100644
index 000000000..bbc95b150
--- /dev/null
+++ b/parity/hypervisor/mod.rs
@@ -0,0 +1,158 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+//! Parity interprocess hypervisor module
+
+// while not included in binary
+#![allow(dead_code)]
+
+pub mod service;
+
+/// Default value for hypervisor ipc listener
+pub const HYPERVISOR_IPC_URL: &'static str = "ipc:///tmp/parity-internal-hyper-status.ipc";
+
+use nanoipc;
+use std::sync::{Arc,RwLock};
+use hypervisor::service::*;
+use std::process::{Command,Child};
+use std::collections::HashMap;
+
+type BinaryId = &'static str;
+
+const BLOCKCHAIN_DB_BINARY: BinaryId = "blockchain";
+
+pub struct Hypervisor {
+ ipc_addr: String,
+ service: Arc,
+ ipc_worker: RwLock>,
+ processes: RwLock>,
+}
+
+impl Hypervisor {
+ /// initializes the Hypervisor service with the open ipc socket for incoming clients
+ pub fn new() -> Hypervisor {
+ Hypervisor::with_url(HYPERVISOR_IPC_URL)
+ }
+
+ /// Starts on the specified address for ipc listener
+ fn with_url(addr: &str) -> Hypervisor{
+ Hypervisor::with_url_and_service(addr, HypervisorService::new())
+ }
+
+ /// Starts with the specified address for the ipc listener and
+ /// the specified list of modules in form of created service
+ fn with_url_and_service(addr: &str, service: Arc) -> Hypervisor {
+ let worker = nanoipc::Worker::new(&service);
+ Hypervisor{
+ ipc_addr: addr.to_owned(),
+ service: service,
+ ipc_worker: RwLock::new(worker),
+ processes: RwLock::new(HashMap::new()),
+ }
+ }
+
+ /// Since one binary can host multiple modules
+ /// we match binaries
+ fn match_module(module_id: &IpcModuleId) -> Option {
+ match *module_id {
+ BLOCKCHAIN_MODULE_ID => Some(BLOCKCHAIN_DB_BINARY),
+ // none means the module is inside the main binary
+ _ => None
+ }
+ }
+
+ /// Creates IPC listener and starts all binaries
+ fn start(&self) {
+ let mut worker = self.ipc_worker.write().unwrap();
+ worker.add_reqrep(&self.ipc_addr).unwrap_or_else(|e| panic!("Hypervisor ipc worker can not start - critical! ({:?})", e));
+
+ for module_id in self.service.module_ids() {
+ self.start_module(module_id);
+ }
+ }
+
+ /// Start binary for the specified module
+ /// Does nothing when it is already started on module is inside the
+ /// main binary
+ fn start_module(&self, module_id: IpcModuleId) {
+ Self::match_module(&module_id).map(|binary_id| {
+ let mut processes = self.processes.write().unwrap();
+ {
+ if processes.get(binary_id).is_some() {
+ // already started for another module
+ return;
+ }
+ }
+ let child = Command::new(binary_id).spawn().unwrap_or_else(
+ |e| panic!("Hypervisor cannot start binary: {}", e));
+ processes.insert(binary_id, child);
+ });
+ }
+
+ /// Reports if all modules are checked in
+ pub fn modules_ready(&self) -> bool {
+ self.service.unchecked_count() == 0
+ }
+
+ /// Waits for every required module to check in
+ pub fn wait_for_startup(&self) {
+ let mut worker = self.ipc_worker.write().unwrap();
+ while !self.modules_ready() {
+ worker.poll()
+ }
+ }
+}
+
+mod tests {
+ use super::*;
+ use std::sync::atomic::{AtomicBool,Ordering};
+ use std::sync::Arc;
+ use super::service::*;
+ use nanoipc;
+
+ #[test]
+ fn can_init() {
+ let url = "ipc:///tmp/test-parity-hypervisor-10.ipc";
+ let test_module_id = 8080u64;
+
+ let hypervisor = Hypervisor::with_url_and_service(url, HypervisorService::with_modules(vec![test_module_id]));
+ assert_eq!(false, hypervisor.modules_ready());
+ }
+
+ #[test]
+ fn can_wait_for_startup() {
+ let url = "ipc:///tmp/test-parity-hypervisor-20.ipc";
+ let test_module_id = 8080u64;
+
+ let hypervisor_ready = Arc::new(AtomicBool::new(false));
+ let hypervisor_ready_local = hypervisor_ready.clone();
+
+ ::std::thread::spawn(move || {
+ while !hypervisor_ready.load(Ordering::Relaxed) { }
+
+ let client = nanoipc::init_client::>(url).unwrap();
+ client.handshake().unwrap();
+ client.module_ready(test_module_id);
+ });
+
+ let hypervisor = Hypervisor::with_url_and_service(url, HypervisorService::with_modules(vec![test_module_id]));
+ hypervisor.start();
+ hypervisor_ready_local.store(true, Ordering::Relaxed);
+ hypervisor.wait_for_startup();
+
+ assert_eq!(true, hypervisor.modules_ready());
+ }
+}
diff --git a/parity/hypervisor/service.rs b/parity/hypervisor/service.rs
new file mode 100644
index 000000000..3171fa11c
--- /dev/null
+++ b/parity/hypervisor/service.rs
@@ -0,0 +1,19 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+//! Parity interprocess hypervisor IPC service
+
+include!(concat!(env!("OUT_DIR"), "/hypervisor_service_cg.rs"));
diff --git a/parity/hypervisor/service.rs.in b/parity/hypervisor/service.rs.in
new file mode 100644
index 000000000..2efef6ffd
--- /dev/null
+++ b/parity/hypervisor/service.rs.in
@@ -0,0 +1,69 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+use std::sync::{RwLock,Arc};
+use std::ops::*;
+use ipc::IpcConfig;
+use std::collections::HashMap;
+
+pub type IpcModuleId = u64;
+
+/// Blockhain database module id
+pub const BLOCKCHAIN_MODULE_ID: IpcModuleId = 2000;
+
+/// IPC service that handles module management
+pub struct HypervisorService {
+ check_list: RwLock>,
+}
+
+#[derive(Ipc)]
+impl HypervisorService {
+ fn module_ready(&self, module_id: u64) -> bool {
+ let mut check_list = self.check_list.write().unwrap();
+ check_list.get_mut(&module_id).map(|mut status| *status = true);
+ check_list.iter().any(|(_, status)| !status)
+ }
+}
+
+impl HypervisorService {
+ /// New service with the default list of modules
+ pub fn new() -> Arc {
+ HypervisorService::with_modules(vec![])
+ }
+
+ /// New service with list of modules that will report for being ready
+ pub fn with_modules(module_ids: Vec) -> Arc {
+ let mut check_list = HashMap::new();
+ for module_id in module_ids {
+ check_list.insert(module_id, false);
+ }
+ Arc::new(HypervisorService {
+ check_list: RwLock::new(check_list),
+ })
+ }
+
+ /// Number of modules still being waited for check-in
+ pub fn unchecked_count(&self) -> usize {
+ self.check_list.read().unwrap().iter().filter(|&(_, status)| !status).count()
+ }
+
+ /// List of all modules within this service
+ pub fn module_ids(&self) -> Vec {
+ self.check_list.read().unwrap().iter().map(|(module_id, _)| module_id).cloned().collect()
+ }
+}
+
+impl ::ipc::IpcConfig for HypervisorService {}
diff --git a/parity/main.rs b/parity/main.rs
index 94466e0e2..bb3693bb7 100644
--- a/parity/main.rs
+++ b/parity/main.rs
@@ -37,6 +37,10 @@ extern crate time;
extern crate number_prefix;
extern crate rpassword;
extern crate semver;
+extern crate ethcore_ipc as ipc;
+extern crate ethcore_ipc_nano as nanoipc;
+extern crate serde;
+extern crate bincode;
// for price_info.rs
#[macro_use] extern crate hyper;
@@ -73,6 +77,7 @@ use webapp::Listening as WebappServer;
mod price_info;
mod upgrade;
+mod hypervisor;
fn die_with_message(msg: &str) -> ! {
println!("ERROR: {}", msg);