Skip to content

Commit

Permalink
Merge pull request #46 from lf-lang/rust.fix-sleeping-barber
Browse files Browse the repository at this point in the history
Fix Sleeping Barber
  • Loading branch information
jhaye authored Sep 19, 2022
2 parents 5acd4e4 + 3be6d1c commit 61c7492
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 77 deletions.
138 changes: 64 additions & 74 deletions Rust/Savina/src/concurrency/SleepingBarber.lf
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
* barber and the factory operate in parallel. Since the complexity of
* computations involved in this benchmark is low, this isn't a problem for
* performance though.
*
*
* @author Christian Menard
* @author Hannes Klein
* @author Johannes Hayeß
*/

target Rust {
build-type: Release,
cargo-features: ["cli"],
Expand All @@ -56,79 +56,70 @@ import BenchmarkRunner from "../lib/BenchmarkRunner.lf";
reactor CustomerFactory(numCustomers:usize(2000), averageProductionRate:usize(1000)) {
state num_customers(numCustomers);
state average_production_rate(averageProductionRate);

input start: unit;
output finished: unit;

output sendCustomer: usize;
input[numCustomers] customerDone: unit;
input[numCustomers] customerReturned: unit;

physical action createNextCustomer: unit;
physical action sendCustomerAgain: usize;

state doneCustomers: usize(0);
state attempts: usize(0);
state next_customer_id: usize(0);
state random: PseudoRandomGenerator;

preamble {=
use crate::pseudo_random::PseudoRandomGenerator;
use crate::reactors::barber::busy_wait;
=}

reaction(start) -> createNextCustomer {=
// reset state
self.doneCustomers = 0;
self.attempts = 0;
self.next_customer_id = 0;
self.random = PseudoRandomGenerator::default();
// start "creating" customers
let create_next_customer = createNextCustomer.clone();
ctx.spawn_physical_thread(move |link| {
link.schedule_physical(&create_next_customer, Asap).unwrap();
});
ctx.schedule(createNextCustomer, Asap);
=}

reaction(createNextCustomer) -> sendCustomer, createNextCustomer {=
// get random production delay
let delay = self.random.gen_range(0..self.average_production_rate as u32);
busy_wait(delay);
let delay = self.random.next_in_range(0..self.average_production_rate as i64);
busy_wait(delay.into());

// send the new customer to the waiting room
info!("Factory: Send customer {} to the waiting room", self.next_customer_id);
self.attempts += 1;
ctx.set(sendCustomer, self.next_customer_id);

self.next_customer_id += 1;
if self.next_customer_id < self.num_customers {
// schedule again
let create_next_customer = createNextCustomer.clone();
ctx.spawn_physical_thread(move |link| {
link.schedule_physical(&create_next_customer, Asap).unwrap();
});
ctx.schedule(createNextCustomer, Asap);
}
=}

reaction(sendCustomerAgain) -> sendCustomer {=
let customer_id = ctx.get(sendCustomerAgain).unwrap();
info!("Factory: Send customer {} again to the waiting room", customer_id);
self.attempts += 1;

ctx.set(sendCustomer, customer_id);
=}

reaction(customerReturned) -> sendCustomerAgain {=
for (i, customer) in customerReturned.into_iter().enumerate() {
if ctx.is_present(&customer) {
let send_customer_again = sendCustomerAgain.clone();
ctx.spawn_physical_thread(move |link| {
link.schedule_physical_with_v(&send_customer_again, Some(i), Asap).unwrap();
});
ctx.schedule_with_v(sendCustomerAgain, Some(i), Asap);
}
}
=}

reaction(customerDone) -> finished {=
for customer in customerDone {
if ctx.is_present(&customer) {
Expand All @@ -145,33 +136,33 @@ reactor CustomerFactory(numCustomers:usize(2000), averageProductionRate:usize(10

reactor WaitingRoom(capacity:usize(1000), numCustomers:usize(2000)) {
state capacity(capacity);

input reset_state: unit;

input receiveCustomer: usize;

output[numCustomers] full: unit;
output[numCustomers] wait: unit;

input barberNext: unit;
output barberEnter: usize;
output barberWait: unit;

state queue: VecDeque<usize>;
state barberAsleep: bool(true);

preamble {=
use std::collections::VecDeque;
=}

reaction(reset_state) {=
self.barberAsleep = true;
=}
=}

reaction(receiveCustomer) -> full, wait, barberEnter {=
let customer_id = ctx.get(receiveCustomer).unwrap();
info!("Room: Customer {} tries to enter", customer_id);

if self.queue.len() == self.capacity {
ctx.set(full.get(customer_id), ());
} else {
Expand All @@ -184,7 +175,7 @@ reactor WaitingRoom(capacity:usize(1000), numCustomers:usize(2000)) {
}
}
=}

reaction(barberNext) -> barberEnter, barberWait {=
if self.queue.is_empty() {
self.barberAsleep = true;
Expand All @@ -197,28 +188,28 @@ reactor WaitingRoom(capacity:usize(1000), numCustomers:usize(2000)) {

reactor Customer(bank_index:usize(0)) {
state bank_index(bank_index);

input roomFull: unit;
input wait: unit;
input startCutting: unit;
input doneCutting: unit;
input doneCutting: unit;

output returned: unit;
output done: unit;

reaction(roomFull) -> returned {=
info!("Customer {}: The wating room is full. I am leaving.", self.bank_index);
ctx.set(returned, ());
=}
=}

reaction(wait) {=
info!("Customer {}: I will wait.", self.bank_index);
=}

reaction(startCutting) {=
info!("Customer {}: I am now being served.", self.bank_index);
=}

reaction(doneCutting) -> done {=
info!("Customer {}: I have been served.", self.bank_index);
ctx.set(done, ());
Expand All @@ -227,64 +218,63 @@ reactor Customer(bank_index:usize(0)) {

reactor Barber(averageHaircutRate:usize(1000), numCustomers:usize(2000)) {
state average_haircut_rate(averageHaircutRate);

input reset_state: unit;
input enter: usize;
input wait: unit;

output[numCustomers] startCutting: unit;
output[numCustomers] doneCutting: unit;
output next: unit;

physical action done: usize;

state random: PseudoRandomGenerator;

reaction(reset_state) {=
self.random = PseudoRandomGenerator::default();
=}
=}

reaction(done) -> doneCutting, next {=
let customer_id = ctx.get(done).unwrap();
ctx.set(doneCutting.get(customer_id), ());
ctx.set(next, ());
=}

reaction(enter) -> startCutting, done {=
let customer_id = ctx.get(enter).unwrap();
ctx.set(startCutting.get(customer_id), ());

// calculate a random delay
let delay = self.random.gen_range(0..self.average_haircut_rate as u32) + 10;

let delay: u32 =
<RandomValue as Into<u32>>::into(self.random.next_in_range(0..self.average_haircut_rate as i64))
+ 10;

// do the actual cutting and apply a physical delay
info!("Barber: Processing customer {}", customer_id);
busy_wait(delay);

// notify the customer
let d = done.clone();
ctx.spawn_physical_thread(move |link| {
link.schedule_physical_with_v(&d, Some(customer_id), Asap).unwrap();
});
ctx.schedule_with_v(done, Some(customer_id), Asap);
=}

reaction(wait) {=
info!("Barber: No customers. Going to sleep.");
=}

preamble {=
use crate::pseudo_random::PseudoRandomGenerator;
use crate::pseudo_random::{PseudoRandomGenerator, RandomValue};
use rand::prelude::*;

pub fn busy_wait(limit: u32) -> u32 {
let mut test = 0;
let mut rng = rand::thread_rng();

for _ in 0..limit {
let _: usize = rng.gen();
test += 1;
}

test
}
=}
Expand All @@ -296,13 +286,13 @@ main reactor (numIterations:usize(12), waitingRoomSize:usize(1000), averageProdu
state average_production_rate(averageProductionRate);
state average_haircut_rate(averageHaircutRate);
state num_haircuts(numHaircuts);

runner = new BenchmarkRunner(num_iterations=numIterations);
factory = new CustomerFactory(numCustomers=numHaircuts, averageProductionRate=averageProductionRate);
room = new WaitingRoom(capacity=waitingRoomSize, numCustomers=numHaircuts);
barber = new Barber(averageHaircutRate=averageHaircutRate, numCustomers=numHaircuts)
customers = new[numHaircuts] Customer();

reaction(startup) {=
print_benchmark_info("SleepingBarberReactorLFRustBenchmark");
print_args!(
Expand All @@ -319,10 +309,10 @@ main reactor (numIterations:usize(12), waitingRoomSize:usize(1000), averageProdu
);
print_system_info();
=}

(runner.start)+ -> factory.start, barber.reset_state, room.reset_state;
factory.finished -> runner.finished;

factory.sendCustomer -> room.receiveCustomer;
room.full -> customers.roomFull;
room.wait -> customers.wait;
Expand All @@ -332,9 +322,9 @@ main reactor (numIterations:usize(12), waitingRoomSize:usize(1000), averageProdu
barber.startCutting -> customers.startCutting;
barber.doneCutting -> customers.doneCutting;
customers.done -> factory.customerDone;

customers.returned -> factory.customerReturned;

preamble {=
use crate::{print_args,reactors::benchmark_runner::{print_system_info, print_benchmark_info}};
=}
Expand Down
6 changes: 6 additions & 0 deletions Rust/Savina/src/lib/pseudo_random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ impl Into<i32> for RandomValue {
}
}

impl Into<u32> for RandomValue {
fn into(self) -> u32 {
*self as u32
}
}

impl Into<u64> for RandomValue {
fn into(self) -> u64 {
*self as u64
Expand Down
14 changes: 11 additions & 3 deletions runner/conf/benchmark/savina_concurrency_barber.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ targets:
waiting_room_size: ["-D", "waitingRoomSize=<value>"]
production_rate: ["-D", "averageProductionRate=<value>"]
haircut_rate: ["-D", "averageHaircutRate=<value>"]



lf-rust:
copy_sources:
- "${bench_path}/Rust/Savina/src/lib"
- "${bench_path}/Rust/Savina/src/concurrency"
lf_file: "concurrency/SleepingBarber.lf"
binary: "sleeping_barber"
run_args:
haircuts: ["--main-num-haircuts", "<value>"]
waiting_room_size: ["--main-waiting-room-size", "<value>"]
production_rate: ["--main-average-production-rate", "<value>"]
haircut_rate: ["--main-average-haircut-rate", "<value>"]

0 comments on commit 61c7492

Please sign in to comment.