ipc_queue/
position.rs

1/* Copyright (c) Fortanix, Inc.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7use super::*;
8use std::sync::atomic::Ordering;
9
10/// `PositionMonitor<T>` records the current read/write positions of a queue.
11///
12/// The `PositionMonitor` works by keeping track how many times the ring buffer wrapped around,
13/// and the current offsets that are used.
14/// The former (how many times the ring buffer wrapped around) is kept in an `AtomicU64`
15/// that is updated each time a value is read.
16/// The latter (current offsets) is kept by looking at the `Fifo` directly.
17///
18/// Even though a queue is comprised of a limited number of slots
19/// arranged as a ring buffer, we can assign a position to each value written/
20/// read to/from the queue. This is useful in case we want to know whether or
21/// not a particular value written to the queue has been read.
22#[allow(dead_code)]
23pub struct PositionMonitor<T: 'static> {
24    read_epoch: Arc<AtomicU64>,
25    fifo: Fifo<T>,
26}
27
28/// A read position in a queue.
29#[allow(dead_code)]
30pub struct ReadPosition(u64);
31
32/// A write position in a queue.
33#[allow(dead_code)]
34pub struct WritePosition(u64);
35
36impl<T> PositionMonitor<T> {
37    #[allow(dead_code)]
38    pub (crate) fn new(read_epoch: Arc<AtomicU64>,fifo: Fifo<T>) -> PositionMonitor<T> {
39        PositionMonitor {
40            read_epoch,
41            fifo,
42        }
43    }
44
45    pub fn read_position(&self) -> ReadPosition {
46        let current = self.fifo.current_offsets(Ordering::Relaxed);
47        let read_epoch = self.read_epoch.load(Ordering::Relaxed);
48        ReadPosition((read_epoch << 32) | (current.read_offset() as u64))
49    }
50
51    pub fn write_position(&self) -> WritePosition {
52        let current = self.fifo.current_offsets(Ordering::Relaxed);
53        let mut write_epoch = self.read_epoch.load(Ordering::Relaxed);
54        // Write epoch keeps track of how many times the write offset wrapped around
55        // the ring buffer. Write epochs are not tracked separately, only read epoch are.
56        // We know, however, that objects are always written to the buffer first.
57        // So, the high bit used in the write and read offsets tell us whether writes
58        // already wrapped around the ring buffer, while reads have not yet.
59        if current.read_high_bit() != current.write_high_bit() {
60            write_epoch += 1;
61        }
62        WritePosition((write_epoch << 32) | (current.write_offset() as u64))
63    }
64}
65
66impl<T> Clone for PositionMonitor<T> {
67    fn clone(&self) -> Self {
68        Self {
69            read_epoch: self.read_epoch.clone(),
70            fifo: self.fifo.clone(),
71        }
72    }
73}
74
75impl ReadPosition {
76    /// A `WritePosition` can be compared to a `ReadPosition` **correctly** if
77    /// the ring buffer wrapped around at most 2³¹ (2 to the power of 31) times.
78    ///
79    /// Returns `None` if the read position and the write position cannot be compared,
80    /// `Some(true)` if the read position is strictly higher than the write position,
81    /// `Some(false)` otherwise
82    pub fn is_past(&self, write: &WritePosition) -> Option<bool> {
83        let (read, write) = (self.0, write.0);
84
85        let hr = read & (1 << 63);
86        let hw = write & (1 << 63);
87        if hr == hw {
88            Some(read > write)
89        } else {
90            None
91        }
92    }
93}