[][src]Struct pagecache::Tx

pub struct Tx<P> where
    P: DeserializeOwned + Serialize
{ /* fields omitted */ }

A handle to an ongoing pagecache transaction. Ensures that any state which is removed from a shared in-memory data structure is not destroyed until all possible readers have concluded.

Methods

impl<P> Tx<P> where
    P: DeserializeOwned + Serialize + Send + Sync
[src]

pub fn new(ts: u64) -> Self[src]

Creates a new Tx with a given timestamp.

pub fn commit(self) -> TxResult<()>[src]

Atomically commit this transaction by checking all read and written pages for conflicts, and then writing changes in a way that cannot be partially recovered (will either be 100% recovered or 100% aborted in the case of a conflict or crash that happens before the entire write set can be persisted to disk).

This is optimistic, which gets better performance with many threads that write to separate pages, but may abort if threads are writing to the same pages.

pub fn allocate<'g>(&'g self, new: P) -> TxResult<(PageId, PagePtr<'g, P>)>[src]

Create a new page, trying to reuse old freed pages if possible to maximize underlying PageTable pointer density. Returns the page ID and its pointer for use in future replace and link operations.

pub fn free<'g>(
    &'g self,
    pid: PageId,
    old: PagePtr<'g, P>
) -> TxResult<CasResult<'g, P, ()>>
[src]

Free a particular page.

Try to atomically add a PageFrag to the page. Returns Ok(new_key) if the operation was successful. Returns Err(None) if the page no longer exists. Returns Err(Some(actual_key)) if the atomic append fails.

pub fn replace<'g>(
    &'g self,
    pid: PageId,
    old: PagePtr<'g, P>,
    new: P
) -> TxResult<CasResult<'g, P, P>>
[src]

Replace an existing page with a different set of PageFrags. Returns Ok(new_key) if the operation was successful. Returns Err(None) if the page no longer exists. Returns Err(Some(actual_key)) if the atomic swap fails.

pub fn get<'g>(&'g self, pid: PageId) -> TxResult<(PagePtr<'g, P>, Vec<&'g P>)>[src]

Try to retrieve a page by its logical ID.

Methods from Deref<Target = Guard>

pub fn defer<F, R>(&self, f: F) where
    F: FnOnce() -> R + Send + 'static, 
[src]

Stores a function so that it can be executed at some point after all currently pinned threads get unpinned.

This method first stores f into the thread-local (or handle-local) cache. If this cache becomes full, some functions are moved into the global cache. At the same time, some functions from both local and global caches may get executed in order to incrementally clean up the caches as they fill up.

There is no guarantee when exactly f will be executed. The only guarantee is that it won't be executed until all currently pinned threads get unpinned. In theory, f might never run, but the epoch-based garbage collection will make an effort to execute it reasonably soon.

If this method is called from an unprotected guard, the function will simply be executed immediately.

pub unsafe fn defer_unchecked<F, R>(&self, f: F) where
    F: FnOnce() -> R, 
[src]

Stores a function so that it can be executed at some point after all currently pinned threads get unpinned.

This method first stores f into the thread-local (or handle-local) cache. If this cache becomes full, some functions are moved into the global cache. At the same time, some functions from both local and global caches may get executed in order to incrementally clean up the caches as they fill up.

There is no guarantee when exactly f will be executed. The only guarantee is that it won't be executed until all currently pinned threads get unpinned. In theory, f might never run, but the epoch-based garbage collection will make an effort to execute it reasonably soon.

If this method is called from an unprotected guard, the function will simply be executed immediately.

Safety

The given function must not hold reference onto the stack. It is highly recommended that the passed function is always marked with move in order to prevent accidental borrows.

use crossbeam_epoch as epoch;

let guard = &epoch::pin();
let message = "Hello!";
unsafe {
    // ALWAYS use `move` when sending a closure into `defer_unchecked`.
    guard.defer_unchecked(move || {
        println!("{}", message);
    });
}

Apart from that, keep in mind that another thread may execute f, so anything accessed by the closure must be Send.

We intentionally didn't require F: Send, because Rust's type systems usually cannot prove F: Send for typical use cases. For example, consider the following code snippet, which exemplifies the typical use case of deferring the deallocation of a shared reference:

This example is not tested
let shared = Owned::new(7i32).into_shared(guard);
guard.defer_unchecked(move || shared.into_owned()); // `Shared` is not `Send`!

While Shared is not Send, it's safe for another thread to call the deferred function, because it's called only after the grace period and shared is no longer shared with other threads. But we don't expect type systems to prove this.

Examples

When a heap-allocated object in a data structure becomes unreachable, it has to be deallocated. However, the current thread and other threads may be still holding references on the stack to that same object. Therefore it cannot be deallocated before those references get dropped. This method can defer deallocation until all those threads get unpinned and consequently drop all their references on the stack.

