Skip to content

Commit

Permalink
Added support for setting callbacks for supervisors and updated the c…
Browse files Browse the repository at this point in the history
…allbacks example.
  • Loading branch information
r3v2d0g committed Nov 9, 2019
1 parent e87d791 commit 66f979d
Show file tree
Hide file tree
Showing 3 changed files with 366 additions and 301 deletions.
275 changes: 166 additions & 109 deletions bastion/examples/callbacks.rs
Expand Up @@ -3,116 +3,173 @@ use bastion::prelude::*;
fn main() {
Bastion::init();

Bastion::supervisor(|supervisor| {
let children_ref = supervisor.children_ref(|children| {
let callbacks = Callbacks::new()
.with_before_start(|| {
// This children group is first in order, so its `before_start` will
// get called before the other one's...
println!("1: before_start");
})
.with_before_restart(|| {
// This children group is first in order and the other children group
// stopped (thus, its `after_stop` has already been called and
// `before_restart` won't be)...
println!("9: before_restart");
})
.with_after_restart(|| {
// This children group is first in order, so its `after_restart` will
// get called before the other one's...
println!("10: after_restart");

// The other children group has not been restarted yet...
println!("11: stop");
// This will stop both children group...
Bastion::stop();
})
.with_after_stop(|| {
// This will get called after the other children group has been restarted
// (because the supervisor will then get a message from the system saying
// that it needs to stop running)...
println!("13: after_stop");
});

children
.with_exec(|ctx| {
async move {
// This might not get called before the other child's `stop`
// (this is up to the executor) and/or `recv`...
println!("3|4|5: recv");
// This will await for the other child to be stopped
// and to call `tell`...
ctx.recv().await.expect("Couldn't receive the message.");

// The other child has stopped and sent a message to stop
// this child from awaiting...
println!("8: err");
// This will make both children group get restarted...
Err(())
}
})
.with_callbacks(callbacks)
});

supervisor
.with_strategy(SupervisionStrategy::OneForAll)
.children(|children| {
let callbacks = Callbacks::new()
.with_before_start(|| {
// This children group is second in order, so its `before_start` will
// get called after the other one's...
println!("2: before_start");
})
.with_before_restart(|| {
// This won't happen because this child never faults...
unreachable!();
})
.with_after_restart(|| {
// This children group is second in order, so its `after_restart` will
// get called after the other one's...
println!("12: after_restart");
})
.with_after_stop(move || {
// This will get called both after this child's `recv` (see there why)
// and after the other children group's `stop` (which stops the system)...
println!("6|14: after_stop");

// Nothing will get printed in between because the other child's `exec`
// future is pending...
println!("7|15: tell");
// This will "tell" a message to the other child, making it finish
// `await`ing on `ctx.recv()` and return an error...
children_ref.elems()[0].tell(()).ok();
});

children
.with_exec(|ctx| {
async move {
// This might not get called before the other child's `recv`
// (this is up to the executor)...
println!("3|4|5: stop");
// This will stop this children gruop once the future becomes
// pending...
ctx.parent()
.stop()
.expect("Couldn't stop the children group.");

// This might not get called before the other child's `recv`
// (this is up to the executor)...
println!("4|5: recv");
// This will make the future pending and allow it to stop (because
// `ctx.current().stop()` was called earlier)...
ctx.recv().await.expect("Couldn't receive the message.");

// Note that this will never get there...
Ok(())
}
})
.with_callbacks(callbacks)
})
})
.expect("Couldn't create the supervisor.");
Bastion::supervisor(sp).expect("Couldn't create the supervisor.");

Bastion::start();
Bastion::block_until_stopped();
}

fn sp(supervisor: Supervisor) -> Supervisor {
let callbacks = Callbacks::new()
.with_before_start(|| {
// This will get called before others `before_start`s of this example
// because the others all are defined for elements supervised by
// this supervisor...
println!("(sp ) before_start");
})
.with_after_stop(|| {
// This will get called after others `after_stop`s of this example
// because the others all are defined for elements supervised by
// this supervisor...
println!("(sp ) after_stop");
});


let children_ref = supervisor.children_ref(sp_ch);

supervisor
.supervisor(|sp| sp_sp(sp, children_ref))
.with_strategy(SupervisionStrategy::OneForAll)
.with_callbacks(callbacks)
}

