1 | //! Benchmark the delay in propagating OS signals to any listeners. |
2 | #![cfg (unix)] |
3 | |
4 | use criterion::{criterion_group, criterion_main, Criterion}; |
5 | use std::future::Future; |
6 | use std::pin::Pin; |
7 | use std::task::{Context, Poll}; |
8 | use tokio::runtime; |
9 | use tokio::signal::unix::{signal, SignalKind}; |
10 | use tokio::sync::mpsc; |
11 | |
12 | struct Spinner { |
13 | count: usize, |
14 | } |
15 | |
16 | impl Future for Spinner { |
17 | type Output = (); |
18 | |
19 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
20 | if self.count > 3 { |
21 | Poll::Ready(()) |
22 | } else { |
23 | self.count += 1; |
24 | cx.waker().wake_by_ref(); |
25 | Poll::Pending |
26 | } |
27 | } |
28 | } |
29 | |
30 | impl Spinner { |
31 | fn new() -> Self { |
32 | Self { count: 0 } |
33 | } |
34 | } |
35 | |
36 | pub fn send_signal(signal: libc::c_int) { |
37 | use libc::{getpid, kill}; |
38 | |
39 | unsafe { |
40 | assert_eq!(kill(getpid(), signal), 0); |
41 | } |
42 | } |
43 | |
44 | fn many_signals(c: &mut Criterion) { |
45 | let num_signals = 10; |
46 | let (tx, mut rx) = mpsc::channel(num_signals); |
47 | |
48 | // Intentionally single threaded to measure delays in propagating wakes |
49 | let rt = runtime::Builder::new_current_thread() |
50 | .enable_all() |
51 | .build() |
52 | .unwrap(); |
53 | |
54 | let spawn_signal = |kind| { |
55 | let tx = tx.clone(); |
56 | rt.spawn(async move { |
57 | let mut signal = signal(kind).expect("failed to create signal" ); |
58 | |
59 | while signal.recv().await.is_some() { |
60 | if tx.send(()).await.is_err() { |
61 | break; |
62 | } |
63 | } |
64 | }); |
65 | }; |
66 | |
67 | for _ in 0..num_signals { |
68 | // Pick some random signals which don't terminate the test harness |
69 | spawn_signal(SignalKind::child()); |
70 | spawn_signal(SignalKind::io()); |
71 | } |
72 | drop(tx); |
73 | |
74 | // Turn the runtime for a while to ensure that all the spawned |
75 | // tasks have been polled at least once |
76 | rt.block_on(Spinner::new()); |
77 | |
78 | c.bench_function("many_signals" , |b| { |
79 | b.iter(|| { |
80 | rt.block_on(async { |
81 | send_signal(libc::SIGCHLD); |
82 | for _ in 0..num_signals { |
83 | rx.recv().await.expect("channel closed" ); |
84 | } |
85 | |
86 | send_signal(libc::SIGIO); |
87 | for _ in 0..num_signals { |
88 | rx.recv().await.expect("channel closed" ); |
89 | } |
90 | }); |
91 | }) |
92 | }); |
93 | } |
94 | |
95 | criterion_group!(signal_group, many_signals); |
96 | |
97 | criterion_main!(signal_group); |
98 | |