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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
//! Reactor is the executor and manager of all actors,
//!
//! serves as
//! - Actor Runner Schedule and Execution
//! - Actor Register maintenance and update
//!

#[cfg(not(feature = "log"))]
use crate::log;
use crate::{queue::Queue, register::Register};
use alloc::{boxed::Box, vec::Vec};
use core::{
    future::Future as CoreFuture,
    hint,
    pin::Pin,
    sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering},
    task::{Context as CoreContext, Poll},
};

/// Object that [Reactor](Reactor) manipulate with
pub struct ReactorPair {
    inner: Pin<Box<dyn CoreFuture<Output = ()>>>,
    handle: ReactorHandle,
}

impl ReactorPair {
    /// create an instance
    pub fn new<F>(fut: F) -> Self
    where
        F: CoreFuture<Output = ()> + 'static,
    {
        ReactorPair {
            inner: Box::pin(fut),
            handle: ReactorHandle::new(),
        }
    }
}

static POLL_PENDING: AtomicBool = AtomicBool::new(false);
static POLL_BUDGET: AtomicUsize = AtomicUsize::new(3);

/// Future execution reach an phase
/// where needs some uncertain time
///
/// **NOTE** that
/// - polling the in-memory-vector-based
/// stream does not take uncertain time
/// - polling remote html page does
/// - polling a sleep-based future does
pub fn pending_polled() {
    POLL_PENDING.store(true, Ordering::Release);
}

/// Future execution reach an phase
/// where needs some uncertain time
///
/// **NOTE** that
/// - polling the in-memory-vector-based
/// stream does not take uncertain time
/// - polling remote html page does
/// - polling a sleep-based future does
pub fn is_pending_polled() -> bool {
    POLL_PENDING.load(Ordering::Acquire)
}

/// increase the poll budget to enable
/// more future wake up
pub fn inc_poll_budget(delta: usize) {
    POLL_BUDGET.fetch_add(delta, Ordering::Release);
}

/// decrease the poll budget when
/// wake up the context
#[allow(dead_code)]
fn dec_poll_budget(delta: usize) -> usize {
    POLL_BUDGET.fetch_sub(delta, Ordering::Release)
}

/// reset it
fn reset_poll_pending() {
    POLL_PENDING.store(false, Ordering::Release);
}

/// reset it
fn reset_poll_budget() {
    POLL_BUDGET.store(3, Ordering::Release);
}

static HANDLECOUNT: AtomicUsize = AtomicUsize::new(1);

/// handle of Reactor Pair
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ReactorHandle(usize);

impl ReactorHandle {
    pub(crate) fn new() -> Self {
        Self(HANDLECOUNT.fetch_add(1, Ordering::Relaxed) as _)
    }
}

/// orders that Reactor execute
pub enum ReactingOrder {
    /// exit the program
    Exit(i32),
    /// Abort a future
    Abort(ReactorHandle),
    /// executing a future
    Execute(ReactorPair),
}

unsafe impl Send for ReactingOrder {}
unsafe impl Sync for ReactingOrder {}

static REACTORSEAL: AtomicBool = AtomicBool::new(false);
static REACTOREXIT: AtomicBool = AtomicBool::new(false);
static REACTOREXITCODE: AtomicIsize = AtomicIsize::new(0);
static REACTORCACHE: Queue<ReactingOrder> = Queue::new_null();
static QUEUENULLINIT: AtomicBool = AtomicBool::new(false);

/// static mutable Reactor where store
/// all Pairs
static mut REACTOR: Reactor = Reactor { inner: Vec::new() };

/// Executor and Manager of all actors
///
/// Schedule task for execution
/// and maintains the [Register](Register)
/// queue
pub struct Reactor {
    pub(crate) inner: Vec<ReactorPair>,
}

