fast_stm/
lib.rs

1//! This library implements
2//! [software transactional memory](https://en.wikipedia.org/wiki/Software_transactional_memory),
3//! often abbreviated with STM.
4//!
5//! It is designed closely to haskells STM library. Read Simon Marlow's
6//! *Parallel and Concurrent Programming in Haskell*
7//! for more info. Especially the chapter about
8//! Performance is also important for using STM in rust.
9//!
10//! With locks the sequential composition of two
11//! two threadsafe actions is no longer threadsafe because
12//! other threads may interfer in between of these actions.
13//! Applying a third lock to protect both may lead to common sources of errors
14//! like deadlocks or race conditions.
15//!
16//! Unlike locks Software transactional memory is composable.
17//! It is typically implemented by writing all read and write
18//! operations in a log. When the action has finished and
19//! all the used `TVar`s are consistent, the writes are commited as
20//! a single atomic operation.
21//! Otherwise the computation repeats. This may lead to starvation,
22//! but avoids common sources of bugs.
23//!
24//! Panicing within STM does not poison the `TVar`s. STM ensures consistency by
25//! never committing on panic.
26//!
27//! # Features
28//!
29//! This library has features that can be used to tweak the behavior of the implementation:
30//!
31//! - `early-conflict-detection` -- when reading a variable that was already read before in a
32//!   transaction, check if the value has changed to detect inconsistency before the commit routine
33//! - `hash-registers` -- implement internal read and write registers using a `HashMap` instead of
34//!   a `BTreeMap`
35//!   - this may lead to improved performance if your transactions are longer / read-heavy, due to
36//!     lookup computational complexity
37//!   - the hash algorithm is provided by the `rustc-hash` crate, not the `std`
38//! - `wait-on-retry` -- if `retry` is called explictly in a transaction, the thread will go to
39//!   sleep and wait for one of the variables read in the initial transaction to change before
40//!   re-attempting computation
41//!
42//! By default, only the `wait-on-retry` feature is enabled, to keep the behavior identical to the
43//! original library.
44//!
45//! # Usage
46//!
47//! You should only use the functions that are transaction-safe.
48//! Transaction-safe functions don't have side effects, except those provided by `TVar`.
49//! Mutexes and other blocking mechanisms are especially dangerous, because they can
50//! interfere with the internal locking scheme of the transaction and therefore
51//! cause deadlocks.
52//!
53//! Note, that Transaction-safety does *not* mean safety in the rust sense, but is a
54//! subset of allowed behavior. Even if code is not transaction-safe, no segmentation
55//! faults will happen.
56//!
57//! You can run the top-level atomic operation by calling `atomically`.
58//!
59//!
60//! ```
61//! # use fast_stm::atomically;
62//! atomically(|trans| {
63//!     // some action
64//!     // return value as `Result`, for example
65//!     Ok(42)
66//! });
67//! ```
68//!
69//! Nested calls to `atomically` are not allowed. A run-time check prevents this.
70//! Instead of using atomically internally, add a `&mut Transaction` parameter and
71//! return `StmResult`.
72//!
73//! Use ? on `StmResult`, to propagate a transaction error through the system.
74//! Do not handle the error yourself.
75//!
76//! ```
77//! # use fast_stm::{atomically, TVar};
78//! let var = TVar::new(0);
79//!
80//! let x = atomically(|trans| {
81//!     var.write(trans, 42)?; // Pass failure to parent.
82//!     var.read(trans) // Return the value saved in var.
83//! });
84//!
85//! println!("var = {}", x);
86//! // var = 42
87//!
88//! ```
89//!
90//! # Transaction safety
91//!
92//! Software transactional memory is completely safe in the rust sense, so
93//! undefined behavior will never occur.
94//! Still there are multiple rules that
95//! you should obey when dealing with software transactional memory.
96//!
97//! * Don't run code with side effects, especially no IO-code.
98//!   Transactions repeat in failure cases. Using IO would repeat this IO-code.
99//!   Return a closure if you have to.
100//! * Don't handle `StmResult` yourself.
101//!   Use `Transaction::or` to combine alternative paths and `optionally` to check if an inner
102//!   function has failed. Always use `?` and
103//!   never ignore a `StmResult`.
104//! * Don't run `atomically` inside of another. `atomically` is designed to have side effects
105//!   and will therefore break transaction safety.
106//!   Nested calls are detected at runtime and handled with panicking.
107//!   When you use STM in the inner of a function, then
108//!   express it in the public interface, by taking `&mut Transaction` as parameter and
109//!   returning `StmResult<T>`. Callers can safely compose it into
110//!   larger blocks.
111//! * Don't mix locks and transactions. Your code will easily deadlock or slow
112//!   down unpredictably.
113//! * Don't use inner mutability to change the content of a `TVar`.
114//!
115//! Panicking in a transaction is transaction-safe. The transaction aborts and
116//! all changes are discarded. No poisoning or half written transactions happen.
117//!
118//! # Speed
119//!
120//! Generally keep your atomic blocks as small as possible, because
121//! the more time you spend, the more likely it is, to collide with
122//! other threads. For STM, reading `TVar`s is quite slow, because it
123//! needs to look them up in the log every time.
124//! Every used `TVar` increases the chance of collisions. Therefore you should
125//! keep the amount of accessed variables as low as needed.
126//!
127
128// document features
129#![allow(unexpected_cfgs)]
130#![cfg_attr(nightly, feature(doc_cfg))]
131// Extra linting with exceptions
132#![warn(clippy::pedantic)]
133#![allow(clippy::missing_errors_doc)]
134#![allow(clippy::module_name_repetitions)]
135#![allow(clippy::must_use_candidate)]
136#![allow(clippy::should_panic_without_expect)]
137
138extern crate parking_lot;
139
140mod result;
141mod transaction;
142mod tvar;
143
144#[cfg(test)]
145mod test;
146
147pub use result::*;
148pub use transaction::Transaction;
149pub use transaction::TransactionControl;
150pub use tvar::TVar;
151
152/// Convert a `TransactionClosureResult<T, E_A>` to `TransactionClosureResult<T, E_B>`.
153///
154/// This macro is used to cleanly write transactions where multiple kind of errors are
155/// possible during execution. The macro will not fail as long as the specified target
156/// error `$to` implements `From<E>`, `E` being the error possibly returned by `$op`.
157/// It expands to:
158///
159/// ```ignore
160/// $op.map_err(|e| match e {
161///         fast_stm::TransactionError::Abort(e) => fast_stm::TransactionError::Abort($to::from(e)),
162///         fast_stm::TransactionError::Stm(e) => fast_stm::TransactionError::Stm(e),
163///     })?
164/// ```
165///
166/// # Example
167///
168/// ```rust
169/// # use fast_stm::{abort, atomically_with_err, try_or_coerce, Transaction, TransactionClosureResult};
170///
171/// struct Error1;
172/// struct Error2;
173///
174/// impl From<Error1> for Error2 {
175///     fn from(e: Error1) -> Self {
176///         Error2
177///     }
178/// }
179///
180/// fn op1(trans: &mut Transaction) -> TransactionClosureResult<(), Error1> {
181///     // ...
182///     Ok(())
183/// }
184///
185/// fn op2(trans: &mut Transaction) -> TransactionClosureResult<(), Error2> {
186///     // ...
187///     Ok(())
188/// }
189///
190/// let res: Result<(), Error2> = atomically_with_err(|trans| {
191///     try_or_coerce!(op1(trans), Error2);
192///     op2(trans)?;   
193///     Ok(())
194/// });
195/// ```
196#[macro_export]
197macro_rules! try_or_coerce {
198    ($op: expr, $to: ident) => {
199        $op.map_err(|e| match e {
200            $crate::TransactionError::Abort(e) => $crate::TransactionError::Abort($to::from(e)),
201            $crate::TransactionError::Stm(e) => $crate::TransactionError::Stm(e),
202        })?
203    };
204}
205
206#[inline]
207/// Call `abort` to abort a transaction and pass the error as the return value.
208///
209/// # Examples
210///
211/// ```
212/// # use fast_stm::*;
213/// struct MyError;
214///
215/// let execute_once: Result<u32, _> = atomically_with_err(|_| {
216///     abort(MyError)
217/// });
218///
219/// assert!(execute_once.is_err());
220/// ```
221pub fn abort<T, E>(e: E) -> TransactionClosureResult<T, E> {
222    Err(TransactionError::Abort(e))
223}
224
225#[inline]
226/// Call `retry` to abort an operation and run the whole transaction again.
227///
228/// Semantically `retry` allows spin-lock-like behavior, but the library
229/// blocks until one of the used `TVar`s has changed, to keep CPU-usage low.
230///
231/// `Transaction::or` allows to define alternatives. If the first function
232/// wants to retry, then the second one has a chance to run.
233///
234/// # Examples
235///
236/// ```no_run
237/// # use fast_stm::*;
238/// let infinite_retry: i32 = atomically(|_| retry());
239/// ```
240pub fn retry<T>() -> StmClosureResult<T> {
241    Err(StmError::Retry)
242}
243
244/// Run a function atomically by using Software Transactional Memory.
245/// It calls to `Transaction::with` internally, but is more explicit.
246pub fn atomically<T, F>(f: F) -> T
247where
248    F: Fn(&mut Transaction) -> StmClosureResult<T>,
249{
250    Transaction::with(f)
251}
252
253/// Run a function atomically by using Software Transactional Memory.
254/// It calls to `Transaction::with_err` internally, but is more explicit.
255pub fn atomically_with_err<T, E, F>(f: F) -> Result<T, E>
256where
257    F: Fn(&mut Transaction) -> TransactionClosureResult<T, E>,
258{
259    Transaction::with_err(f)
260}
261
262#[inline]
263/// Unwrap `Option` or call retry if it is `None`.
264///
265/// `optionally` is the inverse of `unwrap_or_retry`.
266///
267/// # Example
268///
269/// ```
270/// # use fast_stm::*;
271/// let x = TVar::new(Some(42));
272///
273/// atomically(|tx| {
274///         let inner = unwrap_or_retry(x.read(tx)?)?;
275///         assert_eq!(inner, 42); // inner is always 42.
276///         Ok(inner)
277///     }
278/// );
279/// ```
280pub fn unwrap_or_retry<T>(option: Option<T>) -> StmClosureResult<T> {
281    match option {
282        Some(x) => Ok(x),
283        None => retry(),
284    }
285}
286
287#[inline]
288/// Unwrap `Option` or call abort if it is `None`.
289pub fn unwrap_or_abort<T, E>(option: Option<T>, e: E) -> TransactionClosureResult<T, E> {
290    match option {
291        Some(x) => Ok(x),
292        None => abort(e),
293    }
294}
295
296#[inline]
297/// Retry until `cond` is true.
298///
299/// # Example
300///
301/// ```
302/// # use fast_stm::*;
303/// let var = TVar::new(42);
304///
305/// let x = atomically(|tx| {
306///     let v = var.read(tx)?;
307///     guard(v==42)?;
308///     // v is now always 42.
309///     Ok(v)
310/// });
311/// assert_eq!(x, 42);
312/// ```
313pub fn guard(cond: bool) -> StmClosureResult<()> {
314    if cond {
315        Ok(())
316    } else {
317        retry()
318    }
319}
320
321#[inline]
322/// Optionally run a transaction `f`. If `f` fails with a `retry()`, it does
323/// not cancel the whole transaction, but returns `None`.
324///
325/// Note that `optionally` does not always recover the function, if
326/// inconsistencies where found.
327///
328/// `unwrap_or_retry` is the inverse of `optionally`.
329///
330/// # Example
331///
332/// ```
333/// # use fast_stm::*;
334/// let x:Option<i32> = atomically(|tx|
335///     optionally(tx, |_| retry()));
336/// assert_eq!(x, None);
337/// ```
338pub fn optionally<T, F>(tx: &mut Transaction, f: F) -> StmClosureResult<Option<T>>
339where
340    F: Fn(&mut Transaction) -> StmClosureResult<T>,
341{
342    tx.or(|t| f(t).map(Some), |_| Ok(None))
343}
344
345#[cfg(test)]
346mod test_lib {
347    use super::*;
348
349    #[test]
350    fn infinite_retry() {
351        let terminated = test::terminates(300, || {
352            let _infinite_retry: i32 = atomically(|_| retry());
353        });
354        assert!(!terminated);
355    }
356
357    #[test]
358    fn stm_nested() {
359        let var = TVar::new(0);
360
361        let x = atomically(|tx| {
362            var.write(tx, 42)?;
363            var.read(tx)
364        });
365
366        assert_eq!(42, x);
367    }
368
369    /// Run multiple threads.
370    ///
371    /// Thread 1: Read a var, block until it is not 0 and then
372    /// return that value.
373    ///
374    /// Thread 2: Wait a bit. Then write a value.
375    ///
376    /// Check if Thread 1 is woken up correctly and then check for
377    /// correctness.
378    #[test]
379    fn threaded() {
380        use std::thread;
381        use std::time::Duration;
382
383        let var = TVar::new(0);
384        // Clone for other thread.
385        let varc = var.clone();
386
387        let x = test::async_test(
388            800,
389            move || {
390                atomically(|tx| {
391                    let x = varc.read(tx)?;
392                    if x == 0 {
393                        retry()
394                    } else {
395                        Ok(x)
396                    }
397                })
398            },
399            || {
400                thread::sleep(Duration::from_millis(100));
401
402                atomically(|tx| var.write(tx, 42));
403            },
404        )
405        .unwrap();
406
407        assert_eq!(42, x);
408    }
409
410    /// test if a STM calculation is rerun when a Var changes while executing
411    #[test]
412    fn read_write_interfere() {
413        use std::thread;
414        use std::time::Duration;
415
416        // create var
417        let var = TVar::new(0);
418        let varc = var.clone(); // Clone for other thread.
419
420        // spawn a thread
421        let t = thread::spawn(move || {
422            atomically(|tx| {
423                // read the var
424                let x = varc.read(tx)?;
425                // ensure that x varc changes in between
426                thread::sleep(Duration::from_millis(500));
427
428                // write back modified data this should only
429                // happen when the value has not changed
430                varc.write(tx, x + 10)
431            });
432        });
433
434        // ensure that the thread has started and already read the var
435        thread::sleep(Duration::from_millis(100));
436
437        // now change it
438        atomically(|tx| var.write(tx, 32));
439
440        // finish and compare
441        let _ = t.join();
442        assert_eq!(42, var.read_atomic());
443    }
444
445    #[test]
446    fn or_simple() {
447        let var = TVar::new(42);
448
449        let x = atomically(|tx| tx.or(|_| retry(), |tx| var.read(tx)));
450
451        assert_eq!(x, 42);
452    }
453
454    /// A variable should not be written,
455    /// when another branch was taken
456    #[test]
457    fn or_nocommit() {
458        let var = TVar::new(42);
459
460        let x = atomically(|tx| {
461            tx.or(
462                |tx| {
463                    var.write(tx, 23)?;
464                    retry()
465                },
466                |tx| var.read(tx),
467            )
468        });
469
470        assert_eq!(x, 42);
471    }
472
473    #[test]
474    fn or_nested_first() {
475        let var = TVar::new(42);
476
477        let x = atomically(|tx| tx.or(|tx| tx.or(|_| retry(), |_| retry()), |tx| var.read(tx)));
478
479        assert_eq!(x, 42);
480    }
481
482    #[test]
483    fn or_nested_second() {
484        let var = TVar::new(42);
485
486        let x = atomically(|tx| tx.or(|_| retry(), |t| t.or(|t2| var.read(t2), |_| retry())));
487
488        assert_eq!(x, 42);
489    }
490
491    #[test]
492    fn unwrap_some() {
493        let x = Some(42);
494        let y = atomically(|_| unwrap_or_retry(x));
495        assert_eq!(y, 42);
496    }
497
498    #[test]
499    fn unwrap_none() {
500        let x: Option<i32> = None;
501        assert_eq!(unwrap_or_retry(x), retry());
502    }
503
504    #[test]
505    fn guard_true() {
506        let x = guard(true);
507        assert_eq!(x, Ok(()));
508    }
509
510    #[test]
511    fn guard_false() {
512        let x = guard(false);
513        assert_eq!(x, retry());
514    }
515
516    #[test]
517    fn optionally_succeed() {
518        let x = atomically(|t| optionally(t, |_| Ok(42)));
519        assert_eq!(x, Some(42));
520    }
521
522    #[test]
523    fn optionally_fail() {
524        let x: Option<i32> = atomically(|t| optionally(t, |_| retry()));
525        assert_eq!(x, None);
526    }
527}