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
120
121
122
123
124
125
126
127
128
129
//! OC-Wasm-Cassette provides a convenient wrapper that makes it easy to use an `async fn` as a
//! top-level function in an OC-Wasm application.
//!
//! Usage is as simple as:
//! ```rust
//! async fn main() -> Infallible {
//!     // Your code here
//! }
//!
//! #[no_mangle]
//! pub extern "C" fn run(arg: i32) -> i32 {
//!     oc_wasm_cassette::run(arg, main)
//! }
//! ```

#![no_std]
#![warn(
        // Turn on extra language lints.
        future_incompatible,
        missing_abi,
        nonstandard_style,
        rust_2018_idioms,
        single_use_lifetimes,
        trivial_casts,
        trivial_numeric_casts,
        unused,
        unused_crate_dependencies,
        unused_import_braces,
        unused_lifetimes,
        unused_qualifications,

        // Turn on extra Rustdoc lints.
        rustdoc::all,

        // Turn on extra Clippy lints.
        clippy::cargo,
        clippy::pedantic,
)]

extern crate alloc;
use alloc::boxed::Box;
use cassette::Cassette;
use core::convert::Infallible;
use core::future::Future;
use core::pin::Pin;
use oc_wasm_futures::sleep;
use sync_unsafe_cell::SyncUnsafeCell;

/// All the state of the application, encapsulated into one object.
enum State {
	/// The application has never run before and startup processes need to run.
	Uninitialized,

	/// The application is running.
	Running(Cassette<Pin<Box<dyn Future<Output = Infallible> + Sync>>>),
}

impl State {
	fn run<Fut: Future<Output = Infallible> + Sync + 'static, F: Fn() -> Fut>(
		&mut self,
		_: i32,
		main: F,
	) -> i32 {
		match self {
			Self::Uninitialized => {
				// Construct the executor.
				*self = Self::Running(Cassette::new(Box::pin(main())));

				// Yield for zero time and then execute the application next invocation.
				0
			}
			Self::Running(executor) => {
				sleep::check_for_wakeups();
				let _: Option<Infallible> = executor.poll_on(); // No need to examine return value, it cannot be Some(Infallible).
				sleep::shortest_requested()
			}
		}
	}
}

/// Runs the application for a period of time.
///
/// The `MainFuture` type is the type of future returned by the `main` function. The `Main` type is
/// the type of the `main` function itself. The `arg` parameter is the `i32` parameter that OC-Wasm
/// passes to the top-level `run` function. The `main` parameter is the main function, which is
/// typically an `async fn`. The return value should be returned from the top-level `run` function.
///
/// # Panics
/// This function panics if it is re-entered (i.e. if it is called while it is already in
/// progress).
pub fn run<MainFuture: Future<Output = Infallible> + Sync + 'static, Main: Fn() -> MainFuture>(
	arg: i32,
	main: Main,
) -> i32 {
	// Records whether the run() function is currently in progress.
	static RUNNING: SyncUnsafeCell<bool> = SyncUnsafeCell::new(false);

	// Stores the state that persists from tick to tick.
	static STATE: SyncUnsafeCell<State> = SyncUnsafeCell::new(State::Uninitialized);

	// Check for re-entry.
	//
	// SAFETY: OC-Wasm is single-threaded; therefore, there cannot be another thread accessing
	// RUNNING at the same time. Nothing ever takes a reference to RUNNING (only core::ptr
	// operations are done on it) so reference rules cannot be violated either.
	let reentered = unsafe { core::ptr::replace(RUNNING.get(), true) };
	assert!(!reentered, "oc_wasm_cassette::run() re-entered");

	// Get a mutable reference to STATE.
	//
	// SAFETY: We just checked that this function has not been re-entered, so no other call is in
	// progress. The STATE variable is named exactly once (besides its definition) in this
	// function, on the following line, so no second mutable reference can be created elsewhere in
	// the function. The STATE variable is scoped within the function, so no second mutable
	// reference can be created anywhere else either.
	let state: &mut State = unsafe { &mut *STATE.get() };

	// Delegate to the state.
	let ret = state.run(arg, main);

	// Clear the re-entry flag.
	//
	// SAFETY: OC-Wasm is single-threaded; therefore, there cannot be another thread accessing
	// RUNNING at the same time. Nothing ever takes a reference to RUNNING (only core::ptr
	// operations are done on it) so reference rules cannot be violated either.
	unsafe { core::ptr::write(RUNNING.get(), false) };

	ret
}