Skip to content

Commit ac49a3d

Browse files
committed
Merge branch 'master' into stable_vec
2 parents e82dfe2 + 001f410 commit ac49a3d

37 files changed

+941
-193
lines changed

.github/workflows/main.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
name: Test
77
runs-on: ubuntu-latest
88
steps:
9-
- uses: actions/checkout@master
9+
- uses: actions/checkout@v2
1010
- name: Update rustup
1111
run: rustup self update
1212
- name: Install Rust
@@ -15,10 +15,12 @@ jobs:
1515
rustup toolchain install nightly -c rust-docs
1616
rustup default nightly
1717
- name: Install mdbook
18+
env:
19+
MDBOOK_VER: v0.4.3
1820
run: |
1921
mkdir bin
20-
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.3.5/mdbook-v0.3.5-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin
21-
echo "##[add-path]$(pwd)/bin"
22+
curl -sSL https://github.com/rust-lang/mdBook/releases/download/${{ env.MDBOOK_VER }}/mdbook-${{ env.MDBOOK_VER }}-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin
23+
echo "$(pwd)/bin" >> $GITHUB_PATH
2224
- name: Report versions
2325
run: |
2426
rustup --version

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
*.html
22
book
3+
4+
# linkcheck stuff
5+
linkcheck
6+
linkchecker
7+
linkcheck.sh

src/SUMMARY.md

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,64 @@
33
[Introduction](README.md)
44

