stm_core/
lib.rs

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