Simple shared types for multi-threaded Rust programs.
This library simplifies a number of shared-object patterns that are used in multi-threaded programs such as web-servers.
Advantages of keepcalm
:
Shared
] or [SharedMut
], no matter whether it's
a mutex, read/write lock, read/copy/update primitive, or a read-only shared [std::sync::Arc
].project!
]able, which means you can adjust the granularity of your locks at any time without having to refactor the whole
system. If you want finer-grained locks at a later date, the code that uses the shared containers doesn't change!Send
thanks to the parking_lot
crate.The following container types are available:
| Container | Equivalent | Notes |
|--------------------------------|-----------------------|-------|
| SharedMut (RwLock) | Arc<RwLock<T>>
| This is the default shared-mutable type.
| SharedMut (Mutex) | Arc<Mutex<T>>
| In some cases it may be necessary to serialize both read and writes. For example, with types that are not Sync
.
| SharedMut (read/copy/update) | Arc<RwLock<Arc<T>
| When the write lock of an RCU container is dropped, the values written are committed to the value in the container.
| Shared (default) | Arc
| This is the default shared-immutable type. Note that this is slightly more verbose: [Shared
] does not [std::ops::Deref
] to the underlying type and requires calling [Shared::read
].
| Shared ([Shared::new_unsync
])| Arc<Mutex<T>>
| For types that are not Sync
, a Mutex
is used to serialize read-only access.
| Shared ([SharedMut::shared
]) | n/a | This provides a read-only view into a read-write container and has no direct equivalent.
The traditional Rust shared object patterns tend to be somewhat verbose, for example:
```rust
struct Foo {
mystring: Arc
We can reduce a some of the ceremony and verbosity with keepcalm
:
```rust
struct Foo {
mystring: SharedMut
The [SharedMut
] object hides the complexity of managing Arc<Mutex<T>>
, Arc<RwLock<T>>
, and other synchronization types
behind a single interface:
```rust
let object = "123".to_string(); let shared = SharedMut::new(object); shared.read(); ```
By default, a [SharedMut
] object uses Arc<RwLock<T>>
under the hood, but you can choose the synchronization primitive at
construction time. The [SharedMut
] object erases the underlying primitive and you can use them interchangeably:
```rust
fn use_shared(shared: SharedMut
let shared = SharedMut::new("123".tostring()); useshared(shared); let shared = SharedMut::newwithtype("123".tostring(), Implementation::Mutex); useshared(shared); ```
Managing the poison state of synchronization primitives can be challenging as well. Rust will poison a Mutex
or RwLock
if you
hold a lock while a panic!
occurs.
The SharedMut
type allows you to specify a [PoisonPolicy
] at construction time. By default, if a synchronization
primitive is poisoned, the SharedMut
will panic!
on access. This can be configured so that poisoning is ignored:
```rust
let shared = SharedMut::newwithpolicy("123".to_string(), PoisonPolicy::Ignore); ```
The [Shared
] object is similar to Rust's [std::sync::Arc
], but adds the ability to project.
Both [Shared
] and [SharedMut
] allow projection into the underlying type. Projection can be used to select
either a subset of a type, or to cast a type to a trait.
Note that projections are always linked to the root object!
Casting:
```rust
let shared = SharedMut::new("123".tostring());
let sharedasref: SharedMut
Subset of a struct/tuple:
```rust
struct Foo { tuple: (String, usize) }
let shared = SharedMut::new(Foo::default());
let shared_string: SharedMut
shared_string.write() += "hello, world"; assert_eq!(shared.read().tuple.0, "hello, world"); assert_eq!(shared_string.read(), "hello, world"); ```
Both [Shared
] and [SharedMut
] support unsized types, but due to current limitations in the language (see [std::ops::CoerceUnsized
] for details),
you need to construct them in special ways.
Unsized traits are supported, but you will either need to specify Send + Sync
in the shared type, or [project_cast!
] the object:
```rust
// In this form, Send + Sync
are visible in the shared type
let boxed: Box
// In this form, Send + Sync
are erased via projection
let shared = SharedMut::new("123".tostring());
let sharedasref: SharedMut
Unsized slices are supported using a box:
```rust
let boxed: Box<[i32]> = Box::new([1, 2, 3]); let shared: SharedMut<[i32]> = SharedMut::from_box(boxed); ```