fn sp_ch(children: Children) -> Children {
let callbacks = Callbacks::new()
.with_before_start(|| {
// This will be the first `before_start` callback to get called after
// "sp"'s because this is its first supervised element in order...
println!("(sp.ch ) before_start");
})
.with_before_restart(|| {
// This will be the first `before_restart` callback to get called
// after "sp" restarts its supervised elements because this is its
// first supervised element in order...
println!("(sp.ch ) before_restart");
})
.with_after_restart(|| {
// This might get called before, after or in-between "sp.sp.ch"'s
// `before_start` and "sp.sp"'s `after_restart`...
println!("(sp.ch ) after_restart");

// This might get called before, after or in-between "sp.sp.ch"'s
// `before_start` and "sp.sp"'s `after_restart`...
println!("(sp.ch ) stop");
// This will stop the whole system (once "sp" finished restarting its
// supervised elements and both "sp.ch" and "sp.sp.ch"'s futures
// become pending)...
Bastion::stop();
})
.with_after_stop(|| {
// This will get called after `recv` but might get called before,
// after or in-between "sp.sp"'s `after_stop` and "sp.sp.ch"'s
// `after_stop`...
println!("(sp.ch ) after_stop");
});

children
.with_exec(|ctx| {
async move {
// This will get called two times:
// - a first time after `before_start` but before, after or
// in-between "sp.sp"'s `before_start and "sp.sp.ch"'s `before_start`,
// `stop`, `recv` and `after_stop`...
// - a second time after `after_restart` but before, after or
// in-between "sp.sp"'s `before_restart`, `after_restart` and
// `after_stop` and "sp.sp.ch"'s `before_start`, `stop`, `recv` and
// `after_stop`...
println!("(sp.ch ) recv");
// This will wait for the message sent by "sp.sp.ch"'s `tell` (when
// its `after_stop` gets called)...
ctx.recv().await.expect("Couldn't receive the message.");

// Once the message has been received, the future will return an
// error to make "sp" restart "sp.ch" and "sp.sp" (because its
// supervision strategy is "one-for-all")...
println!("(sp.ch ) err");
Err(())
}
})
.with_callbacks(callbacks)
}

fn sp_sp(supervisor: Supervisor, children_ref: ChildrenRef) -> Supervisor {
let callbacks = Callbacks::new()
.with_before_start(|| {
// This will get called after "sp.ch"'s `before_start` and might get
// called before or after its `recv`...
println!("(sp.sp ) before_start");
})
.with_before_restart(|| {
// This will get called after "sp.ch"'s `before_restart`...
println!("(sp.sp ) before_restart");
})
.with_after_restart(|| {
// This will get called after "sp.sp.ch"'s `before_start` and might
// get called before, after or in-between its `recv` and "sp.ch"'s
// `recv`...
println!("(sp.sp ) after_restart");
})
.with_after_stop(|| {
// This will get called after "sp.sp.ch"'s `after_stop` but might get
// called before or after "sp.ch"'s `recv`...
println!("(sp.sp ) after_stop");
});

supervisor
.children(|sp| sp_sp_ch(sp, children_ref))
.with_callbacks(callbacks)
}

fn sp_sp_ch(children: Children, children_ref: ChildrenRef) -> Children {
let callbacks = Callbacks::new()
.with_before_start(|| {
// This will get called two times:
// - a first time after "sp.sp"'s `before_start` and before or after
// "sp.ch"'s `recv`
// - a second time after "sp.sp"'s `before_restart` and before, after
// or in-between "sp.ch"'s `after_restart` and `stop`...
println!("(sp.sp.ch) before_start");
})
// This won't get called because this children group only stop itself
// (thus, `after_stop` would have already been called)...
.with_before_restart(|| unreachable!())
// This won't get called because this children group only stops itself
// (thus, `before_start` will get called instead)...
.with_after_restart(|| unreachable!())
.with_after_stop(move || {
// This will get called two times, both after `recv` but before or
// after "sp.ch"'s `recv`...
println!("(sp.sp.ch) after_stop");

// This might get called before or after "sp.ch"'s `recv`...
println!("(sp.sp.ch) tell");
// This will send a message to "sp.ch", making it fault and making
// "sp" restart "sp.ch" and "sp.sp" (because its supervision strategy
// is "one-for-one")...
children_ref.elems()[0].tell(()).ok();
});

children
.with_exec(|ctx| {
async move {
// This will get called two times, both after `before_start` and
// before or after "sp.ch"'s `recv`...
println!("(sp.sp.ch) stop");
// This will stop this children group once this future becomes
// pending...
ctx.parent()
.stop()
.expect("Couldn't stop the children group.");

// This might get called before or after "sp.ch"'s `recv`...
println!("(sp.sp.ch) recv");
// This will make this future pending, thus allowing the children
// group to stop...
ctx.recv().await.expect("Couldn't receive the message.");

// Note that this future will never get there...
Ok(())
}
})
.with_callbacks(callbacks)
}

0 comments on commit 66f979d

Please sign in to comment.