1use crate::errors::{CompleteWritingError, WriteError};
4use crate::{FilePath, Sentinel, SharedFileType, WriteState};
5use crossbeam::atomic::AtomicCell;
6use pin_project::{pin_project, pinned_drop};
7use std::io::{Error, ErrorKind, IoSlice};
8use std::path::PathBuf;
9use std::pin::Pin;
10use std::sync::Arc;
11use std::task::{Context, Poll};
12use tokio::io;
13use tokio::io::AsyncWrite;
14
15#[pin_project(PinnedDrop)]
23pub struct SharedFileWriter<T> {
24 #[pin]
26 file: T,
27 sentinel: Arc<Sentinel<T>>,
29}
30
31impl<T> SharedFileWriter<T> {
32 pub(crate) fn new(file: T, sentinel: Arc<Sentinel<T>>) -> Self {
33 Self { file, sentinel }
34 }
35
36 pub fn file_path(&self) -> &PathBuf
38 where
39 T: FilePath,
40 {
41 self.file.file_path()
42 }
43
44 pub async fn sync_all(&self) -> Result<(), T::SyncError>
46 where
47 T: SharedFileType,
48 {
49 self.file.sync_all().await?;
50 Self::sync_committed_and_written(&self.sentinel);
51 self.sentinel.wake_readers();
52 Ok(())
53 }
54
55 pub async fn sync_data(&self) -> Result<(), T::SyncError>
57 where
58 T: SharedFileType,
59 {
60 self.file.sync_data().await?;
61 Self::sync_committed_and_written(&self.sentinel);
62 self.sentinel.wake_readers();
63 Ok(())
64 }
65
66 pub async fn complete(self) -> Result<(), CompleteWritingError>
71 where
72 T: SharedFileType,
73 {
74 if self.sync_all().await.is_err() {
75 return Err(CompleteWritingError::SyncError);
76 }
77 self.complete_no_sync()
78 }
79
80 pub fn complete_no_sync(self) -> Result<(), CompleteWritingError> {
85 self.finalize_state()
86 }
87
88 fn sync_committed_and_written(sentinel: &Arc<Sentinel<T>>) {
96 match sentinel.state.load() {
97 WriteState::Pending(_committed, written) => {
98 sentinel.state.store(WriteState::Pending(written, written));
99 }
100 WriteState::Completed(_) => {}
101 WriteState::Failed => {}
102 }
103 }
104
105 fn finalize_state(&self) -> Result<(), CompleteWritingError> {
109 let result = match self.sentinel.state.load() {
110 WriteState::Pending(_committed, written) => {
111 debug_assert_eq!(
115 _committed, written,
116 "The number of committed bytes is less than the number of written bytes - call sync before dropping"
117 );
118 self.sentinel.state.store(WriteState::Completed(written));
119 Ok(())
120 }
121 WriteState::Completed(_) => Ok(()),
122 WriteState::Failed => Err(CompleteWritingError::FileWritingFailed),
123 };
124
125 self.sentinel.wake_readers();
126 result
127 }
128
129 fn update_state(state: &AtomicCell<WriteState>, written: usize) -> Result<usize, Error> {
137 match state.load() {
138 WriteState::Pending(committed, previously_written) => {
139 let count = previously_written + written;
140 state.store(WriteState::Pending(committed, count));
141 Ok(count)
142 }
143 WriteState::Completed(count) => {
144 if written != 0 {
147 return Err(Error::new(ErrorKind::BrokenPipe, WriteError::FileClosed));
148 }
149 Ok(count)
150 }
151 WriteState::Failed => Err(Error::from(ErrorKind::Other)),
152 }
153 }
154
155 fn handle_poll_write_result(
160 sentinel: &Sentinel<T>,
161 poll: Poll<Result<usize, Error>>,
162 ) -> Poll<Result<usize, Error>> {
163 match poll {
164 Poll::Ready(result) => match result {
165 Ok(written) => match Self::update_state(&sentinel.state, written) {
166 Ok(_) => Poll::Ready(Ok(written)),
167 Err(e) => Poll::Ready(Err(e)),
168 },
169 Err(e) => {
170 sentinel.state.store(WriteState::Failed);
171 sentinel.wake_readers();
172 Poll::Ready(Err(e))
173 }
174 },
175 Poll::Pending => Poll::Pending,
176 }
177 }
178}
179
180#[pinned_drop]
181impl<T> PinnedDrop for SharedFileWriter<T> {
182 fn drop(mut self: Pin<&mut Self>) {
183 self.finalize_state().ok();
184 }
185}
186
187impl<T> AsyncWrite for SharedFileWriter<T>
188where
189 T: AsyncWrite,
190{
191 fn poll_write(
192 self: Pin<&mut Self>,
193 cx: &mut Context<'_>,
194 buf: &[u8],
195 ) -> Poll<io::Result<usize>> {
196 let this = self.project();
197 let poll = this.file.poll_write(cx, buf);
198 Self::handle_poll_write_result(this.sentinel, poll)
199 }
200
201 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
202 let this = self.project();
203 match this.file.poll_flush(cx) {
204 Poll::Ready(result) => match result {
205 Ok(()) => {
206 Self::sync_committed_and_written(this.sentinel);
207 this.sentinel.wake_readers();
208 Poll::Ready(Ok(()))
209 }
210 Err(e) => {
211 this.sentinel.state.store(WriteState::Failed);
212 this.sentinel.wake_readers();
213 Poll::Ready(Err(e))
214 }
215 },
216 Poll::Pending => Poll::Pending,
217 }
218 }
219
220 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
221 let this = self.project();
222 match this.file.poll_shutdown(cx) {
223 Poll::Ready(result) => match result {
224 Ok(()) => {
225 if let WriteState::Pending(_committed, written) = this.sentinel.state.load() {
226 debug_assert_eq!(_committed, written);
227 this.sentinel.state.store(WriteState::Completed(written));
228 }
229
230 this.sentinel.wake_readers();
233 Poll::Ready(Ok(()))
234 }
235 Err(e) => {
236 this.sentinel.state.store(WriteState::Failed);
237 this.sentinel.wake_readers();
238 Poll::Ready(Err(e))
239 }
240 },
241 Poll::Pending => Poll::Pending,
242 }
243 }
244
245 fn poll_write_vectored(
246 self: Pin<&mut Self>,
247 cx: &mut Context<'_>,
248 bufs: &[IoSlice<'_>],
249 ) -> Poll<Result<usize, Error>> {
250 let this = self.project();
251 let poll = this.file.poll_write_vectored(cx, bufs);
252 Self::handle_poll_write_result(this.sentinel, poll)
253 }
254
255 fn is_write_vectored(&self) -> bool {
256 self.file.is_write_vectored()
257 }
258}