use crossbeam_epoch::{self as epoch, Atomic, Owned};
use std::sync::atomic::Ordering::SeqCst;

let a = Atomic::new("foo");

// Now suppose that `a` is shared among multiple threads and concurrently
// accessed and modified...

// Pin the current thread.
let guard = &epoch::pin();

// Steal the object currently stored in `a` and swap it with another one.
let p = a.swap(Owned::new("bar").into_shared(guard), SeqCst, guard);

if !p.is_null() {
    // The object `p` is pointing to is now unreachable.
    // Defer its deallocation until all currently pinned threads get unpinned.
    unsafe {
        // ALWAYS use `move` when sending a closure into `defer_unchecked`.
        guard.defer_unchecked(move || {
            println!("{} is now being deallocated.", p.deref());
            // Now we have unique access to the object pointed to by `p` and can turn it
            // into an `Owned`. Dropping the `Owned` will deallocate the object.
            drop(p.into_owned());
        });
    }
}

pub unsafe fn defer_destroy<T>(&self, ptr: Shared<T>)[src]

Stores a destructor for an object so that it can be deallocated and dropped at some point after all currently pinned threads get unpinned.

This method first stores the destructor into the thread-local (or handle-local) cache. If this cache becomes full, some destructors are moved into the global cache. At the same time, some destructors from both local and global caches may get executed in order to incrementally clean up the caches as they fill up.

There is no guarantee when exactly the destructor will be executed. The only guarantee is that it won't be executed until all currently pinned threads get unpinned. In theory, the destructor might never run, but the epoch-based garbage collection will make an effort to execute it reasonably soon.

If this method is called from an unprotected guard, the destructor will simply be executed immediately.

Safety

The object must not be reachable by other threads anymore, otherwise it might be still in use when the destructor runs.

Apart from that, keep in mind that another thread may execute the destructor, so the object must be sendable to other threads.

We intentionally didn't require T: Send, because Rust's type systems usually cannot prove T: Send for typical use cases. For example, consider the following code snippet, which exemplifies the typical use case of deferring the deallocation of a shared reference:

This example is not tested
let shared = Owned::new(7i32).into_shared(guard);
guard.defer_destroy(shared); // `Shared` is not `Send`!

While Shared is not Send, it's safe for another thread to call the destructor, because it's called only after the grace period and shared is no longer shared with other threads. But we don't expect type systems to prove this.

Examples

When a heap-allocated object in a data structure becomes unreachable, it has to be deallocated. However, the current thread and other threads may be still holding references on the stack to that same object. Therefore it cannot be deallocated before those references get dropped. This method can defer deallocation until all those threads get unpinned and consequently drop all their references on the stack.

use crossbeam_epoch::{self as epoch, Atomic, Owned};
use std::sync::atomic::Ordering::SeqCst;

let a = Atomic::new("foo");

// Now suppose that `a` is shared among multiple threads and concurrently
// accessed and modified...

// Pin the current thread.
let guard = &epoch::pin();

// Steal the object currently stored in `a` and swap it with another one.
let p = a.swap(Owned::new("bar").into_shared(guard), SeqCst, guard);

if !p.is_null() {
    // The object `p` is pointing to is now unreachable.
    // Defer its deallocation until all currently pinned threads get unpinned.
    unsafe {
        guard.defer_destroy(p);
    }
}

pub fn flush(&self)[src]

Clears up the thread-local cache of deferred functions by executing them or moving into the global cache.

Call this method after deferring execution of a function if you want to get it executed as soon as possible. Flushing will make sure it is residing in in the global cache, so that any thread has a chance of taking the function and executing it.

If this method is called from an unprotected guard, it is a no-op (nothing happens).

Examples

use crossbeam_epoch as epoch;

let guard = &epoch::pin();
unsafe {
    guard.defer(move || {
        println!("This better be printed as soon as possible!");
    });
}
guard.flush();

pub fn collector(&self) -> Option<&Collector>[src]

Returns the Collector associated with this guard.

This method is useful when you need to ensure that all guards used with a data structure come from the same collector.

If this method is called from an unprotected guard, then None is returned.

Examples

use crossbeam_epoch as epoch;

let mut guard1 = epoch::pin();
let mut guard2 = epoch::pin();
assert!(guard1.collector() == guard2.collector());

Trait Implementations

impl<P> Deref for Tx<P> where
    P: DeserializeOwned + Serialize
[src]

type Target = Guard

The resulting type after dereferencing.

Auto Trait Implementations

impl<P> !Send for Tx<P>

impl<P> !Sync for Tx<P>

Blanket Implementations

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> Any for T where
    T: 'static + ?Sized
[src]