Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: replace RwLock<Slab> with a lock free slab #1625

Merged
merged 94 commits into from Oct 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
0ac34ae
add vendored sharded slab
hawkw Oct 2, 2019
2b7bd5d
replace RwLock<Slab> with sharded slab
hawkw Oct 2, 2019
79fe407
remove unused dependencies
hawkw Oct 2, 2019
7f092fb
add loom dev dependency
hawkw Oct 2, 2019
1cc6040
fix sharded slab tests
hawkw Oct 2, 2019
1501ada
remove test printlns
hawkw Oct 2, 2019
51c55b8
remove custom config stuff; hardcode consts
hawkw Oct 2, 2019
d32df15
remove superfluous ABA guard
hawkw Oct 2, 2019
350e060
misc fixup
hawkw Oct 2, 2019
56dbbdd
rm unused
hawkw Oct 2, 2019
74847ad
fix clippy lint
hawkw Oct 2, 2019
777f9b8
fix clippy lints part 2: the clippening
hawkw Oct 2, 2019
e3e4461
fix indices being too long on 32-bit architectures
hawkw Oct 2, 2019
e9bcf0c
shards only need their own tids for debug _assert
hawkw Oct 2, 2019
e3b5f36
Review feedback
hawkw Oct 7, 2019
e1a099d
naming
hawkw Oct 7, 2019
57d95fe
increment generation in remove (fix possible race)
hawkw Oct 8, 2019
4ccdfbe
`Slot::next` doesn't need to be atomic
hawkw Oct 8, 2019
23736df
undo upstream test changes
hawkw Oct 8, 2019
a1413d0
fix remove_value not checking generation
hawkw Oct 9, 2019
e438cda
placate clippy
hawkw Oct 9, 2019
e129b45
ensure old keys are invalid
hawkw Oct 9, 2019
1c5ed4c
remove config trait
hawkw Oct 10, 2019
14e7ff0
move ABA guard back to reactor
hawkw Oct 10, 2019
903d873
add tests for token repeats
hawkw Oct 10, 2019
ffd1a66
fix unused
hawkw Oct 10, 2019
9ae2945
fix unused var when tracing is disabled
hawkw Oct 10, 2019
61507c7
make clippy happy
hawkw Oct 10, 2019
d659a85
fix concurrent immutable/mutable access
hawkw Oct 10, 2019
371027c
fix test path attrs that broke rustfmt
hawkw Oct 10, 2019
1c2689f
cas loop doesn't need to be a spin loop
hawkw Oct 10, 2019
81b162a
remove options and reset scheduledio in place (#1653)
hawkw Oct 11, 2019
580a765
remove unneeded cfg attributes
hawkw Oct 11, 2019
84f66d7
add comments to `Pack`
hawkw Oct 11, 2019
8739a1f
add comments to tid module
hawkw Oct 11, 2019
2892926
allow using a single shard as a slab
hawkw Oct 16, 2019
e0c3b0e
nicer printlns in tests
hawkw Oct 16, 2019
41077eb
wip
hawkw Oct 17, 2019
a0cf588
rename insert to alloc
hawkw Oct 21, 2019
225fe5d
move aba guards back to slots
hawkw Oct 21, 2019
310c236
update tests to work w/ scheduledio
hawkw Oct 21, 2019
7959201
actually use max threads cfg
hawkw Oct 21, 2019
da901ac
cleanup
hawkw Oct 21, 2019
6c0ecf2
actually switch over to single shard
hawkw Oct 21, 2019
a5b9f22
rm unused aba guard from scheduledio
hawkw Oct 21, 2019
8b58ab6
temp remove unused import
hawkw Oct 21, 2019
daabb87
review feedback: use &mut for ShardIter
hawkw Oct 22, 2019
a11fbcc
review feedback: remove prev_sz
hawkw Oct 22, 2019
3d7ba44
review feedback: enforce local access for `fill`
hawkw Oct 22, 2019
870288f
rename `fill` -> `alloc_page`
hawkw Oct 22, 2019
54b54e2
fix missing prev_sz
hawkw Oct 22, 2019
3459ba3
fix iter
hawkw Oct 22, 2019
8940552
fix missing re-export breaking tests
hawkw Oct 22, 2019
3456105
Revert "review feedback: remove prev_sz"
hawkw Oct 22, 2019
b929dfe
Revert "fix missing prev_sz"
hawkw Oct 22, 2019
fd7a822
seperately fuzz remote free list stack
hawkw Oct 22, 2019
9605f53
add test for concurrent remove_remotes
hawkw Oct 22, 2019
bb4fbe8
fix test print
hawkw Oct 22, 2019
18c32a2
fix preemption bound
hawkw Oct 22, 2019
a586024
fix test
hawkw Oct 22, 2019
f22a525
decr preemption bound
hawkw Oct 22, 2019
d08b222
better test for transfer stack
hawkw Oct 23, 2019
0235f15
factor out repeated test-util code
hawkw Oct 23, 2019
abe8d35
rm unused imports
hawkw Oct 23, 2019
2404ecf
placate clippy
hawkw Oct 23, 2019
baf47f8
import fixies
hawkw Oct 23, 2019
c668820
add single-shard versions of small slab tests
hawkw Oct 23, 2019
8e4104b
make stack orderings stricter
hawkw Oct 23, 2019
4017560
make generations sequentially consistent
hawkw Oct 23, 2019
2e06ab5
nicer debug formats in tests
hawkw Oct 23, 2019
9fa0987
make stack ops less strict
hawkw Oct 23, 2019
72f5f9e
Merge branch 'master' into eliza/net-driver-slab
hawkw Oct 23, 2019
5bbf83b
additional tests for generations
hawkw Oct 24, 2019
4958bce
store readiness & generation in the same atomic
hawkw Oct 24, 2019
326e548
clean up & refactor
hawkw Oct 24, 2019
8e73a19
fix logic error
hawkw Oct 24, 2019
6f59858
placate clippy
hawkw Oct 24, 2019
1e10db9
fix syntax
hawkw Oct 25, 2019
bc77a8d
Merge branch 'master' into eliza/net-driver-slab
hawkw Oct 25, 2019
b33addf
post-merge fixup
hawkw Oct 25, 2019
4c1b5f4
remove test printlns
hawkw Oct 25, 2019
c1c2c7c
don't put macros in lib that are only needed in net
hawkw Oct 25, 2019
0f8298b
fix wrong ordering
hawkw Oct 25, 2019
25a6582
separate out loom tests
hawkw Oct 25, 2019
81233ec
actually separate loom tests
hawkw Oct 25, 2019
c7876f8
fix `set_readiness` not being a proper fetch op
hawkw Oct 26, 2019
84c2701
document confusing methods
hawkw Oct 26, 2019
7dac6d3
nicer panic messages, etc
hawkw Oct 26, 2019
384e76a
fixup
hawkw Oct 27, 2019
10a9f0f
fix missing lazy-static dependency
hawkw Oct 27, 2019
0271eb8
fix loom thread local breaking other tests
hawkw Oct 27, 2019
f4d5a10
maybe fix loom tests
hawkw Oct 28, 2019
8555055
agh
hawkw Oct 28, 2019
7af94d1
fix tests failing without debug assertions
hawkw Oct 28, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions azure-pipelines.yml
Expand Up @@ -83,6 +83,7 @@ jobs:
rust: beta
crates:
- tokio-executor
- tokio

# Try cross compiling
- template: ci/azure-cross-compile.yml
Expand Down
10 changes: 9 additions & 1 deletion tokio/Cargo.toml
Expand Up @@ -39,7 +39,7 @@ fs = ["tokio-executor/blocking"]
io = ["tokio-io", "bytes", "iovec"]
macros = ["tokio-macros"]
net-full = ["tcp", "udp", "uds"]
net-driver = ["mio", "tokio-executor/blocking"]
net-driver = ["mio", "tokio-executor/blocking", "lazy_static"]
rt-current-thread = [
"timer",
"tokio-executor/current-thread",
Expand Down Expand Up @@ -110,6 +110,10 @@ version = "0.3.8"
default-features = false
optional = true

[target.'cfg(loom)'.dependencies]
# play nice with loom tests in other crates.
loom = "0.2.11"

[dev-dependencies]
tokio-test = { version = "=0.2.0-alpha.6", path = "../tokio-test" }
tokio-util = { version = "=0.2.0-alpha.6", path = "../tokio-util" }
Expand All @@ -127,5 +131,9 @@ serde_json = "1.0"
tempfile = "3.1.0"
time = "0.1"

# sharded slab tests
loom = "0.2.11"
proptest = "0.9.4"

[package.metadata.docs.rs]
all-features = true
4 changes: 3 additions & 1 deletion tokio/src/lib.rs
Expand Up @@ -69,7 +69,6 @@
//! }
//! }
//! ```

macro_rules! if_runtime {
($($i:item)*) => ($(
#[cfg(any(
Expand Down Expand Up @@ -97,6 +96,9 @@ pub mod io;
#[cfg(feature = "net-driver")]
pub mod net;

#[cfg(feature = "net-driver")]
mod loom;

pub mod prelude;

#[cfg(feature = "process")]
Expand Down
45 changes: 45 additions & 0 deletions tokio/src/loom.rs
@@ -0,0 +1,45 @@
//! This module abstracts over `loom` and `std::sync` depending on whether we
//! are running tests or not.
pub(crate) use self::inner::*;

#[cfg(all(test, loom))]
mod inner {
pub(crate) use loom::sync::CausalCell;
pub(crate) use loom::sync::Mutex;
pub(crate) mod atomic {
pub(crate) use loom::sync::atomic::*;
pub(crate) use std::sync::atomic::Ordering;
}
}

#[cfg(not(all(test, loom)))]
mod inner {
use std::cell::UnsafeCell;
pub(crate) use std::sync::atomic;
pub(crate) use std::sync::Mutex;

#[derive(Debug)]
pub(crate) struct CausalCell<T>(UnsafeCell<T>);

impl<T> CausalCell<T> {
pub(crate) fn new(data: T) -> CausalCell<T> {
CausalCell(UnsafeCell::new(data))
}

#[inline(always)]
pub(crate) fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(*const T) -> R,
{
f(self.0.get())
}

#[inline(always)]
pub(crate) fn with_mut<F, R>(&self, f: F) -> R
where
F: FnOnce(*mut T) -> R,
{
f(self.0.get())
}
}
}
8 changes: 8 additions & 0 deletions tokio/src/net/driver/mod.rs
Expand Up @@ -124,7 +124,15 @@
//! [`PollEvented`]: struct.PollEvented.html
//! [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
//! [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
#[cfg(loom)]
macro_rules! loom_thread_local {
($($tts:tt)+) => { loom::thread_local!{ $($tts)+ } }
}

#[cfg(not(loom))]
macro_rules! loom_thread_local {
($($tts:tt)+) => { std::thread_local!{ $($tts)+ } }
}
pub(crate) mod platform;
mod reactor;
mod registration;
Expand Down
53 changes: 53 additions & 0 deletions tokio/src/net/driver/reactor/dispatch/iter.rs
@@ -0,0 +1,53 @@
use super::{
page::{self, ScheduledIo},
Shard,
};
use std::slice;

pub(in crate::net::driver::reactor) struct UniqueIter<'a> {
pub(super) shards: slice::IterMut<'a, Shard>,
pub(super) pages: slice::Iter<'a, page::Shared>,
pub(super) slots: Option<page::Iter<'a>>,
}

impl<'a> Iterator for UniqueIter<'a> {
type Item = &'a ScheduledIo;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(item) = self.slots.as_mut().and_then(|slots| slots.next()) {
return Some(item);
}

if let Some(page) = self.pages.next() {
self.slots = page.iter();
}

if let Some(shard) = self.shards.next() {
self.pages = shard.iter();
} else {
return None;
}
}
}
}

pub(in crate::net::driver::reactor) struct ShardIter<'a> {
pub(super) pages: slice::IterMut<'a, page::Shared>,
pub(super) slots: Option<page::Iter<'a>>,
}