55
* [Meet Safe and Unsafe](meet-safe-and-unsafe.md)
6-
* [How Safe and Unsafe Interact](safe-unsafe-meaning.md)
7-
* [What Unsafe Can Do](what-unsafe-does.md)
8-
* [Working with Unsafe](working-with-unsafe.md)
6+
* [How Safe and Unsafe Interact](safe-unsafe-meaning.md)
7+
* [What Unsafe Can Do](what-unsafe-does.md)
8+
* [Working with Unsafe](working-with-unsafe.md)
99
* [Data Layout](data.md)
10-
* [repr(Rust)](repr-rust.md)
11-
* [Exotically Sized Types](exotic-sizes.md)
12-
* [Other reprs](other-reprs.md)
10+
* [repr(Rust)](repr-rust.md)
11+
* [Exotically Sized Types](exotic-sizes.md)
12+
* [Other reprs](other-reprs.md)
1313
* [Ownership](ownership.md)
14-
* [References](references.md)
15-
* [Aliasing](aliasing.md)
16-
* [Lifetimes](lifetimes.md)
17-
* [Limits of Lifetimes](lifetime-mismatch.md)
18-
* [Lifetime Elision](lifetime-elision.md)
19-
* [Unbounded Lifetimes](unbounded-lifetimes.md)
20-
* [Higher-Rank Trait Bounds](hrtb.md)
21-
* [Subtyping and Variance](subtyping.md)
22-
* [Drop Check](dropck.md)
23-
* [PhantomData](phantom-data.md)
24-
* [Splitting Borrows](borrow-splitting.md)
14+
* [References](references.md)
15+
* [Aliasing](aliasing.md)
16+
* [Lifetimes](lifetimes.md)
17+
* [Limits of Lifetimes](lifetime-mismatch.md)
18+
* [Lifetime Elision](lifetime-elision.md)
19+
* [Unbounded Lifetimes](unbounded-lifetimes.md)
20+
* [Higher-Rank Trait Bounds](hrtb.md)
21+
* [Subtyping and Variance](subtyping.md)
22+
* [Drop Check](dropck.md)
23+
* [PhantomData](phantom-data.md)
24+
* [Splitting Borrows](borrow-splitting.md)
2525
* [Type Conversions](conversions.md)
26-
* [Coercions](coercions.md)
27-
* [The Dot Operator](dot-operator.md)
28-
* [Casts](casts.md)
29-
* [Transmutes](transmutes.md)
26+
* [Coercions](coercions.md)
27+
* [The Dot Operator](dot-operator.md)
28+
* [Casts](casts.md)
29+
* [Transmutes](transmutes.md)
3030
* [Uninitialized Memory](uninitialized.md)
31-
* [Checked](checked-uninit.md)
32-
* [Drop Flags](drop-flags.md)
33-
* [Unchecked](unchecked-uninit.md)
31+
* [Checked](checked-uninit.md)
32+
* [Drop Flags](drop-flags.md)
33+
* [Unchecked](unchecked-uninit.md)
3434
* [Ownership Based Resource Management](obrm.md)
35-
* [Constructors](constructors.md)
36-
* [Destructors](destructors.md)
37-
* [Leaking](leaking.md)
35+
* [Constructors](constructors.md)
36+
* [Destructors](destructors.md)
37+
* [Leaking](leaking.md)
3838
* [Unwinding](unwinding.md)
39-
* [Exception Safety](exception-safety.md)
40-
* [Poisoning](poisoning.md)
39+
* [Exception Safety](exception-safety.md)
40+
* [Poisoning](poisoning.md)
4141
* [Concurrency](concurrency.md)
42-
* [Races](races.md)
43-
* [Send and Sync](send-and-sync.md)
44-
* [Atomics](atomics.md)
42+
* [Races](races.md)
43+
* [Send and Sync](send-and-sync.md)
44+
* [Atomics](atomics.md)
4545
* [Implementing Vec](vec.md)
46-
* [Layout](vec-layout.md)
47-
* [Allocating](vec-alloc.md)
48-
* [Push and Pop](vec-push-pop.md)
49-
* [Deallocating](vec-dealloc.md)
50-
* [Deref](vec-deref.md)
51-
* [Insert and Remove](vec-insert-remove.md)
52-
* [IntoIter](vec-into-iter.md)
53-
* [RawVec](vec-raw.md)
54-
* [Drain](vec-drain.md)
55-
* [Handling Zero-Sized Types](vec-zsts.md)
56-
* [Final Code](vec-final.md)
46+
* [Layout](vec-layout.md)
47+
* [Allocating](vec-alloc.md)
48+
* [Push and Pop](vec-push-pop.md)
49+
* [Deallocating](vec-dealloc.md)
50+
* [Deref](vec-deref.md)
51+
* [Insert and Remove](vec-insert-remove.md)
52+
* [IntoIter](vec-into-iter.md)
53+
* [RawVec](vec-raw.md)
54+
* [Drain](vec-drain.md)
55+
* [Handling Zero-Sized Types](vec-zsts.md)
56+
* [Final Code](vec-final.md)
5757
* [Implementing Arc and Mutex](arc-and-mutex.md)
58+
* [Arc](arc.md)
59+
* [Layout](arc-layout.md)
60+
* [Base Code](arc-base.md)
61+
* [Cloning](arc-clone.md)
62+
* [Dropping](arc-drop.md)
63+
* [Final Code](arc-final.md)
5864
* [FFI](ffi.md)
5965
* [Beneath `std`](beneath-std.md)
60-
* [#[panic_handler]](panic-handler.md)
66+
* [#[panic_handler]](panic-handler.md)

src/arc-and-mutex.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ Knowing the theory is all fine and good, but the *best* way to understand
44
something is to use it. To better understand atomics and interior mutability,
55
we'll be implementing versions of the standard library's Arc and Mutex types.
66

7-
TODO: ALL OF THIS OMG
7+
TODO: Mutex

src/arc-base.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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+
```rust,ignore
14+
impl<T> Arc<T> {
15+
pub fn new(data: T) -> Arc<T> {
16+
// We start the reference count at 1, as that first reference is the
17+
// current pointer.
18+
let boxed = Box::new(ArcInner {
19+
rc: AtomicUsize::new(1),
20+
data,
21+
});
22+
Arc {
23+
// It is okay to call `.unwrap()` here as we get a pointer from
24+
// `Box::into_raw` which is guaranteed to not be null.
25+
ptr: NonNull::new(Box::into_raw(boxed)).unwrap(),
26+
phantom: PhantomData,
27+
}
28+
}
29+
}
30+
```
31+
32+
## Send and Sync
33+
34+
Since we're building a concurrency primitive, we'll need to be able to send it
35+
across threads. Thus, we can implement the `Send` and `Sync` marker traits. For
36+
more information on these, see [the section on `Send` and
37+
`Sync`](send-and-sync.md).
38+
39+
This is okay because:
40+
* You can only get a mutable reference to the value inside an `Arc` if and only
41+
if it is the only `Arc` referencing that data (which only happens in `Drop`)
42+
* We use atomics for the shared mutable reference counting
43+
44+
```rust,ignore
45+
unsafe impl<T: Sync + Send> Send for Arc<T> {}
46+
unsafe impl<T: Sync + Send> Sync for Arc<T> {}
47+
```
48+
49+
We need to have the bound `T: Sync + Send` because if we did not provide those
50+
bounds, it would be possible to share values that are thread-unsafe across a
51+
thread boundary via an `Arc`, which could possibly cause data races or
52+
unsoundness.
53+
54+
For example, if those bounds were not present, `Arc<Rc<u32>>` would be `Sync` or
55+
`Send`, meaning that you could clone the `Rc` out of the `Arc` to send it across
56+
a thread (without creating an entirely new `Rc`), which would create data races
57+
as `Rc` is not thread-safe.
58+
59+
## Getting the `ArcInner`
60+
61+
To dereference the `NonNull<T>` pointer into a `&T`, we can call
62+
`NonNull::as_ref`. This is unsafe, unlike the typical `as_ref` function, so we
63+
must call it like this:
64+
```rust,ignore
65+
unsafe { self.ptr.as_ref() }
66+
```
67+
68+
We'll be using this snippet a few times in this code (usually with an associated
69+
`let` binding).
70+
71+
This unsafety is okay because while this `Arc` is alive, we're guaranteed that
72+
the inner pointer is valid.
73+
74+
## Deref
75+
76+
Alright. Now we can make `Arc`s (and soon will be able to clone and destroy them correctly), but how do we get
77+
to the data inside?
78+
79+
What we need now is an implementation of `Deref`.
80+
81+
We'll need to import the trait:
82+
```rust,ignore
83+
use std::ops::Deref;
84+
```
85+
86+
And here's the implementation:
87+
```rust,ignore
88+
impl<T> Deref for Arc<T> {
89+
type Target = T;
90+
91+
fn deref(&self) -> &T {
92+
let inner = unsafe { self.ptr.as_ref() };
93+
&inner.data
94+
}
95+
}
96+
```
97+
98+
Pretty simple, eh? This simply dereferences the `NonNull` pointer to the
99+
`ArcInner<T>`, then gets a reference to the data inside.
100+
101+
## Code
102+
103+
Here's all the code from this section:
104+
```rust,ignore
105+
use std::ops::Deref;
106+
107+
impl<T> Arc<T> {
108+
pub fn new(data: T) -> Arc<T> {
109+
// We start the reference count at 1, as that first reference is the
110+
// current pointer.
111+
let boxed = Box::new(ArcInner {
112+
rc: AtomicUsize::new(1),
113+
data,
114+
});
115+
Arc {
116+
// It is okay to call `.unwrap()` here as we get a pointer from
117+
// `Box::into_raw` which is guaranteed to not be null.
118+
ptr: NonNull::new(Box::into_raw(boxed)).unwrap(),
119+
phantom: PhantomData,
120+
}
121+
}
122+
}
123+
124+
unsafe impl<T: Sync + Send> Send for Arc<T> {}
125+
unsafe impl<T: Sync + Send> Sync for Arc<T> {}
126+
127+
128+
impl<T> Deref for Arc<T> {
129+
type Target = T;
130+
131+
fn deref(&self) -> &T {
132+
let inner = unsafe { self.ptr.as_ref() };
133+
&inner.data
134+
}
135+
}
136+
```

src/arc-clone.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Cloning
2+
3+
Now that we've got some basic code set up, we'll need a way to clone the `Arc`.
4+
5+
Basically, we need to:
6+
1. Increment the atomic reference count
7+
2. Construct a new instance of the `Arc` from the inner pointer
8+
9+
First, we need to get access to the `ArcInner`:
10+
```rust,ignore
11+
let inner = unsafe { self.ptr.as_ref() };
12+
```
13+
14+
We can update the atomic reference count as follows:
15+
```rust,ignore
16+
let old_rc = inner.rc.fetch_add(1, Ordering::???);
17+
```
18+
19+
But what ordering should we use here? We don't really have any code that will
20+
need atomic synchronization when cloning, as we do not modify the internal value
21+
while cloning. Thus, we can use a Relaxed ordering here, which implies no
22+
happens-before relationship but is atomic. When `Drop`ping the Arc, however,
23+
we'll need to atomically synchronize when decrementing the reference count. This
24+
is described more in [the section on the `Drop` implementation for
25+
`Arc`](arc-drop.md). For more information on atomic relationships and Relaxed
26+
ordering, see [the section on atomics](atomics.md).
27+
28+
Thus, the code becomes this:
29+
```rust,ignore
30+
let old_rc = inner.rc.fetch_add(1, Ordering::Relaxed);
31+
```
32+
33+
We'll need to add another import to use `Ordering`:
34+
```rust,ignore
35+
use std::sync::atomic::Ordering;
36+
```
37+
38+
However, we have one problem with this implementation right now. What if someone
39+
decides to `mem::forget` a bunch of Arcs? The code we have written so far (and
40+
will write) assumes that the reference count accurately portrays how many Arcs
41+
are in memory, but with `mem::forget` this is false. Thus, when more and more
42+
Arcs are cloned from this one without them being `Drop`ped and the reference
43+
count being decremented, we can overflow! This will cause use-after-free which
44+
is **INCREDIBLY BAD!**
45+
46+
To handle this, we need to check that the reference count does not go over some
47+
arbitrary value (below `usize::MAX`, as we're storing the reference count as an
48+
`AtomicUsize`), and do *something*.
49+
50+
The standard library's implementation decides to just abort the program (as it
51+
is an incredibly unlikely case in normal code and if it happens, the program is
52+
probably incredibly degenerate) if the reference count reaches `isize::MAX`
53+
(about half of `usize::MAX`) on any thread, on the assumption that there are
54+
probably not about 2 billion threads (or about **9 quintillion** on some 64-bit
55+
machines) incrementing the reference count at once. This is what we'll do.
56+
57+
It's pretty simple to implement this behaviour:
58+
```rust,ignore
59+
if old_rc >= isize::MAX as usize {
60+
std::process::abort();
61+
}
62+
```
63+
64+
Then, we need to return a new instance of the `Arc`:
65+
```rust,ignore
66+
Self {
67+
ptr: self.ptr,
68+
phantom: PhantomData
69+
}
70+
```
71+
72+
Now, let's wrap this all up inside the `Clone` implementation:
73+
```rust,ignore
74+
use std::sync::atomic::Ordering;
75+
76+
impl<T> Clone for Arc<T> {
77+
fn clone(&self) -> Arc<T> {
78+
let inner = unsafe { self.ptr.as_ref() };
79+
// Using a relaxed ordering is alright here as we don't need any atomic
80+
// synchronization here as we're not modifying or accessing the inner
81+
// data.
82+
let old_rc = inner.rc.fetch_add(1, Ordering::Relaxed);
83+
84+
if old_rc >= isize::MAX as usize {
85+
std::process::abort();
86+
}
87+
88+
Self {
89+
ptr: self.ptr,
90+
phantom: PhantomData,
91+
}
92+
}
93+
}
94+
```

0 commit comments

Comments
 (0)