Commit 7c8b1aff authored by MrMan's avatar MrMan

Initial commit: Working example

parents
/target
**/*.rs.bk
# emacs
*~
\#*
[[package]]
name = "lazy_static"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_syscall"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rust_component_pattern_example"
version = "0.1.0"
dependencies = [
"simple-signal 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "simple-signal"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fb497c35d362b6a331cfd94956a07fc2c78a4604cdbee844a81170386b996dd3"
"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1"
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
"checksum simple-signal 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53f7da44adcc42667d57483bd93f81295f27d66897804b757573b61b6f13288b"
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[package]
name = "rust_component_pattern_example"
version = "0.1.0"
authors = ["t3hmrman <[email protected]>"]
[dependencies]
simple-signal = "1.1.1"
time = "0.1"
\ No newline at end of file
use std::thread::JoinHandle;
use std::thread;
use std::sync::mpsc::{Receiver, Sender};
use components::*;
use time::now_utc;
use std::sync::mpsc::channel;
use simple_signal::{Signal, set_handler as set_signal_handler};
#[derive(Debug)]
pub enum ClockRequest {
GetCurrentTime,
GetCurrentFormatString,
ReplaceFormatString(String)
}
#[derive(Debug)]
pub struct Clock {
clock_cfg: ClockCfg,
cmd_bus_tx: Sender<ComponentRequest<ClockRequest>>,
cmd_bus_rx: Receiver<ComponentRequest<ClockRequest>>
}
#[derive(Debug)]
pub struct ClockCfg {
format_str: String,
}
impl ClockCfg {
pub fn new() -> ClockCfg {
ClockCfg {
format_str: String::from("%Y-%m-%d %H:%M:%S")
}
}
}
impl Clock {
pub fn new() -> Clock {
let (cmd_bus_tx, cmd_bus_rx) = channel();
let clock_cfg = ClockCfg::new();
Clock {
clock_cfg,
cmd_bus_tx,
cmd_bus_rx
}
}
}
impl Component for Clock {
fn get_name(&self) -> &'static str { "clock" }
fn start(&mut self) -> ! {
loop {
// TODO: process messages
}
}
fn stop(&self) -> Result<(), ComponentError> {
// TODO: send message to stop to the component
Ok(())
}
}
impl Configurable<ClockCfg> for Clock {
fn update_config(&mut self, updated: Option<ClockCfg>) -> Result<(), ComponentError> {
if let Some(new_cfg) = updated {
self.clock_cfg = new_cfg
}
Ok(())
}
fn get_config(&self) -> &ClockCfg { &self.clock_cfg }
}
impl HandlesSimpleRequests<ClockRequest> for Clock {
fn recv_request(&self) -> Result<ComponentRequest<ClockRequest>, ComponentError> {
self.cmd_bus_rx.recv().map_err(ComponentError::from)
}
fn get_request_chan(&self) -> Result<Sender<ComponentRequest<ClockRequest>>, ComponentError> {
Ok(self.cmd_bus_tx.clone())
}
fn handle_simple_req(&mut self, envelope: RequestResponse<ClockRequest>) -> Result<(), ComponentError> {
match envelope {
// handle GetCurrentTime
RequestResponse { req: ClockRequest::GetCurrentTime, resp_chan: resp } => {
let _ = resp.send(Some(Box::new(now_utc())));
Ok(())
}
// handle GetCurrentFormatString
RequestResponse { req: ClockRequest::GetCurrentFormatString, resp_chan: resp } => {
let _ = resp.send(Some(Box::new(self.clock_cfg.format_str.clone())));
Ok(())
}
// handle ReplaceFormatString
RequestResponse { req: ClockRequest::ReplaceFormatString(new_str), resp_chan: _ } => {
self.clock_cfg.format_str = new_str;
Ok(())
}
}
}
}
pub fn spawn<F: 'static>(constructor: F) -> Result<(JoinHandle<Result<(), ComponentError>>, Sender<ComponentRequest<ClockRequest>>), ComponentError>
where
F: Fn() -> Clock + Send,
{
let (tx, rx) = channel();
let cloned_tx = tx.clone();
let handle = thread::spawn(move || {
let mut c = constructor();
c.cmd_bus_tx = cloned_tx;
c.cmd_bus_rx = rx;
// Set up SIGINT handling for this clock component in particular
let cloned_tx = c.cmd_bus_tx.clone();
set_signal_handler(&[Signal::Int, Signal::Term], move |_signals| {
println!("clock component received SIGINT/SIGTERM!");
cloned_tx.send(ComponentRequest::Lifecycle(ComponentLifecycleRequest::Shutdown)).expect("failed to send shutdown");
});
// Control loop
loop {
let msg = c.recv_request()?;
match msg {
// Lifecycle handling
ComponentRequest::Lifecycle(req) => {
match req {
ComponentLifecycleRequest::Shutdown => { break }
}
},
// Simple request-response handling
ComponentRequest::Operation(req) => {
// Maybe we should do something with the result below...
let _ = c.handle_simple_req(req)?;
thread::yield_now();
}
}
}
Ok(())
});
Ok((handle, tx))
}
pub mod clock;
use std::sync::mpsc::{Sender, RecvError};
use std::any::Any;
#[derive(Debug)]
pub enum ComponentError {
NotStarted,
NotImplemented,
InvalidConfiguration,
FailedMessageReceive(RecvError)
}
#[derive(Debug)]
pub enum ComponentLifecycleRequest {
Shutdown
}
#[derive(Debug)]
pub enum ComponentRequest<T> {
Lifecycle(ComponentLifecycleRequest),
Operation(RequestResponse<T>)
}
pub trait Component {
fn get_name(&self) -> &str;
fn start(&mut self) -> !;
fn stop(&self) -> Result<(), ComponentError>;
}
pub trait Configurable<C> where Self: Component {
// Note that you could get around the &mut self here if you used a std::sync::Mutex wrapped around the config class (C)
// handling the synchronization in the interior.
fn update_config(&mut self, updated: Option<C>) -> Result<(), ComponentError>;
fn get_config(&self) -> &C;
}
pub trait HandlesSimpleRequests<RQ> {
fn recv_request(&self) -> Result<ComponentRequest<RQ>, ComponentError>;
fn get_request_chan(&self) -> Result<Sender<ComponentRequest<RQ>>, ComponentError>;
fn handle_simple_req(&mut self, envelope: RequestResponse<RQ>) -> Result<(), ComponentError>;
}
#[derive(Debug)]
pub struct RequestResponse<RQ> {
pub req: RQ,
pub resp_chan: Sender<Option<Box<Any + Send>>>
}
impl From<RecvError> for ComponentError {
fn from(e: RecvError) -> ComponentError { ComponentError::FailedMessageReceive(e) }
}
extern crate simple_signal;
extern crate time;
pub mod components;
extern crate rust_component_pattern_example;
extern crate time;
use std::time::Duration;
use std::thread;
use std::sync::mpsc::channel;
use rust_component_pattern_example::components::clock::{spawn as spawn_clock, Clock, ClockRequest};
use rust_component_pattern_example::components::{ComponentRequest, ComponentLifecycleRequest, RequestResponse};
fn main() {
println!("Spawning two clock components...");
let (clock_one_handle, clock_one_tx) = spawn_clock( || Clock::new()).expect("failed to spawn clock one");
let (clock_two_handle, clock_two_tx) = spawn_clock( || Clock::new()).expect("failed to spawn clock two");
// Send GetCurrentTime to clock_one, synchronously wait for a response
println!("\nGetting clock one's time synchronously (blocking main thread waiting on response)...");
let (resp_tx, resp_rx) = channel();
clock_one_tx.send(
ComponentRequest::Operation(
RequestResponse {
req: ClockRequest::GetCurrentTime,
resp_chan: resp_tx
}
)
).expect("failed to send to clock one");
let result = resp_rx.recv();
println!("Received result from clock_one: {:?}", result);
println!("\nGetting clock two's time asynchronously from another thread after three seconds...");
let cloned_clock_two_tx = clock_two_tx.clone();
let async_work = thread::spawn(move || {
// Wait 3 seconds
thread::sleep(Duration::from_secs(3));
// Send GetCurrentTime to clock_two return the result
let (resp_tx, resp_rx) = channel();
cloned_clock_two_tx.send(
ComponentRequest::Operation(
RequestResponse {
req: ClockRequest::GetCurrentTime,
resp_chan: resp_tx
}
)
).expect("failed to send to clock_two from thread");
let result = resp_rx.recv();
println!("Received result from clock_two: {:?}", result);
});
// Shutdown both components after waiting 5 seconds
thread::spawn(move || {
thread::sleep(Duration::from_secs(5));
println!("\nSending shutdown to both clock components!");
let _ = clock_one_tx.send(ComponentRequest::Lifecycle(ComponentLifecycleRequest::Shutdown)).expect("failed to send shutdown");
let _ = clock_two_tx.send(ComponentRequest::Lifecycle(ComponentLifecycleRequest::Shutdown)).expect("failed to send shutdown");
});
// Wait for both threads to finish
println!("\nthread::join on async work and both clock handles...");
let _ = async_work.join().expect("failed to join work thread");
let _ = clock_one_handle.join().expect("failed to join clock one thread");
let _ = clock_two_handle.join().expect("failed to join clock two thread");
println!("DONE!");
}
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