openthread_fuchsia/backing/
alarm.rs

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
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use super::*;
use crate::Platform;
use fuchsia_async as fasync;
//use futures::channel::mpsc as fmpsc;
use std::task::{Context, Poll};

// Number of entries in the timer wakeup buffer.
// This value was chosen somewhat arbitrarily, with the only
// requirement being that it be larger than what should happen
// during normal operation.
const TIMER_BUFFER_LEN: usize = 20;

pub(crate) struct AlarmInstance {
    pub task_alarm: Cell<Option<fasync::Task<()>>>,
    pub timer_sender: fmpsc::Sender<usize>,
}

impl AlarmInstance {
    pub(crate) fn new() -> (AlarmInstance, fmpsc::Receiver<usize>) {
        let (timer_sender, timer_receiver) = fmpsc::channel(TIMER_BUFFER_LEN);

        (AlarmInstance { task_alarm: Cell::new(None), timer_sender }, timer_receiver)
    }

    fn on_alarm_milli_get_now(&self) -> u32 {
        #[no_mangle]
        unsafe extern "C" fn otPlatAlarmMilliGetNow() -> u32 {
            // SAFETY: Must only be called from OpenThread thread,
            PlatformBacking::as_ref().alarm.on_alarm_milli_get_now()
        }
        (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_millis() as u32
    }

    fn on_time_get(&self) -> u64 {
        #[no_mangle]
        unsafe extern "C" fn otPlatTimeGet() -> u64 {
            // SAFETY: Must only be called from OpenThread thread,
            PlatformBacking::as_ref().alarm.on_time_get()
        }
        (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_micros() as u64
    }

    fn on_alarm_milli_start_at(&self, instance: Option<&ot::Instance>, t0: u32, dt: u32) {
        #[no_mangle]
        unsafe extern "C" fn otPlatAlarmMilliStartAt(
            instance: *mut otsys::otInstance,
            t0: u32,
            dt: u32,
        ) {
            AlarmInstance::on_alarm_milli_start_at(
                // SAFETY: Must only be called from OpenThread thread,
                &PlatformBacking::as_ref().alarm,
                // SAFETY: `instance` must be a pointer to a valid `otInstance`
                ot::Instance::ref_from_ot_ptr(instance),
                t0,
                dt,
            )
        }

        trace!(
            tag = "alarm",
            "on_alarm_milli_start_at: scheduling alarm for {:?}ms after {:?}",
            dt,
            t0
        );

        let ot_instance_ptrval =
            instance.map(ot::Boxable::as_ot_ptr).map(|x| x as usize).unwrap_or(0usize);
        let mut timer_sender = self.timer_sender.clone();

        let future = async move {
            let now_in_millis =
                (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_millis() as u32;
            let offset = ((now_in_millis - t0) as i32).min(0) as u32;
            let duration = if offset <= dt {
                Duration::from_millis((dt - offset) as u64)
            } else {
                Duration::ZERO
            };
            trace!(
                tag = "alarm",
                "on_alarm_milli_start_at: helper task now waiting {:?}",
                duration
            );
            fasync::Timer::new(duration).await;
            trace!(tag="alarm", "on_alarm_milli_start_at: helper task finished waiting, now sending ot_instance_ptrval");
            timer_sender.send(ot_instance_ptrval).await.unwrap();
        };

        // Make and spawn a helper task that waits for the duration
        // and then puts the pointer value into the timer sender channel.
        // The receiver end of the channel is being serviced by Platform::process_poll,
        // which makes sure that the timer callback gets fired on the main
        // thread. The previous alarm task, if any, is cancelled.
        self.task_alarm.set(Some(fasync::Task::spawn(future)));
    }

    fn on_alarm_milli_stop(&self, _instance: Option<&ot::Instance>) {
        #[no_mangle]
        unsafe extern "C" fn otPlatAlarmMilliStop(instance: *mut otsys::otInstance) {
            AlarmInstance::on_alarm_milli_stop(
                // SAFETY: Must only be called from OpenThread thread,
                &PlatformBacking::as_ref().alarm,
                // SAFETY: `instance` must be a pointer to a valid `otInstance`
                ot::Instance::ref_from_ot_ptr(instance),
            )
        }

        if self.task_alarm.take().is_some() {
            trace!(tag = "alarm", "on_alarm_milli_stop: Alarm cancelled");
        }
    }

    fn on_alarm_fired(&self, instance: &ot::Instance, value: usize) {
        trace!(tag = "alarm", "on_alarm_fired");

        let instance_ptr = instance.as_ot_ptr();
        assert_eq!(instance_ptr as usize, value, "Got wrong pointer from timer receiver");

        // SAFETY: Must be called with a valid pointer to otInstance,
        //         must also only be called from the main OpenThread thread,
        //         which is a guarantee of this method.
        unsafe {
            if otsys::otPlatDiagModeGet() {
                otsys::otPlatDiagAlarmFired(instance_ptr);
            }

            otsys::otPlatAlarmMilliFired(instance_ptr);
        }
    }
}

impl Platform {
    pub(crate) fn process_poll_alarm(&mut self, instance: &ot::Instance, cx: &mut Context<'_>) {
        while let Poll::Ready(Some(value)) = self.timer_receiver.poll_next_unpin(cx) {
            // SAFETY: Guaranteed to only be called from the OpenThread thread.
            unsafe { PlatformBacking::as_ref() }.alarm.on_alarm_fired(instance, value);
        }
    }
}