impl<'a> Iterator for ShardIter<'a> {
type Item = &'a ScheduledIo;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(item) = self.slots.as_mut().and_then(|slots| slots.next()) {
return Some(item);
}
if let Some(page) = self.pages.next() {
self.slots = page.iter();
} else {
return None;
}
}
}
}
36 changes: 36 additions & 0 deletions tokio/src/net/driver/reactor/dispatch/mod.rs
@@ -0,0 +1,36 @@
//! A lock-free concurrent slab.

#[cfg(all(test, loom))]
macro_rules! test_println {
($($arg:tt)*) => {
println!("{:?} {}", crate::net::driver::reactor::dispatch::Tid::current(), format_args!($($arg)*))
}
}

mod iter;
mod pack;
mod page;
mod sharded_slab;
mod tid;

#[cfg(all(test, loom))]
// this is used by sub-modules
use self::tests::test_util;
use pack::{Pack, WIDTH};
use sharded_slab::Shard;
#[cfg(all(test, loom))]
pub(crate) use sharded_slab::Slab;
pub(crate) use sharded_slab::{SingleShard, MAX_SOURCES};
use tid::Tid;

#[cfg(target_pointer_width = "64")]
const MAX_THREADS: usize = 4096;
#[cfg(target_pointer_width = "32")]
const MAX_THREADS: usize = 2048;
const INITIAL_PAGE_SIZE: usize = 32;
const MAX_PAGES: usize = WIDTH / 4;
// Chosen arbitrarily.
const RESERVED_BITS: usize = 5;

