Corundum provides persistent memory support for Rust applications. This is useful for developing safe persistent memory applications without concerning much about crash consistency and data loss. More details of its design and implementation will be available in our ASPLOS 2021 paper (Please read the extended abstract for a brief overview on Corundum).
Carefully using Rust's strict type checking rules and borrowing mechanism, Corundum guarantees that the implementation is free of common persistent memory related bugs. Corundum leaves the software implementation with zero persistent memory related problems of the following types:
Developers will see these issues during the design time. Therefore, it lowers the risk of making mistakes. Corundum's programming model consists of using safe persistent pointers and software transactional memory.
Three pointer-wrappers lie at the heart of Corundum interface. Developers may use them to allocate persistent memory safely.
Pbox<T>
: the simplest form of dynamic allocation,Prc<T>
: a single-thread reference counted pointer for shared
persistent objects,Parc<T>
: a thread-safe reference-counted pointer for
shared persistent objects.Corundum depends on some unstable features of Rust. Therefore, it requires nightly Rust compiler 1.50.0-nightly. Please run the following commands to download the latest version of Rust (See https://www.rust-lang.org/tools/install for more details).
shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default nightly
Corundum is also partially dependent on a few 3rd party crates which are listed
in Cargo.toml
.
Use either of the following instructions to add Corundum in your Cargo.toml
dependencies section:
toml
[dependencies]
corundum = "0.3.3"
Or
toml
[dependencies]
corundum = { git = "https://github.com/NVSL/Corundum.git" }
If you wish to enable a feature like pin_journals
, please add it to the
features
attribute. For example:
toml
[dependencies]
corundum = { version="0.3.2", features=["pin_journals", "no_pthread"] }
A memory pool is a type that implements all necessary interfaces for working
with persistent memory. You can use the default memory pool, or define a new
memory pool type. The latter requires your type implementing
MemPool
trait. Please see the
pass-through allocator as an example. To automatically
implement a new pool type, pool!()
macro is provided which creates a new module
with a BuddyAlloc
type.
rust
corundum::pool!(my_pool);
The first thing to do is to open the memory pool file(s) before using it. You
can do this by using either open()
or open_no_root()
methods. The first one
returns a the root
object given a root object type. The second one returns a
guard
object; the pool remains open as long as the root
/guard
object is in
the scope. The open functions take a pool file path and a flag set to create
the pool file.
rust
if let Ok(_) = my_pool::BuddyAlloc::open_no_root("image", O_F) {
println!("Image file is formatted and ready to use");
} else {
println!("No image file found");
}
rust
if let Ok(root) = my_pool::BuddyAlloc::open::<Root>("image", O_F) {
println!("Image file is formatted and the root object is created ({:?})", root);
} else {
println!("No image file");
}
You may define any data structure with the given pointers, and without any raw pointers or references. Corundum helps you to write the right code down the road.
```rust use corundum::rc::Prc; use corundum::cell::LogCell;
type A = BuddyAlloc;
struct MyData {
id: i32,
link: Option
You may find it disturbing to specify the pool in every type. Corundum uses type
aliasing and procedural macros to provide an easier way for defining new data
structures. The pool!()
macro aliases all persistent types associated with the
internal pool type. For example
```rust pool!(mypool); use mypool::*;
struct MyData {
id: i32,
link: Option
PClone
and Root
procedural macros can also be used to automatically derive
the implementation of the corresponding traits for the type.
```rust use corundum::default::*;
struct MyData {
id: i32,
link: Option
Corundum does not allow any modification to the protected data outside a
transaction. To let mutably borrowing the protected data, you may wrap it in
PCell
, PMutex
, etc.,
and use their corresponding interface for interior mutability which requires a
reference to a journal object. To obtain a journal, you may use transaction
.
rust
transaction(|j| {
let my_data = Prc::new(LogRefCell::new(
MyData {
id: 1,
link: None
}), j);
let mut my_data = my_data.borrow_mut(j);
my_data.id = 2;
})
We provide a docker image for running performance tests and compare Corundum with a bunch of other persistent memory libraries. The following commands pulls the docker image and runs a container with all dependencies and opponent libraries pre-installed.
sh
$ sudo sysctl -w kernel.perf_event_paranoid=-1
$ wget https://raw.githubusercontent.com/NVSL/Corundum/main/eval/docker-default.json
$ docker run --security-opt \
seccomp=./docker-default.json \
--mount type=tmpfs,destination=/mnt/pmem0 \
-it mhz88/corundum:latest bin/bash
Alternatively, mount the pmem on the host or use /dev/shm
to emulate it, and bind the directory using -v
option:
docker run -v /dev/shm:/mnt/pmem0 -it mhz88/corundum:latest bin/bash
Please visit the Documentation
page for
more information.
Please feel free to report any bug using GitHub issues.
If you have other questions or suggestions, you can contact us at cse-nvsl-discuss@eng.ucsd.edu.
Corundum crate is licensed under Apache License, Version 2.0, (http://www.apache.org/licenses/LICENSE-2.0).
Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions.