pub struct Parc<T: PSafe + ?Sized, A: MemPool> { /* private fields */ }
Expand description

A thread-safe reference-counting persistent pointer. ‘Parc’ stands for ‘Persistent Atomically Reference Counted’.

The main aspect of Parc<T> is that its atomic counters are also transactional to provide failure atomicity which means that functions pclone, downgrade, and upgrade require a Journal to operate. In other words, you need to wrap them in a transaction. The counters are atomic, so it is safe to share it in multiple threads.

Since Parc uses reference counting for resource management, it inherits the cyclic references problem. Please visit this for the information on how Weak helps to resolve that issue.

Unlike Arc, Parc does not implement Send to prevent memory leak. The reason is that if a Parc is created in a transaction without being reachable from the root object, and moves to a thread, due to being RAII, its drop function gets called in the other thread outside the original transaction. Therefore, it destroys allocation consistency and leaves the Parc unreachable in the memory if a crash happens between the original transaction is done and the drop function is called.

To allow sharing, Parc provides a safe mechanism to cross the thread boundaries. When you need to share it, you can obtain a VWeak object by calling demote() function. The VWeak object is both Sync and Send and acts like a volatile reference. Calling VWeak::promote() gives access to data by creating a new reference of type Parc inside the other thread, if the referent is still available. Calling demote() is dynamically prohibited to be inside a transaction. Therefore, the Parc should be already reachable from the root object and packed outside a transaction.

Examples

use corundum::default::*;
use std::thread;
 
type P = Allocator;
 
let p = P::open::<Parc<i32>>("foo.pool", O_CF).unwrap();
let v = p.demote();
let mut threads = vec![];
 
for i in 0..10 {
    let p = v.clone();
    threads.push(thread::spawn(move || {
        transaction(|j| {
            if let Some(p) = p.promote(j) {
                println!("access {} from thread {}", *p, i);
            }
        }).unwrap();
    }));
}
 
for t in threads {
    t.join().unwrap();
}

Mutability

Parc doesn’t provide mutable reference to the inner value. To allow interior mutability, you may use Parc<PMutex<T,P>,P> (or in short, Parc<PMutex<T>> using aliased types).

use corundum::default::*;
use std::thread;
 
type P = Allocator;
 
let p = P::open::<Parc<PMutex<i32>>>("foo.pool", O_CF).unwrap();
let v = p.demote();
let mut threads = vec![];
 
for i in 0..10 {
    let p = v.clone();
    threads.push(thread::spawn(move || {
        transaction(|j| {
            if let Some(p) = p.promote(j) {
                let mut p = p.lock(j);
                *p += 1;
                println!("thread {} makes it {}", i, *p);
            }
        }).unwrap();
    }));
}
 
for t in threads {
    t.join().unwrap();
}
 
let res = transaction(|j| {
    *p.lock(j)
}).unwrap();
 
assert_eq!(res, 10);

Implementations

Constructs a new Parc<T>.

Examples
use corundum::sync::Parc;

Heap::transaction(|j| {
    let five = Parc::new(5, j);
}).unwrap();

Constructs a new Parc with uninitialized contents.

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;

corundum::transaction(|j| {
    let mut five = Parc::<u32,Heap>::new_uninit(j);

    let five = unsafe {
        // Deferred initialization:
        Parc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);

        five.assume_init()
    };

    assert_eq!(*five, 5)
}).unwrap();

Constructs a new Parc with uninitialized contents, with the memory being filled with 0 bytes.

See MaybeUninit::zeroed for examples of correct and incorrect usage of this method.

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;

Heap::transaction(|j| {
    let zero = Parc::<u32,Heap>::new_zeroed(j);
    let zero = unsafe { zero.assume_init() };

    assert_eq!(*zero, 0)
}).unwrap();

Converts to Parc<T>.

Safety

As with MaybeUninit::assume_init, it is up to the caller to guarantee that the inner value really is in an initialized state. Calling this when the content is not yet fully initialized causes immediate undefined behavior.

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;

corundum::transaction(|j| {
    let mut five = Parc::<u32,Heap>::new_uninit(j);

    let five = unsafe {
        // Deferred initialization:
        Parc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);

        five.assume_init()
    };

    assert_eq!(*five, 5);
}).unwrap();

Returns a mutable reference into the given Parc, if there are no other Parc or Weak pointers to the same allocation.

Returns None otherwise, because it is not safe to mutate a shared value. It only works for Parc<MaybeUninit<T>> to be able to defer the initialization.

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;

corundum::transaction(|j| {
    let mut five = Parc::<u32,Heap>::new_uninit(j);

    let five = unsafe {
        // Deferred initialization:
        Parc::get_mut(&mut five).unwrap().as_mut_ptr().write(5);

        five.assume_init()
    };

    assert_eq!(*five, 5)
}).unwrap();

Returns a mutable reference into the given Parc, without any check.

It only works for Parc<MaybeUninit<T>> to be able to defer the initialization.

Safety

Any other Parc or Weak pointers to the same allocation must not be dereferenced for the duration of the returned borrow. This is trivially the case if no such pointers exist, for example immediately after Parc::new.

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;

