diff --git a/Cargo.toml b/Cargo.toml index 258a30287cfdafbbc1e850669b231fda1cbb8fe4..d6978deb8b50cbaffd0d11dd124dccbef5758d6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,12 +37,14 @@ crossbeam-utils = "0.7.2" lazy_static = "1.4.0" [lib] -# compile target: dynamically linked library using C ABI -crate-type = ["cdylib"] +crate-type = [ + "rlib", # for use as a Rust dependency. + "cdylib" # for FFI use, typically C. +] [features] default = ["ffi", "session_optimization"] ffi = [] # see src/ffi/mod.rs -ffi_pseudo_socket_api = ["ffi", "libc", "os_socketaddr"]# see src/ffi/pseudo_socket_api.rs +ffi_pseudo_socket_api = ["ffi", "libc", "os_socketaddr"]# see src/ffi/pseudo_socket_api.rs. endpoint_logging = [] # see src/macros.rs session_optimization = [] # see src/runtime/setup.rs \ No newline at end of file diff --git a/README.md b/README.md index 3a07b4e3665fd9bedf13e2baf9957c8e3776c4ee..f1af66eb79cd31422574eea105daded09be271a6 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,21 @@ # Reowolf 1.0 Implementation +This library provides connectors as a generalization of sockets for use in communication over the internet. This repository is one of the deliverables of the [Reowolf project](https://nlnet.nl/project/Reowolf/) funded by the NLNet foundation. + ## Compilation instructions -1. Install the latest stable Rust toolchain using Rustup. See https://rustup.rs/ for further instructions. -1. Run `cargo build --release` to download source dependencies, and compile the library with release-level optimizations. - - The resulting dylib can be found in target/release/, to be used with the header file reowolf.h. - - Note: A list of immediate ancestor dependencies is visible in Cargo.toml. - - Note: Run `cargo test --release` to run unit tests with release-level optimizations. - -## Build options -- `cargo build --release` produces the dylib object, exposing connector API for Rust. The C FFI is also included, and corresponds to the header file `reowolf.h`. -- `cargo build --release --features ffi_pseudo_socket_api` is only available on Linux, and also generates functions which comprise the pseudo-socket C FFI. +1. Install the latest stable Rust toolchain (`rustup install stable`) using the [rustup](https://rustup.rs/) CLI tool, available for most platforms. +1. Have `cargo` (the Rust language package manager) download source dependencies, and compile the library with release-level optimizations with `cargo build --release`: + - The resulting dylib can be found in `./target/release/`, to be used with the header file: `./reowolf.h`. + - *Note*: A list of immediate ancestor dependencies is visible in `./Cargo.toml`. + - *Note*: Run `cargo test --release` to run unit tests with release-level optimizations. + +## Using the library +- The library may be used as a Rust dependency by adding it as a git dependency, i.e., by adding line `reowolf_rs = { git = "https://scm.cwi.nl/FM/reowolf" }` to downstream crate's manifest file, `Cargo.toml`. +- The library may be used as a dynamically-linked library using its C ABI as the cdylib written to `./target/release` when compiled with release optimizations, in combination to the header file `./reowolf.h`. +- When compiled on Linux, the compiled library will include definitions of pseudo-socket procedures declared in `./pseudo-socket.h` when compiled with `cargo build --release --features ffi_pseudo_socket_api`. The added functionality is only needed when requiring that connectors expose a socket-like API. + +## Examples +The `examples` directory contains example usages of connectors for message passing over the internet. The programs within require that the library is compiled as a dylib (see above). ## Notes 3. Running `cbindgen > reowolf.h` from the root will overwrite the header file. (WIP) This is only necessary to update it. \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000000000000000000000000000000000..293f0aa6eae88cf2ec0bdd29807db33fb519f7e3 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,31 @@ +# Examples +This directory contains a set of programs for demonstrating the use of connectors for communications over the internet. + +## Setting up and running +First, ensure that the Reowolf has been compiled, such that a dylib is present in `reowolf/target/release/`; see the parent directory for instructions on compiling the library. + +The examples are designed to be run with the examples folder as your working directory (`reowolf/examples`), containing a local copy of the dylic. Two convenience scripts are made available for copying the library: `cpy_dll.sh` and `cpy_so.sh` for platforms Linux and Windows respectively. + +Compiling the example programs is done using Python 3, with `python3 ./make.py`, which will crawl the example directory's children and compiling any C source files. + +## Groups of examples and takeaways +### Incremental Connector API examples +The examples contained within directories with names prefixed with `incremental_` demonstrate usage of the connector API. This set of examples is characterized by each example being self-contained, designed to be run in isolation. The examples are best understood when read in the order of their directories' names (from 1 onward), as they demonstrate the functionality of the connector API starting from the most (trivially) simple connectors, to more complex connectors incorporating multiple network channels and components. + +Each example source file is prefixed by a multi-line comment, explaining what a reader is intended to take away from the example. + +### Presentation examples +The examples contained within directories with names matching `pres_` are designed to accompany the Reowolf demonstration slides (inclusion here TODO). +Examples include interactions whose distributed sessions span multiple source files. What follows is a list of the sessions' consituent programs, along with what the session intends to demonstrate + +1. {pres_1/amy, pres_1/bob}: Connectors can be used to transmit messages over the internet in a fashion comparable to that of UDP sockets. + +2. {pres_1/amy, pres_2/bob}: The protocol descriptions used to configure components are loaded and parsed at runtime. Consequently, changing these descriptions can change the behavior of the system without recompiling any of the constituent programs. +2. {pres_3/amy, pres_3/bob}: *Atomicity*. Message exchange actions are grouped into synchronous interactions. Actions occur with observable effects IFF their interactions are well-formed, and complete successfully. For example, a put succeeds IFF its accompanying get succeeds. +2. {pres_3/amy, pres_4/bob}: *Nondeterminism*. Programs/components can express flexibility by providing mutually-exclusive firing patterns on their ports, as a nondeterministic choice. Which (if any) choice occurs can be determined after synchronization by inspecting the return value of `connector_sync`. Atomicity + Nondeterminism = Specialization of behavior. +2. {pres_5/amy, pres_5/bob}: When no synchronous interaction is found before some consituent program times out, the system RECOVERS to the synchronous state at the start of the round, allowing components to try again. + +### Interoperability examples +The examples contained within directories with names matching `interop_` demonstrate the use of different APIs for communication over UDP channels. The three given programs are intended to be run together, each as its own process. + +Each example source file is prefixed by a multi-line comment, explaining what a reader is intended to take away from the example. \ No newline at end of file diff --git a/examples/1_minimal/amy.c b/examples/incr_1/amy.c similarity index 62% rename from examples/1_minimal/amy.c rename to examples/incr_1/amy.c index ed89ee43272cd721a8f47c5bc575045003246ac9..0221f34b9289c36ea81c17e469e02142ef0e182d 100644 --- a/examples/1_minimal/amy.c +++ b/examples/incr_1/amy.c @@ -1,3 +1,8 @@ +/* This example demonstrates: +- how protocol description structures are created and destroyed +- how connectors are created and destroyed +- there are procedures allowing for debugging the states of connectors +*/ #include #include #include "../../reowolf.h" diff --git a/examples/2_runtime_pdl/amy.c b/examples/incr_2/amy.c similarity index 62% rename from examples/2_runtime_pdl/amy.c rename to examples/incr_2/amy.c index 065e9707535075bb7b0e6f0de0def31fd0bbe664..4bc181796aae7f3a3ac579bb8daac97b87264876 100644 --- a/examples/2_runtime_pdl/amy.c +++ b/examples/incr_2/amy.c @@ -1,3 +1,7 @@ +/* This example demonstrates: +- protocol descriptions are parsed from ascii text expressed in Reowolf's protocol language, PDL +- protocol descriptions load their PDL at runtime; component's definitions can be changed without recompiling the main program +*/ #include #include #include "../../reowolf.h" diff --git a/examples/3_trivial_connect/amy.c b/examples/incr_3/amy.c similarity index 80% rename from examples/3_trivial_connect/amy.c rename to examples/incr_3/amy.c index 642e33be011a62d355ecb7e101dc789eb7a6f7db..6402835e81f8df08fde75955767c911aa48a8046 100644 --- a/examples/3_trivial_connect/amy.c +++ b/examples/incr_3/amy.c @@ -1,3 +1,6 @@ +/* This example demonstrates: +- Connectors begin in a "setup" state, which is existing with `connector_connect` +*/ #include #include #include "../../reowolf.h" diff --git a/examples/4_port_pair/amy.c b/examples/incr_4/amy.c similarity index 82% rename from examples/4_port_pair/amy.c rename to examples/incr_4/amy.c index d8ac4090a14125c92d009985d4a9f95c535c8517..bbb5fe9e0e822d416ffe1797365e37c0cca88d6a 100644 --- a/examples/4_port_pair/amy.c +++ b/examples/incr_4/amy.c @@ -1,3 +1,6 @@ +/* This example demonstrates: +- Connectors in the setup state can add new channels by creating port pairs +*/ #include #include #include "../../reowolf.h" diff --git a/examples/5_put_get/amy.c b/examples/incr_5/amy.c similarity index 56% rename from examples/5_put_get/amy.c rename to examples/incr_5/amy.c index b483a4073539ec6dcef587dade9dcdba8a90198f..ce05abf932c9363866ec72973e5ba66e9af2a50b 100644 --- a/examples/5_put_get/amy.c +++ b/examples/incr_5/amy.c @@ -1,3 +1,12 @@ +/* This example demonstrates: +- After connecting, ports can exchange messages +- Message exchange is conducted in two phases: + 1. preparing with `connector_put`, `connector_get`, and + 2. completing with `connector_sync`. + This paradigm is similar to that for sockets in non-blocking mode. +- The connector stores messages received during sync. they can be inspected using `connector_gotten_bytes`. +- Ports created using `connector_add_port_pair` behave as a synchronous channel; messages sent in one end are received at the other. +*/ #include #include #include "../../reowolf.h" @@ -17,7 +26,7 @@ int main(int argc, char** argv) { connector_put_bytes(c, putter, "hello", 5); connector_get(c, getter); - connector_sync(c, -1); + connector_sync(c, -1); // -1 means infinite timeout duration size_t msg_len; const char * msg_ptr = connector_gotten_bytes(c, getter, &msg_len); printf("Got msg `%.*s`\n", (int) msg_len, msg_ptr); diff --git a/examples/6_atomic/amy.c b/examples/incr_6/amy.c similarity index 76% rename from examples/6_atomic/amy.c rename to examples/incr_6/amy.c index 0b0c18e88e0309cfb67a9cc6d1ee7d4fe1a949af..cdddc76f60d33fbab115f9a3484982a1b28071b1 100644 --- a/examples/6_atomic/amy.c +++ b/examples/incr_6/amy.c @@ -1,3 +1,7 @@ +/* This example demonstrates: +- Synchronized PUTs and GETs always succeed together. +- Attemping to put without a corresponding GET results in `connector_sync` failing at runtime. +*/ #include #include #include "../../reowolf.h" @@ -19,7 +23,7 @@ int main(int argc, char** argv) { printf("Let's try to put without get\n"); connector_put_bytes(c, putter, "hello", 5); // connector_get(c, getter); - int err = connector_sync(c, 5000); + int err = connector_sync(c, 5000); // 5000 means 5000millisec timeout duration printf("Error code %d with string `%s`\n", err, reowolf_error_peek(NULL)); protocol_description_destroy(pd); diff --git a/examples/7_recovery/amy.c b/examples/incr_7/amy.c similarity index 91% rename from examples/7_recovery/amy.c rename to examples/incr_7/amy.c index c3adb7649c14ce5910403fa6b5b8504500dfdb34..c2d4ed302e458849792268ef0abcf9d91dab710b 100644 --- a/examples/7_recovery/amy.c +++ b/examples/incr_7/amy.c @@ -1,3 +1,6 @@ +/* This example demonstrates: +- Synchronous rounds that fail result in RECOVERY; no messages are sent or received. +*/ #include #include #include "../../reowolf.h" diff --git a/examples/8_net_ports/amy.c b/examples/incr_8/amy.c similarity index 54% rename from examples/8_net_ports/amy.c rename to examples/incr_8/amy.c index 593842508670db75018d3006bdc7950136ffcf2e..56faa210bf906fb264faed5a56a266965ffd6eee 100644 --- a/examples/8_net_ports/amy.c +++ b/examples/incr_8/amy.c @@ -1,3 +1,14 @@ +/* This example demonstrates: +- Synchronous channels can span the network if created by pairs of `connector_add_net_port` calls, + each binding a port to the same address, with complementary polarities. +- Ports created this way have two notions of polarity: + - (port) Polarity in {Putter, Getter}: + - Putter => resulting port can `connector_put`, + - Getter => resulting port can `connector_get`, + - Endpoint Polarity in {Active, Passive}: + - Passive => underlying transport socket will `bind` to the given address, + - Active => underlying transport socket will `connect` to the given address, +*/ #include #include #include "../../reowolf.h" @@ -10,7 +21,7 @@ int main(int argc, char** argv) { printf("Error str `%s`\n", reowolf_error_peek(NULL)); PortId putter, getter; - FfiSocketAddr addr = {{127,0,0,1}, 8000}; + FfiSocketAddr addr = {{127, 0, 0, 1}, 8000}; // ipv4 127.0.0.1 (localhost) transport port 8000 connector_add_net_port(c, &putter, addr, Polarity_Putter, EndpointPolarity_Active); printf("Error str `%s`\n", reowolf_error_peek(NULL)); @@ -20,7 +31,6 @@ int main(int argc, char** argv) { connector_connect(c, 4000); printf("Error str `%s`\n", reowolf_error_peek(NULL)); - protocol_description_destroy(pd); connector_destroy(c); return 0; diff --git a/examples/9_net_self_putget/amy.c b/examples/incr_9/amy.c similarity index 83% rename from examples/9_net_self_putget/amy.c rename to examples/incr_9/amy.c index b4e784c246f6a20e06eab4bfd577f5f17a26cddf..1e92f51de8903fb7691d9710f434537fc9e87521 100644 --- a/examples/9_net_self_putget/amy.c +++ b/examples/incr_9/amy.c @@ -1,3 +1,7 @@ +/* This example demonstrates: +- Once created, ports transport messages regardless of whether + they were created with `connector_add_port_pair` or `connector_add_net_port`. +*/ #include #include #include "../../reowolf.h" @@ -10,7 +14,7 @@ int main(int argc, char** argv) { printf("Error str `%s`\n", reowolf_error_peek(NULL)); PortId putter, getter; - FfiSocketAddr addr = {{127,0,0,1}, 8000}; + FfiSocketAddr addr = {{127, 0, 0, 1}, 8000}; connector_add_net_port(c, &putter, addr, Polarity_Putter, EndpointPolarity_Active); printf("Error str `%s`\n", reowolf_error_peek(NULL)); connector_add_net_port(c, &getter, addr, Polarity_Getter, EndpointPolarity_Passive); diff --git a/examples/interop_socket/main.c b/examples/interop_1_socket/main.c similarity index 80% rename from examples/interop_socket/main.c rename to examples/interop_1_socket/main.c index e3c019002265388a3353531ced868703b4329015..eaa341e83dfeb3fd70ecb053dd46d5028c0652ac 100644 --- a/examples/interop_socket/main.c +++ b/examples/interop_1_socket/main.c @@ -1,3 +1,8 @@ +/* This example demonstrates: +- conventional UDP socket API can be used in a connection-oriented fashion + - first setup with `bind` and `connect` + - henceforth communicating with connected peer using `send` and `recv` in blocking mode. +*/ #include // definies socketaddr_in #include // defines printf #include // defines malloc, free diff --git a/examples/interop_pseudo_socket/main.c b/examples/interop_2_pseudo_socket/main.c similarity index 76% rename from examples/interop_pseudo_socket/main.c rename to examples/interop_2_pseudo_socket/main.c index fc63d14aafd84195133ad28e01e7555f9de226e3..edfd574caeebb26218dc7a24689a0ca0437b0f07 100644 --- a/examples/interop_pseudo_socket/main.c +++ b/examples/interop_2_pseudo_socket/main.c @@ -1,3 +1,9 @@ +/* This example demonstrates: +- Reowolf's pseudo-socket API mimics the API of UDP sockets +- This example corresponds closely with that of the socket API, differing only in: + 1. an added import of the pseudo-socket header file + 2. (pseudo-)socket operations' identifiers are prefixed with `rw_` (short for "Reowolf"). +*/ #include // definies socketaddr_in #include // defines printf #include // defines malloc, free diff --git a/examples/interop_connector/main.c b/examples/interop_3_connector/main.c similarity index 77% rename from examples/interop_connector/main.c rename to examples/interop_3_connector/main.c index 7baebcbd6fbf38ae17a19807b1fa17ea1aee9725..c883cd17b07a4c89eca039c8c5ae1e670a867f96 100644 --- a/examples/interop_connector/main.c +++ b/examples/interop_3_connector/main.c @@ -1,3 +1,8 @@ +/* This example demonstrates: +- connector API can create UDP mediator components, each given a BIND and CONNECT socket address, + which communicates via a returned putter and getter port pair. +- messages passed to the UDP mediator are forwarded as UDP datagrams into the network. +*/ #include #include #include "../../reowolf.h" diff --git a/examples/pres_1/amy.c b/examples/pres_1/amy.c index 63ea181f18b816de47d8e53b6c884f1afed8eee8..c779ea7934b128e862a5a824c229d205a57ac9ab 100644 --- a/examples/pres_1/amy.c +++ b/examples/pres_1/amy.c @@ -1,8 +1,5 @@ - #include "../../reowolf.h" #include "../utility.c" - - int main(int argc, char** argv) { char msgbuf[64]; // ask user what message to send @@ -16,7 +13,7 @@ int main(int argc, char** argv) { // ... with 1 outgoing network connection PortId p0; - FfiSocketAddr addr = {{127,0,0,1}, 8000}; + FfiSocketAddr addr = {{127, 0, 0, 1}, 8000}; connector_add_net_port(c, &p0, addr, Polarity_Putter, EndpointPolarity_Passive); rw_err_peek(c); diff --git a/examples/pres_1/bob.c b/examples/pres_1/bob.c index c421e0cc1d9f1929c9e5d5cc632790c23a2586ab..c6b7ef3c365e4db687e727db336b9c0812ec258e 100644 --- a/examples/pres_1/bob.c +++ b/examples/pres_1/bob.c @@ -1,8 +1,5 @@ - #include "../../reowolf.h" #include "../utility.c" - - int main(int argc, char** argv) { // Create a connector, configured with our (trivial) protocol. Arc_ProtocolDescription * pd = protocol_description_parse("", 0); diff --git a/examples/pres_2/bob.c b/examples/pres_2/bob.c index f5da91926e15f62bd6e0f0733bf89a97d313526b..5051d58602f30c9aca9a4e3ee0ccde8d064c511a 100644 --- a/examples/pres_2/bob.c +++ b/examples/pres_2/bob.c @@ -1,8 +1,5 @@ - #include "../../reowolf.h" #include "../utility.c" - - int main(int argc, char** argv) { // Create a connector, configured with a protocol defined in a file char * pdl = buffer_pdl("./eg_protocols.pdl");