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

Make the crate no_std + alloc #44

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
64671df
Started replacing `std` for `core` and `alloc`.
Jan 29, 2020
b15845b
Created an `std` feature for the crate, which is on by default.
Jan 29, 2020
73e3da2
Formatted the code with `cargo fmt`.
Jan 29, 2020
e4bda31
Made it so that the main functionality of the crate is not limited to…
Jan 29, 2020
93f6068
Fixed tests.
Jan 29, 2020
6cc5506
Formatted the code.
Jan 29, 2020
9da3663
Use `std::thread::yield_now()` when possible.
Jan 29, 2020
ae15433
Configured imports according to the use of the `std` feature.
Jan 29, 2020
a627cd9
Configured imports according to the use of the `std` feature.
Jan 29, 2020
75cc0d2
Merge branch 'master' of https://github.com/GarkGarcia/flurry
Jan 29, 2020
06f174b
Removed the `cfg_if` business.
Jan 29, 2020
a6c734f
Added comment about the use of `core::sync::atomic::spin_loop_hint()`.
Jan 29, 2020
2da8b06
Worked on fixing issues with `no_std`.
Jan 30, 2020
0928883
Fixed issues regarding the absence of `epoch::pin` in `no_std` enviro…
Jan 30, 2020
366c90c
Removed unnecessary lifetime parameters.
Jan 30, 2020
bb3e0e9
Updated `azure-pipelines.yml`.
Jan 30, 2020
5f94576
Minor changes.
Jan 30, 2020
3b0728e
Integrated `ahash::RandomState` as the dafault value for the `S` type…
Jan 30, 2020
c21ccd4
Added note about criptographic security in the `HashMap` docs.
Jan 30, 2020
57010a0
Fixed `missing_debug_implementations` issue.
Jan 30, 2020
64d2d94
Merge branch 'master' into gg/master
jonhoo Jan 30, 2020
69bb181
cargo fmt
jonhoo Jan 30, 2020
309d746
Add target to use it
jonhoo Jan 30, 2020
7b32a16
num_cpus is a std thing
jonhoo Jan 30, 2020
adb58e0
Make map generic over lock type
jonhoo Jan 30, 2020
f9ad723
Fix tests to match new generic-over-lock API
jonhoo Jan 30, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,28 @@ maintenance = { status = "experimental" }

[features]
sanitize = ['crossbeam-epoch/sanitize']
std = ["crossbeam-epoch/std", "num_cpus", "parking_lot"]
default = ["std"]

[dependencies]
crossbeam-epoch = "0.9"
parking_lot = "0.10"
num_cpus = "1.12.0"
lock_api = "0.3.3"

[dependencies.parking_lot]
version = "0.10"
optional = true

[dependencies.num_cpus]
version = "1.12.0"
optional = true

[dependencies.crossbeam-epoch]
version = "0.9"
default-features = false
features = ["alloc"]

[dependencies.ahash]
version = "0.3.2"
default-features = false

[dev-dependencies]
rand = "0.7"
Expand Down
11 changes: 11 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ jobs:
condition: ne(variables.CACHE_RESTORED, 'true')
- script: cargo deny check
displayName: cargo deny
- job: no_std
displayName: "Compile-check on no_std target"
pool:
vmImage: ubuntu-16.04
steps:
- template: install-rust.yml@templates
parameters:
targets:
- thumbv7m-none-eabi
- bash: cargo check --target thumbv7m-none-eabi --no-default-features
displayName: cargo check
GarkGarcia marked this conversation as resolved.
Show resolved Hide resolved
- job: miri
displayName: "Run miri on test suite"
dependsOn: deny
Expand Down
40 changes: 29 additions & 11 deletions src/iter/iter.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
use super::NodeIter;
use core::sync::atomic::Ordering;
use crossbeam_epoch::Guard;
use std::sync::atomic::Ordering;

/// An iterator over a map's entries.
///
/// See [`HashMap::iter`](crate::HashMap::iter) for details.
#[derive(Debug)]
pub struct Iter<'g, K, V> {
pub(crate) node_iter: NodeIter<'g, K, V>,
pub struct Iter<'g, K, V, L>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a default L for all of the iterator types too?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bah, then we'd have to duplicate all of these too. One other option here is to define a default value on no_std that panics when used. It ain't great at all, but it would allow us to not duplicate all the structs.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose that's why hashbrown uses an uninhabited default on no-std.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, hashbrown uses ahash on no_std. It only uses an uninhabited default when compiled as a dependency for libstd (because the libstd build system is weird and makes dependencies difficult).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, I misunderstood that context, and hashbrown doesn't need a lock like this. An uninhabited default might still work though, or we could use macro_rules! to hide the duplication.

