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}