corundum::transaction(|j| {
    let mut five = Parc::<u32,Heap>::new_uninit(j);

    let five = unsafe {
        // Deferred initialization:
        Parc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);

        five.assume_init()
    };

    assert_eq!(*five, 5);
}).unwrap();

Creates a new Weak pointer to this allocation.

The Weak pointer can be upgraded later in a transaction.

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;

Heap::transaction(|j| {
    let five = Parc::new(5, j);
    let _weak_five = Parc::downgrade(&five, j);
}).unwrap()

Creates a new sharable VWeak pointer to this allocation.

Errors

This function requires the allocation to be reachable from the persistent root. Therefore, it panics if it gets called inside a transaction.

Examples
use corundum::default::*;
 
type P = Allocator;
 
let obj = P::open::<Parc<i32>>("foo.pool", O_CF).unwrap();
 
let v = obj.demote();
assert_eq!(Parc::strong_count(&obj), 1);
 
P::transaction(|j| {
    if let Some(obj) = v.promote(j) {
        assert_eq!(Parc::strong_count(&obj), 2);
    }
}).unwrap();
 
assert_eq!(Parc::strong_count(&obj), 1);

Demote without dynamically checking transaction boundaries

Gets the number of Weak pointers to this allocation.

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;

Heap::transaction(|j| {
    let five = Parc::new(5, j);

    let _weak_five = Parc::downgrade(&five, j);
    assert_eq!(1, Parc::weak_count(&five));
}).unwrap()

Gets the number of Strong pointers to this allocation.

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;
use corundum::clone::PClone;

Heap::transaction(|j| {
    let five = Parc::new(5, j);
    let _also_five = Parc::pclone(&five, j);
    assert_eq!(2, Parc::strong_count(&five));
}).unwrap();

Returns true if the two Parcs point to the same allocation (in a vein similar to std::ptr::eq).

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;
use corundum::clone::PClone;

Heap::transaction(|j| {
    let five = Parc::new(5, j);
    let same_five = Parc::pclone(&five, j);
    let other_five = Parc::new(5, j);

    assert!(Parc::ptr_eq(&five, &same_five));
    assert!(!Parc::ptr_eq(&five, &other_five));
}).unwrap();

Initializes boxed data with value in-place if it is None

This function should not be called from a transaction as it updates data without taking high-level logs. If transaction is unsuccessful, there is no way to recover data. However, it is safe to use it outside a transaction because it uses low-level logs to provide safety for a single update without drop. A dynamic check at the beginning makes sure of that.

Examples
use corundum::default::*;
 
type P = Allocator;

let root = P::open::<Option<Parc<i32>>>("foo.pool", O_CF).unwrap();

Parc::initialize(&*root, 25);
 
let value = **root.as_ref().unwrap();
assert_eq!(value, 25);

Trait Implementations

Converts this type into a shared reference of the (usually inferred) input type.

Immutably borrows from an owned value. Read more

Formats the value using the given formatter. Read more

The resulting type after dereferencing.

Dereferences the value.

Formats the value using the given formatter. Read more

Drops the Parc safely

This will decrement the strong reference count. If the strong reference count reaches zero then the only other references (if any) are Weak, so we drop the inner value on commit using a DropOnCommit log.

Examples
use corundum::alloc::heap::*;
use corundum::sync::Parc;
use corundum::clone::PClone;

struct Foo;

impl Drop for Foo {
    fn drop(&mut self) {
        println!("dropped!");
    }
}

Heap::transaction(|j| {
    let foo  = Parc::new(Foo, j);
    let foo2 = Parc::pclone(&foo, j);

    drop(foo);    // Doesn't print anything
    drop(foo2);   // Prints "dropped!"
}).unwrap();

Feeds this value into the given Hasher. Read more

Feeds a slice of this type into the given Hasher. Read more

This method returns an Ordering between self and other. Read more

Compares and returns the maximum of two values. Read more

Compares and returns the minimum of two values. Read more

Restrict a value to a certain interval. Read more

Performs copy-assignment from source. Read more

This method tests for self and other values to be equal, and is used by ==. Read more

This method tests for !=.

This method returns an ordering between self and other values if one exists. Read more

This method tests less than (for self and other) and is used by the < operator. Read more

This method tests less than or equal to (for self and other) and is used by the <= operator. Read more

This method tests greater than (for self and other) and is used by the > operator. Read more

This method tests greater than or equal to (for self and other) and is used by the >= operator. Read more

Size of the object on Persistent Memory Assuming that self is not on PM, or considered else were, the size of allocated persistent memory is the sum of all persistent objects pointed by this object. Read more

Size of the object on Persistent Memory including Self Assuming that self is also on PM (e.g. the root object), the size of allocated persistent memory includes the size of all objects pointed by this object and the size Self. Read more

Size of the object on Persistent Memory Assuming that self is not on PM, or considered else were, the size of allocated persistent memory is the sum of all persistent objects pointed by this object. Read more

Size of the object on Persistent Memory including Self Assuming that self is also on PM (e.g. the root object), the size of allocated persistent memory includes the size of all objects pointed by this object and the size Self. Read more

Formats the value using the given formatter.

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Converts the given value to a String. Read more

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.