use crate::host::*;
use crate::host::scene_core::*;
use futures::prelude::*;
use futures::future;
use futures::stream;
use futures::channel::mpsc;
use std::collections::{HashMap};
use serde::*;
pub static IDLE_NOTIFICATION_PROGRAM: StaticSubProgramId = StaticSubProgramId::called("flo_scene::idle_request");
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Serialize, Deserialize)]
pub enum IdleRequest {
WhenIdle(SubProgramId),
SuppressNotifications,
ResumeNotifications,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Serialize, Deserialize)]
pub struct IdleNotification;
impl SceneMessage for IdleRequest {
fn default_target() -> StreamTarget { (*IDLE_NOTIFICATION_PROGRAM).into() }
fn allow_thread_stealing_by_default() -> bool { true }
#[inline]
fn message_type_name() -> String { "flo_scene::IdleRequest".into() }
}
impl SceneMessage for IdleNotification {
fn default_target() -> StreamTarget { StreamTarget::None }
#[inline]
fn message_type_name() -> String { "flo_scene::IdleNotification".into() }
}
enum IdleProgramMsg {
Request(SubProgramId, IdleRequest),
CoreIsIdle(usize)
}
pub (crate) async fn idle_subprogram(input_stream: InputStream<IdleRequest>, context: SceneContext) {
let input_stream = input_stream.messages_with_sources();
let mut suppressions = HashMap::new();
let mut pending_notifications = vec![];
let weak_core = context.scene_core();
let (send_idle, recv_idle) = mpsc::channel(1);
if let Some(core) = weak_core.upgrade() {
SceneCore::send_idle_notifications_to(&core, send_idle);
}
let mut input_stream = stream::select(input_stream.map(|(subprogram_id, msg)| IdleProgramMsg::Request(subprogram_id, msg)), recv_idle.map(|count| IdleProgramMsg::CoreIsIdle(count)));
while let Some(request) = input_stream.next().await {
use IdleProgramMsg::*;
use IdleRequest::*;
match request {
Request(_, WhenIdle(send_message_to)) => {
if let Some(core) = weak_core.upgrade() {
let idle_count = SceneCore::notify_on_next_idle(&core);
pending_notifications.push((idle_count, send_message_to));
}
},
Request(sender_id, SuppressNotifications) => {
(*suppressions.entry(sender_id).or_insert(0usize)) += 1;
}
Request(sender_id, ResumeNotifications) => {
if let Some(count) = suppressions.get_mut(&sender_id) {
*count -= 1;
if *count == 0 {
suppressions.remove(&sender_id);
if let Some(core) = weak_core.upgrade() {
SceneCore::notify_on_next_idle(&core);
}
}
}
},
CoreIsIdle(idle_count) => {
if suppressions.is_empty() {
let mut ready_notifications = vec![];
let mut new_pending_notifications = vec![];
for (expected_count, program_id) in pending_notifications.into_iter() {
if expected_count < idle_count {
ready_notifications.push(program_id);
} else {
new_pending_notifications.push((expected_count, program_id));
}
}
pending_notifications = new_pending_notifications;
if !pending_notifications.is_empty() {
if let Some(core) = weak_core.upgrade() {
SceneCore::notify_on_next_idle(&core);
}
}
future::join_all(ready_notifications.into_iter()
.flat_map(|program_id| context.send(program_id).ok())
.map(|mut stream| async move { stream.send(IdleNotification).await.ok(); }))
.await;
}
}
}
}
}