where
L: lock_api::RawMutex,
{
pub(crate) node_iter: NodeIter<'g, K, V, L>,
pub(crate) guard: &'g Guard,
}

impl<'g, K, V> Iterator for Iter<'g, K, V> {
impl<'g, K, V, L> Iterator for Iter<'g, K, V, L>
where
L: lock_api::RawMutex,
{
type Item = (&'g K, &'g V);
fn next(&mut self) -> Option<Self::Item> {
let node = self.node_iter.next()?;
Expand All @@ -26,11 +32,17 @@ impl<'g, K, V> Iterator for Iter<'g, K, V> {
///
/// See [`HashMap::keys`](crate::HashMap::keys) for details.
#[derive(Debug)]
pub struct Keys<'g, K, V> {
pub(crate) node_iter: NodeIter<'g, K, V>,
pub struct Keys<'g, K, V, L>
where
L: lock_api::RawMutex,
{
pub(crate) node_iter: NodeIter<'g, K, V, L>,
}

impl<'g, K, V> Iterator for Keys<'g, K, V> {
impl<'g, K, V, L> Iterator for Keys<'g, K, V, L>
where
L: lock_api::RawMutex,
{
type Item = &'g K;
fn next(&mut self) -> Option<Self::Item> {
let node = self.node_iter.next()?;
Expand All @@ -42,12 +54,18 @@ impl<'g, K, V> Iterator for Keys<'g, K, V> {
///
/// See [`HashMap::values`](crate::HashMap::values) for details.
#[derive(Debug)]
pub struct Values<'g, K, V> {
pub(crate) node_iter: NodeIter<'g, K, V>,
pub struct Values<'g, K, V, L>
where
L: lock_api::RawMutex,
{
pub(crate) node_iter: NodeIter<'g, K, V, L>,
pub(crate) guard: &'g Guard,
}

impl<'g, K, V> Iterator for Values<'g, K, V> {
impl<'g, K, V, L> Iterator for Values<'g, K, V, L>
where
L: lock_api::RawMutex,
{
type Item = &'g V;
fn next(&mut self) -> Option<Self::Item> {
let node = self.node_iter.next()?;
Expand All @@ -61,9 +79,9 @@ impl<'g, K, V> Iterator for Values<'g, K, V> {
#[cfg(test)]
mod tests {
use crate::HashMap;
use core::iter::FromIterator;
use crossbeam_epoch as epoch;
use std::collections::HashSet;
use std::iter::FromIterator;

#[test]
fn iter() {
Expand Down
57 changes: 38 additions & 19 deletions src/iter/traverser.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
#[cfg(not(feature = "std"))]
extern crate alloc;

use crate::node::{BinEntry, Node};
use crate::raw::Table;
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
use core::sync::atomic::Ordering;
use crossbeam_epoch::{Guard, Shared};
use std::sync::atomic::Ordering;

#[derive(Debug)]
pub(crate) struct NodeIter<'g, K, V> {
pub(crate) struct NodeIter<'g, K, V, L>
where
L: lock_api::RawMutex,
{
/// Current table; update if resized
table: Option<&'g Table<K, V>>,
table: Option<&'g Table<K, V, L>>,

stack: Option<Box<TableStack<'g, K, V>>>,
spare: Option<Box<TableStack<'g, K, V>>>,
stack: Option<Box<TableStack<'g, K, V, L>>>,
spare: Option<Box<TableStack<'g, K, V, L>>>,

/// The last bin entry iterated over
prev: Option<&'g Node<K, V>>,
prev: Option<&'g Node<K, V, L>>,

/// Index of bin to use next
index: usize,
Expand All @@ -29,8 +37,11 @@ pub(crate) struct NodeIter<'g, K, V> {
guard: &'g Guard,
}

impl<'g, K, V> NodeIter<'g, K, V> {
pub(crate) fn new(table: Shared<'g, Table<K, V>>, guard: &'g Guard) -> Self {
impl<'g, K, V, L> NodeIter<'g, K, V, L>
where
L: lock_api::RawMutex,
{
pub(crate) fn new(table: Shared<'g, Table<K, V, L>>, guard: &'g Guard) -> Self {
let (table, len) = if table.is_null() {
(None, 0)
} else {
Expand All @@ -53,7 +64,7 @@ impl<'g, K, V> NodeIter<'g, K, V> {
}
}

fn push_state(&mut self, t: &'g Table<K, V>, i: usize, n: usize) {
fn push_state(&mut self, t: &'g Table<K, V, L>, i: usize, n: usize) {
let mut s = self.spare.take();
if let Some(ref mut s) = s {
self.spare = s.next.take();
Expand Down Expand Up @@ -109,8 +120,11 @@ impl<'g, K, V> NodeIter<'g, K, V> {
}
}

impl<'g, K, V> Iterator for NodeIter<'g, K, V> {
type Item = &'g Node<K, V>;
impl<'g, K, V, L> Iterator for NodeIter<'g, K, V, L>
where
L: lock_api::RawMutex,
{
type Item = &'g Node<K, V, L>;
fn next(&mut self) -> Option<Self::Item> {
let mut e = None;
if let Some(prev) = self.prev {
Expand Down Expand Up @@ -177,30 +191,35 @@ impl<'g, K, V> Iterator for NodeIter<'g, K, V> {
}

#[derive(Debug)]
struct TableStack<'g, K, V> {
struct TableStack<'g, K, V, L>
where
L: lock_api::RawMutex,
{
length: usize,
index: usize,
table: &'g Table<K, V>,
next: Option<Box<TableStack<'g, K, V>>>,
table: &'g Table<K, V, L>,
next: Option<Box<TableStack<'g, K, V, L>>>,
}

#[cfg(test)]
mod tests {
use super::*;
use crate::raw::Table;
use crossbeam_epoch::{self as epoch, Atomic, Owned};
use parking_lot::Mutex;
use lock_api::Mutex;

type L = parking_lot::RawMutex;

#[test]
fn iter_new() {
let guard = epoch::pin();
let iter = NodeIter::<usize, usize>::new(Shared::null(), &guard);
let iter = NodeIter::<usize, usize, L>::new(Shared::null(), &guard);
assert_eq!(iter.count(), 0);
}

#[test]
fn iter_empty() {
let table = Owned::new(Table::<usize, usize>::new(16));
let table = Owned::new(Table::<usize, usize, L>::new(16));
let guard = epoch::pin();
let table = table.into_shared(&guard);
let iter = NodeIter::new(table, &guard);
Expand All @@ -219,7 +238,7 @@ mod tests {
key: 0usize,
value: Atomic::new(0usize),
next: Atomic::null(),
lock: Mutex::new(()),
lock: Mutex::<L, _>::new(()),
}));

let table = Owned::new(Table::from(bins));
Expand Down Expand Up @@ -254,7 +273,7 @@ mod tests {
for bin in &mut bins[8..] {
*bin = Atomic::new(BinEntry::Moved(&*deep_table as *const _));
}
let table = Owned::new(Table::<usize, usize>::from(bins));
let table = Owned::new(Table::<usize, usize, L>::from(bins));
let guard = epoch::pin();
let table = table.into_shared(&guard);
{
Expand Down
16 changes: 10 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,14 @@
//! more efficient operation than if everything had to be atomically reference-counted.
//!
//! [`crossbeam::epoch`]: https://docs.rs/crossbeam/0.7/crossbeam/epoch/index.html
#![deny(
missing_docs,
missing_debug_implementations,
intra_doc_link_resolution_failure
)]
#![deny(missing_docs, intra_doc_link_resolution_failure)]
#![warn(rust_2018_idioms)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "std", deny(missing_debug_implementations))]

#[cfg(not(feature = "std"))]
#[macro_use]
extern crate alloc;

mod map;
mod node;
Expand All @@ -211,5 +213,7 @@ pub use map::HashMap;

/// Types needed to safely access shared data concurrently.
pub mod epoch {
pub use crossbeam_epoch::{pin, Guard};
#[cfg(feature = "std")]
pub use crossbeam_epoch::pin;
pub use crossbeam_epoch::Guard;
}
Loading