Skip to content

Commit

Permalink
Merge #495
Browse files Browse the repository at this point in the history
495: Make it possible to disable parallel feature r=torkleyy a=omni-viral

This makes it possible to user specs in wasm environment

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/slide-rs/specs/495)
<!-- Reviewable:end -->


Co-authored-by: Zakarum <scareaangel@gmail.com>
  • Loading branch information
bors[bot] and Zakarum committed Oct 19, 2018
2 parents faa384b + 84cf2a5 commit 0c2f48e
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 126 deletions.
10 changes: 6 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "specs"
version = "0.12.3"
version = "0.12.4"
description = """
Specs is an Entity-Component System library written in Rust.
"""
Expand All @@ -24,14 +24,14 @@ travis-ci = { repository = "slide-rs/specs" }
crossbeam = "0.4.1"
derivative = "1"
fnv = "1.0"
hibitset = { version = "0.5.2", features = ["parallel"] }
hibitset = { version = "0.5.2", default-features = false }
log = "0.4"
mopa = "0.2"
shred = "0.7.0"
shred = { version = "0.7.0", default-features = false }
shrev = "1.0.0"
shred-derive = "0.5.0"
tuple_utils = "0.2"
rayon = "1.0.0"
rayon = { version = "1.0.0", optional = true }
nonzero_signed = "1.0.1"

futures = { version = "0.1", optional = true }
Expand All @@ -41,6 +41,8 @@ serde = { version = "1.0", optional = true, features = ["serde_derive"] }
rudy = { version = "0.1", optional = true }

[features]
default = ["parallel"]
parallel = ["rayon", "shred/parallel", "hibitset/parallel"]
common = ["futures"]
nightly = ["shred/nightly"]

Expand Down
5 changes: 4 additions & 1 deletion src/bitset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

use hibitset::{AtomicBitSet, BitSet, BitSetAnd, BitSetLike, BitSetNot, BitSetOr, BitSetXor};

use join::{Join, ParJoin};
use join::Join;
#[cfg(feature = "parallel")]
use join::ParJoin;
use world::Index;

macro_rules! define_bit_join {
Expand All @@ -25,6 +27,7 @@ macro_rules! define_bit_join {
}
}

#[cfg(feature = "parallel")]
unsafe impl<$( $lifetime, )* $( $arg ),*> ParJoin for $bitset
where $( $arg: BitSetLike ),*
{ }
Expand Down
123 changes: 10 additions & 113 deletions src/join.rs → src/join/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
//! Joining of components for iteration over entities with specific components.
use std;
use std::cell::UnsafeCell;

use hibitset::{BitIter, BitProducer, BitSetAll, BitSetAnd, BitSetLike};
use rayon::iter::plumbing::{bridge_unindexed, Folder, UnindexedConsumer, UnindexedProducer};
use rayon::iter::ParallelIterator;
use hibitset::{BitIter, BitSetAll, BitSetAnd, BitSetLike};
use std::ops::{Deref, DerefMut};
use tuple_utils::Split;
use shred::{Fetch, FetchMut, Read, ReadExpect, Resource, Write, WriteExpect};

use world::{Entities, Entity, Index};

#[cfg(feature = "parallel")]
mod par_join;

#[cfg(feature = "parallel")]
pub use self::par_join::{ParJoin, JoinParIter};

/// `BitAnd` is a helper method to & bitsets together resulting in a tree.
pub trait BitAnd {
/// The combined bitsets.
Expand Down Expand Up @@ -238,23 +241,6 @@ pub trait Join {
}
}

/// The purpose of the `ParJoin` trait is to provide a way
/// to access multiple storages in parallel at the same time with
/// the merged bit set.
pub unsafe trait ParJoin: Join {
/// Create a joined parallel iterator over the contents.
fn par_join(self) -> JoinParIter<Self>
where
Self: Sized,
{
if <Self as Join>::is_unconstrained() {
println!("WARNING: `ParJoin` possibly iterating through all indices, you might've made a join with all `MaybeJoin`s, which is unbounded in length.");
}

JoinParIter(self)
}
}

/// A `Join`-able structure that yields all indices, returning `None` for all
/// missing elements and `Some(T)` for found elements.
///
Expand Down Expand Up @@ -398,98 +384,6 @@ impl<J: Join> std::iter::Iterator for JoinIter<J> {
}
}

/// `JoinParIter` is a `ParallelIterator` over a group of `Storages`.
#[must_use]
pub struct JoinParIter<J>(J);

impl<J> ParallelIterator for JoinParIter<J>
where
J: Join + Send,
J::Mask: Send + Sync,
J::Type: Send,
J::Value: Send,
{
type Item = J::Type;

fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let (keys, values) = unsafe { self.0.open() };
// Create a bit producer which splits on up to three levels
let producer = BitProducer((&keys).iter(), 3);
let values = UnsafeCell::new(values);

bridge_unindexed(JoinProducer::<J>::new(producer, &values), consumer)
}
}

struct JoinProducer<'a, J>
where
J: Join + Send,
J::Mask: Send + Sync + 'a,
J::Type: Send,
J::Value: Send + 'a,
{
keys: BitProducer<'a, J::Mask>,
values: &'a UnsafeCell<J::Value>,
}

impl<'a, J> JoinProducer<'a, J>
where
J: Join + Send,
J::Type: Send,
J::Value: 'a + Send,
J::Mask: 'a + Send + Sync,
{
fn new(keys: BitProducer<'a, J::Mask>, values: &'a UnsafeCell<J::Value>) -> Self {
JoinProducer { keys, values }
}
}

unsafe impl<'a, J> Send for JoinProducer<'a, J>
where
J: Join + Send,
J::Type: Send,
J::Value: 'a + Send,
J::Mask: 'a + Send + Sync,
{}

impl<'a, J> UnindexedProducer for JoinProducer<'a, J>
where
J: Join + Send,
J::Type: Send,
J::Value: 'a + Send,
J::Mask: 'a + Send + Sync,
{
type Item = J::Type;
fn split(self) -> (Self, Option<Self>) {
let (cur, other) = self.keys.split();
let values = self.values;
let first = JoinProducer::new(cur, values);
let second = other.map(|o| JoinProducer::new(o, values));

(first, second)
}

fn fold_with<F>(self, folder: F) -> F
where
F: Folder<Self::Item>,
{
let JoinProducer { values, keys, .. } = self;
let iter = keys.0.map(|idx| unsafe {
// This unsafe block should be safe if the `J::get`
// can be safely called from different threads with distinct indices.

// The indices here are guaranteed to be distinct because of the fact
// that the bit set is split.
J::get(&mut *values.get(), idx)
});

folder.consume_iter(iter)
}
}

macro_rules! define_open {
// use variables to indicate the arity of the tuple
($($from:ident),*) => {
Expand Down Expand Up @@ -523,6 +417,7 @@ macro_rules! define_open {
unconstrained
}
}
#[cfg(feature = "parallel")]
unsafe impl<$($from,)*> ParJoin for ($($from),*,)
where $($from: ParJoin),*,
($(<$from as Join>::Mask,)*): BitAnd,
Expand Down Expand Up @@ -582,6 +477,7 @@ macro_rules! immutable_resource_join {
}
}

#[cfg(feature = "parallel")]
unsafe impl<'a, 'b, T> ParJoin for &'a $ty
where
&'a T: ParJoin,
Expand Down Expand Up @@ -616,6 +512,7 @@ macro_rules! mutable_resource_join {
}
}

#[cfg(feature = "parallel")]
unsafe impl<'a, 'b, T> ParJoin for &'a mut $ty
where
&'a mut T: ParJoin,
Expand Down
118 changes: 118 additions & 0 deletions src/join/par_join.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

use std::cell::UnsafeCell;

use hibitset::{BitProducer, BitSetLike};

use rayon::iter::plumbing::{bridge_unindexed, Folder, UnindexedConsumer, UnindexedProducer};
use rayon::iter::ParallelIterator;
use join::Join;


/// The purpose of the `ParJoin` trait is to provide a way
/// to access multiple storages in parallel at the same time with
/// the merged bit set.
pub unsafe trait ParJoin: Join {
/// Create a joined parallel iterator over the contents.
fn par_join(self) -> JoinParIter<Self>
where
Self: Sized,
{
if <Self as Join>::is_unconstrained() {
println!("WARNING: `ParJoin` possibly iterating through all indices, you might've made a join with all `MaybeJoin`s, which is unbounded in length.");
}

JoinParIter(self)
}
}

/// `JoinParIter` is a `ParallelIterator` over a group of `Storages`.
#[must_use]
pub struct JoinParIter<J>(J);

impl<J> ParallelIterator for JoinParIter<J>
where
J: Join + Send,
J::Mask: Send + Sync,
J::Type: Send,
J::Value: Send,
{
type Item = J::Type;

fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let (keys, values) = unsafe { self.0.open() };
// Create a bit producer which splits on up to three levels
let producer = BitProducer((&keys).iter(), 3);
let values = UnsafeCell::new(values);

bridge_unindexed(JoinProducer::<J>::new(producer, &values), consumer)
}
}

struct JoinProducer<'a, J>
where
J: Join + Send,
J::Mask: Send + Sync + 'a,
J::Type: Send,
J::Value: Send + 'a,
{
keys: BitProducer<'a, J::Mask>,
values: &'a UnsafeCell<J::Value>,
}

impl<'a, J> JoinProducer<'a, J>
where
J: Join + Send,
J::Type: Send,
J::Value: 'a + Send,
J::Mask: 'a + Send + Sync,
{
fn new(keys: BitProducer<'a, J::Mask>, values: &'a UnsafeCell<J::Value>) -> Self {
JoinProducer { keys, values }
}
}

unsafe impl<'a, J> Send for JoinProducer<'a, J>
where
J: Join + Send,
J::Type: Send,
J::Value: 'a + Send,
J::Mask: 'a + Send + Sync,
{}

impl<'a, J> UnindexedProducer for JoinProducer<'a, J>
where
J: Join + Send,
J::Type: Send,
J::Value: 'a + Send,
J::Mask: 'a + Send + Sync,
{
type Item = J::Type;
fn split(self) -> (Self, Option<Self>) {
let (cur, other) = self.keys.split();
let values = self.values;
let first = JoinProducer::new(cur, values);
let second = other.map(|o| JoinProducer::new(o, values));

(first, second)
}

fn fold_with<F>(self, folder: F) -> F
where
F: Folder<Self::Item>,
{
let JoinProducer { values, keys, .. } = self;
let iter = keys.0.map(|idx| unsafe {
// This unsafe block should be safe if the `J::get`
// can be safely called from different threads with distinct indices.

// The indices here are guaranteed to be distinct because of the fact
// that the bit set is split.
J::get(&mut *values.get(), idx)
});

folder.consume_iter(iter)
}
}
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ extern crate hibitset;
extern crate log;
extern crate nonzero_signed;
extern crate mopa;
#[cfg(feature = "parallel")]
extern crate rayon;
extern crate shrev;
extern crate tuple_utils;
Expand Down Expand Up @@ -222,12 +223,14 @@ pub mod storage;
pub mod world;

pub use hibitset::BitSet;
pub use join::{Join, ParJoin};
pub use join::Join;
#[cfg(feature = "parallel")]
pub use join::ParJoin;
pub use shred::{Accessor, Dispatcher, DispatcherBuilder, Read, ReadExpect, Resources, RunNow,
StaticAccessor, System, SystemData, Write, WriteExpect};
pub use shrev::ReaderId;

#[cfg(not(target_os = "emscripten"))]
#[cfg(feature = "parallel")]
pub use shred::AsyncDispatcher;

pub use changeset::ChangeSet;
Expand Down
8 changes: 5 additions & 3 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
//! Contains all of the most common traits, structures,
pub use hibitset::BitSet;
pub use join::{Join, ParJoin};
pub use join::Join;
#[cfg(feature = "parallel")]
pub use join::ParJoin;
pub use shred::{Accessor, Dispatcher, DispatcherBuilder, Read, ReadExpect, Resources, RunNow,
StaticAccessor, System, SystemData, Write, WriteExpect};
pub use shrev::ReaderId;

#[cfg(not(target_os = "emscripten"))]
#[cfg(feature = "parallel")]
pub use rayon::iter::ParallelIterator;
#[cfg(not(target_os = "emscripten"))]
#[cfg(feature = "parallel")]
pub use shred::AsyncDispatcher;

pub use changeset::ChangeSet;
Expand Down
Loading

0 comments on commit 0c2f48e

Please sign in to comment.