[netstack3] Use zx::Time instead of std::time::Instant for timers
- Previously, we used std::time::Instant because the core's
EventDispatcher trait used that type in order to avoid a platform-
specific dependency on zx::Time; this required some gymnastics,
and meant that we had an open TODO to figure out how to convert
from std::time::Instant to zx::Time
- Now that the EventDispatcher trait provides an abstraction for
the instant at which a timer is scheduled, we can just use zx::Time
directly without exposing that detail to the core, and thus avoid
violating the core's platform-agnosticism requirement
- This also allows us to implement EventDispatcher::schedule_timeout_instant,
which was previously a TODO for the reasons described above
NET-2367 #done
Change-Id: Ic40de7fbaed7ac1f1861cc60f3db14c8937cf743
diff --git a/src/connectivity/network/netstack3/core/src/lib.rs b/src/connectivity/network/netstack3/core/src/lib.rs
index 2242b39..236b92c3 100644
--- a/src/connectivity/network/netstack3/core/src/lib.rs
+++ b/src/connectivity/network/netstack3/core/src/lib.rs
@@ -275,20 +275,27 @@
/// Schedule a callback to be invoked after a timeout.
///
- /// `schedule_timeout` schedules `f` to be invoked after `duration` has elapsed, overwriting any
- /// previous timeout with the same ID.
+ /// `schedule_timeout` schedules `f` to be invoked after `duration` has
+ /// elapsed, overwriting any previous timeout with the same ID.
///
- /// If there was previously a timer with that ID, return the time at which is was scheduled to
- /// fire.
- fn schedule_timeout(&mut self, duration: time::Duration, id: TimerId) -> Option<Self::Instant>;
+ /// If there was previously a timer with that ID, return the time at which
+ /// is was scheduled to fire.
+ ///
+ /// # Panics
+ ///
+ /// `schedule_timeout` may panic if `duration` is large enough that
+ /// `self.now() + duration` overflows.
+ fn schedule_timeout(&mut self, duration: time::Duration, id: TimerId) -> Option<Self::Instant> {
+ self.schedule_timeout_instant(self.now().checked_add(duration).unwrap(), id)
+ }
/// Schedule a callback to be invoked at a specific time.
///
- /// `schedule_timeout_instant` schedules `f` to be invoked at `time`, overwriting any previous
- /// timeout with the same ID.
+ /// `schedule_timeout_instant` schedules `f` to be invoked at `time`,
+ /// overwriting any previous timeout with the same ID.
///
- /// If there was previously a timer with that ID, return the time at which is was scheduled to
- /// fire.
+ /// If there was previously a timer with that ID, return the time at which
+ /// is was scheduled to fire.
fn schedule_timeout_instant(
&mut self,
time: Self::Instant,
diff --git a/src/connectivity/network/netstack3/src/eventloop.rs b/src/connectivity/network/netstack3/src/eventloop.rs
index e96a593..0347875 100644
--- a/src/connectivity/network/netstack3/src/eventloop.rs
+++ b/src/connectivity/network/netstack3/src/eventloop.rs
@@ -77,9 +77,10 @@
use fuchsia_async as fasync;
use fuchsia_zircon as zx;
+use std::convert::TryFrom;
use std::fs::File;
use std::marker::PhantomData;
-use std::time::{Duration, Instant};
+use std::time::Duration;
use failure::{bail, Error};
use fidl::endpoints::{RequestStream, ServiceMarker};
@@ -619,7 +620,7 @@
}
struct TimerInfo {
- time: Instant,
+ time: zx::Time,
id: TimerId,
abort_handle: AbortHandle,
}
@@ -642,26 +643,45 @@
}
}
-impl EventDispatcher for EventLoopInner {
- type Instant = Instant;
+/// A thin wrapper around `zx::Time` that implements `core::Instant`.
+#[derive(Copy, Clone)]
+struct ZxTime(zx::Time);
- fn now(&self) -> Instant {
- Instant::now()
+impl netstack3_core::Instant for ZxTime {
+ fn duration_since(&self, earlier: ZxTime) -> Duration {
+ assert!(self.0 >= earlier.0);
+ // guaranteed not to panic because the assertion ensures that the
+ // difference is non-negative, and all non-negative i64 values are also
+ // valid u64 values
+ Duration::from_nanos(u64::try_from(self.0.into_nanos() - earlier.0.into_nanos()).unwrap())
}
- fn schedule_timeout(&mut self, duration: Duration, id: TimerId) -> Option<Instant> {
- // We need to separately keep track of the time at which the future completes (a Zircon
- // Time object) and the time at which the user expects it to complete. You cannot convert
- // between Zircon Time objects and std::time::Instant objects (since std::time::Instance is
- // opaque), so we generate two different time objects to keep track of.
- let zircon_time = zx::Time::after(zx::Duration::from(duration));
- let rust_time = self.now() + duration;
+ fn checked_add(&self, duration: Duration) -> Option<ZxTime> {
+ Some(ZxTime(zx::Time::from_nanos(
+ self.0.into_nanos().checked_add(i64::try_from(duration.as_nanos()).ok()?)?,
+ )))
+ }
+ fn checked_sub(&self, duration: Duration) -> Option<ZxTime> {
+ Some(ZxTime(zx::Time::from_nanos(
+ self.0.into_nanos().checked_sub(i64::try_from(duration.as_nanos()).ok()?)?,
+ )))
+ }
+}
+
+impl EventDispatcher for EventLoopInner {
+ type Instant = ZxTime;
+
+ fn now(&self) -> ZxTime {
+ ZxTime(zx::Time::get(zx::ClockId::Monotonic))
+ }
+
+ fn schedule_timeout_instant(&mut self, time: ZxTime, id: TimerId) -> Option<ZxTime> {
let old_timer = self.cancel_timeout(id);
let mut timer_send = self.event_send.clone();
let timeout = async move {
- await!(fasync::Timer::new(zircon_time));
+ await!(fasync::Timer::new(time.0));
timer_send.send(Event::TimerEvent(id));
// The timer's cancel function is called by the receiver, so that
// this async block does not need to have a reference to the
@@ -669,7 +689,7 @@
};
let (abort_handle, abort_registration) = AbortHandle::new_pair();
- self.timers.push(TimerInfo { time: rust_time, id, abort_handle });
+ self.timers.push(TimerInfo { time: time.0, id, abort_handle });
let timeout = Abortable::new(timeout, abort_registration);
let timeout = timeout.unwrap_or_else(|_| ());
@@ -678,12 +698,6 @@
old_timer
}
- fn schedule_timeout_instant(&mut self, _time: Instant, _id: TimerId) -> Option<Instant> {
- // It's not possible to convert a std::time::Instant to a Zircon Time, so this API will
- // need some more thought. Punting on this until we need it.
- unimplemented!()
- }
-
// TODO(wesleyac): Fix race
//
// There is a potential race in the following condition:
@@ -699,7 +713,7 @@
// ensuring that the timer future cannot fire until the main queue is empty.
// See `GroupAvailable` in `src/connectivity/wlan/wlanstack/src/future_util.rs`
// for an example of how to do this.
- fn cancel_timeout(&mut self, id: TimerId) -> Option<Instant> {
+ fn cancel_timeout(&mut self, id: TimerId) -> Option<ZxTime> {
let index =
self.timers.iter().enumerate().find_map(
|x| {
@@ -711,7 +725,7 @@
},
)?;
self.timers[index].abort_handle.abort();
- Some(self.timers.remove(index).time)
+ Some(ZxTime(self.timers.remove(index).time))
}
}