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}