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}