Trait corundum::alloc::MemPool [−][src]
pub unsafe trait MemPool where
Self: 'static + Sized, {}Show 62 methods
unsafe fn pre_alloc(size: usize) -> (*mut u8, u64, usize, usize); unsafe fn pre_dealloc(ptr: *mut u8, size: usize) -> usize; fn name() -> &'static str { ... } fn open_no_root(_path: &str, _flags: u32) -> Result<Self> { ... } unsafe fn close() -> Result<()> { ... } fn zone(_off: u64) -> usize { ... } fn open<'a, U: 'a + PSafe + RootObj<Self>>(
_path: &str,
_flags: u32
) -> Result<RootCell<'a, U, Self>> { ... } fn is_open() -> bool { ... } unsafe fn format(_path: &str) -> Result<()> { ... } unsafe fn apply_flags(path: &str, flags: u32) -> Result<()> { ... } fn allocated(_off: u64, _len: usize) -> bool { ... } fn verify() -> bool { ... } unsafe fn off_unchecked<T: ?Sized>(x: *const T) -> u64 { ... } unsafe fn get_unchecked<'a, T: 'a + ?Sized>(off: u64) -> &'a T { ... } unsafe fn get_mut_unchecked<'a, T: 'a + ?Sized>(off: u64) -> &'a mut T { ... } unsafe fn deref_slice_unchecked<'a, T: 'a>(off: u64, len: usize) -> &'a [T] { ... } unsafe fn deref_slice_unchecked_mut<'a, T: 'a>(
off: u64,
len: usize
) -> &'a mut [T] { ... } unsafe fn deref<'a, T: 'a>(off: u64) -> Result<&'a T> { ... } unsafe fn deref_mut<'a, T: 'a>(off: u64) -> Result<&'a mut T> { ... } fn off<T: ?Sized>(x: *const T) -> Result<u64> { ... } fn rng() -> Range<u64> { ... } fn start() -> u64 { ... } fn end() -> u64 { ... } fn size() -> usize { ... } fn available() -> usize { ... } fn used() -> usize { ... } fn valid<T: ?Sized>(p: *const T) -> bool { ... } fn contains(addr: u64) -> bool { ... } unsafe fn alloc(size: usize) -> (*mut u8, u64, usize) { ... } unsafe fn dealloc(ptr: *mut u8, size: usize) { ... } unsafe fn log64(_off: u64, _val: u64, _zone: usize) { ... } unsafe fn drop_on_failure(_off: u64, _len: usize, _zone: usize) { ... } unsafe fn prepare(_zone: usize) { ... } unsafe fn perform(_zone: usize) { ... } unsafe fn discard(_zone: usize) { ... } unsafe fn alloc_zeroed(size: usize) -> *mut u8 { ... } unsafe fn new<'a, T: PSafe + 'a>(x: T, j: &Journal<Self>) -> &'a mut T { ... } unsafe fn new_slice<'a, T: PSafe + 'a>(
x: &'a [T],
journal: &Journal<Self>
) -> &'a mut [T] { ... } unsafe fn new_copy<'a, T>(x: &T, j: &Journal<Self>) -> &'a mut T
where
T: 'a + ?Sized, { ... } unsafe fn new_copy_slice<'a, T: 'a>(
x: &[T],
j: &Journal<Self>
) -> &'a mut [T] { ... } unsafe fn atomic_new<'a, T: 'a>(x: T) -> (&'a mut T, u64, usize, usize) { ... } unsafe fn atomic_new_slice<'a, T: 'a + PSafe>(
x: &'a [T]
) -> (&'a mut [T], u64, usize, usize) { ... } unsafe fn new_uninit<'a, T: PSafe + 'a>(j: &Journal<Self>) -> &'a mut T { ... } unsafe fn new_uninit_for_layout(
size: usize,
journal: &Journal<Self>
) -> *mut u8 { ... } unsafe fn atomic_new_uninit<'a, T: 'a>() -> (&'a mut T, u64, usize, usize) { ... } unsafe fn alloc_for_value<'a, T: ?Sized>(x: &T) -> &'a mut T { ... } unsafe fn free<'a, T: PSafe + ?Sized>(x: &mut T) { ... } unsafe fn free_slice<'a, T: PSafe>(x: &mut [T]) { ... } unsafe fn free_nolog<'a, T: ?Sized>(x: &T) { ... } unsafe fn drop_journal(_journal: &mut Journal<Self>) { ... } unsafe fn journals_head() -> &'static u64 { ... } unsafe fn journals<T, F: Fn(&mut HashMap<ThreadId, (u64, i32)>) -> T>(
_: F
) -> T { ... } unsafe fn recover() { ... } unsafe fn commit() { ... } unsafe fn commit_no_clear() { ... } unsafe fn clear() { ... } unsafe fn rollback() -> bool { ... } unsafe fn rollback_no_clear() { ... } fn transaction<T, F: FnOnce(&'static Journal<Self>) -> T>(
body: F
) -> Result<T>
where
F: TxInSafe + UnwindSafe,
T: TxOutSafe, { ... } fn gen() -> u32 { ... } fn tx_gen() -> u32 { ... } fn print_info() { ... }
Expand description
Persistent Memory Pool
This trait can be used to define a persistent memory pool type. The
methods of MemPool
trait do not have a reference to self in order to make
sure that all information that it works with, including the virtual address
boundaries, are static. Therefore, all objects with the same memory
allocator will share a unique memory pool type. Having a strong set of type
checking rules, Rust prevents referencing from one memory pool to another.
To implement a new memory pool, you should define a new type with static
values, that implements MemPool
. You may redefine the default allocator as
a new pool using pool!()
which creates a pool module and generates the
necessary code segments of type BuddyAlloc
.
Examples
The following example shows how to use MemPool
to track allocations of a
single numerical object of type i32
.
use std::alloc::{alloc,dealloc,realloc,Layout}; struct TrackAlloc {} unsafe impl MemPool for TrackAlloc { fn rng() -> Range<u64> { 0..u64::MAX } unsafe fn pre_alloc(size: usize) -> (*mut u8, u64, usize, usize) { let p = alloc(Layout::from_size_align_unchecked(size, 4)); println!("A block of {} bytes is allocated at {}", size, p as u64); (p, p as u64, size, 0) } unsafe fn pre_dealloc(p: *mut u8, size: usize) -> usize { println!("A block of {} bytes at {} is deallocated", size, p as u64); dealloc(p, Layout::from_size_align_unchecked(size, 1)); 0 } } unsafe { let (p, _, _) = TrackAlloc::alloc(1); *p = 10; println!("loc {} contains {}", p as u64, *p); TrackAlloc::dealloc(p, 1); }
The following example shows how to use pool!()
to define a multiple
pools.
// Declare p1 module pool!(p1); // Declare p2 module pool!(p2); let _pool1 = p1::BuddyAlloc::open_no_root("p1.pool", O_CF).unwrap(); let _pool2 = p2::BuddyAlloc::open_no_root("p2.pool", O_CF).unwrap(); transaction(|j| { // Create a Pbox object in p1 let b = p1::Pbox::new(10, j); }).unwrap(); transaction(|j| { // Create a Prc object in p2 let p = p2::Prc::new(10, j); }).unwrap();
Safety
This is the developer’s responsibility to manually drop allocated objects.
One way for memory management is to use pointer wrappers that implement
Drop
trait and deallocate the object on drop. Unsafe
methods does not guarantee persistent memory safety.
pmem
crate provides Pbox
, Prc
, and Parc
for memory management using
RAII. They internally use the unsafe methods.
Required methods
Prepares allocation without performing it
This function is used internally for low-level atomicity in memory
allocation. As an example, please see drop_on_failure
.
It returns a 4-tuple: 1. Raw pointer 2. Offset 3. Size 4. Zone index
Examples
unsafe { let (ptr, _, _, z) = P::pre_alloc(8); *ptr = 10; P::perform(z); }
Prepares deallocation without performing it
This function is used internally for low-level atomicity in memory
allocation. As an example, please see drop_on_failure
.
It returns the zone in which the deallocation happens.
Examples
unsafe { let (ptr, _, _) = P::alloc(8); *ptr = 10; let zone = P::pre_dealloc(ptr, 8); assert_eq!(*ptr, 10); P::perform(zone); assert_ne!(*ptr, 10); }
Provided methods
fn open_no_root(_path: &str, _flags: u32) -> Result<Self>
fn open_no_root(_path: &str, _flags: u32) -> Result<Self>
Opens a new pool without any root object. This function is for testing and is not useful in real applications as none of the allocated objects in persistent region is durable. The reason is that they are not reachable from a root object as it doesn’t exists. All objects can live only in the scope of a transaction.
Flags
- O_C: create a memory pool file if not exists
- O_F: format the memory pool file
- O_CNE: create a memory pool file if not exists
- O_CF: create and format a new memory pool file
- O_CFNE: create and format a memory pool file only if not exists
See open_flags
for more options.
Commits all changes and clears the logs for all threads
This method should be called while dropping the MemPool
object to
make sure that all uncommitted changes outside transactions, such as
reference counters, are persistent.
Opens a pool and retrieves the root object
The root type should implement RootObj
trait in order to create a
root object on its absence. This function [creates and] returns an
immutable reference to the root object. The pool remains open as long as
the root object is in the scope. Like other persistent objects, the root
object is immutable and it is modifiable via interior mutability.
Flags
- O_C: create a memory pool file if not exists
- O_F: format the memory pool file
- O_CNE: create a memory pool file if not exists
- O_CF: create and format a new memory pool file
- O_CFNE: create and format a memory pool file only if not exists
See open_flags
for more options.
Examples
use corundum::default::*; let root = BuddyAlloc::open::<i32>("foo.pool", O_CF).unwrap(); assert_eq!(*root, i32::default());
Single-thread Shared Root Object
Prc
<
PCell
<T>>
can be used in order to have a mutable shared
root object, as follows.
use corundum::default::*; type Root = Prc<PCell<i32>>; let root = BuddyAlloc::open::<Root>("foo.pool", O_CF).unwrap(); let data = root.get(); if data == i32::default() { println!("Initializing data"); // This block runs only once to initialize the root object transaction(|j| { root.set(10, j); }).unwrap(); } assert_eq!(root.get(), 10);
Thread-safe Root Object
If you need a thread-safe root object, you may want to wrap the root object
in Parc
<
PMutex
<T>>
, as shown in the example below:
use corundum::default::*; use std::thread; type Root = Parc<PMutex<i32>>; let root = BuddyAlloc::open::<Root>("foo.pool", O_CF).unwrap(); let mut threads = vec!(); for _ in 0..10 { let root = Parc::demote(&root); threads.push(thread::spawn(move || { transaction(|j| { if let Some(root) = root.promote(j) { let mut root = root.lock(j); *root += 10; } }).unwrap(); })); } for thread in threads { thread.join().unwrap(); } transaction(|j| { let data = root.lock(j); assert_eq!(*data % 100, 0); }).unwrap();
Errors
- A volatile memory pool (e.g.
Heap
) doesn’t have a root object. - The pool should be open before accessing the root object.
unsafe fn off_unchecked<T: ?Sized>(x: *const T) -> u64
unsafe fn off_unchecked<T: ?Sized>(x: *const T) -> u64
unsafe fn get_unchecked<'a, T: 'a + ?Sized>(off: u64) -> &'a T
unsafe fn get_unchecked<'a, T: 'a + ?Sized>(off: u64) -> &'a T
unsafe fn get_mut_unchecked<'a, T: 'a + ?Sized>(off: u64) -> &'a mut T
unsafe fn get_mut_unchecked<'a, T: 'a + ?Sized>(off: u64) -> &'a mut T
unsafe fn deref_slice_unchecked<'a, T: 'a>(off: u64, len: usize) -> &'a [T]
unsafe fn deref_slice_unchecked<'a, T: 'a>(off: u64, len: usize) -> &'a [T]
unsafe fn deref_slice_unchecked_mut<'a, T: 'a>(
off: u64,
len: usize
) -> &'a mut [T]
unsafe fn deref_slice_unchecked_mut<'a, T: 'a>(
off: u64,
len: usize
) -> &'a mut [T]
Acquires a mutable reference pointer to the object
Checks if addr
is in the valid address range if this allocator
addr
contains the scalar of a virtual address. If you have a raw
fat pointer of type T, you can obtain its virtual address by converting
it into a thin pointer and then u64
.
Examples
let p = Box::new(1); println!("Address {:#x} contains value '{}'", p.as_ref() as *const _ as u64, *p);
Allocate memory as described by the given size
.
Returns a pointer to newly-allocated memory.
Safety
This function is unsafe because undefined behavior can result
if the caller does not ensure that size
has non-zero.
The allocated block of memory may or may not be initialized.
Using alloc
may lead to memory leak if the transaction fails
after this function successfully returns. To allocate memory in
a failure-atomic manner, use pre_alloc
, Log::drop_on_failure
,
and perform
functions respectively.
Deallocate the block of memory at the given ptr
pointer with the
given size
.
Safety
This function is unsafe because undefined behavior can result if the caller does not ensure all of the following:
-
ptr
must denote a block of memory currently allocated via this allocator, -
size
must be the same size that was used to allocate that block of memory.
Adds a low-level log to update as 64-bit obj
to val
when
perform()
is called. As an example, please see Log::set()
.
unsafe fn drop_on_failure(_off: u64, _len: usize, _zone: usize)
unsafe fn drop_on_failure(_off: u64, _len: usize, _zone: usize)
Adds a low-level DropOnFailure
log to perform inside the allocator.
This is internally used to atomically allocate a new objects. Calling
perform()
drops these logs.
Examples
unsafe { // Prepare an allocation. The allocation is not durable yet. In case // of a crash, the prepared allocated space is gone. It is fine // because it has not been used. The `pre_` and `perform` functions // form a low-level atomic section. let (obj, off, len, zone) = P::pre_alloc(1); // Create a low-level DropOnFailure log. This log is going to be used // when a crash happens while performing the changes made by the // preparation functions. If a crash happens before that, these logs // will be discarded. P::drop_on_failure(off, len, zone); // It is fine to work with the prepared raw pointer. All changes in // the low-level atomic section are considered as part of the // allocation and will be gone in case of a crash, as the allocation // will be dropped. *obj = 20; // Transaction ends here. The perform function sets the `operating` // flag to show that the prepared changes are being materialized. // This flag remains set until the end of materialization. In case // of a crash while operating, the recovery procedure first continues // the materialization, and then uses the `DropOnFailure` logs to // reclaim the allocation. `perform` function realizes the changes // made by the `pre_` function on the given memory zone. P::perform(zone); }
In case of not using pre_alloc
or pre_dealloc
, starts a low-level
atomic section on a given zone.
Performs the prepared operations
It materializes the changes made by pre_alloc
,
pre_dealloc
, and
pre_realloc
. See drop_on_failure
for more
details.
Discards the prepared operations
Discards the changes made by pre_alloc
,
pre_dealloc
, and
pre_realloc
. See drop_on_failure
for more
details.
unsafe fn alloc_zeroed(size: usize) -> *mut u8
unsafe fn alloc_zeroed(size: usize) -> *mut u8
Behaves like alloc
, but also ensures that the contents
are set to zero before being returned.
Safety
This function is unsafe for the same reasons that alloc
is.
However the allocated block of memory is guaranteed to be initialized.
Errors
Returning a null pointer indicates that either memory is exhausted
or size
does not meet allocator’s size constraints, just as in alloc
.
Clients wishing to abort computation in response to an
allocation error are encouraged to call the handle_alloc_error
function,
rather than directly invoking panic!
or similar.
Allocates new memory and then places x
into it with DropOnFailure
log
Allocates a new slice and then places x
into it with DropOnAbort
log
Allocates new memory and then copies x
into it with DropOnFailure
log
unsafe fn new_copy_slice<'a, T: 'a>(x: &[T], j: &Journal<Self>) -> &'a mut [T]
unsafe fn new_copy_slice<'a, T: 'a>(x: &[T], j: &Journal<Self>) -> &'a mut [T]
Allocates new memory and then copies x
into it with DropOnFailure
log
Allocates new memory and then places x
into it without realizing the allocation
unsafe fn atomic_new_slice<'a, T: 'a + PSafe>(
x: &'a [T]
) -> (&'a mut [T], u64, usize, usize)
unsafe fn atomic_new_slice<'a, T: 'a + PSafe>(
x: &'a [T]
) -> (&'a mut [T], u64, usize, usize)
Allocates new memory and then places x
into it without realizing the allocation
unsafe fn new_uninit<'a, T: PSafe + 'a>(j: &Journal<Self>) -> &'a mut T
unsafe fn new_uninit<'a, T: PSafe + 'a>(j: &Journal<Self>) -> &'a mut T
Allocates new memory without copying data
Allocates new memory without copying data
Allocates new memory without copying data and realizing the allocation
unsafe fn alloc_for_value<'a, T: ?Sized>(x: &T) -> &'a mut T
unsafe fn alloc_for_value<'a, T: ?Sized>(x: &T) -> &'a mut T
Allocates new memory for value x
unsafe fn free_slice<'a, T: PSafe>(x: &mut [T])
unsafe fn free_slice<'a, T: PSafe>(x: &mut [T])
Creates a DropOnCommit
log for the value x
unsafe fn free_nolog<'a, T: ?Sized>(x: &T)
unsafe fn free_nolog<'a, T: ?Sized>(x: &T)
Frees the allocation for value x
immediately
unsafe fn drop_journal(_journal: &mut Journal<Self>)
unsafe fn drop_journal(_journal: &mut Journal<Self>)
Drops a journal
from memory
unsafe fn journals_head() -> &'static u64
unsafe fn journals_head() -> &'static u64
Returns a reference to the offset of the first journal
Runs a closure with a mutable reference to a thread->journal HashMap
Commits all changes and clears the logs for one thread
If the transaction is nested, it postpones the commit to the top most transaction.
Safety
This function is for internal use and should not be called elsewhere.
unsafe fn commit_no_clear()
unsafe fn commit_no_clear()
Commits all changes without clearing the logs
If the transaction is nested, it postpones the commit to the top most transaction.
Safety
This function is for internal use and should not be called elsewhere.
Clears the logs
If the transaction is nested, it postpones the clear to the top most transaction.
Safety
This function is for internal use and should not be called elsewhere.
Discards all changes and clears the logs
If the transaction is nested, it propagates the panic up to the top most transaction to make all of them tainted. It returns true if it runs the rollback procedure; otherwise false.
Safety
This function is for internal use and should not be called elsewhere.
unsafe fn rollback_no_clear()
unsafe fn rollback_no_clear()
Discards all changes without clearing the logs
If the transaction is nested, it propagates the panic upto the top most transaction to make all of them tainted.
Safety
This function is for internal use and should not be called elsewhere.
fn transaction<T, F: FnOnce(&'static Journal<Self>) -> T>(body: F) -> Result<T> where
F: TxInSafe + UnwindSafe,
T: TxOutSafe,
fn transaction<T, F: FnOnce(&'static Journal<Self>) -> T>(body: F) -> Result<T> where
F: TxInSafe + UnwindSafe,
T: TxOutSafe,
Executes commands atomically with respect to system crashes
The transaction
function takes a closure with one argument of type
&Journal<Self>
. Before running the closure, it atomically creates a
Journal
object, if required, and prepares an immutable reference to
it. Since there is no other safe way to create a Journal
object, it
ensures that every function taking an argument of type &Journal<P>
is
enforced to be invoked from a transaction.
The captured types are bounded to be TxInSafe
, unless explicitly
asserted otherwise using AssertTxInSafe
type wrapper. This
guarantees the volatile state consistency, as well as the persistent
state.
The returned type should be TxOutSafe
. This prevents sending out
unreachable persistent objects. The only way out of a transaction for
a persistent object is to be reachable by the root object.
Examples
use corundum::default::*; type P = BuddyAlloc; let root = P::open::<PCell<i32>>("foo.pool", O_CF).unwrap(); let old = root.get(); let new = BuddyAlloc::transaction(|j| { root.set(root.get() + 1, j); root.get() }).unwrap(); assert_eq!(new, old + 1);
fn print_info()
fn print_info()
Prints memory information