Skip to content

Commit 2cb213b

Browse files
authored
test(s2n-quic-dc): wire up bach::net (#2560)
1 parent 8129a20 commit 2cb213b

File tree

43 files changed

+1582
-427
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1582
-427
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ jobs:
136136
rustup toolchain install ${{ env.RUST_NIGHTLY_TOOLCHAIN }} --profile minimal
137137
rustup override set ${{ env.RUST_NIGHTLY_TOOLCHAIN }}
138138
139-
140139
- uses: camshaft/rust-cache@v1
141140

142141
- uses: camshaft/install@v1
@@ -208,11 +207,13 @@ jobs:
208207
- rust: stable
209208
os: ubuntu-latest
210209
target: aarch64-unknown-linux-gnu
211-
# s2n-quic-dc doesn't currently build on aarch64
212-
exclude: --exclude s2n-quic-dc --exclude s2n-quic-dc-benches
210+
# s2n-quic-dc tests are too slow to be emulated with QEMU
211+
exclude: --exclude s2n-quic-dc --exclude s2n-quic-dc-benches
213212
- rust: stable
214213
os: ubuntu-latest
215214
target: i686-unknown-linux-gnu
215+
# s2n-quic-dc requires a large number of threads for testing, which isn't supported on i686
216+
exclude: --exclude s2n-quic-dc --exclude s2n-quic-dc-benches
216217
- rust: stable
217218
os: ubuntu-latest
218219
target: x86_64-unknown-linux-musl
@@ -223,7 +224,7 @@ jobs:
223224
target: native
224225
env: S2N_QUIC_PLATFORM_FEATURES_OVERRIDE=""
225226
# s2n-quic-dc requires platform features
226-
exclude: --exclude s2n-quic-dc --exclude s2n-quic-dc-benches
227+
exclude: --exclude s2n-quic-dc --exclude s2n-quic-dc-benches
227228
- rust: stable
228229
os: ubuntu-latest
229230
target: native
@@ -295,7 +296,7 @@ jobs:
295296
# multiple versioned binaries, like /usr/bin/llvm-symbolizer-13. This step
296297
# finds the latest symbolizer and use it as the "base" llvm-symbolizer binary.
297298
#
298-
# llvm-symbolizer is necessary to get nice stack traces from asan errors.
299+
# llvm-symbolizer is necessary to get nice stack traces from asan errors.
299300
# Otherwise the stack trace just contains a hex address like "0x55bc6a28a9b6"
300301
- name: set llvm symbolizer
301302
run: |
@@ -305,7 +306,7 @@ jobs:
305306
env:
306307
RUSTDOCFLAGS: -Zsanitizer=address
307308
RUSTFLAGS: -Zsanitizer=address
308-
# We got a few globals that aren't cleaned up. Need to
309+
# We got a few globals that aren't cleaned up. Need to
309310
# determine if we should reenable this in the future.
310311
ASAN_OPTIONS: detect_leaks=false
311312
run: |
@@ -698,7 +699,6 @@ jobs:
698699
role-session-name: S2nQuicGHAS3Session
699700
aws-region: us-west-2
700701

701-
702702
- name: Upload to S3
703703
if: github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name
704704
id: s3

dc/s2n-quic-dc/Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ tokio = ["tokio/io-util", "tokio/net", "tokio/rt-multi-thread", "tokio/time"]
2525
arrayvec = "0.7"
2626
atomic-waker = "1"
2727
aws-lc-rs = "1.12"
28-
bach = { version = "0.0.11", optional = true }
28+
bach = { version = "0.0.11", features = [
29+
"net",
30+
"tokio-compat",
31+
], optional = true }
2932
bitflags = "2"
3033
bolero-generator = { version = "0.13", default-features = false, optional = true }
3134
bytes = "1"
@@ -60,7 +63,7 @@ parking_lot = "0.12"
6063
bitvec = { version = "1.0.1", default-features = false }
6164

6265
[dev-dependencies]
63-
bach = "0.0.11"
66+
bach = { version = "0.0.11", features = ["net", "tokio-compat"] }
6467
bolero = "0.13"
6568
bolero-generator = "0.13"
6669
insta = "1"

dc/s2n-quic-dc/src/clock.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,55 @@ use core::{fmt, pin::Pin, task::Poll, time::Duration};
55
use s2n_quic_core::{ensure, time};
66
use tracing::trace;
77

8+
#[macro_use]
9+
mod macros;
10+
11+
#[cfg(any(test, feature = "testing"))]
12+
pub mod bach;
813
#[cfg(feature = "tokio")]
914
pub mod tokio;
1015
pub use time::clock::Cached;
1116

1217
pub use time::Timestamp;
18+
19+
use crate::either::Either;
1320
pub type SleepHandle = Pin<Box<dyn Sleep>>;
1421

1522
pub trait Clock: 'static + Send + Sync + fmt::Debug + time::Clock {
1623
fn sleep(&self, amount: Duration) -> (SleepHandle, Timestamp);
24+
25+
fn timer(&self) -> Timer
26+
where
27+
Self: Sized,
28+
{
29+
Timer::new(self)
30+
}
31+
}
32+
33+
impl<A, B> Clock for Either<A, B>
34+
where
35+
A: Clock,
36+
B: Clock,
37+
{
38+
fn sleep(&self, amount: Duration) -> (SleepHandle, Timestamp) {
39+
match self {
40+
Either::A(a) => a.sleep(amount),
41+
Either::B(b) => b.sleep(amount),
42+
}
43+
}
44+
}
45+
46+
impl<A, B> time::Clock for Either<A, B>
47+
where
48+
A: time::Clock,
49+
B: time::Clock,
50+
{
51+
fn get_time(&self) -> Timestamp {
52+
match self {
53+
Either::A(a) => a.get_time(),
54+
Either::B(b) => b.get_time(),
55+
}
56+
}
1757
}
1858

