From 11e245f41f7b858a5b7e89183c872cfe433ffe30 Mon Sep 17 00:00:00 2001 From: Dorian Weber <weber@informatik.hu-berlin.de> Date: Thu, 20 Mar 2025 17:14:48 +0100 Subject: [PATCH] Updated the README and renamed some files. --- README.md | 18 +-- c/Makefile | 2 +- c/{decompress-tokenize.c => coroutines.c} | 2 +- slx/barbershop.slx | 90 ------------- slx/ferry.slx | 157 ---------------------- slx/philosophers.slx | 152 --------------------- 6 files changed, 9 insertions(+), 412 deletions(-) rename c/{decompress-tokenize.c => coroutines.c} (99%) delete mode 100644 slx/barbershop.slx delete mode 100644 slx/ferry.slx delete mode 100644 slx/philosophers.slx diff --git a/README.md b/README.md index a9566bf..a5c86b9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ ## Introduction -This Rust project provides a framework for creating and running simulations using asynchronous processes. By leveraging Rust's concurrency features, it allows you to simulate various scenarios, making it a valuable tool for studying the mechanics of simulation libraries in projects involving complex, process-driven models. +This Rust project provides a minimally-viable library for creating and running simulations using asynchronous processes. By leveraging Rust's concurrency features, it allows you to simulate various scenarios, making it a valuable tool for studying the mechanics of simulation libraries in projects involving complex, process-based models. + +This simulation core is not meant to be used in production code, see [odem-rs] for an earnest attempt. ## Features @@ -14,22 +16,15 @@ The project includes: - `barbershop` - `ferry` - `philosophers` -- **SLX Code (`slx`)**: Contains SLX code for the same examples, including benchmarking provisions with real-time measurements. -- **C++ Code (`cpp`)**: Contains three subprojects for the examples using ODEMx. -- **C Code (`c`)**: Demonstrates the transformation of an ordinary routine into a coroutine for a decompressor-tokenizer example. -- **ODEMx (`odemx-lite`)**: A clean version without dependencies, compiled using a C++14-conforming compiler to run benchmarks. +- **Decompressor-Tokenizer (`bin/coroutines.rs`)**: An educational example with an executor, a corresponding spawner, and a channel that shows the mechanism without using unsafe code. +- **Decompressor-Tokenizer (`c/coroutines.c`)**: The same example implemented manually in C to illustrate the transformation performed by the Rust compiler on `async`/`await`. ## Getting Started ### Prerequisites - **Rust Compiler**: Latest stable version is recommended. [Install Rust][Rust-compiler] -- **C++14-Conforming Compiler**: Required for the ODEMx benchmarks. Popular compilers like MSVC, Clang, MinGW, and GCC should work out of the box. -- **SLX Compiler**: Required for the SLX benchmarks (both student and full versions work). - -#### Note on SLX Compiler - -Currently, obtaining the SLX compiler may be challenging because the creator, James O. Henriksen, passed away in 2020, and the company's original website is no longer available. If you wish to obtain the free version of SLX for benchmarking, please contact me directly. +- no other dependencies ### Building the Project @@ -72,3 +67,4 @@ possible. Extra-special thanks to Lukas Markeffsky for helping me to refine this discussions and by resolving its soundness problems through a refactoring of the code. [Rust-compiler]: https://rust.sh +[odem-rs]: https://crates.io/crates/odem-rs diff --git a/c/Makefile b/c/Makefile index 63a6d73..b210cc3 100644 --- a/c/Makefile +++ b/c/Makefile @@ -2,7 +2,7 @@ .SUFFIXES: .PHONY: all run clean -TAR = decompress-tokenize +TAR = coroutines SRC = $(wildcard *.c) OBJ = $(SRC:%.c=%.o) DEP = $(OBJ:%.o=%.d) diff --git a/c/decompress-tokenize.c b/c/coroutines.c similarity index 99% rename from c/decompress-tokenize.c rename to c/coroutines.c index 401ac0b..795b291 100644 --- a/c/decompress-tokenize.c +++ b/c/coroutines.c @@ -26,7 +26,7 @@ enum Tag { WORD, PUNCT }; /* Names of the token tags. */ static const char *TOKEN_TAG[] = { - [WORD] = "WORD", [PUNCT] = "PUNCT" + [WORD] = "Word", [PUNCT] = "Punct" }; /* Token type with tag and value. */ diff --git a/slx/barbershop.slx b/slx/barbershop.slx deleted file mode 100644 index 50aa666..0000000 --- a/slx/barbershop.slx +++ /dev/null @@ -1,90 +0,0 @@ -import <h7> - -// enable this macro for the benchmark -// #define BENCH - -module barbershop { - // globally shared data - facility joe; - random_variable wait_time; - - // helper constants - constant int BENCH_SAMPLES = 100; - constant double SIM_DURATION = 60.0*24.0*7.0*3.0; // simulation runs over 3 weeks - constant int SEED_A = 100000; - constant int SEED_S = 200000; - - // Customer process with access to the barber and a random processing delay. - class Customer(double _delay) { - double delay = _delay; - actions { - // access the barber and record the time for the report - double arrival_time = time; - seize joe; - tabulate wait_time = time - arrival_time; - - // spend time - advance delay; - // release the barber - release joe; - } - } - - procedure barbershop(double duration) { - // pseudo random number generators referenced from within the main process - rn_stream rng_a seed = SEED_A; - rn_stream rng_s seed = SEED_S; - - fork { - forever { - advance rv_uniform(rng_a, 12.0, 24.0); - if (time >= duration) terminate; - activate new Customer( - rv_uniform(rng_s, 12.0, 18.0) - ); - } - } - - // wait until the store closes - advance duration; - - // finish processing the queue (no more customers arrive) - wait until FNU(joe); - } - - procedure main(int argc, string(*) argv[*]) { - // interpret the optional command line arguments for simulation - // duration and number of timed runs in this batch - double sim_duration = SIM_DURATION; - int bench_samples = BENCH_SAMPLES; - if (argc > 1) { - read string = argv[1] (sim_duration); - if (argc > 2) { - read string = argv[2] (bench_samples); - } - } - - #ifdef BENCH - random_variable samples; - double real_start, real_end; - int i; - - // remove the random variable from the system as to prevent resets on 'clear system' - remove &samples from random_variable_set; - - for (i = 1; i <= bench_samples; ++i) { - real_start = real_time(); - barbershop(sim_duration); - real_end = real_time(); - tabulate samples = real_end - real_start; - empty rn_stream_set; - clear system; - } - - print(sample_sum(samples)) "_.________"; - #else - barbershop(sim_duration); - report wait_time; - #endif - } -} \ No newline at end of file diff --git a/slx/ferry.slx b/slx/ferry.slx deleted file mode 100644 index d24648d..0000000 --- a/slx/ferry.slx +++ /dev/null @@ -1,157 +0,0 @@ -import <h7> - -// enable this macro for the benchmark -// #define BENCH - -module ferry { - constant int BENCH_SAMPLES = 100; - constant double SIM_DURATION = 24.0*60.0*7.0; - constant double HARBOR_DISTANCE = 10.0; - constant double FERRY_TIMEOUT = 5.0; - constant int FERRY_CAPACITY = 5; - constant int FERRY_COUNT = 2; - constant int HARBOR_COUNT = 4; - - random_variable ferry_cargo_len; - random_variable ferry_load_time; - random_variable car_wait_time; - - passive class Car(double duration) { - double arrival_time = time; - double load_duration = duration; - } - - class Pier { - rn_stream rng; - set(Car) ranked FIFO landing_site; - - actions { - double load_duration; - forever { - advance rv_expo(rng, 10.0); - do load_duration = rv_normal(rng, 0.5, 0.2); - while (load_duration < 0.0); - place new Car(load_duration) into landing_site; - } - } - } - - class Ferry(int _capacity, double _timeout, double _travel_time) { - pointer(Car) cargo[_capacity]; - double timeout = _timeout; - double travel_time = _travel_time; - set(Pier) ranked FIFO piers; - pointer(Pier) pier; - pointer(Car) car; - - actions { - double begin_loading, begin_waiting; - int len = 0; - int i; - - forever { - for (pier = each Pier in piers) { - // unload the cars - for (i = 1; i <= len; ++i) { - advance cargo[i]->load_duration; - } - - len = 0; - begin_loading = time; - - // wait until new cars arrive or a timeout occurs - while (len < array_upper_bound(cargo, 1)) { - begin_waiting = time; - wait until pier->landing_site.size > 0 - || time >= begin_waiting + timeout; - car = first Car in pier->landing_site; - - if (car != NULL) { - // a car arrived in time - remove car from pier->landing_site; - tabulate car_wait_time = time - car->arrival_time; - advance car->load_duration; - cargo[++len] = car; - } else { - // the timeout has been triggered - break; - } - } - - tabulate ferry_load_time = time - begin_loading; - tabulate ferry_cargo_len = len; - - // travel to the next harbor - advance travel_time; - } - } - } - } - - procedure ferry(double duration, int ferries, int harbors) { - pointer(Pier) ports[harbors]; - pointer(Ferry) ferry; - pointer(Car) car; - int i, j; - - // create all of the harbors - for (i = 1; i <= harbors; ++i) { - ports[i] = new Pier; - activate ports[i]; - } - - // create all of the ferries - for (i = 1; i <= ferries; ++i) { - ferry = new Ferry(FERRY_CAPACITY, FERRY_TIMEOUT, HARBOR_DISTANCE); - for (j = 1; j <= harbors; ++j) - place ports[(i + j - 2) % harbors + 1] into ferry->piers; - activate ferry; - } - - // await the end of the simulation - advance duration; - - // take cars into account that weren't picked up by a ferry - for (i = 1; i <= harbors; ++i) { - for (car = each Car in ports[i]->landing_site) { - tabulate car_wait_time = time - car->arrival_time; - } - } - } - - procedure main(int argc, string(*) argv[*]) { - // interpret the optional command line arguments for simulation - // duration and number of timed runs in this batch - double sim_duration = SIM_DURATION; - int bench_samples = BENCH_SAMPLES; - if (argc > 1) { - read string = argv[1] (sim_duration); - if (argc > 2) { - read string = argv[2] (bench_samples); - } - } - - #ifdef BENCH - random_variable samples; - double real_start, real_end; - int i; - - // remove the random variable from the system as to prevent resets on 'clear system' - remove &samples from random_variable_set; - - for (i = 1; i <= bench_samples; ++i) { - real_start = real_time(); - ferry(sim_duration, FERRY_COUNT, HARBOR_COUNT); - real_end = real_time(); - tabulate samples = real_end - real_start; - empty rn_stream_set; - clear system; - } - - print(sample_sum(samples)) "_.________"; - #else - ferry(sim_duration, FERRY_COUNT, HARBOR_COUNT); - report system; - #endif - } -} \ No newline at end of file diff --git a/slx/philosophers.slx b/slx/philosophers.slx deleted file mode 100644 index e156676..0000000 --- a/slx/philosophers.slx +++ /dev/null @@ -1,152 +0,0 @@ -import <h7> - -#define SLX2 - -// enable this macro for the benchmark -// #define BENCH - -module philosophers { - constant int BENCH_SAMPLES = 100; - constant int PHILOSOPHER_COUNT = 5; - constant int EXPERIMENT_COUNT = 500; - - random_variable sim_duration; - - passive class Table(int count) { - control boolean forks[count]; - control int forks_held = 0; - control int forks_awaited = 0; - - clear { - int i; - for (i = 1; i <= array_upper_bound(forks, 1); ++i) { - forks[i] = FALSE; - } - forks_held = 0; - forks_awaited = 0; - } - - // blocks until the requested fork is available and acquires it - method acquire_fork(int i) { - int num = ((i - 1) % array_upper_bound(forks, 1)) + 1; - forks_awaited++; - wait until forks[num] == FALSE; - forks[num] = TRUE; - forks_awaited--; - forks_held++; - } - - // returns the requested fork to the table - method release_fork(int i) { - forks[((i - 1) % array_upper_bound(forks, 1)) + 1] = FALSE; - forks_held--; - } - } - - class Philosopher(pointer(Table) my_table, int my_seat) { - pointer(Table) table = my_table; - int seat = my_seat; - rn_stream rng; - - actions { - forever { - double eating_duration; - - // spend some time pondering the nature of things - advance rv_expo(rng, 1.0); - - // acquire the first fork - table->acquire_fork(seat); - - // introduce an artificial delay to leave room for deadlocks - advance rv_uniform(rng, 0.1, 0.2); - - // acquire the second fork - table->acquire_fork(seat + 1); - - // spend some time eating - do eating_duration = rv_normal(rng, 0.5, 0.2); - while (eating_duration < 0.0); - advance eating_duration; - - // release the forks - table->release_fork(seat + 1); - table->release_fork(seat); - } - } - } - - procedure philosophers(int count, int reruns) { - pointer(Table) table = new Table(count); - pointer(Philosopher) philos[count]; - int i, j; - - // create the philosopher-processes and seat them - for (i = 1; i <= count; ++i) { - philos[i] = new Philosopher(table, i); - activate philos[i]; - } - - // since we want to collect statistics over several runs, - // exclude the random variable from the system - remove &sim_duration from random_variable_set; - - // run the simulation a number of times - for (j = 1; j <= reruns; ++j) { - // wait for the precise configuration indicating a deadlock - wait until table->forks_held == count - && table->forks_awaited == count; - - // tabulate the current system time - tabulate sim_duration = time; - - // reset the table as well as the simulation system - clear *table; - clear system; - - // activate another set of philosophers - for (i = 1; i <= count; ++i) { - activate philos[i]; - } - } - - // re-insert the random variable into the system again - place &sim_duration into random_variable_set; - } - - procedure main(int argc, string(*) argv[*]) { - // interpret the optional command line arguments for number - // of experiments per run and number of timed runs in this batch - int experiment_count = EXPERIMENT_COUNT; - int bench_samples = BENCH_SAMPLES; - if (argc > 1) { - read string = argv[1] (experiment_count); - if (argc > 2) { - read string = argv[2] (bench_samples); - } - } - - #ifdef BENCH - random_variable samples; - double real_start, real_end; - int i; - - // remove the random variable from the system as to prevent resets on 'clear system' - remove &samples from random_variable_set; - - for (i = 1; i <= bench_samples; ++i) { - real_start = real_time(); - philosophers(PHILOSOPHER_COUNT, experiment_count); - real_end = real_time(); - tabulate samples = real_end - real_start; - empty rn_stream_set; - clear system; - } - - print(sample_sum(samples)) "_.________"; - #else - philosophers(PHILOSOPHER_COUNT, experiment_count); - report sim_duration; - #endif - } -} \ No newline at end of file -- GitLab