1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Main task executing functionality
//!
//! The main ndless executor and reactor. Calling [`block_on`] will wait for the
//! future to complete, then return its result. You'll often want to combine
//! this with [`join`] or [`first`] to run multiple things at once.
//!
//! # Example
//! ```
//! use ndless_async::task::{AsyncListeners, block_on};
//! use ndless_async::StreamExt;
//! use ndless::input::Key;
//! use ndless::prelude::*;
//!
//! let listeners = AsyncListeners::new();
//! block_on(&listeners, listen(&listeners));
//!
//! async fn listen(listeners: &AsyncListeners) {
//!     let mut keypad = listeners.keypad();
//!     while let Some(event) = keypad.next().await {
//!         println!("{:?}", key);
//!         if event.key == Key::Esc {
//!             break;
//!         }
//!     }
//! }
//! ```

use alloc::{sync::Arc, task::Wake};
use core::future::Future;
use core::sync::atomic::{AtomicBool, Ordering};
use core::task::{Context, Poll, Waker};

use futures_util::pin_mut;
use ndless::hw::idle;
use ndless::timer::disable_sleep;

use crate::timer::TimerListener;
use crate::yield_now::{Yield, YieldListener};

/// Spawns a task and blocks until the future resolves, returning its result.
pub fn block_on<T>(listeners: &AsyncListeners, task: impl Future<Output = T>) -> T {
	let wake_marker = Arc::new(TaskWaker {
		wake_marker: AtomicBool::new(true),
	});
	let waker = Waker::from(wake_marker.clone());
	let mut context = Context::from_waker(&waker);
	pin_mut!(task);
	let mut task = task;
	loop {
		listeners.timer.poll();
		listeners.yielder.poll();
		while wake_marker.wake_marker.load(Ordering::Relaxed) {
			match task.as_mut().poll(&mut context) {
				Poll::Ready(val) => {
					disable_sleep();
					return val;
				}
				Poll::Pending => {
					wake_marker.wake_marker.store(false, Ordering::Relaxed);
				}
			}
			listeners.timer.poll();
			listeners.yielder.poll();
		}
		listeners.timer.config_sleep();
		idle();
		disable_sleep();
	}
}

struct TaskWaker {
	wake_marker: AtomicBool,
}

impl TaskWaker {
	fn wake_task(&self) {
		self.wake_marker.store(true, Ordering::Relaxed);
	}
}

impl Wake for TaskWaker {
	fn wake(self: Arc<Self>) {
		self.wake_task();
	}

	fn wake_by_ref(self: &Arc<Self>) {
		self.wake_task();
	}
}

/// Handler for listening to system events.
///
/// Create one with `AsyncListeners::default()` and pass it to [`block_on`], as
/// well as your future. See the [module-level documentation][self] for more.
#[derive(Default)]
pub struct AsyncListeners {
	timer: TimerListener,
	yielder: YieldListener,
}

impl AsyncListeners {
	pub fn new() -> Self {
		Default::default()
	}
	/// Returns a [`TimerListener`] instance, which may be used to schedule
	/// timers.
	pub fn timer(&self) -> &TimerListener {
		&self.timer
	}
	/// Allows other tasks to run before coming back to this one. Useful when
	/// doing something computationally intensive, to allow things like keyboard
	/// handlers and timers to run. Note that the calculator will not go to
	/// sleep if this is called in a loop. Use [`timer`][AsyncListeners::timer]
	/// instead if a delay is desired between each iteration. If no other tasks
	/// are scheduled, this task is continued immediately.
	pub fn yield_now(&self) -> Yield {
		self.yielder.yield_now()
	}
}