Commit 4c3064d6 authored by Hai Dang's avatar Hai Dang
Browse files

update rustdoc comments

parent 6fe0c042
Pipeline #42948 passed with stage
in 20 minutes and 22 seconds
//! A doubly-linked list built with `GhostCell`. This variant uses `sync::Arc` for memory management.
use crate::{GhostCell, GhostToken};
use std::sync::{Arc, Weak};
/// A doubly-linked list node.
pub struct Node<'id, T> {
data: T,
prev: Option<WeakNodePtr<'id, T>>,
next: Option<NodePtr<'id, T>>,
}
/// A `Weak` pointer to a node.
pub type WeakNodePtr<'id, T> = Weak<GhostCell<'id, Node<'id, T>>>;
/// A strong `Arc` pointer to a node.
pub type NodePtr<'id, T> = Arc<GhostCell<'id, Node<'id, T>>>;
impl<'id, T> Node<'id, T> {
......@@ -47,7 +52,7 @@ impl<'id, T> Node<'id, T> {
}
}
/// Insert `next` right after `node` in the list.
/// Insert `node2` right after `node1` in the list.
pub fn insert_next<'a>(
node1: &NodePtr<'id, T>,
node2: NodePtr<'id, T>,
......@@ -72,7 +77,7 @@ impl<'id, T> Node<'id, T> {
node1.borrow_mut(token).next = Some(node2);
}
/// Traverse immutably
/// Construct an imutable iterator to traverse immutably.
pub fn iter<'iter>(
node: &'iter NodePtr<'id, T>,
token: &'iter GhostToken<'id>,
......@@ -98,6 +103,7 @@ impl<'id, T> Node<'id, T> {
}
}
/// Immutable interior traversal.
pub fn iterate(
node: &NodePtr<'id, T>,
token: &GhostToken<'id>,
......@@ -112,6 +118,7 @@ impl<'id, T> Node<'id, T> {
}
}
/// An immutable iterator.
pub struct Iter<'id, 'iter, T> {
// We could probably weaken this to make the outermost reference a `'iter`, but that
// would make the types more complicated, so we don't.
......
//! A doubly-linked list built with `GhostCell`. This variant uses `typed_arena::Arena` for memory management.
use crate::{GhostCell, GhostToken};
use typed_arena::Arena as TypedArena;
/// A doubly-linked list node.
pub struct Node<'arena, 'id, T> {
data: T,
prev: Option<NodeRef<'arena, 'id, T>>,
next: Option<NodeRef<'arena, 'id, T>>,
}
/// A reference to a node.
pub type NodeRef<'arena, 'id, T> = &'arena GhostCell<'id, Node<'arena, 'id, T>>;
impl<'arena, 'id, T> Node<'arena, 'id, T> {
......@@ -54,7 +58,7 @@ impl<'arena, 'id, T> Node<'arena, 'id, T> {
node2.next = node1_old_next;
}
/// Remove this node to and from its adjacent nodes.
/// Remove `node` to and from its adjacent nodes.
pub fn remove(node: NodeRef<'arena, 'id, T>, token: &mut GhostToken<'id>) {
// Step 1: unlink the prev and next pointers nodes adjacent to node.
let old_prev = node.borrow(token).prev;
......@@ -72,6 +76,7 @@ impl<'arena, 'id, T> Node<'arena, 'id, T> {
// node.next = None;
}
/// Construct an imutable iterator to traverse immutably.
pub fn iter<'iter>(
node: NodeRef<'arena, 'id, T>,
token: &'iter GhostToken<'id>,
......@@ -97,7 +102,7 @@ impl<'arena, 'id, T> Node<'arena, 'id, T> {
}
}
/// Immutable interior iteration
/// Immutable interior iteration.
pub fn iterate(
node: NodeRef<'arena, 'id, T>,
token: &GhostToken<'id>,
......@@ -112,6 +117,7 @@ impl<'arena, 'id, T> Node<'arena, 'id, T> {
}
}
/// An immutable iterator.
pub struct Iter<'arena, 'id, 'iter, T> {
// We could probably weaken this to make the outermost reference a `'iter`, but that
// would make the types more complicated, so we don't.
......
//! Unsafe: tread carefully!
//!
//! # GhostCell: A cell with ghost ownership.
//!
//! Often, interior mutability in Rust comes with unsatisfying tradeoffs: thread
//! safety, generality of types, runtime overhead (both time and space) or even
//! runtime failure.
//! To avoid some of these issues, one can improve interior mutability by taking
//! advantage of Rust's ownership, but the trick is unfortunately
//! limited to `Cell`s where one has direct ownership.
//!
//! Here, we extend this trick to `GhostCell` where one has *logical* (ghost)
//! ownership, with the following techniques:
//!
//! - Invariant lifetimes (to generate unforgeable, compile-time unique lifetime
//! tokens).
//! - Higher rank lifetimes (to ensure code is parametric with respect to the
//! chosen token).
//! - Ghost ownership (to allow ownership to be held by the token's owner, rather
//! than by the cells themselves).
//!
//! The API works as follows:
//! 1. create a `GhostToken`, within an appropriate scope (a "context").
//! 2. create cells that reference the ghost token. They must reference exactly
//! one ghost token, due to lifetime invariance, and no two ghost tokens can
//! be the same, due to lifetime parametricity.
//! 3. to access values at a given lifetime and mutability, borrow the token
//! with that lifetime and mutability. As a result, Rust's guarantees about
//! ownership and lifetimes flow from the token to its owned cells.
//!
//! [The methods provided by this type have been formally verified in Coq.](http://plv.mpi-sws.org/rustbelt/ghostcell/)
pub mod dlist_arc;
pub mod dlist_arena;
use core::{cell::UnsafeCell, marker::PhantomData};
/// Unsafe: tread carefully!
/// GhostCell: A cell with ghost ownership.
///
/// Often, interior mutability in Rust comes with unsatisfying tradeoffs: thread
/// safety, generality of types, runtime overhead (both time and space) or even
/// runtime failure.
/// To avoid some of these issues, one can improve interior mutability by taking
/// advantage of Rust's ownership information, but the trick is unfortunately
/// limited to Cells where one has direct ownership.
/// Here, we extend this trick to Cells where you have *logical* ownership, with
/// the following techniques:
///
/// - Invariant lifetimes (to generate unforgeable, compile-time unique lifetime
/// tokens).
/// - Higher rank lifetimes (to ensure code is parametric with respect to the
/// chosen token).
/// - Ghost ownership (to allow ownership to be held by the token owner, rather
/// than by the value itself).
///
/// The API works as follows: first, you create a ghost token (owner), within an
/// appropriate scope. Then, you create cells that reference the ghost owner
/// (they must reference exactly one ghost token, due to lifetime invariance,
/// and no two ghost tokens can be the same, due to lifetime parametricity).
/// Finally, to access values at a given lifetime and mutability, you borrow the
/// owner's token with that lifetime and mutability. As a result, Rust's
/// guarantees about ownership and lifetimes flow from the owner to its owned
/// cells.
///
/// The methods provided by this type have been formally verified in Coq.
/// An invariant lifetime--required in order to make sure that a GhostCell can
/// be owned by a single ghost token.
#[derive(Clone, Copy, Default)]
......@@ -49,109 +49,106 @@ impl<'id> InvariantLifetime<'id> {
/// A ghost token.
///
/// Once created, a GhostToken can neither be cloned nor copied.
/// Once created, a `GhostToken` can neither be cloned nor copied.
///
/// Note that GhostToken does not need to know which types it is protecting. The
/// reason is that in order to do anything with a GhostCell<T>, both the owner
/// *and* a reference to the GhostCell are required. Since a GhostCell inherits
/// its trait markers (and other information) from the type of data it is
/// protecting, GhostToken does not need to; it only needs to carry around
/// ownership information of the entire set of Cells.
/// For example, one could create a GhostCell<'id, Rc<T>> and then send its
/// owning GhostToken<'id> to another thread, but since one cannot actually send
/// the GhostCell to another thread, it is not possible to create a race
/// condition in this way. Similarly, one could not send a reference to a
/// GhostCell<'id, GhostCell<T>> to another thread, so it would not be possible
/// to create races through its owning GhostToken<'id> even if the
/// GhostToken<'id> were borrowed immutably.
/// Note that `GhostToken` does not need to know which types it is protecting. The
/// reason is that in order to do anything with a `GhostCell<T>`, both the token
/// *and* a reference to the `GhostCell` are required. Since a `GhostCell` inherits
/// trait markers (and other information) from the type `T` of data it is
/// protecting, `GhostToken` does not need to; it only needs to carry around
/// ownership of the entire set of `GhostCell`s.
/// For example, one could create a `GhostCell<'id, Rc<T>>` and then send its
/// owning `GhostToken<'id>` to another thread, but since one cannot actually send
/// the `GhostCell` to another thread, it is not possible to create a race
/// condition in this way.
///
/// Note also that 'id is totally disjoint from T itself--it is only used as a
/// unique compile-time identifier for a set of (GhostCell of) Ts, and otherwise
/// has no relationship to T.
/// Note also that `'id` is totally disjoint from `T` itself--it is only used as
/// a unique compile-time identifier for a set of (`GhostCell`s of) `T`s, and
/// otherwise has no relationship to `T`.
pub struct GhostToken<'id> {
_marker: InvariantLifetime<'id>,
}
/// A ghost cell.
///
/// A ghost cell acts exactly like a T, except that its contents are inaccessible except through
/// its owning GhostToken.
/// A `GhostCell` acts exactly like a `T`, except that its contents are
/// accessible only through its owning `GhostToken`.
#[derive(Default)]
#[repr(transparent)]
pub struct GhostCell<'id, T: ?Sized> {
_marker: InvariantLifetime<'id>,
value: UnsafeCell<T>, // invariant in T
value: UnsafeCell<T>, // invariant in `T`
}
/// As noted above, GhostToken<'id> does not need to worry about what types it
/// is protecting, since information about Send and Sync are already carried by
/// the GhostCell<'id, T>. Therefore, it's always safe to send a GhostToken
/// `GhostToken<'id>` does not need to worry about what types it is protecting,
/// since information about `Send` and `Sync` is already carried by
/// the `GhostCell<'id, T>`. Therefore, it's always safe to send a `GhostToken`
/// between threads.
unsafe impl<'id> Send for GhostToken<'id> {}
/// As noted above, GhostToken<'id> does not need to worry about what types it
/// is protecting, since information about Send and Sync are already carried by
/// the GhostCell<'id, T>. Therefore, it's always safe to share a GhostToken
/// `GhostToken<'id>` does not need to worry about what types it is protecting,
/// since information about `Send` and `Sync` is already carried by
/// the `GhostCell<'id, T>`. Therefore, it's always safe to share a `GhostToken`
/// between threads.
unsafe impl<'id> Sync for GhostToken<'id> {}
/// GhostCell<'id, T> implements Send iff T does. This is safe because in order
/// to access the T mutably within a GhostCell<T>, you need both a mutable
/// reference to its owning GhostToken and an immutable reference to T, and both
/// references must have the same lifetime.
/// `GhostCell<'id, T>` implements `Send` iff `T` does. This is safe because in
/// order to access the `T` mutably within a `GhostCell<T>`, you need both a
/// mutable reference to its owning `GhostToken` and an immutable reference to
/// `GhostCell<T>`, and both references must have the same lifetime.
unsafe impl<'id, T> Sync for GhostCell<'id, T> where T: Send + Sync {}
/// GhostCell<'id, T> implements Sync iff T is Send + Sync. This is safe
/// because in order to access the T immutably within a GhostCell<T>, you need
/// both an immutable reference to its owning GhostToken and an immutable
/// reference to T, and both references must have the same lifetime.
/// `GhostCell<'id, T>` implements `Sync` iff `T` is `Send + Sync`. This is safe
/// because in order to access the `T` immutably within a `GhostCell<T>`, you
/// need both an immutable reference to its owning `GhostToken` and an immutable
/// reference to `GhostCell<T>`, and both references must have the same lifetime.
unsafe impl<'id, T> Send for GhostCell<'id, T> where T: Send {}
impl<'id> GhostToken<'id> {
/// Create a new `GhostToken` with a particular lifetime identifier, `'id`.
///
/// The argument function f must be parametric with respect to its lifetime
/// parameter 'new_id; this guarantees that 'id is chosen by new, not the
/// client. Because 'id is invariant with respect to GhostToken, and cannot
/// be chosen by the client to replicate an existing GhostToken, we
/// know that 'id is unique per call of new.
/// The argument function `f` must be parametric with respect to its lifetime
/// parameter `'new_id`; this guarantees that `'id` is chosen by `new`, not
/// the client. Because `'id` is invariant with respect to `GhostToken`, and
/// cannot be chosen by the client to replicate an existing `GhostToken`, we
/// know that `'id` is unique per call of `new`.
#[inline]
pub fn new<F, R>(f: F) -> R
where
F: for<'new_id> FnOnce(GhostToken<'new_id>) -> R,
{
// We choose the lifetime; it is definitely unique for each new instance
// of GhostToken.
// of `GhostToken`.
let token = GhostToken {
_marker: InvariantLifetime::new(),
};
// Return the result of running f. Note that the GhostToken itself
// cannot be returned, because R cannot mention the lifetime 'id, so the
// GhostToken only exists within its scope.
// Return the result of running `f`. Note that the `GhostToken` itself
// cannot be returned, because `R` cannot mention the lifetime `'id`, so
// the `GhostToken` only exists within its scope.
f(token)
}
}
impl<'id, T> GhostCell<'id, T> {
/// Creates a new cell that belongs to the token at lifetime 'id. This
/// consumes the value of type T. From this point on, the only way to access
/// the inner value is by using a GhostToken with the same 'id.
/// Since 'id is always chosen parametrically, and 'id is invariant for both
/// the GhostCell and the GhostToken, if one chooses 'id to correspond to an
/// existing GhostToken<'id>, that is the only GhostToken<'id> to which the
/// GhostCell belongs. Therefore, there is no way to access the value
/// through more than one token at a time.
/// Creates a new cell that belongs to the token at lifetime `'id`. This
/// consumes the value of type `T`. From this point on, the only way to access
/// the inner value is by using a `GhostToken` with the same `'id`. Since
/// `'id` is always chosen parametrically, and `'id` is invariant for both
/// the `GhostCell` and the `GhostToken`, if one chooses `'id` to correspond
/// to an existing `GhostToken<'id>`, that is the only `GhostToken<'id>` to
/// which the `GhostCell` belongs. Therefore, there is no way to access the
/// value through more than one token at a time.
///
/// As with GhostToken itself, note that 'id has no relationship to T---it
/// is only used as a unique, static marker.
/// As with `GhostToken` itself, note that `'id` has no relationship to
/// `T`---it is only used as a unique, static marker.
///
/// A subtle point to make is around Drop. If T's Drop implementation is run,
/// and T has a reference to a GhostToken<'id>, it seems that since the
/// invariant that GhostToken<'id> must be accessed mutably to get a mutable
/// reference to the T inside a GhostCell<'id, T> is being bypassed, there
/// could be a soundness bug here. Fortunately, thanks to dropck, such
/// pathological cases appear to be ruled out. For example, this code will
/// not compile:
/// A subtle point to make is around `Drop`. If `T`'s `Drop` implementation
/// is run, and `T` has a reference to a `GhostToken<'id>`, it seems that
/// since the invariant that `GhostToken<'id>` must be accessed mutably to
/// get a mutable reference to the `T` inside a `GhostCell<'id, T>` is being
/// bypassed, there could be a soundness bug here. Fortunately, thanks to
/// `dropck`, such pathological cases appear to be ruled out. For example,
/// this code will not compile:
///
/// ```compile_fail
/// use util::ghost_cell::{GhostToken, GhostCell};
......@@ -179,16 +176,15 @@ impl<'id, T> GhostCell<'id, T> {
/// });
/// ```
///
/// It will compile if the manual Drop implementation is removed, but only
/// pathological Drop implementations are an issue here. I believe there
/// It will compile if the manual `Drop` implementation is removed, but only
/// pathological `Drop` implementations are an issue here. I believe there
/// are two factors at work: one, in order to have a reference to a value,
/// the token must outlive the reference. Two, types must *strictly* outlive
/// the lifetimes of things they reference if they have a nontrivial Drop
/// the lifetimes of things they reference if they have a nontrivial `Drop`
/// implementation. As a result, if there is any reference to a `GhostCell`
/// containing the type being dropped from within the type being dropped,
/// and it has a nontrivial Drop implementation, it will not be possible to
/// complete the cycle. To illustrate more clearly,
/// this fails, too:
/// and it has a nontrivial `Drop` implementation, it will not be possible to
/// complete the cycle. To illustrate more clearly, this fails, too:
///
/// ```compile_fail
/// fn foo() {
......@@ -203,7 +199,7 @@ impl<'id, T> GhostCell<'id, T> {
/// }
/// ```
///
/// So any conceivable way to peek at a self-reference within a Drop
/// So any conceivable way to peek at a self-reference within a `Drop`
/// implementation is probably covered.
#[inline]
pub const fn new(value: T) -> Self {
......@@ -221,7 +217,7 @@ impl<'id, T> GhostCell<'id, T> {
/// Get an immutable reference to the item that lives for as long as the
/// owning token is immutably borrowed (the lifetime 'a).
/// owning token is immutably borrowed (the lifetime `'a`).
#[inline]
pub fn borrow<'a>(&'a self, _token: &'a GhostToken<'id>) -> &'a T {
unsafe {
......@@ -241,7 +237,7 @@ impl<'id, T> GhostCell<'id, T> {
#[inline]
pub fn borrow_mut<'a>(&'a self, _token: &'a mut GhostToken<'id>) -> &'a mut T {
unsafe {
// We know the token and lifetime are both borrowed at 'a, and the
// We know the token and lifetime are both borrowed at `'a`, and the
// token is borrowed mutably; therefore, nobody else has a mutable
// reference to this token. As a result, all items in the set are
// currently unaliased, so we can take out a mutable reference to
......@@ -291,8 +287,8 @@ impl<'id, T> GhostCell<'id, [T]> {
}
impl<'id, T: Clone> GhostCell<'id, T> {
/// Convenience method to clone the GhostCell when T is Clone, as long as
// the token is available.
/// Convenience method to clone the `GhostCell` when `T` is `Clone`, as long
/// as the token is available.
#[inline]
pub fn clone(&self, token: &GhostToken<'id>) -> Self {
GhostCell::new(self.borrow(token).clone())
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment