mirror of https://github.com/bastion-rs/bastion
named Distributor (#319)
* wip * wip, a lot left! * a bit of debug statements and more funsies * tell and tell_everyone are working. * ok we're almost there, lets see if i can handle a vec<replies> and if i can register ppl to a new Distributor * yay it works! * lints * 19 lints remaining * 9 warnings to go * docs + tests * i think we re good to go * example fix * san * move miri to a .sh file * bump nightlies and anyhow * clippy pass + prepare to merge
This commit is contained in:
parent
e923d55503
commit
c6016a95f6
|
@ -14,7 +14,7 @@ jobs:
|
|||
- name: Install
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2021-01-01
|
||||
toolchain: nightly-2021-03-20
|
||||
override: true
|
||||
- uses: davidB/rust-cargo-make@v1
|
||||
with:
|
||||
|
@ -24,13 +24,4 @@ jobs:
|
|||
RUST_BACKTRACE: full
|
||||
RUST_LOG: 'trace'
|
||||
run: |
|
||||
rustup component add miri
|
||||
cargo miri setup
|
||||
cargo clean
|
||||
# Do some Bastion shake
|
||||
cd src/bastion && \
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-ignore-leaks" cargo miri test --features lever/nightly dispatcher && \
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-ignore-leaks" cargo miri test --features lever/nightly path && \
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-ignore-leaks" cargo miri test --features lever/nightly broadcast && \
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-ignore-leaks" cargo miri test --features lever/nightly children_ref && \
|
||||
cd -
|
||||
tools/miri.sh
|
||||
|
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
- name: Install
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2021-01-01
|
||||
toolchain: nightly-2021-03-20
|
||||
override: true
|
||||
- uses: davidB/rust-cargo-make@v1
|
||||
with:
|
||||
|
|
|
@ -30,7 +30,8 @@ tokio-runtime = ["tokio"]
|
|||
|
||||
[dependencies]
|
||||
bastion-utils = "0.3.2"
|
||||
lightproc = "0.3"
|
||||
# lightproc = "0.3"
|
||||
lightproc = { path = "../lightproc" }
|
||||
# bastion-utils = { path = "../bastion-utils" }
|
||||
|
||||
crossbeam-utils = "0.8"
|
||||
|
|
|
@ -18,7 +18,7 @@ pub fn get_num_cores() -> Option<usize> {
|
|||
///
|
||||
/// Sets the current threads affinity
|
||||
pub fn set_for_current(core_id: CoreId) {
|
||||
tracing::info!("Executor: placement: set affinity on core {}", core_id.id);
|
||||
tracing::trace!("Executor: placement: set affinity on core {}", core_id.id);
|
||||
set_for_current_helper(core_id);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,8 +52,8 @@ rustdoc-args = ["--cfg", "feature=\"docs\""]
|
|||
[dependencies]
|
||||
bastion-executor = { git = "https://github.com/bastion-rs/bastion.git" }
|
||||
lightproc = "0.3"
|
||||
# bastion-executor = { version = "= 0.3.7-alpha.0", path = "../bastion-executor" }
|
||||
# lightproc = { version = "= 0.3", path = "../lightproc" }
|
||||
# bastion-executor = { path = "../bastion-executor" }
|
||||
# lightproc = { path = "../lightproc" }
|
||||
|
||||
lever = "0.1"
|
||||
futures = "0.3.5"
|
||||
|
@ -73,9 +73,12 @@ artillery-core = { version = "0.1.2-alpha.3", optional = true }
|
|||
# Log crates
|
||||
tracing-subscriber = "0.2.6"
|
||||
tracing = "0.1.15"
|
||||
anyhow = "1.0.31"
|
||||
anyhow = "1.0"
|
||||
crossbeam-queue = "0.3.0"
|
||||
log = "0.4.14"
|
||||
lasso = {version = "0.5", features = ["multi-threaded"] }
|
||||
once_cell = "1.7.2"
|
||||
thiserror = "1.0.24"
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
nuclei = "0.1"
|
||||
|
@ -91,6 +94,7 @@ rayon = "1.3.1"
|
|||
num_cpus = "1.13.0"
|
||||
# hello_tokio example
|
||||
tokio = { version="1.1", features = ["time", "macros"] }
|
||||
# bastion-executor = { path = "../bastion-executor" }
|
||||
bastion-executor = { git = "https://github.com/bastion-rs/bastion.git" }
|
||||
once_cell = "1.5.2"
|
||||
tokio-test = "0.4.0"
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
///! Create a conference.
|
||||
///!
|
||||
///! 1st Group
|
||||
///! Staff (5) - Going to organize the event // OK
|
||||
///!
|
||||
///! 2nd Group
|
||||
///! Enthusiasts (50) - interested in participating to the conference (haven't registered yet) // OK
|
||||
///!
|
||||
///! 3rd Group
|
||||
///! Attendees (empty for now) - Participate
|
||||
///!
|
||||
///! Enthusiast -> Ask one of the staff members "when is the conference going to happen ?" // OK
|
||||
///! Broadcast / Question => Answer 0 or 1 Staff members are going to reply eventually? // OK
|
||||
///!
|
||||
///! Staff -> Send a Leaflet to all of the enthusiasts, letting them know that they can register. // OK
|
||||
///!
|
||||
///! "hey conference <awesomeconference> is going to happen. will you be there?"
|
||||
///! Broadcast / Question -> if people reply with YES => fill the 3rd group
|
||||
///! some enthusiasts are now attendees
|
||||
///!
|
||||
///! Staff -> send the actual schedule and misc infos to Attendees
|
||||
///! Broadcast / Statement (Attendees)
|
||||
///!
|
||||
///! An attendee sends a thank you note to one staff member (and not bother everyone)
|
||||
///! One message / Statement (Staff) // OK
|
||||
///!
|
||||
///! ```rust
|
||||
///! let staff = Distributor::named("staff");
|
||||
///! let enthusiasts = Distributor::named("enthusiasts");
|
||||
///! let attendees = Disitributor::named("attendees");
|
||||
///! // Enthusiast -> Ask the whole staff "when is the conference going to happen ?"
|
||||
///! ask_one(Message + Clone) -> Result<impl Future<Output = Reply>, CouldNotSendError>
|
||||
///! // await_one // await_all
|
||||
///! // first ? means "have we been able to send the question?"
|
||||
///! // it s in a month
|
||||
///! let replies = staff.ask_one("when is the conference going to happen ?")?.await?;
|
||||
///! ask_everyone(Message + Clone) -> Result<impl Stream<Item = Reply>, CouldNotSendError>
|
||||
///! let participants = enthusiasts.ask_everyone("here's our super nice conference, it s happening people!").await?;
|
||||
///! for participant in participants {
|
||||
///! // grab the sender and add it to the attendee recipient group
|
||||
///! }
|
||||
///! // send the schedule
|
||||
///! tell_everyone(Message + Clone) -> Result<(), CouldNotSendError>
|
||||
///! attendees.tell_everyone("hey there, conf is in a week, here s where and how it s going to happen")?;
|
||||
///! // send a thank you note
|
||||
///! tell(Message) -> Result<(), CouldNotSendError>
|
||||
///! staff.tell_one("thank's it was amazing")?;
|
||||
///! children
|
||||
///! .with_redundancy(10)
|
||||
///! .with_distributor(Distributor::named("staff"))
|
||||
///! // We create the function to exec when each children is called
|
||||
///! .with_exec(move |ctx: BastionContext| async move { /* ... */ })
|
||||
///! children
|
||||
///! .with_redundancy(100)
|
||||
///! .with_distributor(Distributor::named("enthusiasts"))
|
||||
///! // We create the function to exec when each children is called
|
||||
///! .with_exec(move |ctx: BastionContext| async move { /* ... */ })
|
||||
///! children
|
||||
///! .with_redundancy(0)
|
||||
///! .with_distributor(Distributor::named("attendees"))
|
||||
///! // We create the function to exec when each children is called
|
||||
///! .with_exec(move |ctx: BastionContext| async move { /* ... */ })
|
||||
///! ```
|
||||
use anyhow::{anyhow, Context, Result as AnyResult};
|
||||
use bastion::distributor::*;
|
||||
use bastion::prelude::*;
|
||||
use tracing::Level;
|
||||
|
||||
// true if the person attends the conference
|
||||
#[derive(Debug)]
|
||||
struct RSVP {
|
||||
attends: bool,
|
||||
child_ref: ChildRef,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ConferenceSchedule {
|
||||
start: std::time::Duration,
|
||||
end: std::time::Duration,
|
||||
misc: String,
|
||||
}
|
||||
|
||||
/// cargo r --features=tokio-runtime distributor
|
||||
#[tokio::main]
|
||||
async fn main() -> AnyResult<()> {
|
||||
let subscriber = tracing_subscriber::fmt()
|
||||
.with_max_level(Level::INFO)
|
||||
.finish();
|
||||
tracing::subscriber::set_global_default(subscriber).unwrap();
|
||||
|
||||
// Initialize bastion
|
||||
Bastion::init();
|
||||
|
||||
// 1st Group
|
||||
Bastion::supervisor(|supervisor| {
|
||||
supervisor.children(|children| {
|
||||
// Iniit staff
|
||||
// Staff (5 members) - Going to organize the event
|
||||
children
|
||||
.with_redundancy(5)
|
||||
.with_distributor(Distributor::named("staff"))
|
||||
.with_exec(organize_the_event)
|
||||
})
|
||||
})
|
||||
// 2nd Group
|
||||
.and_then(|_| {
|
||||
Bastion::supervisor(|supervisor| {
|
||||
supervisor.children(|children| {
|
||||
// Enthusiasts (50) - interested in participating to the conference (haven't registered yet)
|
||||
children
|
||||
.with_redundancy(50)
|
||||
.with_distributor(Distributor::named("enthusiasts"))
|
||||
.with_exec(be_interested_in_the_conference)
|
||||
})
|
||||
})
|
||||
})
|
||||
.map_err(|_| anyhow!("couldn't setup the bastion"))?;
|
||||
|
||||
Bastion::start();
|
||||
|
||||
// Wait a bit until everyone is ready
|
||||
// std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
|
||||
let staff = Distributor::named("staff");
|
||||
let enthusiasts = Distributor::named("enthusiasts");
|
||||
let attendees = Distributor::named("attendees");
|
||||
|
||||
// Enthusiast -> Ask one of the staff members "when is the conference going to happen ?"
|
||||
let answer = staff.ask_one("when is the next conference going to happen?")?;
|
||||
MessageHandler::new(
|
||||
answer
|
||||
.await
|
||||
.expect("coulnd't find out when the next conference is going to happen :("),
|
||||
)
|
||||
.on_tell(|reply: String, _sender_addr| {
|
||||
tracing::info!("received a reply to my message:\n{}", reply);
|
||||
});
|
||||
|
||||
// "hey conference <awesomeconference> is going to happen. will you be there?"
|
||||
// Broadcast / Question -> if people reply with YES => fill the 3rd group
|
||||
let answers = enthusiasts
|
||||
.ask_everyone("hey, the conference is going to happen, will you be there?")
|
||||
.expect("couldn't ask everyone");
|
||||
|
||||
for answer in answers.into_iter() {
|
||||
MessageHandler::new(answer.await.expect("couldn't receive reply"))
|
||||
.on_tell(|rsvp: RSVP, _| {
|
||||
if rsvp.attends {
|
||||
tracing::info!("{:?} will be there! :)", rsvp.child_ref.id());
|
||||
attendees
|
||||
.subscribe(rsvp.child_ref)
|
||||
.expect("couldn't subscribe attendee");
|
||||
} else {
|
||||
tracing::error!("{:?} won't make it :(", rsvp.child_ref.id());
|
||||
}
|
||||
})
|
||||
.on_fallback(|unknown, _sender_addr| {
|
||||
tracing::error!(
|
||||
"distributor_test: uh oh, I received a message I didn't understand\n {:?}",
|
||||
unknown
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Ok now that attendees have subscribed, let's send information around!
|
||||
tracing::info!("Let's send invitations!");
|
||||
// Staff -> send the actual schedule and misc infos to Attendees
|
||||
let total_sent = attendees
|
||||
.tell_everyone(ConferenceSchedule {
|
||||
start: std::time::Duration::from_secs(60),
|
||||
end: std::time::Duration::from_secs(3600),
|
||||
misc: "it's going to be amazing!".to_string(),
|
||||
})
|
||||
.context("couldn't let everyone know the conference is available!")?;
|
||||
|
||||
tracing::error!("total number of attendees: {}", total_sent.len());
|
||||
|
||||
tracing::info!("the conference is running!");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
||||
|
||||
// An attendee sends a thank you note to one staff member (and not bother everyone)
|
||||
staff
|
||||
.tell_one("the conference was amazing thank you so much!")
|
||||
.context("couldn't thank the staff members :(")?;
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
// And we're done!
|
||||
Bastion::stop();
|
||||
|
||||
// BEWARE, this example doesn't return
|
||||
Bastion::block_until_stopped();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn organize_the_event(ctx: BastionContext) -> Result<(), ()> {
|
||||
loop {
|
||||
MessageHandler::new(ctx.recv().await?)
|
||||
.on_question(|message: &str, sender| {
|
||||
tracing::info!("received a question: \n{}", message);
|
||||
sender
|
||||
.reply("uh i think it will be next month!".to_string())
|
||||
.unwrap();
|
||||
})
|
||||
.on_tell(|message: &str, _| {
|
||||
tracing::info!("received a message: \n{}", message);
|
||||
})
|
||||
.on_fallback(|unknown, _sender_addr| {
|
||||
tracing::error!(
|
||||
"staff: uh oh, I received a message I didn't understand\n {:?}",
|
||||
unknown
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn be_interested_in_the_conference(ctx: BastionContext) -> Result<(), ()> {
|
||||
loop {
|
||||
MessageHandler::new(ctx.recv().await?)
|
||||
.on_tell(|message: std::sync::Arc<&str>, _| {
|
||||
tracing::info!(
|
||||
"child {}, received a broadcast message:\n{}",
|
||||
ctx.current().id(),
|
||||
message
|
||||
);
|
||||
})
|
||||
.on_tell(|schedule: ConferenceSchedule, _| {
|
||||
tracing::info!(
|
||||
"child {}, received broadcast conference schedule!:\n{:?}",
|
||||
ctx.current().id(),
|
||||
schedule
|
||||
);
|
||||
})
|
||||
.on_question(|message: &str, sender| {
|
||||
tracing::info!("received a question: \n{}", message);
|
||||
// ILL BE THERE!
|
||||
sender
|
||||
.reply(RSVP {
|
||||
attends: rand::random(),
|
||||
child_ref: ctx.current().clone(),
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
#[cfg(feature = "tokio-runtime")]
|
||||
use anyhow::Result as AnyResult;
|
||||
#[cfg(feature = "tokio-runtime")]
|
||||
use bastion::prelude::*;
|
||||
#[cfg(feature = "tokio-runtime")]
|
||||
use tokio;
|
||||
#[cfg(feature = "tokio-runtime")]
|
||||
use tracing::{error, warn, Level};
|
||||
|
||||
/// `cargo run --features=tokio-runtime --example hello_tokio`
|
||||
|
|
|
@ -72,10 +72,10 @@ mod prime_number {
|
|||
fn number_or_panic(number_to_return: u128) -> u128 {
|
||||
// Let's roll a dice
|
||||
if rand::random::<u8>() % 6 == 0 {
|
||||
panic!(format!(
|
||||
panic!(
|
||||
"I was about to return {} but I chose to panic instead!",
|
||||
number_to_return
|
||||
))
|
||||
)
|
||||
}
|
||||
number_to_return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
Create a conference.
|
||||
|
||||
1st Group
|
||||
Staff (5) - Going to organize the event // OK
|
||||
|
||||
2nd Group
|
||||
Enthusiasts (50) - interested in participating to the conference (haven't registered yet) // OK
|
||||
|
||||
3rd Group
|
||||
Attendees (empty for now) - Participate
|
||||
|
||||
Enthusiast -> Ask one of the staff members "when is the conference going to happen ?" // OK
|
||||
Broadcast / Question => Answer 0 or 1 Staff members are going to reply eventually? // OK
|
||||
|
||||
Staff -> Send a Leaflet to all of the enthusiasts, letting them know that they can register. // OK
|
||||
|
||||
"hey conference <awesomeconference> is going to happen. will you be there?"
|
||||
Broadcast / Question -> if people reply with YES => fill the 3rd group
|
||||
some enthusiasts are now attendees
|
||||
|
||||
Staff -> send the actual schedule and misc infos to Attendees
|
||||
Broadcast / Statement (Attendees)
|
||||
|
||||
An attendee sends a thank you note to one staff member (and not bother everyone)
|
||||
One message / Statement (Staff) // OK
|
||||
|
||||
```rust
|
||||
let staff = Distributor::named("staff");
|
||||
|
||||
let enthusiasts = Distributor::named("enthusiasts");
|
||||
|
||||
let attendees = Disitributor::named("attendees");
|
||||
|
||||
// Enthusiast -> Ask the whole staff "when is the conference going to happen ?"
|
||||
ask_one(Message + Clone) -> Result<impl Future<Output = Reply>, CouldNotSendError>
|
||||
// await_one // await_all
|
||||
// first ? means "have we been able to send the question?"
|
||||
// it s in a month
|
||||
let replies = staff.ask_one("when is the conference going to happen ?")?.await?;
|
||||
|
||||
ask_everyone(Message + Clone) -> Result<impl Stream<Item = Reply>, CouldNotSendError>
|
||||
let participants = enthusiasts.ask_everyone("here's our super nice conference, it s happening people!").await?;
|
||||
|
||||
for participant in participants {
|
||||
// grab the sender and add it to the attendee recipient group
|
||||
}
|
||||
|
||||
// send the schedule
|
||||
tell_everyone(Message + Clone) -> Result<(), CouldNotSendError>
|
||||
attendees.tell_everyone("hey there, conf is in a week, here s where and how it s going to happen")?;
|
||||
|
||||
// send a thank you note
|
||||
tell(Message) -> Result<(), CouldNotSendError>
|
||||
staff.tell_one("thank's it was amazing")?;
|
||||
|
||||
children
|
||||
.with_redundancy(10)
|
||||
.with_distributor(Distributor::named("staff"))
|
||||
// We create the function to exec when each children is called
|
||||
.with_exec(move |ctx: BastionContext| async move { /* ... */ })
|
||||
children
|
||||
.with_redundancy(100)
|
||||
.with_distributor(Distributor::named("enthusiasts"))
|
||||
// We create the function to exec when each children is called
|
||||
.with_exec(move |ctx: BastionContext| async move { /* ... */ })
|
||||
|
||||
children
|
||||
.with_redundancy(0)
|
||||
.with_distributor(Distributor::named("attendees"))
|
||||
// We create the function to exec when each children is called
|
||||
.with_exec(move |ctx: BastionContext| async move { /* ... */ })
|
||||
```
|
|
@ -258,7 +258,7 @@ impl Bastion {
|
|||
std::panic::set_hook(Box::new(|_| ()));
|
||||
}
|
||||
|
||||
lazy_static::initialize(&SYSTEM);
|
||||
let _ = &SYSTEM;
|
||||
}
|
||||
|
||||
/// Creates a new [`Supervisor`], passes it through the specified
|
||||
|
|
|
@ -125,12 +125,14 @@ impl Child {
|
|||
fn stopped(&mut self) {
|
||||
debug!("Child({}): Stopped.", self.id());
|
||||
self.remove_from_dispatchers();
|
||||
let _ = self.remove_from_distributors();
|
||||
self.bcast.stopped();
|
||||
}
|
||||
|
||||
fn faulted(&mut self) {
|
||||
debug!("Child({}): Faulted.", self.id());
|
||||
self.remove_from_dispatchers();
|
||||
let _ = self.remove_from_distributors();
|
||||
|
||||
let parent = self.bcast.parent().clone().into_children().unwrap();
|
||||
let path = self.bcast.path().clone();
|
||||
|
@ -305,6 +307,10 @@ impl Child {
|
|||
error!("couldn't add actor to the registry: {}", e);
|
||||
return;
|
||||
};
|
||||
if let Err(e) = self.register_to_distributors() {
|
||||
error!("couldn't add actor to the distributors: {}", e);
|
||||
return;
|
||||
};
|
||||
|
||||
loop {
|
||||
#[cfg(feature = "scaling")]
|
||||
|
@ -400,6 +406,35 @@ impl Child {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds the actor into each distributor declared in the parent node.
|
||||
fn register_to_distributors(&self) -> AnyResult<()> {
|
||||
if let Some(parent) = self.bcast.parent().clone().into_children() {
|
||||
let child_ref = self.child_ref.clone();
|
||||
let distributors = parent.distributors();
|
||||
|
||||
let global_dispatcher = SYSTEM.dispatcher();
|
||||
distributors
|
||||
.iter()
|
||||
.map(|&distributor| {
|
||||
global_dispatcher.register_recipient(distributor, child_ref.clone())
|
||||
})
|
||||
.collect::<AnyResult<Vec<_>>>()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Cleanup the actor's record from each declared distributor.
|
||||
fn remove_from_distributors(&self) -> AnyResult<()> {
|
||||
if let Some(parent) = self.bcast.parent().clone().into_children() {
|
||||
let child_ref = self.child_ref.clone();
|
||||
let distributors = parent.distributors();
|
||||
|
||||
let global_dispatcher = SYSTEM.dispatcher();
|
||||
global_dispatcher.remove_recipient(distributors, child_ref)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Cleanup the actor's record from each declared dispatcher.
|
||||
fn remove_from_dispatchers(&self) {
|
||||
if let Some(parent) = self.bcast.parent().clone().into_children() {
|
||||
|
|
|
@ -1,16 +1,64 @@
|
|||
//!
|
||||
//! Allows users to communicate with Child through the mailboxes.
|
||||
use crate::broadcast::Sender;
|
||||
use crate::context::BastionId;
|
||||
use crate::envelope::{Envelope, RefAddr};
|
||||
use crate::message::{Answer, BastionMessage, Message};
|
||||
use crate::path::BastionPath;
|
||||
use crate::{broadcast::Sender, message::Msg};
|
||||
use crate::{
|
||||
distributor::Distributor,
|
||||
message::{Answer, BastionMessage, Message},
|
||||
};
|
||||
use crate::{path::BastionPath, system::STRING_INTERNER};
|
||||
use futures::channel::mpsc::TrySendError;
|
||||
use std::cmp::{Eq, PartialEq};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
/// `SendError`s occur when a message couldn't be dispatched through a distributor
|
||||
pub enum SendError {
|
||||
#[error("couldn't send message. Channel Disconnected.")]
|
||||
/// Channel has been closed before we could send a message
|
||||
Disconnected(Msg),
|
||||
#[error("couldn't send message. Channel is Full.")]
|
||||
/// Channel is full, can't send a message
|
||||
Full(Msg),
|
||||
#[error("couldn't send a message I should have not sent. {0}")]
|
||||
/// This error is returned when we try to send a message
|
||||
/// that is not a BastionMessage::Message variant
|
||||
Other(anyhow::Error),
|
||||
#[error("No available Distributor matching {0}")]
|
||||
/// The distributor we're trying to dispatch messages to is not registered in the system
|
||||
NoDistributor(String),
|
||||
#[error("Distributor has 0 Recipients")]
|
||||
/// The distributor we're trying to dispatch messages to has no recipients
|
||||
EmptyRecipient,
|
||||
}
|
||||
|
||||
impl From<TrySendError<Envelope>> for SendError {
|
||||
fn from(tse: TrySendError<Envelope>) -> Self {
|
||||
let is_disconnected = tse.is_disconnected();
|
||||
match tse.into_inner().msg {
|
||||
BastionMessage::Message(msg) => {
|
||||
if is_disconnected {
|
||||
Self::Disconnected(msg)
|
||||
} else {
|
||||
Self::Full(msg)
|
||||
}
|
||||
}
|
||||
other => Self::Other(anyhow::anyhow!("{:?}", other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Distributor> for SendError {
|
||||
fn from(distributor: Distributor) -> Self {
|
||||
Self::NoDistributor(STRING_INTERNER.resolve(distributor.interned()).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A "reference" to an element of a children group, allowing to
|
||||
/// communicate with it.
|
||||
|
@ -215,6 +263,75 @@ impl ChildRef {
|
|||
self.send(env).map_err(|env| env.into_msg().unwrap())
|
||||
}
|
||||
|
||||
/// Try to send a message to the child this `ChildRef` is referencing.
|
||||
/// This message is intended to be used outside of Bastion context when
|
||||
/// there is no way for receiver to identify message sender
|
||||
///
|
||||
/// This method returns `()` if it succeeded, or a `SendError`(../child_ref/enum.SendError.html)
|
||||
/// otherwise.
|
||||
///
|
||||
/// # Argument
|
||||
///
|
||||
/// * `msg` - The message to send.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// // The message that will be "told"...
|
||||
/// const TELL_MSG: &'static str = "A message containing data (tell).";
|
||||
///
|
||||
/// # let children_ref =
|
||||
/// // Create a new child...
|
||||
/// Bastion::children(|children| {
|
||||
/// children.with_exec(|ctx: BastionContext| {
|
||||
/// async move {
|
||||
/// // ...which will receive the message "told"...
|
||||
/// msg! { ctx.recv().await?,
|
||||
/// msg: &'static str => {
|
||||
/// assert_eq!(msg, TELL_MSG);
|
||||
/// // Handle the message...
|
||||
/// };
|
||||
/// // This won't happen because this example
|
||||
/// // only "tells" a `&'static str`...
|
||||
/// _: _ => ();
|
||||
/// }
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// })
|
||||
/// }).expect("Couldn't create the children group.");
|
||||
///
|
||||
/// # let child_ref = &children_ref.elems()[0];
|
||||
/// // Later, the message is "told" to the child...
|
||||
/// child_ref.try_tell_anonymously(TELL_MSG).expect("Couldn't send the message.");
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn try_tell_anonymously<M: Message>(&self, msg: M) -> Result<(), SendError> {
|
||||
debug!("ChildRef({}): Try Telling message: {:?}", self.id(), msg);
|
||||
let msg = BastionMessage::tell(msg);
|
||||
let env = Envelope::from_dead_letters(msg);
|
||||
self.try_send(env).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Sends a message to the child this `ChildRef` is referencing,
|
||||
/// allowing it to answer.
|
||||
/// This message is intended to be used outside of Bastion context when
|
||||
|
@ -302,7 +419,6 @@ impl ChildRef {
|
|||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
pub fn ask_anonymously<M: Message>(&self, msg: M) -> Result<Answer, M> {
|
||||
debug!("ChildRef({}): Asking message: {:?}", self.id(), msg);
|
||||
let (msg, answer) = BastionMessage::ask(msg, self.addr());
|
||||
|
@ -313,6 +429,100 @@ impl ChildRef {
|
|||
Ok(answer)
|
||||
}
|
||||
|
||||
/// Try to send a message to the child this `ChildRef` is referencing,
|
||||
/// allowing it to answer.
|
||||
/// This message is intended to be used outside of Bastion context when
|
||||
/// there is no way for receiver to identify message sender
|
||||
///
|
||||
/// This method returns [`Answer`](../message/struct.Answer.html) if it succeeded, or a `SendError`(../child_ref/enum.SendError.html)
|
||||
/// otherwise.
|
||||
///
|
||||
/// # Argument
|
||||
///
|
||||
/// * `msg` - The message to send.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// // The message that will be "asked"...
|
||||
/// const ASK_MSG: &'static str = "A message containing data (ask).";
|
||||
/// // The message the will be "answered"...
|
||||
/// const ANSWER_MSG: &'static str = "A message containing data (answer).";
|
||||
///
|
||||
/// # let children_ref =
|
||||
/// // Create a new child...
|
||||
/// Bastion::children(|children| {
|
||||
/// children.with_exec(|ctx: BastionContext| {
|
||||
/// async move {
|
||||
/// // ...which will receive the message asked...
|
||||
/// msg! { ctx.recv().await?,
|
||||
/// msg: &'static str =!> {
|
||||
/// assert_eq!(msg, ASK_MSG);
|
||||
/// // Handle the message...
|
||||
///
|
||||
/// // ...and eventually answer to it...
|
||||
/// answer!(ctx, ANSWER_MSG);
|
||||
/// };
|
||||
/// // This won't happen because this example
|
||||
/// // only "asks" a `&'static str`...
|
||||
/// _: _ => ();
|
||||
/// }
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// })
|
||||
/// }).expect("Couldn't create the children group.");
|
||||
///
|
||||
/// # Bastion::children(|children| {
|
||||
/// # children.with_exec(move |ctx: BastionContext| {
|
||||
/// # let child_ref = children_ref.elems()[0].clone();
|
||||
/// # async move {
|
||||
/// // Later, the message is "asked" to the child...
|
||||
/// let answer: Answer = child_ref.try_ask_anonymously(ASK_MSG).expect("Couldn't send the message.");
|
||||
///
|
||||
/// // ...and the child's answer is received...
|
||||
/// msg! { answer.await.expect("Couldn't receive the answer."),
|
||||
/// msg: &'static str => {
|
||||
/// assert_eq!(msg, ANSWER_MSG);
|
||||
/// // Handle the answer...
|
||||
/// };
|
||||
/// // This won't happen because this example
|
||||
/// // only answers a `&'static str`...
|
||||
/// _: _ => ();
|
||||
/// }
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # })
|
||||
/// # }).unwrap();
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn try_ask_anonymously<M: Message>(&self, msg: M) -> Result<Answer, SendError> {
|
||||
debug!("ChildRef({}): Try Asking message: {:?}", self.id(), msg);
|
||||
let (msg, answer) = BastionMessage::ask(msg, self.addr());
|
||||
let env = Envelope::from_dead_letters(msg);
|
||||
self.try_send(env).map(|_| answer)
|
||||
}
|
||||
|
||||
/// Sends a message to the child this `ChildRef` is referencing
|
||||
/// to tell it to stop its execution.
|
||||
///
|
||||
|
@ -425,6 +635,11 @@ impl ChildRef {
|
|||
.map_err(|err| err.into_inner())
|
||||
}
|
||||
|
||||
pub(crate) fn try_send(&self, env: Envelope) -> Result<(), SendError> {
|
||||
trace!("ChildRef({}): Sending message: {:?}", self.id(), env);
|
||||
self.sender.unbounded_send(env).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn sender(&self) -> &Sender {
|
||||
&self.sender
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//!
|
||||
//! Children are a group of child supervised under a supervisor
|
||||
use crate::broadcast::{Broadcast, Parent, Sender};
|
||||
use crate::callbacks::{CallbackType, Callbacks};
|
||||
use crate::child::{Child, Init};
|
||||
use crate::child_ref::ChildRef;
|
||||
|
@ -13,6 +12,10 @@ use crate::path::BastionPathElement;
|
|||
#[cfg(feature = "scaling")]
|
||||
use crate::resizer::{ActorGroupStats, OptimalSizeExploringResizer, ScalingRule};
|
||||
use crate::system::SYSTEM;
|
||||
use crate::{
|
||||
broadcast::{Broadcast, Parent, Sender},
|
||||
distributor::Distributor,
|
||||
};
|
||||
use anyhow::Result as AnyResult;
|
||||
|
||||
use bastion_executor::pool;
|
||||
|
@ -106,6 +109,7 @@ pub struct Children {
|
|||
started: bool,
|
||||
// List of dispatchers attached to each actor in the group.
|
||||
dispatchers: Vec<Arc<Box<Dispatcher>>>,
|
||||
distributors: Vec<Distributor>,
|
||||
// The name of children
|
||||
name: Option<String>,
|
||||
#[cfg(feature = "scaling")]
|
||||
|
@ -131,6 +135,7 @@ impl Children {
|
|||
let pre_start_msgs = Vec::new();
|
||||
let started = false;
|
||||
let dispatchers = Vec::new();
|
||||
let distributors = Vec::new();
|
||||
let name = None;
|
||||
#[cfg(feature = "scaling")]
|
||||
let resizer = Box::new(OptimalSizeExploringResizer::default());
|
||||
|
@ -146,6 +151,7 @@ impl Children {
|
|||
pre_start_msgs,
|
||||
started,
|
||||
dispatchers,
|
||||
distributors,
|
||||
name,
|
||||
#[cfg(feature = "scaling")]
|
||||
resizer,
|
||||
|
@ -240,7 +246,9 @@ impl Children {
|
|||
.map(|dispatcher| dispatcher.dispatcher_type())
|
||||
.collect();
|
||||
|
||||
ChildrenRef::new(id, sender, path, children, dispatchers)
|
||||
let distributors = self.distributors.clone();
|
||||
|
||||
ChildrenRef::new(id, sender, path, children, dispatchers, distributors)
|
||||
}
|
||||
|
||||
/// Sets the name of this children group.
|
||||
|
@ -420,6 +428,50 @@ impl Children {
|
|||
self
|
||||
}
|
||||
|
||||
/// Appends a distributor to the children.
|
||||
///
|
||||
/// By default supervised elements aren't added to any distributor.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `distributor` - An instance of struct that implements the
|
||||
/// [`RecipientHandler`] trait.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// #
|
||||
/// Bastion::children(|children| {
|
||||
/// children
|
||||
/// .with_distributor(Distributor::named("my distributor"))
|
||||
/// }).expect("Couldn't create the children group.");
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
/// [`RecipientHandler`]: crate::dispatcher::RecipientHandler
|
||||
pub fn with_distributor(mut self, distributor: Distributor) -> Self {
|
||||
self.distributors.push(distributor);
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "scaling")]
|
||||
/// Sets a custom resizer for the Children.
|
||||
///
|
||||
|
@ -450,6 +502,7 @@ impl Children {
|
|||
/// #
|
||||
/// Bastion::children(|children| {
|
||||
/// children
|
||||
/// .with_redundancy(1)
|
||||
/// .with_resizer(
|
||||
/// OptimalSizeExploringResizer::default()
|
||||
/// .with_lower_bound(10)
|
||||
|
@ -462,7 +515,7 @@ impl Children {
|
|||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn with_resizer(mut self, mut resizer: OptimalSizeExploringResizer) -> Self {
|
||||
pub fn with_resizer(mut self, resizer: OptimalSizeExploringResizer) -> Self {
|
||||
self.redundancy = resizer.lower_bound() as usize;
|
||||
self.resizer = Box::new(resizer);
|
||||
self
|
||||
|
@ -646,6 +699,9 @@ impl Children {
|
|||
if let Err(e) = self.remove_dispatchers() {
|
||||
warn!("couldn't remove all dispatchers from the registry: {}", e);
|
||||
};
|
||||
if let Err(e) = self.remove_distributors() {
|
||||
warn!("couldn't remove all distributors from the registry: {}", e);
|
||||
};
|
||||
self.bcast.stopped();
|
||||
}
|
||||
|
||||
|
@ -654,6 +710,9 @@ impl Children {
|
|||
if let Err(e) = self.remove_dispatchers() {
|
||||
warn!("couldn't remove all dispatchers from the registry: {}", e);
|
||||
};
|
||||
if let Err(e) = self.remove_distributors() {
|
||||
warn!("couldn't remove all distributors from the registry: {}", e);
|
||||
};
|
||||
self.bcast.faulted();
|
||||
}
|
||||
|
||||
|
@ -1099,4 +1158,14 @@ impl Children {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes all declared local distributors from the global dispatcher.
|
||||
pub(crate) fn remove_distributors(&self) -> AnyResult<()> {
|
||||
let global_dispatcher = SYSTEM.dispatcher();
|
||||
|
||||
for distributor in self.distributors.iter() {
|
||||
global_dispatcher.remove_distributor(distributor)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
//!
|
||||
//! Allows users to communicate with children through the mailboxes.
|
||||
use crate::broadcast::Sender;
|
||||
use crate::child_ref::ChildRef;
|
||||
use crate::context::BastionId;
|
||||
use crate::dispatcher::DispatcherType;
|
||||
use crate::envelope::Envelope;
|
||||
use crate::message::{BastionMessage, Message};
|
||||
use crate::path::BastionPath;
|
||||
use crate::system::SYSTEM;
|
||||
use crate::{child_ref::ChildRef, distributor::Distributor};
|
||||
use std::cmp::{Eq, PartialEq};
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
@ -22,6 +22,7 @@ pub struct ChildrenRef {
|
|||
path: Arc<BastionPath>,
|
||||
children: Vec<ChildRef>,
|
||||
dispatchers: Vec<DispatcherType>,
|
||||
distributors: Vec<Distributor>,
|
||||
}
|
||||
|
||||
impl ChildrenRef {
|
||||
|
@ -31,6 +32,7 @@ impl ChildrenRef {
|
|||
path: Arc<BastionPath>,
|
||||
children: Vec<ChildRef>,
|
||||
dispatchers: Vec<DispatcherType>,
|
||||
distributors: Vec<Distributor>,
|
||||
) -> Self {
|
||||
ChildrenRef {
|
||||
id,
|
||||
|
@ -38,6 +40,7 @@ impl ChildrenRef {
|
|||
path,
|
||||
children,
|
||||
dispatchers,
|
||||
distributors,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,6 +119,40 @@ impl ChildrenRef {
|
|||
&self.dispatchers
|
||||
}
|
||||
|
||||
/// Returns a list of distributors that can be used for
|
||||
/// communication with other actors in the same group(s).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// #
|
||||
/// # let children_ref = Bastion::children(|children| children).unwrap();
|
||||
/// let distributors = children_ref.distributors();
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn distributors(&self) -> &Vec<Distributor> {
|
||||
&self.distributors
|
||||
}
|
||||
|
||||
/// Returns a list of [`ChildRef`] referencing the elements
|
||||
/// of the children group this `ChildrenRef` is referencing.
|
||||
///
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
//! Special module that allows users to interact and communicate with a
|
||||
//! group of actors through the dispatchers that holds information about
|
||||
//! actors grouped together.
|
||||
use crate::child_ref::ChildRef;
|
||||
use crate::envelope::SignedMessage;
|
||||
use crate::{
|
||||
child_ref::{ChildRef, SendError},
|
||||
message::{Answer, Message},
|
||||
};
|
||||
use crate::{distributor::Distributor, envelope::SignedMessage};
|
||||
use anyhow::Result as AnyResult;
|
||||
use lever::prelude::*;
|
||||
use std::fmt::{self, Debug};
|
||||
|
@ -18,6 +21,10 @@ use tracing::{debug, trace};
|
|||
/// the Bastion identifier as the key and the module name as the value.
|
||||
pub type DispatcherMap = LOTable<ChildRef, String>;
|
||||
|
||||
/// Type alias for the recipients hashset.
|
||||
/// Each key-value pair stores the Bastion identifier as the key.
|
||||
pub type RecipientMap = LOTable<ChildRef, ()>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Defines types of the notifications handled by the dispatcher
|
||||
/// when the group of actors is changing.
|
||||
|
@ -43,6 +50,27 @@ pub enum BroadcastTarget {
|
|||
Group(String),
|
||||
}
|
||||
|
||||
/// A `Recipient` is responsible for maintaining it's list
|
||||
/// of recipients, and deciding which child gets to receive which message.
|
||||
pub trait Recipient {
|
||||
/// Provide this function to declare which recipient will receive the next message
|
||||
fn next(&self) -> Option<ChildRef>;
|
||||
/// Return all recipients that will receive a broadcast message
|
||||
fn all(&self) -> Vec<ChildRef>;
|
||||
/// Add this actor to your list of recipients
|
||||
fn register(&self, actor: ChildRef);
|
||||
/// Remove this actor from your list of recipients
|
||||
fn remove(&self, actor: &ChildRef);
|
||||
}
|
||||
|
||||
/// A `RecipientHandler` is a `Recipient` implementor, that can be stored in the dispatcher
|
||||
pub trait RecipientHandler: Recipient + Send + Sync + Debug {}
|
||||
|
||||
impl RecipientHandler for RoundRobinHandler {}
|
||||
|
||||
/// The default handler, which does round-robin.
|
||||
pub type DefaultRecipientHandler = RoundRobinHandler;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
/// Defines the type of the dispatcher.
|
||||
///
|
||||
|
@ -67,6 +95,48 @@ pub type DefaultDispatcherHandler = RoundRobinHandler;
|
|||
#[derive(Default, Debug)]
|
||||
pub struct RoundRobinHandler {
|
||||
index: AtomicUsize,
|
||||
recipients: RecipientMap,
|
||||
}
|
||||
|
||||
impl RoundRobinHandler {
|
||||
fn public_recipients(&self) -> Vec<ChildRef> {
|
||||
self.recipients
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
if entry.0.is_public() {
|
||||
Some(entry.0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Recipient for RoundRobinHandler {
|
||||
fn next(&self) -> Option<ChildRef> {
|
||||
let entries = self.public_recipients();
|
||||
|
||||
if entries.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let current_index = self.index.load(Ordering::SeqCst) % entries.len();
|
||||
self.index.store(current_index + 1, Ordering::SeqCst);
|
||||
entries.get(current_index).map(std::clone::Clone::clone)
|
||||
}
|
||||
|
||||
fn all(&self) -> Vec<ChildRef> {
|
||||
self.public_recipients()
|
||||
}
|
||||
|
||||
fn register(&self, actor: ChildRef) {
|
||||
let _ = self.recipients.insert(actor, ());
|
||||
}
|
||||
|
||||
fn remove(&self, actor: &ChildRef) {
|
||||
let _ = self.recipients.remove(&actor);
|
||||
}
|
||||
}
|
||||
|
||||
impl DispatcherHandler for RoundRobinHandler {
|
||||
|
@ -80,25 +150,31 @@ impl DispatcherHandler for RoundRobinHandler {
|
|||
}
|
||||
// Each child in turn will receive a message.
|
||||
fn broadcast_message(&self, entries: &DispatcherMap, message: &Arc<SignedMessage>) {
|
||||
let entries = entries
|
||||
let public_childrefs = entries
|
||||
.iter()
|
||||
.filter(|entry| entry.0.is_public())
|
||||
.filter_map(|entry| {
|
||||
if entry.0.is_public() {
|
||||
Some(entry.0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if entries.is_empty() {
|
||||
if public_childrefs.is_empty() {
|
||||
debug!("no public children to broadcast message to");
|
||||
return;
|
||||
}
|
||||
let current_index = self.index.load(Ordering::SeqCst) % entries.len();
|
||||
let current_index = self.index.load(Ordering::SeqCst) % public_childrefs.len();
|
||||
|
||||
if let Some(entry) = entries.get(current_index) {
|
||||
if let Some(entry) = public_childrefs.get(current_index) {
|
||||
debug!(
|
||||
"sending message to child {}/{} - {}",
|
||||
current_index + 1,
|
||||
entries.len(),
|
||||
entry.0.path()
|
||||
entry.path()
|
||||
);
|
||||
entry.0.tell_anonymously(message.clone()).unwrap();
|
||||
entry.tell_anonymously(message.clone()).unwrap();
|
||||
self.index.store(current_index + 1, Ordering::SeqCst);
|
||||
};
|
||||
}
|
||||
|
@ -269,6 +345,7 @@ impl Into<DispatcherType> for String {
|
|||
pub(crate) struct GlobalDispatcher {
|
||||
/// Storage for all registered group of actors.
|
||||
pub dispatchers: LOTable<DispatcherType, Arc<Box<Dispatcher>>>,
|
||||
pub distributors: LOTable<Distributor, Arc<Box<(dyn RecipientHandler)>>>,
|
||||
}
|
||||
|
||||
impl GlobalDispatcher {
|
||||
|
@ -276,6 +353,7 @@ impl GlobalDispatcher {
|
|||
pub(crate) fn new() -> Self {
|
||||
GlobalDispatcher {
|
||||
dispatchers: LOTable::new(),
|
||||
distributors: LOTable::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,19 +410,17 @@ impl GlobalDispatcher {
|
|||
|
||||
/// Broadcasts the given message in according with the specified target.
|
||||
pub(crate) fn broadcast_message(&self, target: BroadcastTarget, message: &Arc<SignedMessage>) {
|
||||
let mut acked_dispatchers: Vec<DispatcherType> = Vec::new();
|
||||
|
||||
match target {
|
||||
let acked_dispatchers = match target {
|
||||
BroadcastTarget::All => self
|
||||
.dispatchers
|
||||
.iter()
|
||||
.map(|pair| pair.0.name().into())
|
||||
.for_each(|group_name| acked_dispatchers.push(group_name)),
|
||||
.collect(),
|
||||
BroadcastTarget::Group(name) => {
|
||||
let target_dispatcher = name.into();
|
||||
acked_dispatchers.push(target_dispatcher);
|
||||
vec![target_dispatcher]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for dispatcher_type in acked_dispatchers {
|
||||
match self.dispatchers.get(&dispatcher_type) {
|
||||
|
@ -363,6 +439,74 @@ impl GlobalDispatcher {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tell<M>(&self, distributor: Distributor, message: M) -> Result<(), SendError>
|
||||
where
|
||||
M: Message,
|
||||
{
|
||||
let child = self.next(distributor)?.ok_or(SendError::EmptyRecipient)?;
|
||||
child.try_tell_anonymously(message).map(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn ask<M>(&self, distributor: Distributor, message: M) -> Result<Answer, SendError>
|
||||
where
|
||||
M: Message,
|
||||
{
|
||||
let child = self.next(distributor)?.ok_or(SendError::EmptyRecipient)?;
|
||||
child.try_ask_anonymously(message).map(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn ask_everyone<M>(
|
||||
&self,
|
||||
distributor: Distributor,
|
||||
message: M,
|
||||
) -> Result<Vec<Answer>, SendError>
|
||||
where
|
||||
M: Message + Clone,
|
||||
{
|
||||
let all_children = self.all(distributor)?;
|
||||
if all_children.is_empty() {
|
||||
Err(SendError::EmptyRecipient)
|
||||
} else {
|
||||
all_children
|
||||
.iter()
|
||||
.map(|child| child.try_ask_anonymously(message.clone()))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tell_everyone<M>(
|
||||
&self,
|
||||
distributor: Distributor,
|
||||
message: M,
|
||||
) -> Result<Vec<()>, SendError>
|
||||
where
|
||||
M: Message + Clone,
|
||||
{
|
||||
let all_children = self.all(distributor)?;
|
||||
if all_children.is_empty() {
|
||||
Err(SendError::EmptyRecipient)
|
||||
} else {
|
||||
all_children
|
||||
.iter()
|
||||
.map(|child| child.try_tell_anonymously(message.clone()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&self, distributor: Distributor) -> Result<Option<ChildRef>, SendError> {
|
||||
self.distributors
|
||||
.get(&distributor)
|
||||
.map(|recipient| recipient.next())
|
||||
.ok_or_else(|| SendError::from(distributor))
|
||||
}
|
||||
|
||||
fn all(&self, distributor: Distributor) -> Result<Vec<ChildRef>, SendError> {
|
||||
self.distributors
|
||||
.get(&distributor)
|
||||
.map(|recipient| recipient.all())
|
||||
.ok_or_else(|| SendError::from(distributor))
|
||||
}
|
||||
|
||||
/// Adds dispatcher to the global registry.
|
||||
pub(crate) fn register_dispatcher(&self, dispatcher: &Arc<Box<Dispatcher>>) -> AnyResult<()> {
|
||||
let dispatcher_type = dispatcher.dispatcher_type();
|
||||
|
@ -386,6 +530,42 @@ impl GlobalDispatcher {
|
|||
self.dispatchers.remove(&dispatcher.dispatcher_type())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Appends the information about actor to the recipients.
|
||||
pub(crate) fn register_recipient(
|
||||
&self,
|
||||
distributor: Distributor,
|
||||
child_ref: ChildRef,
|
||||
) -> AnyResult<()> {
|
||||
if let Some(recipient) = self.distributors.get(&distributor) {
|
||||
recipient.register(child_ref);
|
||||
} else {
|
||||
let actors = DefaultRecipientHandler::default();
|
||||
actors.register(child_ref);
|
||||
self.distributors
|
||||
.insert(distributor, Arc::new(Box::new(actors)))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn remove_recipient(
|
||||
&self,
|
||||
distributor_list: &[Distributor],
|
||||
child_ref: ChildRef,
|
||||
) -> AnyResult<()> {
|
||||
distributor_list.iter().for_each(|distributor| {
|
||||
if let Some(recipient) = self.distributors.get(distributor) {
|
||||
recipient.remove(&child_ref);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes distributor from the global registry.
|
||||
pub(crate) fn remove_distributor(&self, distributor: &Distributor) -> AnyResult<()> {
|
||||
self.distributors.remove(distributor)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -0,0 +1,358 @@
|
|||
//! `Distributor` is a mechanism that allows you to send messages to children.
|
||||
|
||||
use crate::{
|
||||
child_ref::SendError,
|
||||
message::{Answer, Message},
|
||||
prelude::ChildRef,
|
||||
system::{STRING_INTERNER, SYSTEM},
|
||||
};
|
||||
use anyhow::Result as AnyResult;
|
||||
use lasso::Spur;
|
||||
use std::fmt::Debug;
|
||||
|
||||
// Copy is fine here because we're working
|
||||
// with interned strings here
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
/// The `Distributor` is the main message passing mechanism we will use.
|
||||
/// it provides methods that will allow us to send messages
|
||||
/// and add/remove actors to the Distribution list
|
||||
pub struct Distributor(Spur);
|
||||
|
||||
impl Distributor {
|
||||
/// Create a new distributor to send messages to
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// #
|
||||
/// let distributor = Distributor::named("my target group");
|
||||
/// // distributor is now ready to use
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn named(name: impl AsRef<str>) -> Self {
|
||||
Self(STRING_INTERNER.get_or_intern(name.as_ref()))
|
||||
}
|
||||
|
||||
/// Ask a question to a recipient attached to the `Distributor`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// # Bastion::supervisor(|supervisor| {
|
||||
/// # supervisor.children(|children| {
|
||||
/// children
|
||||
/// .with_redundancy(1)
|
||||
/// .with_distributor(Distributor::named("my distributor"))
|
||||
/// .with_exec(|ctx: BastionContext| { // ...
|
||||
/// # async move {
|
||||
/// # loop {
|
||||
/// # let _: Option<SignedMessage> = ctx.try_recv().await;
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// })
|
||||
/// # })
|
||||
/// # });
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # // Wait until everyone is up
|
||||
/// # std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
///
|
||||
/// let distributor = Distributor::named("my distributor");
|
||||
///
|
||||
/// let answer: Answer = distributor.ask_one("hello?").expect("couldn't send question");
|
||||
///
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn ask_one(&self, question: impl Message) -> Result<Answer, SendError> {
|
||||
SYSTEM.dispatcher().ask(*self, question)
|
||||
}
|
||||
|
||||
/// Ask a question to all recipients attached to the `Distributor`
|
||||
///
|
||||
/// Requires a `Message` that implements `Clone`. (it will be cloned and passed to each recipient)
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// # Bastion::supervisor(|supervisor| {
|
||||
/// # supervisor.children(|children| {
|
||||
/// # children
|
||||
/// # .with_redundancy(1)
|
||||
/// # .with_distributor(Distributor::named("my distributor"))
|
||||
/// # .with_exec(|ctx: BastionContext| {
|
||||
/// # async move {
|
||||
/// # loop {
|
||||
/// # let _: Option<SignedMessage> = ctx.try_recv().await;
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # })
|
||||
/// # })
|
||||
/// # });
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # // Wait until everyone is up
|
||||
/// # std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
///
|
||||
/// let distributor = Distributor::named("my distributor");
|
||||
///
|
||||
/// let answer: Vec<Answer> = distributor.ask_everyone("hello?".to_string()).expect("couldn't send question");
|
||||
///
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn ask_everyone(&self, question: impl Message + Clone) -> Result<Vec<Answer>, SendError> {
|
||||
SYSTEM.dispatcher().ask_everyone(*self, question)
|
||||
}
|
||||
|
||||
/// Send a Message to a recipient attached to the `Distributor`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// # Bastion::supervisor(|supervisor| {
|
||||
/// # supervisor.children(|children| {
|
||||
/// # children
|
||||
/// # .with_redundancy(1)
|
||||
/// # .with_distributor(Distributor::named("my distributor"))
|
||||
/// # .with_exec(|ctx: BastionContext| {
|
||||
/// # async move {
|
||||
/// # loop {
|
||||
/// # let _: Option<SignedMessage> = ctx.try_recv().await;
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # })
|
||||
/// # })
|
||||
/// # });
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # // Wait until everyone is up
|
||||
/// # std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
///
|
||||
/// let distributor = Distributor::named("my distributor");
|
||||
///
|
||||
/// let answer: () = distributor.tell_one("hello?").expect("couldn't send question");
|
||||
///
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn tell_one(&self, message: impl Message) -> Result<(), SendError> {
|
||||
SYSTEM.dispatcher().tell(*self, message)
|
||||
}
|
||||
|
||||
/// Send a Message to each recipient attached to the `Distributor`
|
||||
///
|
||||
/// Requires a `Message` that implements `Clone`. (it will be cloned and passed to each recipient)
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// # Bastion::supervisor(|supervisor| {
|
||||
/// # supervisor.children(|children| {
|
||||
/// # children
|
||||
/// # .with_redundancy(1)
|
||||
/// # .with_distributor(Distributor::named("my distributor"))
|
||||
/// # .with_exec(|ctx: BastionContext| {
|
||||
/// # async move {
|
||||
/// # loop {
|
||||
/// # let _: Option<SignedMessage> = ctx.try_recv().await;
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # })
|
||||
/// # })
|
||||
/// # });
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # // Wait until everyone is up
|
||||
/// # std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
///
|
||||
/// let distributor = Distributor::named("my distributor");
|
||||
///
|
||||
/// let answer: () = distributor.tell_one("hello?").expect("couldn't send question");
|
||||
///
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn tell_everyone(&self, message: impl Message + Clone) -> Result<Vec<()>, SendError> {
|
||||
SYSTEM.dispatcher().tell_everyone(*self, message)
|
||||
}
|
||||
|
||||
/// subscribe a `ChildRef` to the named `Distributor`
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// # let children =
|
||||
/// # Bastion::children(|children| {
|
||||
/// # children
|
||||
/// # .with_redundancy(1)
|
||||
/// # .with_distributor(Distributor::named("my distributor"))
|
||||
/// # .with_exec(|ctx: BastionContext| {
|
||||
/// # async move {
|
||||
/// # loop {
|
||||
/// # let _: Option<SignedMessage> = ctx.try_recv().await;
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # })
|
||||
/// # }).unwrap();
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # // Wait until everyone is up
|
||||
/// # std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
/// #
|
||||
/// let child_ref = children.elems()[0].clone();
|
||||
///
|
||||
/// let distributor = Distributor::named("my distributor");
|
||||
///
|
||||
/// // child_ref will now be elligible to receive messages dispatched through distributor
|
||||
/// distributor.subscribe(child_ref).expect("couldn't subscribe child to distributor");
|
||||
///
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn subscribe(&self, child_ref: ChildRef) -> AnyResult<()> {
|
||||
let global_dispatcher = SYSTEM.dispatcher();
|
||||
global_dispatcher.register_recipient(*self, child_ref)
|
||||
}
|
||||
|
||||
/// unsubscribe a `ChildRef` to the named `Distributor`
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bastion::prelude::*;
|
||||
/// #
|
||||
/// # #[cfg(feature = "tokio-runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "tokio-runtime"))]
|
||||
/// # fn main() {
|
||||
/// # run();
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn run() {
|
||||
/// # Bastion::init();
|
||||
/// # let children =
|
||||
/// # Bastion::children(|children| {
|
||||
/// # children
|
||||
/// # .with_redundancy(1)
|
||||
/// # .with_distributor(Distributor::named("my distributor"))
|
||||
/// # .with_exec(|ctx: BastionContext| {
|
||||
/// # async move {
|
||||
/// # loop {
|
||||
/// # let _: Option<SignedMessage> = ctx.try_recv().await;
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # })
|
||||
/// # }).unwrap();
|
||||
/// #
|
||||
/// # Bastion::start();
|
||||
/// # // Wait until everyone is up
|
||||
/// # std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
/// #
|
||||
/// let child_ref = children.elems()[0].clone();
|
||||
///
|
||||
/// let distributor = Distributor::named("my distributor");
|
||||
///
|
||||
/// // child_ref will not receive messages dispatched through the distributor anymore
|
||||
/// distributor.unsubscribe(child_ref).expect("couldn't unsubscribe child to distributor");
|
||||
///
|
||||
/// # Bastion::stop();
|
||||
/// # Bastion::block_until_stopped();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn unsubscribe(&self, child_ref: ChildRef) -> AnyResult<()> {
|
||||
let global_dispatcher = SYSTEM.dispatcher();
|
||||
global_dispatcher.remove_recipient(&vec![*self], child_ref)
|
||||
}
|
||||
|
||||
pub(crate) fn interned(&self) -> &Spur {
|
||||
&self.0
|
||||
}
|
||||
}
|
|
@ -89,6 +89,8 @@ pub mod supervisor;
|
|||
|
||||
pub mod errors;
|
||||
|
||||
pub mod distributor;
|
||||
|
||||
distributed_api! {
|
||||
// pub mod dist_messages;
|
||||
pub mod distributed;
|
||||
|
@ -108,6 +110,7 @@ pub mod prelude {
|
|||
BroadcastTarget, DefaultDispatcherHandler, Dispatcher, DispatcherHandler, DispatcherMap,
|
||||
DispatcherType, NotificationType,
|
||||
};
|
||||
pub use crate::distributor::Distributor;
|
||||
pub use crate::envelope::{RefAddr, SignedMessage};
|
||||
pub use crate::errors::*;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
|
|
|
@ -1063,7 +1063,7 @@ impl<O> MessageHandler<O> {
|
|||
/// specific type.
|
||||
pub fn on_tell<T, F>(self, f: F) -> MessageHandler<O>
|
||||
where
|
||||
T: 'static,
|
||||
T: Debug + 'static,
|
||||
F: FnOnce(T, RefAddr) -> O,
|
||||
{
|
||||
match self.try_into_tell::<T>() {
|
||||
|
@ -1081,6 +1081,7 @@ impl<O> MessageHandler<O> {
|
|||
}
|
||||
|
||||
fn try_into_question<T: 'static>(self) -> Result<(T, AnswerSender), MessageHandler<O>> {
|
||||
debug!("try_into_question with type {}", std::any::type_name::<T>());
|
||||
match self.state.take_message() {
|
||||
Ok(SignedMessage {
|
||||
msg:
|
||||
|
@ -1102,6 +1103,10 @@ impl<O> MessageHandler<O> {
|
|||
fn try_into_broadcast<T: Send + Sync + 'static>(
|
||||
self,
|
||||
) -> Result<(Arc<T>, RefAddr), MessageHandler<O>> {
|
||||
debug!(
|
||||
"try_into_broadcast with type {}",
|
||||
std::any::type_name::<T>()
|
||||
);
|
||||
match self.state.take_message() {
|
||||
Ok(SignedMessage {
|
||||
msg: Msg(MsgInner::Broadcast(msg)),
|
||||
|
@ -1116,7 +1121,8 @@ impl<O> MessageHandler<O> {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_into_tell<T: 'static>(self) -> Result<(T, RefAddr), MessageHandler<O>> {
|
||||
fn try_into_tell<T: Debug + 'static>(self) -> Result<(T, RefAddr), MessageHandler<O>> {
|
||||
debug!("try_into_tell with type {}", std::any::type_name::<T>());
|
||||
match self.state.take_message() {
|
||||
Ok(SignedMessage {
|
||||
msg: Msg(MsgInner::Tell(msg)),
|
||||
|
@ -1125,7 +1131,6 @@ impl<O> MessageHandler<O> {
|
|||
let msg: Box<dyn Any> = msg;
|
||||
Ok((*msg.downcast::<T>().unwrap(), sign))
|
||||
}
|
||||
|
||||
Ok(anything) => Err(MessageHandler::new(anything)),
|
||||
Err(output) => Err(MessageHandler::matched(output)),
|
||||
}
|
||||
|
|
|
@ -249,19 +249,8 @@ impl fmt::Display for BastionPath {
|
|||
impl fmt::Debug for BastionPath {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.this {
|
||||
Some(this @ BastionPathElement::Supervisor(_)) => write!(
|
||||
f,
|
||||
"/{}",
|
||||
self.parent_chain
|
||||
.iter()
|
||||
.map(|id| BastionPathElement::Supervisor(id.clone()))
|
||||
.chain(vec![this.clone()])
|
||||
.map(|el| format!("{:?}", el))
|
||||
.collect::<Vec<String>>()
|
||||
.join("/")
|
||||
),
|
||||
// TODO: combine with the pattern above when or-patterns become stable
|
||||
Some(this @ BastionPathElement::Children(_)) => write!(
|
||||
Some(this @ BastionPathElement::Supervisor(_))
|
||||
| Some(this @ BastionPathElement::Children(_)) => write!(
|
||||
f,
|
||||
"/{}",
|
||||
self.parent_chain
|
||||
|
|
|
@ -12,15 +12,17 @@ use futures::prelude::*;
|
|||
use futures::stream::FuturesUnordered;
|
||||
use futures::{pending, poll};
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use lazy_static::lazy_static;
|
||||
use lasso::ThreadedRodeo;
|
||||
use lightproc::prelude::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
use std::task::Poll;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
lazy_static! {
|
||||
pub(crate) static ref SYSTEM: GlobalSystem = System::init();
|
||||
}
|
||||
pub(crate) static STRING_INTERNER: Lazy<Arc<ThreadedRodeo>> =
|
||||
Lazy::new(|| Arc::new(Default::default()));
|
||||
|
||||
pub(crate) static SYSTEM: Lazy<GlobalSystem> = Lazy::new(System::init);
|
||||
|
||||
pub(crate) struct GlobalSystem {
|
||||
sender: Sender,
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
rustup component add miri
|
||||
cargo miri setup
|
||||
cargo clean
|
||||
# Do some Bastion shake
|
||||
pushd src/bastion && \
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-ignore-leaks" cargo miri test --features lever/nightly dispatcher && \
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-ignore-leaks" cargo miri test --features lever/nightly path && \
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-ignore-leaks" cargo miri test --features lever/nightly broadcast && \
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-ignore-leaks" cargo miri test --features lever/nightly children_ref && \
|
||||
popd
|
Loading…
Reference in New Issue