Skip to content

Instantly share code, notes, and snippets.

@o0Ignition0o
Created June 28, 2020 18:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save o0Ignition0o/3b5dd124d9cfb6b3189e942548db79ad to your computer and use it in GitHub Desktop.
Save o0Ignition0o/3b5dd124d9cfb6b3189e942548db79ad to your computer and use it in GitHub Desktop.
Bastion floating on tide part 2: Building a bastion
use async_std::task;
use bastion::{blocking, context::BastionContext, msg, prelude::ChildrenRef, Bastion};
mod prime_number {
use std::{
iter,
time::{Duration, Instant},
};
#[derive(Debug)]
pub struct Response {
prime_number: u128,
num_digits: usize,
compute_time: Duration,
}
impl std::fmt::Display for Response {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} is a prime number that has {} digits.\nIt was found in {}s and {}ms",
self.prime_number,
self.num_digits,
self.compute_time.as_secs(),
self.compute_time.as_millis() % 1000
)
}
}
// our prime number getter now returns a Response
pub fn get(num_digits: usize) -> Response {
let start = Instant::now();
// Get a prime number
let prime_number = get_prime(num_digits);
// Stop the stopwatch
let elapsed = Instant::now().duration_since(start);
Response {
prime_number,
num_digits,
compute_time: elapsed,
}
}
fn get_prime(num_digits: usize) -> u128 {
let min_bound = get_min_bound(num_digits);
// with num_digits = 4, max_bound == 10000
let max_bound = get_max_bound(num_digits);
// maybe_prime is a number in range [1000, 10000)
// the closing parenthesiss means it won't reach the number.
// the maximum allowed value for maybe_prime is 9999.
use rand::Rng;
let mut maybe_prime = rand::thread_rng().gen_range(min_bound, max_bound);
loop {
if is_prime(maybe_prime) {
return number_or_panic(maybe_prime);
}
// for any integer n > 3,
// there always exists at least one prime number p
// with n < p < 2n - 2
maybe_prime += 1;
// We don't want to return a number
// that doesn't have the right number of digits
if maybe_prime == max_bound {
maybe_prime = min_bound;
}
}
}
// in order to determine if n is prime
// we will use a primality test.
// https://en.wikipedia.org/wiki/Primality_test#Pseudocode
fn is_prime(n: u128) -> bool {
if n <= 3 {
n > 1
} else if n % 2 == 0 || n % 3 == 0 {
false
} else {
for i in (5..=(n as f64).sqrt() as u128).step_by(6) {
if n % i == 0 || n % (i + 2) == 0 {
return false;
}
}
true
}
}
// given a sequence of digits, return the corresponding number
// eg: assert_eq!(1234, digits_to_number(vec![1,2,3,4]))
fn digits_to_number(iter: impl Iterator<Item = usize>) -> u128 {
iter.fold(0, |acc, b| acc * 10 + b as u128)
}
fn get_min_bound(num_digits: usize) -> u128 {
let lower_bound_iter =
iter::once(1usize).chain(iter::repeat(0usize).take(num_digits - 1 as usize));
digits_to_number(lower_bound_iter)
}
fn get_max_bound(num_digits: usize) -> u128 {
let lower_bound_iter = iter::once(1usize).chain(iter::repeat(0usize).take(num_digits));
digits_to_number(lower_bound_iter)
}
fn number_or_panic(number_to_return: u128) -> u128 {
// Let's roll a dice
if rand::random::<u8>() % 6 == 0 {
panic!(format!(
"I was about to return {} but I chose to panic instead!",
number_to_return
))
}
number_to_return
}
}
// `serve_prime_numbers` is the bastion child's behavior.
async fn serve_prime_numbers(ctx: BastionContext) -> Result<(), ()> {
// let's put the context in an arc, so we can pass it to other threads
let arc_ctx = std::sync::Arc::new(ctx);
// a child will keep processing messages until it crashes
// (or until it gets told to shutdown)
loop {
// msg! is our message receiver helper.
// we will only use one variant here
// =!> means messages that can be replied to
// usize means it will only match against messages that are a usize
msg! { arc_ctx.clone().recv().await?,
nb_digits: usize =!> {
// clone the context to send it to a thread
let ctx2 = arc_ctx.clone();
// answer! takes a context and will automagically figure out whom to reply to
blocking!(answer!(ctx2, prime_number::get(nb_digits)).expect("couldn't reply :("));
};
// this is a catch all for any other message we might receive
unknown:_ => {
println!("uh oh, I received a message I didn't understand\n {:?}", unknown);
};
}
}
}
struct PrimeClient {
children_handle: ChildrenRef,
}
impl PrimeClient {
pub fn new(children_handle: ChildrenRef) -> Self {
Self { children_handle }
}
pub async fn request_prime(&self, nb_digits: usize) -> Result<prime_number::Response, ()> {
// Ask our child for a prime number
let reply = self
.children_handle
.elems()
.first()
.expect("no child?")
.ask_anonymously(nb_digits)
.expect("couldn't ask for a prime number");
// wait for the reply
msg! { reply.await?,
response: prime_number::Response => {
Ok(response)
};
// this is a catch all for any other message we might receive
unknown:_ => {
println!("uh oh, I received a message I didn't understand\n {:?}", unknown);
Err(())
};
}
}
}
async fn prime_number(req: tide::Request<PrimeClient>) -> Result<String, tide::Error> {
let d: usize = req.param("digits").unwrap_or(1);
// Use the PrimeClient to ask for a prime number
// PrimeClient is now part of our state
let response = req.state().request_prime(d).await.map_err(|_| {
tide::Error::from_str(
tide::StatusCode::InternalServerError,
"I'm sorry, I couldn't get a prime number",
)
})?;
Ok(format!("{}\n", response))
}
fn main() -> Result<(), std::io::Error> {
// We need a bastion in order to run everything
Bastion::init();
Bastion::start();
// Spawn 1 child that will serve prime numbers
let children_handle =
Bastion::children(|children| children.with_redundancy(1).with_exec(serve_prime_numbers))
.expect("couldn't spawn children, aborting program.");
// Create a prime client...
let prime_client = PrimeClient::new(children_handle);
task::block_on(async move {
// ...That will populate our tide state
let mut app = tide::with_state(prime_client);
app.at("/prime/:digits").get(prime_number);
app.listen("127.0.0.1:8080").await
})?;
Bastion::stop();
Bastion::block_until_stopped();
Ok(())
}
@o0Ignition0o
Copy link
Author

Cargo.toml dependencies:

[dependencies]
bastion = "0.3.4"
tide = "0.11.0"
rand = "0.7.3"
bastion-executor = "0.3.4"
lightproc = "0.3.4"
anyhow = "1.0.31"
async-std = "1.6.2"

Output:

floating_on_tide_2 on  master [?] is 📦 v0.1.0 via 𝗥 v1.46.0 
➜ curl http://127.0.0.1:8080/prime/15
947499788349449 is a prime number that has 15 digits.
It was found in 0s and 410ms

floating_on_tide_2 on  master [?] is 📦 v0.1.0 via 𝗥 v1.46.0 
➜ curl http://127.0.0.1:8080/prime/15
510096850144577 is a prime number that has 15 digits.
It was found in 0s and 327ms

floating_on_tide_2 on  master [?] is 📦 v0.1.0 via 𝗥 v1.46.0 
➜ curl http://127.0.0.1:8080/prime/15
826290052329481 is a prime number that has 15 digits.
It was found in 0s and 427ms

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment