|
| 1 | +# Base Code |
| 2 | + |
| 3 | +Now that we've decided the layout for our implementation of `Arc`, let's create |
| 4 | +some basic code. |
| 5 | + |
| 6 | +## Constructing the Arc |
| 7 | + |
| 8 | +We'll first need a way to construct an `Arc<T>`. |
| 9 | + |
| 10 | +This is pretty simple, as we just need to box the `ArcInner<T>` and get a |
| 11 | +`NonNull<T>` pointer to it. |
| 12 | + |
| 13 | +We start the reference counter at 1, as that first reference is the current |
| 14 | +pointer. As the `Arc` is cloned or dropped, it is updated. It is okay to call |
| 15 | +`unwrap()` on the `Option` returned by `NonNull` as `Box::into_raw` guarantees |
| 16 | +that the pointer returned is not null. |
| 17 | + |
| 18 | +```rust,ignore |
| 19 | +impl<T> Arc<T> { |
| 20 | + pub fn new(data: T) -> Arc<T> { |
| 21 | + // We start the reference count at 1, as that first reference is the |
| 22 | + // current pointer. |
| 23 | + let boxed = Box::new(ArcInner { |
| 24 | + rc: AtomicUsize::new(1), |
| 25 | + data, |
| 26 | + }); |
| 27 | + Arc { |
| 28 | + // It is okay to call `.unwrap()` here as we get a pointer from |
| 29 | + // `Box::into_raw` which is guaranteed to not be null. |
| 30 | + ptr: NonNull::new(Box::into_raw(boxed)).unwrap(), |
| 31 | + _marker: PhantomData, |
| 32 | + } |
| 33 | + } |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +## Send and Sync |
| 38 | + |
| 39 | +Since we're building a concurrency primitive, we'll need to be able to send it |
| 40 | +across threads. Thus, we can implement the `Send` and `Sync` marker traits. For |
| 41 | +more information on these, see [the section on `Send` and |
| 42 | +`Sync`](send-and-sync.md). |
| 43 | + |
| 44 | +This is okay because: |
| 45 | +* You can only get a mutable reference to the value inside an `Arc` if and only |
| 46 | + if it is the only `Arc` referencing that data |
| 47 | +* We use atomic counters for reference counting |
| 48 | + |
| 49 | +```rust,ignore |
| 50 | +unsafe impl<T: Sync + Send> Send for Arc<T> {} |
| 51 | +unsafe impl<T: Sync + Send> Sync for Arc<T> {} |
| 52 | +``` |
| 53 | + |
| 54 | +We need to have the bound `T: Sync + Send` because if we did not provide those |
| 55 | +bounds, it would be possible to share values that are thread-unsafe across a |
| 56 | +thread boundary via an `Arc`, which could possibly cause data races or |
| 57 | +unsoundness. |
| 58 | + |
| 59 | +## Getting the `ArcInner` |
| 60 | + |
| 61 | +We'll now want to make a private helper function, `inner()`, which just returns |
| 62 | +the dereferenced `NonNull` pointer. |
| 63 | + |
| 64 | +To dereference the `NonNull<T>` pointer into a `&T`, we can call |
| 65 | +`NonNull::as_ref`. This is unsafe, unlike the typical `as_ref` function, so we |
| 66 | +must call it like this: |
| 67 | +```rust,ignore |
| 68 | +// inside the impl<T> Arc<T> block from before: |
| 69 | +fn inner(&self) -> &ArcInner<T> { |
| 70 | + unsafe { self.ptr.as_ref() } |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +This unsafety is okay because while this `Arc` is alive, we're guaranteed that |
| 75 | +the inner pointer is valid. |
| 76 | + |
| 77 | +Here's all the code from this section: |
| 78 | +```rust,ignore |
| 79 | +impl<T> Arc<T> { |
| 80 | + pub fn new(data: T) -> Arc<T> { |
| 81 | + // We start the reference count at 1, as that first reference is the |
| 82 | + // current pointer. |
| 83 | + let boxed = Box::new(ArcInner { |
| 84 | + rc: AtomicUsize::new(1), |
| 85 | + data, |
| 86 | + }); |
| 87 | + Arc { |
| 88 | + // It is okay to call `.unwrap()` here as we get a pointer from |
| 89 | + // `Box::into_raw` which is guaranteed to not be null. |
| 90 | + ptr: NonNull::new(Box::into_raw(boxed)).unwrap(), |
| 91 | + _marker: PhantomData, |
| 92 | + } |
| 93 | + } |
| 94 | +
|
| 95 | + fn inner(&self) -> &ArcInner<T> { |
| 96 | + // This unsafety is okay because while this Arc is alive, we're |
| 97 | + // guaranteed that the inner pointer is valid. |
| 98 | + unsafe { self.ptr.as_ref() } |
| 99 | + } |
| 100 | +} |
| 101 | +
|
| 102 | +unsafe impl<T: Sync + Send> Send for Arc<T> {} |
| 103 | +unsafe impl<T: Sync + Send> Sync for Arc<T> {} |
| 104 | +``` |
0 commit comments