#[cfg(test)]
mod tests;
89 changes: 89 additions & 0 deletions tokio/src/net/driver/reactor/dispatch/pack.rs
@@ -0,0 +1,89 @@
pub(super) const WIDTH: usize = std::mem::size_of::<usize>() * 8;

/// Trait encapsulating the calculations required for bit-packing slab indices.
///
/// This allows us to avoid manually repeating some calculations when packing
/// and unpacking indices.
pub(crate) trait Pack: Sized {
// ====== provided by each implementation =================================

/// The number of bits occupied by this type when packed into a usize.
///
/// This must be provided to determine the number of bits into which to pack
/// the type.
const LEN: usize;
/// The type packed on the less significant side of this type.
///
/// If this type is packed into the least significant bit of a usize, this
/// should be `()`, which occupies no bytes.
///
/// This is used to calculate the shift amount for packing this value.
type Prev: Pack;

// ====== calculated automatically ========================================

/// A number consisting of `Self::LEN` 1 bits, starting at the least
/// significant bit.
///
/// This is the higest value this type can represent. This number is shifted
/// left by `Self::SHIFT` bits to calculate this type's `MASK`.
///
/// This is computed automatically based on `Self::LEN`.
const BITS: usize = {
let shift = 1 << (Self::LEN - 1);
shift | (shift - 1)
};
/// The number of bits to shift a number to pack it into a usize with other
/// values.
///
/// This is caculated automatically based on the `LEN` and `SHIFT` constants
/// of the previous value.
const SHIFT: usize = Self::Prev::SHIFT + Self::Prev::LEN;

/// The mask to extract only this type from a packed `usize`.
///
/// This is calculated by shifting `Self::BITS` left by `Self::SHIFT`.
const MASK: usize = Self::BITS << Self::SHIFT;

fn as_usize(&self) -> usize;
fn from_usize(val: usize) -> Self;

#[inline(always)]
fn pack(&self, to: usize) -> usize {
let value = self.as_usize();
debug_assert!(value <= Self::BITS);

(to & !Self::MASK) | (value << Self::SHIFT)
}

#[inline(always)]
fn from_packed(from: usize) -> Self {
let value = (from & Self::MASK) >> Self::SHIFT;
debug_assert!(value <= Self::BITS);
Self::from_usize(value)
}
}

impl Pack for () {
const BITS: usize = 0;
const LEN: usize = 0;
const SHIFT: usize = 0;
const MASK: usize = 0;

type Prev = ();

fn as_usize(&self) -> usize {
unreachable!()
}
fn from_usize(_val: usize) -> Self {
unreachable!()
}

fn pack(&self, _to: usize) -> usize {
unreachable!()
}

fn from_packed(_from: usize) -> Self {
unreachable!()
}
}