1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! This crate provides the [`Lock`] and [`Locked`] types which allow one lock to guard
//! multiple objects.
//!
//! # Motivation
//!
//! The following pattern is common in languages such as C and Java:
//!
//! ```c
//! struct Context {
//! mutex_t mutex;
//! int protected_data1;
//! };
//!
//! struct Child {
//! struct Context *context;
//! int protected_data2;
//! };
//! ```
//!
//! Here, `protected_data1` and `protected_data2` are protected by the same mutex.
//! Whenever the code wants to access either of those fields, it first locks the mutex.
//!
//! Since there is only one mutex, this provides the following properties:
//!
//! 1. The code does not need to reason about locking order to avoid deadlocks.
//! 2. Only one expensive atomic RMW operation has to be performed to access all fields.
//!
//! Using common Rust data structures, this might be modeled as follows:
//!
//! ```
//! use std::cell::Cell;
//! use std::sync::Arc;
//! use parking_lot::{Mutex, ReentrantMutex};
//!
//! struct Context {
//! mutex: Mutex<()>,
//! protected_data1: ReentrantMutex<Cell<i32>>,
//! }
//!
//! struct Child {
//! context: Arc<Context>,
//! protected_data2: ReentrantMutex<Cell<i32>>,
//! }
//! ```
//!
//! The code would then acquire the mutex at a very high level and acquire the reentrant
//! mutexes ad-hoc whenever a field needs to be accessed.
//!
//! This achieves the first property of not having to reason about the lock order, but
//! it does not achieve the second property.
//!
//! This crate improves on this by providing data structures that follow the C model
//! closely.
//!
//! # Example
//!
//! Using the data structures from this crate, the example above could be implemented as
//! follows:
//!
//! ```
//! use std::cell::Cell;
//! use std::sync::Arc;
//! use shared_lock::{Lock, Locked};
//!
//! struct Context {
//! lock: Lock,
//! protected_data1: Locked<Cell<i32>>,
//! }
//!
//! struct Child {
//! context: Arc<Context>,
//! protected_data2: Locked<Cell<i32>>,
//! }
//!
//! // Constructing the objects:
//! let lock = Lock::default();
//! let context = Arc::new(Context {
//! protected_data1: lock.wrap(Cell::new(0)),
//! lock,
//! });
//! let child = Arc::new(Child {
//! context: context.clone(),
//! protected_data2: context.lock.wrap(Cell::new(0)),
//! });
//!
//! // Accessing the objects:
//! let guard = &context.lock.lock();
//! context.protected_data1.get(guard).set(1);
//! child.protected_data2.get(guard).set(1);
//! ```
//!
//! [`Lock`] is a re-entrant lock. If it has not yet been acquired, calling [`Lock::lock`]
//! is about as expensive as acquiring a regular mutex. If it has already been acquired by
//! the current thread, calling [`Lock::lock`] is about as expensive as cloning an [`Rc`].
//!
//! [`Locked::get`] only performs a single non-atomic comparison before returning a
//! reference to the contained object, making it effectively free in most situations.
//!
//! Since [`Locked::get`] can be called any number of times, it only returns a shared
//! reference. The contained data must use interior mutability to be mutated. However,
//! [`Locked`] implements [`Sync`] as long as the contained type implements [`Send`],
//! meaning that the contained type can use cheap non-atomic interior mutability such as
//! [`Cell`] and [`RefCell`].
use ;
pub use ;