use core::any::Any;
use std::sync::mpsc::{self, SendError};
use std::sync::mpsc::{Receiver, Sender};
use std::thread::spawn;
use std::{
marker::PhantomData,
sync::{atomic::AtomicBool, Arc, Weak},
thread::JoinHandle,
time::Duration,
};
use crate::book::*;
use crate::env::*;
use crate::ream::*;
use crate::*;
struct ParallelCreate<S, I> {
input: I,
marker: PhantomData<fn() -> S>,
}
trait CreateItem<E>
where
E: Environment,
Self: Send,
{
fn create(self, mill: &mut MillOf<E>) -> ParallelItem;
fn create_from_box(self: Box<Self>, mill: &mut MillOf<E>) -> ParallelItem;
fn into_input(self: Box<Self>) -> Box<dyn Any>;
}
impl<S, I> CreateItem<S::Env> for ParallelCreate<S, I>
where
S: Sketch + Setup<I> + 'static,
I: Send + 'static,
MillOf<S::Env>: Clone + Send,
{
fn create(self, mill: &mut MillOf<S::Env>) -> ParallelItem {
let input = self.input;
let mill = mill.clone();
let full_alive_arc = Arc::new(AtomicBool::new(true));
let alive_arc = Arc::downgrade(&full_alive_arc);
let handle = spawn(move || parallel_run::<S, I>(mill, input, full_alive_arc));
ParallelItem { alive_arc, handle }
}
fn create_from_box(self: Box<Self>, mill: &mut MillOf<S::Env>) -> ParallelItem {
self.create(mill)
}
fn into_input(self: Box<Self>) -> Box<dyn Any> {
Box::new(self.input)
}
}
struct ParallelItem {
alive_arc: Weak<AtomicBool>,
handle: JoinHandle<()>,
}
#[cfg_attr(docsrs, doc(cfg(feature = "ream-parallel")))]
pub struct Parallel<E> {
to_create_send: Sender<Box<dyn CreateItem<E>>>,
to_create_recv: Receiver<Box<dyn CreateItem<E>>>,
to_creates: Vec<Box<dyn CreateItem<E>>>,
items: Vec<ParallelItem>,
finished: Vec<usize>,
}
impl<E> core::fmt::Debug for Parallel<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Parallel").finish()
}
}
impl<E> Default for Parallel<E>
where
E: Environment,
{
fn default() -> Self {
let (to_create_send, to_create_recv) = mpsc::channel();
Self {
to_create_send,
to_create_recv,
items: Vec::new(),
to_creates: Vec::new(),
finished: Vec::new(),
}
}
}
impl<E> Ream for Parallel<E>
where
E: Environment,
{
type Env = E;
type Error = Box<dyn Any + Send + 'static>;
fn update(&mut self, mill: &mut MillOf<E>) -> Result<Status, Self::Error> {
if !self.to_creates.is_empty() {
for to_create in self.to_creates.drain(..) {
self.items.push(to_create.create_from_box(mill));
}
}
use std::sync::mpsc::TryRecvError::*;
loop {
match self.to_create_recv.try_recv() {
Ok(to_create) => {
self.items.push(to_create.create_from_box(mill));
}
Err(Empty) => {
break;
}
Err(Disconnected) => {
panic!("something happend to channel")
}
}
}
for (i, item) in self.items.iter().enumerate() {
if item.alive_arc.upgrade().is_none() {
self.finished.push(i);
}
}
for i in self.finished.drain(..) {
let item = self.items.swap_remove(i);
item.handle.join()?;
}
if self.items.is_empty() {
Ok(Status::Stop)
} else {
Ok(Status::Continue)
}
}
}
impl<E> Parallel<E>
where
E: Environment,
{
pub fn create_proxy(&self) -> ParallelProxy<E> {
ParallelProxy {
to_create_send: self.to_create_send.clone(),
}
}
}
pub struct ParallelProxy<E> {
to_create_send: Sender<Box<dyn CreateItem<E>>>,
}
impl<E> core::fmt::Debug for ParallelProxy<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ParallelProxy").finish()
}
}
impl<E> Clone for ParallelProxy<E> {
fn clone(&self) -> Self {
Self {
to_create_send: self.to_create_send.clone(),
}
}
}
impl<E> ParallelProxy<E>
where
E: Environment,
MillOf<E>: Clone + Send,
{
pub fn add_page<S>(&self) -> bool
where
S: Sketch<Env = E> + Setup<()> + 'static,
{
self.add_page_with::<S, ()>(()).is_none()
}
pub fn add_page_with<S, I>(&self, input: I) -> Option<I>
where
S: Sketch<Env = E> + Setup<I> + 'static,
I: Send + 'static,
{
match self.to_create_send
.send(Box::new(ParallelCreate::<S, I> {
marker: PhantomData,
input,
})) {
Ok(_) => None,
Err(SendError(create_item)) => {
match create_item.into_input().downcast::<I>() {
Ok(boxed) => Some(*boxed),
Err(_) => unreachable!("Unable to downcast input back into it's original type. This should not be possible."),
}
}
}
}
}
pub trait ParallelPagesExt<E>
where
E: Environment,
{
fn ream_proxy(&self) -> ParallelProxy<E>;
fn add_page<S>(&mut self)
where
S: Sketch<Env = E> + Setup<()> + 'static;
fn add_page_with<S, I>(&mut self, input: I)
where
S: Sketch<Env = E> + Setup<I> + 'static,
I: Send + 'static;
}
fn parallel_run<S, I>(mut mill: MillOf<S::Env>, input: I, alive_arc: Arc<AtomicBool>)
where
S: Setup<I>,
MillOf<S::Env>: Mill,
{
let mut sketch = S::setup(mill.new_page(), input);
let mut group_count: usize = 0;
while let Status::Continue = sketch.get_page_mut().status() {
group_count += 1;
sketch.get_page_mut().start_group();
while let Some(event) = sketch.get_page_mut().next_event_in_group() {
sketch.get_page_mut().before_event(&event);
sketch.handle_environment_event(&event);
sketch.get_page_mut().finish_event(event);
}
sketch.get_page_mut().end_group();
if group_count > 100 {
std::thread::sleep(Duration::from_millis(1));
} else {
std::thread::yield_now();
}
}
drop(alive_arc);
}
impl<E> ParallelPagesExt<E> for Pages<Parallel<E>>
where
E: Environment,
MillOf<E>: Clone + Send,
{
fn ream_proxy(&self) -> ParallelProxy<E> {
self.ream.create_proxy()
}
fn add_page<S>(&mut self)
where
S: Sketch<Env = E> + Setup<()> + 'static,
{
self.add_page_with::<S, ()>(())
}
fn add_page_with<S, I>(&mut self, input: I)
where
S: Sketch<Env = E> + Setup<I> + 'static,
I: Send + 'static,
{
self.ream.to_creates.push(Box::new(ParallelCreate::<S, I> {
marker: PhantomData,
input,
}));
}
}