use std::{ io, pin::Pin, sync::{ atomic::{AtomicBool, Ordering}, mpsc::{self, SyncSender}, Arc, }, task::{Context, Poll}, thread, time::Duration, }; use futures_core::stream::Stream; use crate::event::{ filter::EventFilter, lock_internal_event_reader, poll_internal, read_internal, sys::Waker, Event, InternalEvent, }; /// A stream of `Result`. /// /// **This type is not available by default. You have to use the `event-stream` feature flag /// to make it available.** /// /// It implements the [Stream](futures_core::stream::Stream) /// trait and allows you to receive [`Event`]s with [`async-std`](https://crates.io/crates/async-std) /// or [`tokio`](https://crates.io/crates/tokio) crates. /// /// Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder to see how to use /// it (`event-stream-*`). #[derive(Debug)] pub struct EventStream { poll_internal_waker: Waker, stream_wake_task_executed: Arc, stream_wake_task_should_shutdown: Arc, task_sender: SyncSender, } impl Default for EventStream { fn default() -> Self { let (task_sender, receiver) = mpsc::sync_channel::(1); thread::spawn(move || { while let Ok(task) = receiver.recv() { loop { if let Ok(true) = poll_internal(None, &EventFilter) { break; } if task.stream_wake_task_should_shutdown.load(Ordering::SeqCst) { break; } } task.stream_wake_task_executed .store(false, Ordering::SeqCst); task.stream_waker.wake(); } }); EventStream { poll_internal_waker: lock_internal_event_reader().waker(), stream_wake_task_executed: Arc::new(AtomicBool::new(false)), stream_wake_task_should_shutdown: Arc::new(AtomicBool::new(false)), task_sender, } } } impl EventStream { /// Constructs a new instance of `EventStream`. pub fn new() -> EventStream { EventStream::default() } } struct Task { stream_waker: std::task::Waker, stream_wake_task_executed: Arc, stream_wake_task_should_shutdown: Arc, } // Note to future me // // We need two wakers in order to implement EventStream correctly. // // 1. futures::Stream waker // // Stream::poll_next can return Poll::Pending which means that there's no // event available. We are going to spawn a thread with the // poll_internal(None, &EventFilter) call. This call blocks until an // event is available and then we have to wake up the executor with notification // that the task can be resumed. // // 2. poll_internal waker // // There's no event available, Poll::Pending was returned, stream waker thread // is up and sitting in the poll_internal. User wants to drop the EventStream. // We have to wake up the poll_internal (force it to return Ok(false)) and quit // the thread before we drop. impl Stream for EventStream { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let result = match poll_internal(Some(Duration::from_secs(0)), &EventFilter) { Ok(true) => match read_internal(&EventFilter) { Ok(InternalEvent::Event(event)) => Poll::Ready(Some(Ok(event))), Err(e) => Poll::Ready(Some(Err(e))), #[cfg(unix)] _ => unreachable!(), }, Ok(false) => { if !self .stream_wake_task_executed .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) // https://github.com/rust-lang/rust/issues/80486#issuecomment-752244166 .unwrap_or_else(|x| x) { let stream_waker = cx.waker().clone(); let stream_wake_task_executed = self.stream_wake_task_executed.clone(); let stream_wake_task_should_shutdown = self.stream_wake_task_should_shutdown.clone(); stream_wake_task_should_shutdown.store(false, Ordering::SeqCst); let _ = self.task_sender.send(Task { stream_waker, stream_wake_task_executed, stream_wake_task_should_shutdown, }); } Poll::Pending } Err(e) => Poll::Ready(Some(Err(e))), }; result } } impl Drop for EventStream { fn drop(&mut self) { self.stream_wake_task_should_shutdown .store(true, Ordering::SeqCst); let _ = self.poll_internal_waker.wake(); } }