rusty_junctions/lib.rs
1//! Crate implementing Join Patterns from the [Join Calculus](https://www.microsoft.com/en-us/research/wp-content/uploads/2017/01/join-tutorial.pdf) developed by
2//! Cédric Fournet and Georges Gonthier.
3//!
4//! Join Patterns are based on message-passing concurrency and offer a declarative
5//! way of expression concurrent computation that is entirely thread-safe and
6//! requires no manual coordination efforts.
7//!
8//! See the example of a storage cell written using this crate:
9//!
10//! ```
11//! use rusty_junctions::Junction;
12//!
13//! fn main() {
14//! /* Start of the Join Pattern setup. */
15//!
16//! // Declare a new Junction to create new channels and construct new
17//! // Join Patterns based on them.
18//! let cell = Junction::new();
19//!
20//! // New channel to retrieve the value of the storage cell.
21//! let get = cell.recv_channel::<i32>();
22//!
23//! // New channel to update the value of the storage cell.
24//! let put = cell.send_channel::<i32>();
25//!
26//! // New channel to swap the value of the storage cell for a new one and
27//! // retrieve the value that was just replaced.
28//! let swap = cell.bidir_channel::<i32, i32>();
29//!
30//! // New channel that will actually carry the value so that at no point
31//! // any given thread will have possession over it so concurrency issues
32//! // are avoided by design.
33//! let val = cell.send_channel::<i32>();
34//!
35//! // Set up some clones of the above channels we can move over to the
36//! // thread in which the function body of the Join Pattern will run.
37//! //
38//! // Clones of channels work like clones of the std::sync::mpsc::Sender
39//! // clones - any message sent from the clone will be received as if
40//! // sent from the original.
41//! let get_val = val.clone();
42//! let put_val = val.clone();
43//! let swap_val = val.clone();
44//! let val_val = val.clone();
45//!
46//! // Declare a new Join Pattern to update the storage cell value. If
47//! // both the put and val channel have sent a message, meaning someone
48//! // requested a value update and there is a value to be updated, send
49//! // a new val message through one of val's clones that carries the
50//! // updated value.
51//! cell.when(&put).and(&val).then_do(move |new, _old| {
52//! println!(">> put-val pattern fired with new={}!", new);
53//! put_val.send(new).unwrap();
54//! });
55//!
56//! // Declare a new Join Pattern to retrieve the storage cell value. If
57//! // both the get and val channel have sent a message, meaning someone
58//! // requested the value and there is a value to be given, return that
59//! // value and resend it through one of val's clones so that the value
60//! // is still available in future and not just consumed once.
61//! cell.when(&val).and_recv(&get).then_do(move |v| {
62//! println!(">> val-get pattern fired with v={}!", v);
63//!
64//! get_val.send(v.clone()).unwrap();
65//!
66//! v
67//! });
68//!
69//! // Declare a new Join Pattern to swap the storage cell value with a
70//! // new one and retrieve the old. Essentially works like a combination
71//! // of the previous two Join Patterns, with one crucial distinction:
72//! // with this Join Pattern, the update of the value and the retrieval
73//! // of the old are atomic, meaning that it is guaranteed that even in
74//! // a multithreaded environment with many users accessing the storage
75//! // cell, the value retrieved is exactly the value that has been
76//! // updated.
77//! cell.when(&val).and_bidir(&swap).then_do(move |old, new| {
78//! println!(
79//! ">> val-swap pattern fired with old={} and new={}!",
80//! old, new
81//! );
82//! swap_val.send(new).unwrap();
83//!
84//! old
85//! });
86//!
87//! // Declare a new Join Pattern that mentions the same channel multiple
88//! // times, so if the val channel has sent two messages they will be
89//! // combined into a single messages sent by a clone of val. This ensures
90//! // that eventually, the storage cell will only keep a single value
91//! // around.
92//! cell.when(&val).and(&val).then_do(move |a, b| {
93//! println!(">> val-val pattern fired with a={} and b={}!", a, b);
94//! val_val.send(a + b).unwrap();
95//! });
96//!
97//! /* End of the Join Pattern setup. */
98//!
99//! // Initialise the storage cell by sending an initial value that
100//! // can be picked up in future executions of the above Join Patterns.
101//! val.send(1729).unwrap();
102//!
103//! // Request a value update if one is available.
104//! put.send(42).unwrap();
105//!
106//! // Send another value that will eventually get combined with the
107//! // existing one.
108//! val.send(1).unwrap();
109//!
110//! // Request another value update.
111//! put.send(22).unwrap();
112//!
113//! // Request the current value of the storage cell and print it.
114//! println!("get.recv()={}", get.recv().unwrap());
115//!
116//! // Request a swap of the current value of the storage cell with a new
117//! // one and print the old value that is retrieved as a result.
118//! println!("swap.send_recv()={}", swap.send_recv(16).unwrap());
119//!
120//! // Request the current value of the storage cell again and print it.
121//! println!("get.recv()={}", get.recv().unwrap());
122//! }
123//! ```
124//!
125//! The above example is reasonably complex to show off most of the
126//! capabilities of this crate, as well as the general workflow of setting
127//! up channels, then Join Patterns synchronising the channels and running
128//! arbitrary code, followed by actually sending messages on the declared
129//! channels to satisfy the conditions declared in the Join Patterns that will
130//! trigger an execution of their function body.
131//!
132//! For more examples, visit the [`examples`](https://github.com/smueksch/rusty_junctions/tree/master/examples) folder in the [Rusty Junctions GitHub
133//! repository](https://github.com/smueksch/rusty_junctions).
134
135mod bag;
136pub mod channels;
137mod controller;
138mod counter;
139mod function_transforms;
140mod inverted_index;
141mod junction;
142pub mod patterns;
143pub mod types;
144
145pub use junction::Junction;