std: Refactor liballoc out of lib{std,sync}

This commit is part of the libstd facade RFC, issue #13851. This creates a new
library, liballoc, which is intended to be the core allocation library for all
of Rust. It is pinned on the basic assumption that an allocation failure is an
abort or failure.

This module has inherited the heap/libc_heap modules from std::rt, the owned/rc
modules from std, and the arc module from libsync. These three pointers are
currently the three most core pointer implementations in Rust.

The UnsafeArc type in std::sync should be considered deprecated and replaced by
Arc<Unsafe<T>>. This commit does not currently migrate to this type, but future
commits will continue this refactoring.
This commit is contained in:
Alex Crichton
2014-05-13 16:10:05 -07:00
parent 3da5a5cd18
commit 639759b7f4
16 changed files with 218 additions and 84 deletions

400
src/liballoc/arc.rs Normal file
View File

@@ -0,0 +1,400 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Concurrency-enabled mechanisms for sharing mutable and/or immutable state
* between tasks.
*/
use core::atomics;
use core::clone::Clone;
use core::kinds::{Share, Send};
use core::mem::{min_align_of, size_of, drop};
use core::mem;
use core::ops::{Drop, Deref};
use core::option::{Some, None, Option};
use core::ptr;
use core::ptr::RawPtr;
use heap::deallocate;
/// An atomically reference counted wrapper for shared state.
///
/// # Example
///
/// In this example, a large vector of floats is shared between several tasks.
/// With simple pipes, without `Arc`, a copy would have to be made for each
/// task.
///
/// ```rust
/// extern crate sync;
///
/// use sync::Arc;
///
/// fn main() {
/// let numbers = Vec::from_fn(100, |i| i as f32);
/// let shared_numbers = Arc::new(numbers);
///
/// for _ in range(0, 10) {
/// let child_numbers = shared_numbers.clone();
///
/// spawn(proc() {
/// let local_numbers = child_numbers.as_slice();
///
/// // Work with the local numbers
/// });
/// }
/// }
/// ```
#[unsafe_no_drop_flag]
pub struct Arc<T> {
x: *mut ArcInner<T>,
}
/// A weak pointer to an `Arc`.
///
/// Weak pointers will not keep the data inside of the `Arc` alive, and can be
/// used to break cycles between `Arc` pointers.
#[unsafe_no_drop_flag]
pub struct Weak<T> {
x: *mut ArcInner<T>,
}
struct ArcInner<T> {
strong: atomics::AtomicUint,
weak: atomics::AtomicUint,
data: T,
}
impl<T: Share + Send> Arc<T> {
/// Create an atomically reference counted wrapper.
#[inline]
pub fn new(data: T) -> Arc<T> {
// Start the weak pointer count as 1 which is the weak pointer that's
// held by all the strong pointers (kinda), see std/rc.rs for more info
let x = box ArcInner {
strong: atomics::AtomicUint::new(1),
weak: atomics::AtomicUint::new(1),
data: data,
};
Arc { x: unsafe { mem::transmute(x) } }
}
#[inline]
fn inner<'a>(&'a self) -> &'a ArcInner<T> {
// This unsafety is ok because while this arc is alive we're guaranteed
// that the inner pointer is valid. Furthermore, we know that the
// `ArcInner` structure itself is `Share` because the inner data is
// `Share` as well, so we're ok loaning out an immutable pointer to
// these contents.
unsafe { &*self.x }
}
/// Downgrades a strong pointer to a weak pointer
///
/// Weak pointers will not keep the data alive. Once all strong references
/// to the underlying data have been dropped, the data itself will be
/// destroyed.
pub fn downgrade(&self) -> Weak<T> {
// See the clone() impl for why this is relaxed
self.inner().weak.fetch_add(1, atomics::Relaxed);
Weak { x: self.x }
}
}
impl<T: Share + Send> Clone for Arc<T> {
/// Duplicate an atomically reference counted wrapper.
///
/// The resulting two `Arc` objects will point to the same underlying data
/// object. However, one of the `Arc` objects can be sent to another task,
/// allowing them to share the underlying data.
#[inline]
fn clone(&self) -> Arc<T> {
// Using a relaxed ordering is alright here, as knowledge of the
// original reference prevents other threads from erroneously deleting
// the object.
//
// As explained in the [Boost documentation][1], Increasing the
// reference counter can always be done with memory_order_relaxed: New
// references to an object can only be formed from an existing
// reference, and passing an existing reference from one thread to
// another must already provide any required synchronization.
//
// [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
self.inner().strong.fetch_add(1, atomics::Relaxed);
Arc { x: self.x }
}
}
impl<T: Send + Share> Deref<T> for Arc<T> {
#[inline]
fn deref<'a>(&'a self) -> &'a T {
&self.inner().data
}
}
impl<T: Send + Share + Clone> Arc<T> {
/// Acquires a mutable pointer to the inner contents by guaranteeing that
/// the reference count is one (no sharing is possible).
///
/// This is also referred to as a copy-on-write operation because the inner
/// data is cloned if the reference count is greater than one.
#[inline]
#[experimental]
pub fn make_unique<'a>(&'a mut self) -> &'a mut T {
if self.inner().strong.load(atomics::SeqCst) != 1 {
*self = Arc::new(self.deref().clone())
}
// This unsafety is ok because we're guaranteed that the pointer
// returned is the *only* pointer that will ever be returned to T. Our
// reference count is guaranteed to be 1 at this point, and we required
// the Arc itself to be `mut`, so we're returning the only possible
// reference to the inner data.
unsafe { mem::transmute::<&_, &mut _>(self.deref()) }
}
}
#[unsafe_destructor]
impl<T: Share + Send> Drop for Arc<T> {
fn drop(&mut self) {
// This structure has #[unsafe_no_drop_flag], so this drop glue may run
// more than once (but it is guaranteed to be zeroed after the first if
// it's run more than once)
if self.x.is_null() { return }
// Because `fetch_sub` is already atomic, we do not need to synchronize
// with other threads unless we are going to delete the object. This
// same logic applies to the below `fetch_sub` to the `weak` count.
if self.inner().strong.fetch_sub(1, atomics::Release) != 1 { return }
// This fence is needed to prevent reordering of use of the data and
// deletion of the data. Because it is marked `Release`, the
// decreasing of the reference count sychronizes with this `Acquire`
// fence. This means that use of the data happens before decreasing
// the refernce count, which happens before this fence, which
// happens before the deletion of the data.
//
// As explained in the [Boost documentation][1],
//
// It is important to enforce any possible access to the object in
// one thread (through an existing reference) to *happen before*
// deleting the object in a different thread. This is achieved by a
// "release" operation after dropping a reference (any access to the
// object through this reference must obviously happened before),
// and an "acquire" operation before deleting the object.
//
// [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
atomics::fence(atomics::Acquire);
// Destroy the data at this time, even though we may not free the box
// allocation itself (there may still be weak pointers lying around).
unsafe { drop(ptr::read(&self.inner().data)); }
if self.inner().weak.fetch_sub(1, atomics::Release) == 1 {
atomics::fence(atomics::Acquire);
unsafe { deallocate(self.x as *mut u8, size_of::<ArcInner<T>>(),
min_align_of::<ArcInner<T>>()) }
}
}
}
impl<T: Share + Send> Weak<T> {
/// Attempts to upgrade this weak reference to a strong reference.
///
/// This method will fail to upgrade this reference if the strong reference
/// count has already reached 0, but if there are still other active strong
/// references this function will return a new strong reference to the data
pub fn upgrade(&self) -> Option<Arc<T>> {
// We use a CAS loop to increment the strong count instead of a
// fetch_add because once the count hits 0 is must never be above 0.
let inner = self.inner();
loop {
let n = inner.strong.load(atomics::SeqCst);
if n == 0 { return None }
let old = inner.strong.compare_and_swap(n, n + 1, atomics::SeqCst);
if old == n { return Some(Arc { x: self.x }) }
}
}
#[inline]
fn inner<'a>(&'a self) -> &'a ArcInner<T> {
// See comments above for why this is "safe"
unsafe { &*self.x }
}
}
impl<T: Share + Send> Clone for Weak<T> {
#[inline]
fn clone(&self) -> Weak<T> {
// See comments in Arc::clone() for why this is relaxed
self.inner().weak.fetch_add(1, atomics::Relaxed);
Weak { x: self.x }
}
}
#[unsafe_destructor]
impl<T: Share + Send> Drop for Weak<T> {
fn drop(&mut self) {
// see comments above for why this check is here
if self.x.is_null() { return }
// If we find out that we were the last weak pointer, then its time to
// deallocate the data entirely. See the discussion in Arc::drop() about
// the memory orderings
if self.inner().weak.fetch_sub(1, atomics::Release) == 1 {
atomics::fence(atomics::Acquire);
unsafe { deallocate(self.x as *mut u8, size_of::<ArcInner<T>>(),
min_align_of::<ArcInner<T>>()) }
}
}
}
#[cfg(test)]
#[allow(experimental)]
mod tests {
use std::clone::Clone;
use std::comm::channel;
use std::mem::drop;
use std::ops::{Drop, Deref, DerefMut};
use std::option::{Option, Some, None};
use std::sync::atomics;
use std::task;
use std::vec::Vec;
use super::{Arc, Weak};
use sync::Mutex;
struct Canary(*mut atomics::AtomicUint);
impl Drop for Canary
{
fn drop(&mut self) {
unsafe {
match *self {
Canary(c) => {
(*c).fetch_add(1, atomics::SeqCst);
}
}
}
}
}
#[test]
fn manually_share_arc() {
let v = vec!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
let arc_v = Arc::new(v);
let (tx, rx) = channel();
task::spawn(proc() {
let arc_v: Arc<Vec<int>> = rx.recv();
assert_eq!(*arc_v.get(3), 4);
});
tx.send(arc_v.clone());
assert_eq!(*arc_v.get(2), 3);
assert_eq!(*arc_v.get(4), 5);
info!("{:?}", arc_v);
}
#[test]
fn test_cowarc_clone_make_unique() {
let mut cow0 = Arc::new(75u);
let mut cow1 = cow0.clone();
let mut cow2 = cow1.clone();
assert!(75 == *cow0.make_unique());
assert!(75 == *cow1.make_unique());
assert!(75 == *cow2.make_unique());
*cow0.make_unique() += 1;
*cow1.make_unique() += 2;
*cow2.make_unique() += 3;
assert!(76 == *cow0);
assert!(77 == *cow1);
assert!(78 == *cow2);
// none should point to the same backing memory
assert!(*cow0 != *cow1);
assert!(*cow0 != *cow2);
assert!(*cow1 != *cow2);
}
#[test]
fn test_cowarc_clone_unique2() {
let mut cow0 = Arc::new(75u);
let cow1 = cow0.clone();
let cow2 = cow1.clone();
assert!(75 == *cow0);
assert!(75 == *cow1);
assert!(75 == *cow2);
*cow0.make_unique() += 1;
assert!(76 == *cow0);
assert!(75 == *cow1);
assert!(75 == *cow2);
// cow1 and cow2 should share the same contents
// cow0 should have a unique reference
assert!(*cow0 != *cow1);
assert!(*cow0 != *cow2);
assert!(*cow1 == *cow2);
}
#[test]
fn test_live() {
let x = Arc::new(5);
let y = x.downgrade();
assert!(y.upgrade().is_some());
}
#[test]
fn test_dead() {
let x = Arc::new(5);
let y = x.downgrade();
drop(x);
assert!(y.upgrade().is_none());
}
#[test]
fn weak_self_cyclic() {
struct Cycle {
x: Mutex<Option<Weak<Cycle>>>
}
let a = Arc::new(Cycle { x: Mutex::new(None) });
let b = a.clone().downgrade();
*a.deref().x.lock().deref_mut() = Some(b);
// hopefully we don't double-free (or leak)...
}
#[test]
fn drop_arc() {
let mut canary = atomics::AtomicUint::new(0);
let x = Arc::new(Canary(&mut canary as *mut atomics::AtomicUint));
drop(x);
assert!(canary.load(atomics::Acquire) == 1);
}
#[test]
fn drop_arc_weak() {
let mut canary = atomics::AtomicUint::new(0);
let arc = Arc::new(Canary(&mut canary as *mut atomics::AtomicUint));
let arc_weak = arc.downgrade();
assert!(canary.load(atomics::Acquire) == 0);
drop(arc);
assert!(canary.load(atomics::Acquire) == 1);
drop(arc_weak);
}
}