Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
FP
ghostcell
Commits
4c3064d6
Commit
4c3064d6
authored
Mar 05, 2021
by
Hai Dang
Browse files
update rustdoc comments
parent
6fe0c042
Pipeline
#42948
passed with stage
in 20 minutes and 22 seconds
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
ghostcell/src/dlist_arc.rs
View file @
4c3064d6
//! 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 `n
ext
` right after `node` in the list.
/// Insert `n
ode2
` right after `node
1
` 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
);
}
///
T
raverse immutably
///
Construct an imutable iterator to t
raverse 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.
...
...
ghostcell/src/dlist_arena.rs
View file @
4c3064d6
//! 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.
...
...
ghostcell/src/lib.rs
View file @
4c3064d6
//! 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)
T
s, 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
g
host
c
ell acts exactly like a
T
, except that its contents are
inaccessible except through
/// its owning GhostToken.
/// A
`G
host
C
ell
`
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
())
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment