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

Add a PlannerBuilder #104

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ exclude = ["doc", ".travis.yml"]
mopa = "0.2"
pulse = { version = "0.5", optional = true }
threadpool = { version = "1.3", optional = true }
num_cpus = { version = "1.0", optional = true }
fnv = "1.0"
tuple_utils="0.2"
atom = "0.3"

[features]
default = ["parallel"]
parallel = ["threadpool", "pulse"]
parallel = ["threadpool", "pulse", "num_cpus"]
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ extern crate mopa;
extern crate pulse;
#[cfg(feature="parallel")]
extern crate threadpool;
#[cfg(feature="parallel")]
extern crate num_cpus;
extern crate fnv;
extern crate tuple_utils;
extern crate atom;
Expand Down
125 changes: 115 additions & 10 deletions src/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::sync::{mpsc, Arc};

use pulse::{Pulse, Signal};
use threadpool::ThreadPool;
use num_cpus::get as get_num_cpus;

use super::{Component, JoinIter, World, Entity};

Expand Down Expand Up @@ -92,6 +93,113 @@ impl<C> Drop for SystemGuard<C> {
}
}

/// A builder for the `Planner` struct.
/// All `with_*` methods return `self` to allow chained calls.
///
/// ## World
///
/// You have to specify a world, otherwise the
/// `build` method will panic.
///
/// ## Number of threads
///
/// Specifying the number of threads to use
/// for the thread pool is optional.
///
/// **Note:** Only specify this if you do not
/// want to share the thread pool, because then
/// the number of threads the pool is configured with
/// will determince this property.
///
/// # The thread pool
///
/// Also optional, use this if you want to
/// share the thread pool with other parts of
/// your crate (you should do this if you use
/// threads somwhere else).
///
/// If you didn't specify a thread pool nor a number of
/// threads, a new thread pool with the number of virtual
/// cpus will be created.
pub struct PlannerBuilder {
world: Option<World>,
Copy link
Member

Choose a reason for hiding this comment

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

what is the point of having an Option here in the first place?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I thought about using with_thread_pool, but I thought it's not descriptive enough because you still have another parameter. Yes, you are right I could have asked for world already in the builder constructor, but I think it is nicer to use. Of course it's not compile-time enforced then.

Copy link
Member Author

Choose a reason for hiding this comment

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

Plus, now the pool size defaults to the number of cpus, which should be pretty common. I do have another solution in mind, though:

enum PoolOptions {
    Pool(Arc<ThreadPool>),
    NumThreads(usize),
    NumCpus,
}

impl Planner {
    pub fn from_options(options: PoolOptions) -> Self {
        
    }
}

num_threads: Option<usize>,
thread_pool: Option<Arc<ThreadPool>>,
}

impl PlannerBuilder {
/// Creates a new `PlannerBuilder`.
pub fn new() -> Self {
PlannerBuilder {
world: None,
num_threads: None,
thread_pool: None,
}
}

/// Use this to specify the world for the planner.
///
/// Note that this is required for the creation of a `Planner`.
pub fn with_world(mut self, world: World) -> Self {
self.world = Some(world);

self
}

/// This is an optional property and should only be used
/// if you want to create a thread pool for this planner.
///
/// Also see the documentation of the `PlannerBuilder` struct.
///
/// # Panics
///
/// * Panics if you already used `with_thread_pool`.
pub fn with_num_threads(mut self, num_threads: usize) -> Self {
assert!(self.thread_pool.is_none());

self.num_threads = Some(num_threads);

self
}

/// Share a thread pool with this planner. It is recommended to
/// use this over `with_num_threads` (if you use threads anywhere else).
///
/// # Panics
///
/// * Panics if you already used `with_num_threads`.
pub fn with_thread_pool(mut self, thread_pool: Arc<ThreadPool>) -> Self {
assert!(self.num_threads.is_none());

self.thread_pool = Some(thread_pool);

self
}

/// Builds a planner from the specified properties.
///
/// # Panics
///
/// * Panics if no world was specified
pub fn build<C>(self) -> Planner<C> {
let PlannerBuilder { world, num_threads, thread_pool } = self;
let (sout, sin) = mpsc::channel();

let threader = thread_pool.unwrap_or_else(|| {
Arc::new(ThreadPool::new(num_threads.unwrap_or(get_num_cpus())))
});

Planner {
world: Arc::new(world.expect("A world is required for planner creation")),
systems: Vec::new(),
wait_count: 0,
chan_out: sout,
chan_in: sin,
threader: threader,
}
}
}

/// System execution planner. Allows running systems via closures,
/// distributes the load in parallel using a thread pool.
pub struct Planner<C> {
Expand All @@ -102,21 +210,18 @@ pub struct Planner<C> {
wait_count: usize,
chan_out: mpsc::Sender<SystemInfo<C>>,
chan_in: mpsc::Receiver<SystemInfo<C>>,
threader: ThreadPool,
threader: Arc<ThreadPool>,
}

impl<C: 'static> Planner<C> {
/// Creates a new planner, given the world and the thread count.
///
/// For a more flexible creation, see the `PlannerBuilder`.
pub fn new(world: World, num_threads: usize) -> Planner<C> {
let (sout, sin) = mpsc::channel();
Planner {
world: Arc::new(world),
systems: Vec::new(),
wait_count: 0,
chan_out: sout,
chan_in: sin,
threader: ThreadPool::new(num_threads),
}
PlannerBuilder::new()
.with_world(world)
.with_num_threads(num_threads)
.build()
}
/// Add a system to the dispatched list.
pub fn add_system<S>(&mut self, sys: S, name: &str, priority: Priority) where
Expand Down