1959
pub trait Sleep: Clock + core::future::Future<Output = ()> {
@@ -59,6 +99,12 @@ impl Timer {
5999
trace!(cancel = ?self.target);
60100
self.target = None;
61101
}
102+
103+
pub async fn sleep(&mut self, target: Timestamp) {
104+
use time::clock::Timer;
105+
self.update(target);
106+
core::future::poll_fn(|cx| self.poll_ready(cx)).await
107+
}
62108
}
63109

64110
impl time::clock::Timer for Timer {

dc/s2n-quic-dc/src/clock/bach.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use bach::time::{self, sleep_until, Instant};
5+
6+
impl_clock!();
7+
8+
#[cfg(test)]
9+
mod tests {
10+
use crate::{
11+
clock::{bach::Clock, Timer},
12+
testing::{ext::*, sim},
13+
};
14+
use bach::time::Instant;
15+
use core::time::Duration;
16+
use s2n_quic_core::time::{clock::Timer as _, Clock as _};
17+
18+
#[test]
19+
fn clock_test() {
20+
sim(|| {
21+
async {
22+
let clock = Clock::default();
23+
let mut timer = Timer::new(&clock);
24+
timer.ready().await;
25+
let before = Instant::now();
26+
let wait = Duration::from_secs(1);
27+
timer.update(clock.get_time() + wait);
28+
timer.ready().await;
29+
assert_eq!(before + wait, Instant::now());
30+
}
31+
.primary()
32+
.spawn();
33+
});
34+
}
35+
}

dc/s2n-quic-dc/src/clock/macros.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
macro_rules! impl_clock {
5+
() => {
6+
use super::SleepHandle;
7+
use core::{
8+
fmt,
9+
future::Future,
10+
pin::Pin,
11+
task::{Context, Poll},
12+
time::Duration,
13+
};
14+
use pin_project_lite::pin_project;
15+
use s2n_quic_core::{ready, time::Timestamp};
16+
use tracing::trace;
17+
18+
#[derive(Clone, Debug)]
19+
pub struct Clock(Instant);
20+
21+
impl Default for Clock {
22+
#[inline]
23+
fn default() -> Self {
24+
Self(Instant::now())
25+
}
26+
}
27+
28+
impl s2n_quic_core::time::Clock for Clock {
29+
#[inline]
30+
fn get_time(&self) -> Timestamp {
31+
let time = self.0.elapsed();
32+
unsafe { Timestamp::from_duration(time) }
33+
}
34+
}
35+
36+
pin_project!(
37+
pub struct Sleep {
38+
clock: Clock,
39+
#[pin]
40+
sleep: time::Sleep,
41+
}
42+
);
43+
44+
impl s2n_quic_core::time::Clock for Sleep {
45+
#[inline]
46+
fn get_time(&self) -> Timestamp {
47+
self.clock.get_time()
48+
}
49+
}
50+
51+
impl Future for Sleep {
52+
type Output = ();
53+
54+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
55+
let this = self.project();
56+
ready!(core::future::Future::poll(this.sleep, cx));
57+
Poll::Ready(())
58+
}
59+
}
60+
61+
impl super::Sleep for Sleep {
62+
#[inline]
63+
fn update(self: Pin<&mut Self>, target: Timestamp) {
64+
let target = unsafe { target.as_duration() };
65+
66+
// floor the delay to milliseconds to reduce timer churn
67+
let delay = Duration::from_millis(target.as_millis() as u64);
68+
69+
let target = self.clock.0 + delay;
70+
71+
// if the clock has changed let the sleep future know
72+
trace!(update = ?target);
73+
self.project().sleep.reset(target);
74+
}
75+
}
76+
77+
impl super::Clock for Sleep {
78+
#[inline]
79+
fn sleep(&self, amount: Duration) -> (SleepHandle, Timestamp) {
80+
self.clock.sleep(amount)
81+
}
82+
}
83+
84+
impl fmt::Debug for Sleep {
85+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86+
f.debug_struct("Sleep")
87+
.field("clock", &self.clock)
88+
.field("sleep", &self.sleep)
89+
.finish()
90+
}
91+
}
92+
93+
impl super::Clock for Clock {
94+
#[inline]
95+
fn sleep(&self, amount: Duration) -> (SleepHandle, Timestamp) {
96+
let now = Instant::now();
97+
let sleep = sleep_until(now + amount);
98+
let sleep = Sleep {
99+
clock: self.clone(),
100+
sleep,
101+
};
102+
let sleep = Box::pin(sleep);
103+
let target = now.saturating_duration_since(self.0);
104+
let target = unsafe { Timestamp::from_duration(target) };
105+
(sleep, target)
106+
}
107+
}
108+
};
109+
}

0 commit comments

Comments
 (0)