1use crate::errors::FxfsError;
6use crate::object_handle::{ObjectHandle, ReadObjectHandle};
7use anyhow::{Error, anyhow, ensure};
8use event_listener::{Event, EventListener};
9use fuchsia_sync::Mutex;
10use std::ops::Deref;
11use std::sync::Arc;
12use storage_device::buffer::BufferFuture;
13use storage_units::BlockSize;
14
15pub const CHUNK_SIZE: BlockSize = BlockSize::SIZE_128KIB;
16
17fn block_aligned_size(source: &impl ReadObjectHandle) -> u64 {
18 source.block_size().align_up(source.get_size()).unwrap()
19}
20
21#[repr(transparent)]
24#[derive(Clone)]
25pub struct CachedChunk(Arc<Box<[u8]>>);
26
27impl std::fmt::Debug for CachedChunk {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
29 f.debug_struct("CachedChunk").field("len", &self.len()).finish()
30 }
31}
32
33impl Deref for CachedChunk {
34 type Target = [u8];
35
36 fn deref(&self) -> &Self::Target {
37 &**self.0
38 }
39}
40
41#[derive(Default, Debug)]
42enum Chunk {
43 #[default]
44 Missing,
45 Pending,
46 Present(CachedChunk),
47 Expired(Box<[u8]>),
50}
51
52impl Chunk {
53 fn maybe_purge(&mut self) -> Option<Box<[u8]>> {
57 let this = std::mem::take(self);
58 match this {
59 Chunk::Expired(data) => Some(data),
60 Chunk::Present(chunk) => {
61 match Arc::try_unwrap(chunk.0) {
62 Ok(data) => *self = Chunk::Expired(data),
63 Err(chunk) => *self = Chunk::Present(CachedChunk(chunk)),
64 }
65 None
66 }
67 _ => {
68 *self = this;
69 None
70 }
71 }
72 }
73}
74
75pub struct CachingObjectHandle<S> {
78 source: S,
79
80 chunks: Mutex<Vec<Chunk>>,
83
84 event: Event,
90}
91
92unsafe impl<S> Sync for CachingObjectHandle<S> {}
94
95#[fxfs_trace::trace]
96impl<S: ReadObjectHandle> CachingObjectHandle<S> {
97 pub fn new(source: S) -> Self {
98 let block_size = source.block_size();
99 assert!(block_size.is_aligned(CHUNK_SIZE.get()));
100 let aligned_size = block_aligned_size(&source);
101 let chunk_count = CHUNK_SIZE.align_up_to_blocks(aligned_size) as usize;
102
103 let mut chunks = Vec::<Chunk>::new();
104 chunks.resize_with(chunk_count, Default::default);
105 Self { source, chunks: Mutex::new(chunks), event: Event::new() }
106 }
107
108 pub fn source(&self) -> &S {
109 &self.source
110 }
111
112 pub async fn read(&self, offset: usize) -> Result<CachedChunk, Error> {
116 ensure!(offset < self.source.get_size() as usize, FxfsError::OutOfRange);
117 let chunk_num = (offset as u64 / CHUNK_SIZE) as usize;
118
119 enum Action {
120 Wait(EventListener),
121 Load,
122 }
123 loop {
124 let action = {
125 let mut chunks = self.chunks.lock();
126 match std::mem::take(&mut chunks[chunk_num]) {
127 Chunk::Missing => {
128 chunks[chunk_num] = Chunk::Pending;
129 Action::Load
130 }
131 Chunk::Pending => {
132 chunks[chunk_num] = Chunk::Pending;
133 Action::Wait(self.event.listen())
134 }
135 Chunk::Present(cached_chunk) => {
136 chunks[chunk_num] = Chunk::Present(cached_chunk.clone());
137 return Ok(cached_chunk);
138 }
139 Chunk::Expired(data) => {
140 let cached_chunk = CachedChunk(Arc::new(data));
141 chunks[chunk_num] = Chunk::Present(cached_chunk.clone());
142 return Ok(cached_chunk);
143 }
144 }
145 };
146 match action {
147 Action::Wait(listener) => {
148 listener.await;
149 }
150 Action::Load => {
151 return self.load(chunk_num).await;
152 }
153 }
154 }
155 }
156
157 pub fn try_read(&self, offset: usize) -> Option<CachedChunk> {
158 if offset >= self.source.get_size() as usize {
159 return None;
160 }
161 let chunk_num = (offset as u64 / CHUNK_SIZE) as usize;
162 let mut chunks = self.chunks.lock();
163 match &chunks[chunk_num] {
164 Chunk::Present(cached_chunk) => Some(cached_chunk.clone()),
165 Chunk::Expired(_) => {
166 let Chunk::Expired(data) = std::mem::take(&mut chunks[chunk_num]) else {
167 unreachable!()
168 };
169 let cached_chunk = CachedChunk(Arc::new(data));
170 chunks[chunk_num] = Chunk::Present(cached_chunk.clone());
171 Some(cached_chunk)
172 }
173 _ => None,
174 }
175 }
176
177 #[trace]
178 async fn load(&self, chunk_num: usize) -> Result<CachedChunk, Error> {
179 let drop_guard = scopeguard::guard((), |_| {
182 {
183 let mut chunks = self.chunks.lock();
184 debug_assert!(matches!(chunks[chunk_num], Chunk::Pending));
185 chunks[chunk_num] = Chunk::Missing;
186 }
187 self.event.notify(usize::MAX);
188 });
189
190 let read_start = chunk_num as u64 * CHUNK_SIZE;
191 let len =
192 (std::cmp::min(read_start + CHUNK_SIZE, self.source.get_size()) - read_start) as usize;
193 let aligned_len = (std::cmp::min(read_start + CHUNK_SIZE, block_aligned_size(&self.source))
194 - read_start) as usize;
195
196 let mut read_buf = self.source.allocate_buffer(aligned_len).await;
197 let amount_read = self.source.read_aligned(read_start, read_buf.as_mut()).await?;
198 ensure!(amount_read >= len, anyhow!(FxfsError::Internal).context("Short read"));
199
200 log::debug!("COH {}: Read {len}@{read_start}", self.source.object_id());
201
202 let data = read_buf.subslice(..len).to_vec().into_boxed_slice();
203 let cached_chunk = CachedChunk(Arc::new(data));
204
205 {
206 let mut chunks = self.chunks.lock();
207 debug_assert!(matches!(chunks[chunk_num], Chunk::Pending));
208 chunks[chunk_num] = Chunk::Present(cached_chunk.clone());
209 }
210 self.event.notify(usize::MAX);
211
212 scopeguard::ScopeGuard::into_inner(drop_guard);
213 return Ok(cached_chunk);
214 }
215
216 pub fn purge(&self) {
221 let mut to_deallocate = vec![];
223 let mut chunks = self.chunks.lock();
224 for chunk in chunks.iter_mut() {
225 if let Some(data) = chunk.maybe_purge() {
226 to_deallocate.push(data);
227 }
228 }
229 log::debug!(
230 "COH {}: Purging {} cached chunks ({} bytes)",
231 self.source.object_id(),
232 to_deallocate.len(),
233 to_deallocate.len() as u64 * CHUNK_SIZE
234 );
235 }
236
237 pub fn clear(&self) {
239 let mut to_deallocate = Vec::new();
240 {
241 let mut chunks = self.chunks.lock();
242 for chunk in chunks.iter_mut() {
243 if matches!(chunk, Chunk::Present(_) | Chunk::Expired(_)) {
244 to_deallocate.push(std::mem::take(chunk));
245 }
246 }
247 }
248 log::debug!(
249 "COH {}: Clearing {} cached chunks ({} bytes)",
250 self.source.object_id(),
251 to_deallocate.len(),
252 to_deallocate.len() as u64 * CHUNK_SIZE
253 );
254 }
255}
256
257impl<S: ReadObjectHandle> ObjectHandle for CachingObjectHandle<S> {
258 fn set_trace(&self, v: bool) {
259 self.source.set_trace(v);
260 }
261
262 fn object_id(&self) -> u64 {
263 self.source.object_id()
264 }
265
266 fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
267 self.source.allocate_buffer(size)
268 }
269
270 fn block_size(&self) -> BlockSize {
271 self.source.block_size()
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::{CHUNK_SIZE, CachingObjectHandle};
278 use crate::errors::FxfsError;
279 use crate::object_handle::{ObjectHandle, ReadObjectHandle};
280 use anyhow::{Error, anyhow, ensure};
281 use async_trait::async_trait;
282 use event_listener::Event;
283 use std::sync::Arc;
284 use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
285 use storage_device::Device;
286 use storage_device::buffer::{BufferFuture, MutableBufferRef};
287 use storage_device::fake_device::FakeDevice;
288 use storage_units::BlockSize;
289
290 fn fill_buf(buf: &mut [u8], counter: u8) {
292 for (i, chunk) in buf.chunks_exact_mut(2).enumerate() {
293 chunk[0] = counter;
294 chunk[1] = i as u8;
295 }
296 }
297
298 fn make_buf(counter: u8, size: usize) -> Vec<u8> {
300 let mut buf = vec![0; size];
301 fill_buf(&mut buf, counter);
302 buf
303 }
304
305 struct FakeSource {
306 device: Arc<dyn Device>,
307 size: usize,
308 started: AtomicBool,
309 allow_reads: AtomicBool,
310 wake: Event,
311 counter: AtomicU8,
312 }
313
314 impl FakeSource {
315 fn new(device: Arc<dyn Device>, size: usize) -> Self {
317 FakeSource {
318 started: AtomicBool::new(false),
319 allow_reads: AtomicBool::new(true),
320 size,
321 wake: Event::new(),
322 device,
323 counter: AtomicU8::new(1),
324 }
325 }
326
327 fn start(&self) {
328 self.started.store(true, Ordering::SeqCst);
329 self.wake.notify(usize::MAX);
330 }
331
332 fn allow_reads(&self, allow: bool) {
335 self.allow_reads.store(allow, Ordering::SeqCst);
336 }
337
338 async fn wait_for_start(&self) {
339 while !self.started.load(Ordering::SeqCst) {
340 let listener = self.wake.listen();
341 if self.started.load(Ordering::SeqCst) {
342 break;
343 }
344 listener.await;
345 }
346 }
347 }
348
349 #[async_trait]
350 impl ReadObjectHandle for FakeSource {
351 async fn read_aligned(
352 &self,
353 offset: u64,
354 mut buf: MutableBufferRef<'_>,
355 ) -> Result<usize, Error> {
356 let block_size = self.block_size();
357 ensure!(block_size.is_aligned(offset), FxfsError::InvalidArgs);
358 ensure!(block_size.is_aligned(buf.len() as u64), FxfsError::InvalidArgs);
359 ensure!(self.allow_reads.load(Ordering::SeqCst), anyhow!("Received unexpected read"));
360 let counter = self.counter.fetch_add(1, Ordering::Relaxed);
361 self.wait_for_start().await;
362 let mut data = vec![0u8; buf.len()];
363 fill_buf(&mut data, counter);
364 buf.copy_from_slice(&data);
365 Ok(buf.len())
366 }
367
368 fn get_size(&self) -> u64 {
369 self.size as u64
370 }
371 }
372
373 impl ObjectHandle for FakeSource {
374 fn object_id(&self) -> u64 {
375 0
376 }
377
378 fn block_size(&self) -> BlockSize {
379 BlockSize::new(self.device.block_size()).unwrap()
380 }
381
382 fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
383 self.device.allocate_buffer(size)
384 }
385 }
386
387 #[fuchsia::test]
388 async fn test_read_with_missing_chunk() {
389 let device = Arc::new(FakeDevice::new(1024, 512));
390 let source = FakeSource::new(device, 4096);
391 source.start();
392 let caching_object_handle = CachingObjectHandle::new(source);
393
394 let chunk = caching_object_handle.read(0).await.unwrap();
395 assert_eq!(&*chunk, make_buf(1, 4096));
396 }
397
398 #[fuchsia::test]
399 async fn test_read_with_present_chunk() {
400 let device = Arc::new(FakeDevice::new(1024, 512));
401 let source = FakeSource::new(device, 4096);
402 source.start();
403 let caching_object_handle = CachingObjectHandle::new(source);
404
405 let expected = make_buf(1, 4096);
406 let chunk = caching_object_handle.read(0).await.unwrap();
407 assert_eq!(&*chunk, expected);
408
409 let chunk = caching_object_handle.read(0).await.unwrap();
411 assert_eq!(&*chunk, expected);
412 }
413
414 #[fuchsia::test]
415 async fn test_read_with_pending_chunk() {
416 let device = Arc::new(FakeDevice::new(1024, 512));
417 let source = FakeSource::new(device, 8192);
418 let caching_object_handle = CachingObjectHandle::new(source);
419
420 let mut read_fut1 = std::pin::pin!(caching_object_handle.read(0));
422 let mut read_fut2 = std::pin::pin!(caching_object_handle.read(4096));
423
424 assert!(futures::poll!(&mut read_fut1).is_pending());
427 assert!(futures::poll!(&mut read_fut2).is_pending());
429 caching_object_handle.source.start();
430 assert!(futures::poll!(&mut read_fut2).is_pending());
432 let expected = make_buf(1, 8192);
435 assert_eq!(&*read_fut1.await.unwrap(), expected);
436 assert_eq!(&*read_fut2.await.unwrap(), expected);
438 }
439
440 #[fuchsia::test]
441 async fn test_read_with_notification_for_other_chunk() {
442 let device = Arc::new(FakeDevice::new(1024, 512));
443 let source = FakeSource::new(device, (CHUNK_SIZE + 4096) as usize);
444 let caching_object_handle = CachingObjectHandle::new(source);
445
446 let mut read_fut1 = std::pin::pin!(caching_object_handle.read(0));
447 let mut read_fut2 = std::pin::pin!(caching_object_handle.read(CHUNK_SIZE.get() as usize));
448 let mut read_fut3 = std::pin::pin!(caching_object_handle.read(0));
449
450 assert!(futures::poll!(&mut read_fut1).is_pending());
453 assert!(futures::poll!(&mut read_fut2).is_pending());
454 assert!(futures::poll!(&mut read_fut3).is_pending());
456 caching_object_handle.source.start();
457 assert!(futures::poll!(&mut read_fut3).is_pending());
459 assert_eq!(&*read_fut2.await.unwrap(), make_buf(2, 4096));
461 assert!(futures::poll!(&mut read_fut3).is_pending());
464 let expected = make_buf(1, CHUNK_SIZE.get() as usize);
467 assert_eq!(&*read_fut1.await.unwrap(), expected);
468 assert_eq!(&*read_fut3.await.unwrap(), expected);
470 }
471
472 #[fuchsia::test]
473 async fn test_read_with_dropped_future() {
474 let device = Arc::new(FakeDevice::new(1024, 512));
475 let source = FakeSource::new(device, 4096);
476 let caching_object_handle = CachingObjectHandle::new(source);
477
478 let mut read_fut2 = std::pin::pin!(caching_object_handle.read(0));
479 {
480 let mut read_fut1 = std::pin::pin!(caching_object_handle.read(0));
481
482 assert!(futures::poll!(&mut read_fut1).is_pending());
485 assert!(futures::poll!(&mut read_fut2).is_pending());
487 caching_object_handle.source.start();
488 assert!(futures::poll!(&mut read_fut2).is_pending());
490 }
491 assert_eq!(&*read_fut2.await.unwrap(), make_buf(2, 4096));
496 }
497
498 #[fuchsia::test]
499 async fn test_read_past_end_of_source() {
500 let device = Arc::new(FakeDevice::new(1024, 512));
501 let source = FakeSource::new(device, 300);
502 source.start();
503 let caching_object_handle = CachingObjectHandle::new(source);
504
505 caching_object_handle.read(500).await.expect_err("Read should fail");
506 }
507
508 #[fuchsia::test]
509 async fn test_read_to_end_of_source() {
510 let device = Arc::new(FakeDevice::new(1024, 512));
511 const SOURCE_SIZE: usize = 300;
512 let source = FakeSource::new(device, SOURCE_SIZE);
513 source.start();
514 let caching_object_handle = CachingObjectHandle::new(source);
515
516 let chunk = caching_object_handle.read(0).await.unwrap();
517 assert_eq!(&*chunk, make_buf(1, SOURCE_SIZE));
518 }
519
520 #[fuchsia::test]
521 async fn test_chunk_purging() {
522 let device = Arc::new(FakeDevice::new(1024, 512));
523 let source = Arc::new(FakeSource::new(device, (CHUNK_SIZE + 4096) as usize));
524 source.start();
525 let caching_object_handle =
526 CachingObjectHandle::new(source.clone() as Arc<dyn ReadObjectHandle>);
527
528 let _chunk1 = caching_object_handle.read(0).await.unwrap();
529 caching_object_handle.read(CHUNK_SIZE.get() as usize).await.unwrap();
531
532 source.allow_reads(false);
533
534 caching_object_handle.purge();
537 caching_object_handle.read(0).await.unwrap();
538 caching_object_handle.read(CHUNK_SIZE.get() as usize).await.unwrap();
539
540 caching_object_handle.purge();
541 caching_object_handle.read(0).await.unwrap();
542 caching_object_handle.read(CHUNK_SIZE.get() as usize).await.unwrap();
543
544 caching_object_handle.purge();
546 caching_object_handle.purge();
547 caching_object_handle.read(0).await.unwrap();
548 caching_object_handle
549 .read(CHUNK_SIZE.get() as usize)
550 .await
551 .expect_err("Chunk was not purged");
552 }
553
554 #[fuchsia::test]
555 async fn test_chunk_clearing() {
556 let device = Arc::new(FakeDevice::new(1024, 512));
557 let source = Arc::new(FakeSource::new(device, (CHUNK_SIZE + 4096) as usize));
558 source.start();
559 let caching_object_handle =
560 CachingObjectHandle::new(source.clone() as Arc<dyn ReadObjectHandle>);
561
562 let chunk1 = caching_object_handle.read(0).await.unwrap();
563 caching_object_handle.read(CHUNK_SIZE.get() as usize).await.unwrap();
564 assert!(caching_object_handle.try_read(0).is_some());
565 assert!(caching_object_handle.try_read(CHUNK_SIZE.get() as usize).is_some());
566
567 source.allow_reads(false);
568
569 caching_object_handle.clear();
571 assert!(caching_object_handle.try_read(0).is_none());
572 assert!(caching_object_handle.try_read(CHUNK_SIZE.get() as usize).is_none());
573
574 assert_eq!(&*chunk1, make_buf(1, CHUNK_SIZE.get() as usize));
576
577 source.allow_reads(true);
580 source.started.store(false, Ordering::SeqCst);
581 let mut pending_fut = std::pin::pin!(caching_object_handle.read(0));
582 assert!(futures::poll!(&mut pending_fut).is_pending());
583 caching_object_handle.clear();
584 source.start();
585 assert_eq!(&*pending_fut.await.unwrap(), make_buf(3, CHUNK_SIZE.get() as usize));
586 }
587}