impl Reactor {
    /// get the reference of global
    /// static `REACTOR`
    pub fn new() -> &'static Self {
        // **Safety**: With `INIT` guard, we assure that
        // REACTOR is initialized and its inner
        // pointer points a valid Reactor
        // So it is safe to de-reference the
        // underlying pointer
        unsafe { &REACTOR }
    }

    /// push a pair to the cache queue
    pub fn push(future: ReactorPair) {
        if !QUEUENULLINIT.load(Ordering::Acquire) {
            REACTORCACHE.assume_init();
            QUEUENULLINIT.store(true, Ordering::Release);
        }
        REACTORCACHE.push(ReactingOrder::Execute(future));
    }

    /// drive all reactor's futures into completion
    ///
    // **Safety**: as the inner data is guarded with
    // `REACTORSEAL`, any mutable actions happens
    // will trigger the guardian locked
    pub async fn as_future() {
        unsafe { &mut REACTOR }.await
    }

    /// execute an ReactingOrder
    pub fn execute(order: ReactingOrder) {
        log::debug!("New order for execution");
        if !QUEUENULLINIT.load(Ordering::Acquire) {
            REACTORCACHE.assume_init();
            QUEUENULLINIT.store(true, Ordering::Release);
        }
        REACTORCACHE.push(order);
    }

    /// get the length of polling futures
    pub fn len() -> usize {
        unsafe { REACTOR.inner.len() }
    }

    /// process reactor cache
    /// - poll reacting orders from cache
    fn epoll(inner: &mut Vec<ReactorPair>) {
        while let Some(order) = unsafe { REACTORCACHE.pop() } {
            match order {
                ReactingOrder::Abort(handle) => {
                    log::debug!("Reactor abort future: {:?}", handle.0);
                    for ind in 0..inner.len() {
                        if inner[ind].handle == handle {
                            inner.swap_remove(ind);
                            break;
                        }
                    }
                }
                ReactingOrder::Execute(future) => inner.push(future),
                ReactingOrder::Exit(code) => {
                    log::warn!("Exit code: {} received, Exiting ...", code);
                    REACTOREXIT.store(true, Ordering::Relaxed);
                    REACTOREXITCODE.store(code as _, Ordering::Relaxed);
                }
            }
        }
    }
}

impl CoreFuture for Reactor {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut CoreContext<'_>) -> Poll<()> {
        let mut index = 0;
        let inner = &mut self.as_mut().inner;
        Self::epoll(inner);

        while index < inner.len() {
            // after each item gets polled, check state
            // - exit or not
            if REACTOREXIT.load(Ordering::Relaxed) {
                let code = REACTOREXITCODE.load(Ordering::Relaxed);
                log::warn!("Exit code: {} received, Exiting ...", code);
                // exit the program and
                // clear all futures
                inner.clear();
                break;
            }

            let pair = &mut inner[index];
            match Pin::new(&mut pair.inner).poll(cx) {
                Poll::Pending => {
                    // inner future is not ready
                    // and continue to the next item
                    index += 1;
                }
                Poll::Ready(()) => {
                    // inner future is ready
                    // remove it from queue
                    // and continue to the next item
                    while REACTORSEAL.load(Ordering::Acquire) {
                        hint::spin_loop();
                    }
                    REACTORSEAL.store(true, Ordering::Release);
                    inner.swap_remove(index);
                    REACTORSEAL.store(false, Ordering::Relaxed);
                    Register::update();
                }
            }

            Self::epoll(inner);
        }

        if inner.is_empty() {
            reset_poll_pending();
            reset_poll_budget();
            return Poll::Ready(());
        }
        drop(inner);

        #[cfg(all(
            not(feature = "no-std"),
            all(feature = "unstable", feature = "force-poll")
        ))]
        {
            #[cfg(feature = "wasm32")]
            force_poll_wasm32(cx);
            #[cfg(feature = "std")]
            force_poll_std(cx);
        }
        let _budget = dec_poll_budget(1);
        #[cfg(not(feature = "force-poll"))]
        if _budget > 0 {
            log::trace!("Wake future in reactor");
            cx.waker().wake_by_ref();
        } else {
            log::error!("Unfinished Future HANGING INDEFINITELY. Future is **NOT WAKED** after `Poll::Pending` returned");
        }
        Poll::Pending
    }
}

#[cfg(all(feature = "wasm32", feature = "unstable", feature = "force-poll"))]
#[allow(dead_code)]
fn force_poll_wasm32(cx: &mut CoreContext<'_>) {
    // set timeout then wake
    // will do that
    use crate::rt::wasm_timeout::set_timeout;
    use wasm_bindgen::{prelude::*, JsCast};

    use core::task::Waker;
    use wasm_bindgen::closure::Closure;

    static mut WAKER: Option<Waker> = None;
    unsafe {
        if WAKER.is_none() {
            WAKER.replace(cx.waker().clone());
        }
        // the future has been moved to another
        // environment, then update the waker
        if !WAKER.as_ref().unwrap().will_wake(cx.waker()) {
            WAKER.replace(cx.waker().clone());
        }
    }

    let f = Closure::<dyn Fn()>::new(|| unsafe {
        WAKER.as_ref().unwrap().wake_by_ref();
    })
    .into_js_value()
    .dyn_into::<js_sys::Function>()
    .expect("Closure to js function failed");
    let _ = set_timeout(&f, 50).unwrap_throw();
}

#[cfg(all(feature = "std", feature = "unstable", feature = "force-poll"))]
#[allow(dead_code)]
fn force_poll_std(cx: &mut CoreContext<'_>) {
    std::thread::sleep(std::time::Duration::from_millis(50));
    cx.waker().wake_by_ref();
}