device_virtio_block/interrupt_coalescer.rs
1// Copyright 2026 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Interrupt coalescing for batches of asynchronous request completions.
6
7use std::cell::Cell;
8
9use futures::future::OptionFuture;
10use futures::future::Ready;
11
12/// Holds the current state of deferred interrupts for a batch of requests.
13#[derive(Clone, Copy, PartialEq)]
14enum BatchSignalState {
15 /// No requests have completed in this batch.
16 Idle,
17 /// One request has completed and it called `trigger_interrupt`.
18 Notified,
19 /// Requests have completed without calling `trigger_interrupt`.
20 Pending,
21}
22
23/// Signals the first completion immediately and defers subsequent interrupts until the batch ends.
24pub(super) struct InterruptCoalescer {
25 batch_state: Cell<BatchSignalState>,
26}
27
28impl InterruptCoalescer {
29 /// Creates an idle `InterruptCoalescer`.
30 pub(super) fn new() -> Self {
31 Self {
32 batch_state: Cell::new(BatchSignalState::Idle),
33 }
34 }
35
36 /// Calls `signal` immediately for the first completion, deferring the rest until `complete`.
37 pub(super) fn trigger_interrupt(&self, signal: impl FnOnce()) {
38 if self.batch_state.get() == BatchSignalState::Idle {
39 signal();
40 self.batch_state.set(BatchSignalState::Notified);
41 } else {
42 self.batch_state.set(BatchSignalState::Pending);
43 }
44 }
45
46 /// Calls `signal` if any interrupts are pending due to interrupt coalescing.
47 pub(super) fn complete(&self, signal: impl FnOnce()) {
48 if self.batch_state.replace(BatchSignalState::Idle) == BatchSignalState::Pending {
49 signal();
50 }
51 }
52
53 /// Returns ready whenever a batch is active (Notified or Pending) so the event loop can flush
54 /// and reset the coalescer.
55 pub(super) fn active(&self) -> OptionFuture<Ready<()>> {
56 (self.batch_state.get() != BatchSignalState::Idle)
57 .then(|| futures::future::ready(()))
58 .into()
59 }
60}