1use crate::errors::ReadError;
4use crate::{Sentinel, SharedFileType, WriteState};
5use pin_project::{pin_project, pinned_drop};
6use std::io::{ErrorKind, SeekFrom};
7use std::pin::Pin;
8use std::sync::Arc;
9use std::sync::atomic::{AtomicUsize, Ordering};
10use std::task::{Context, Poll};
11use tokio::io;
12use tokio::io::{AsyncRead, AsyncSeek, ReadBuf};
13use uuid::Uuid;
14
15#[pin_project(PinnedDrop)]
17pub struct SharedFileReader<T> {
18 id: Uuid,
20 #[pin]
22 file: T,
23 sentinel: Arc<Sentinel<T>>,
25 read: AtomicUsize,
28}
29
30static NODE_ID: &[u8; 6] = &[2, 3, 0, 6, 1, 2];
32
33impl<T> SharedFileReader<T>
34where
35 T: SharedFileType<Type = T>,
36{
37 pub(crate) fn new(file: T, sentinel: Arc<Sentinel<T>>) -> Self {
38 Self {
39 id: Uuid::now_v1(NODE_ID),
40 file,
41 sentinel,
42 read: AtomicUsize::new(0),
43 }
44 }
45
46 pub async fn fork(&self) -> Result<Self, T::OpenError> {
48 Ok(Self {
49 id: Uuid::now_v1(NODE_ID),
50 file: self.sentinel.original.open_ro().await?,
51 sentinel: self.sentinel.clone(),
52 read: AtomicUsize::new(0),
53 })
54 }
55}
56
57impl<T> SharedFileReader<T> {
58 pub fn file_size(&self) -> FileSize {
60 match self.sentinel.state.load() {
61 WriteState::Pending(committed, _written) => FileSize::AtLeast(committed),
62 WriteState::Completed(size) => FileSize::Exactly(size),
63 WriteState::Failed => FileSize::Error,
64 }
65 }
66}
67
68#[derive(Debug, Copy, Clone)]
70pub enum FileSize {
71 AtLeast(usize),
74 Exactly(usize),
76 Error,
78}
79
80impl FileSize {
81 pub fn minimum_size(&self) -> Option<usize> {
83 if let Self::AtLeast(len) = self {
84 Some(*len)
85 } else {
86 self.exact_size()
87 }
88 }
89
90 pub fn exact_size(&self) -> Option<usize> {
92 if let Self::Exactly(len) = self {
93 Some(*len)
94 } else {
95 None
96 }
97 }
98}
99
100#[pinned_drop]
101impl<T> PinnedDrop for SharedFileReader<T> {
102 fn drop(mut self: Pin<&mut Self>) {
103 self.sentinel.remove_reader_waker(&self.id)
104 }
105}
106
107impl<T> AsyncRead for SharedFileReader<T>
108where
109 T: AsyncRead,
110{
111 fn poll_read(
112 self: Pin<&mut Self>,
113 cx: &mut Context<'_>,
114 buf: &mut ReadBuf<'_>,
115 ) -> Poll<io::Result<()>> {
116 let read_so_far = self.read.load(Ordering::Acquire);
117
118 let current_total = loop {
119 match self.sentinel.state.load() {
120 WriteState::Pending(committed, _written) => {
121 if read_so_far != committed {
123 break committed;
124 }
125
126 self.sentinel.register_reader_waker(self.id, cx.waker());
134 if let WriteState::Pending(committed, _) = self.sentinel.state.load()
135 && read_so_far == committed
136 {
137 return Poll::Pending;
138 }
139 }
141 WriteState::Completed(count) => {
142 if read_so_far == count {
144 return Poll::Ready(Ok(()));
145 }
146 break count;
147 }
148 WriteState::Failed => {
149 return Poll::Ready(Err(io::Error::new(
150 ErrorKind::BrokenPipe,
151 ReadError::FileClosed,
152 )));
153 }
154 }
155 };
156
157 let read_at_most = (current_total - read_so_far).min(buf.remaining());
160 let mut smaller_buf = buf.take(read_at_most);
161 let read_offset = smaller_buf.filled().len();
162
163 let this = self.project();
164
165 if let Poll::Ready(result) = this.file.poll_read(cx, &mut smaller_buf) {
166 this.sentinel.remove_reader_waker(this.id);
167 if let Err(e) = result {
168 return Poll::Ready(Err(e));
169 }
170
171 let read_now = smaller_buf.filled().len();
173 if read_now != read_offset {
174 unsafe {
180 buf.assume_init(read_now);
181 }
182 buf.advance(read_now);
183
184 let read = read_so_far + (read_now - read_offset);
185 this.read.store(read, Ordering::Release);
186 return Poll::Ready(result);
187 }
188
189 match this.sentinel.state.load() {
192 WriteState::Pending(_, _) => {}
193 WriteState::Completed(_) => return Poll::Ready(Ok(())),
194 WriteState::Failed => {
195 return Poll::Ready(Err(io::Error::new(
196 ErrorKind::BrokenPipe,
197 ReadError::FileClosed,
198 )));
199 }
200 }
201 }
202
203 this.sentinel.register_reader_waker(*this.id, cx.waker());
205 Poll::Pending
206 }
207}
208
209impl<T> AsyncSeek for SharedFileReader<T>
210where
211 T: AsyncSeek,
212{
213 fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> {
214 let this = self.project();
215 this.file.start_seek(position)
216 }
217
218 fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
219 let this = self.project();
220 this.file.poll_complete(cx)
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_exact_size() {
230 assert_eq!(FileSize::Exactly(42).exact_size(), Some(42));
231 assert_eq!(FileSize::AtLeast(41).exact_size(), None);
232 assert_eq!(FileSize::Error.exact_size(), None);
233 }
234
235 #[test]
236 fn test_minimum_size() {
237 assert_eq!(FileSize::Exactly(42).minimum_size(), Some(42));
238 assert_eq!(FileSize::AtLeast(41).minimum_size(), Some(41));
239 assert_eq!(FileSize::Error.minimum_size(), None);
240 }
241}