1use super::{Config, Error, Queue};
10use crate::Context;
11use commonware_codec::CodecShared;
12use commonware_utils::{
13 channel::mpsc,
14 sync::{AsyncMutex, AsyncMutexGuard},
15};
16use std::{ops::Range, sync::Arc};
17use tracing::debug;
18
19type Cell<E, V> = Arc<AsyncMutex<Option<Queue<E, V>>>>;
26
27fn take<E: Context, V: CodecShared>(
29 guard: &mut AsyncMutexGuard<'_, Option<Queue<E, V>>>,
30) -> Result<Queue<E, V>, Error> {
31 guard.take().ok_or(Error::Unavailable)
32}
33
34fn peek<'a, E: Context, V: CodecShared>(
36 guard: &'a AsyncMutexGuard<'_, Option<Queue<E, V>>>,
37) -> Result<&'a Queue<E, V>, Error> {
38 guard.as_ref().ok_or(Error::Unavailable)
39}
40
41fn peek_mut<'a, E: Context, V: CodecShared>(
43 guard: &'a mut AsyncMutexGuard<'_, Option<Queue<E, V>>>,
44) -> Result<&'a mut Queue<E, V>, Error> {
45 guard.as_mut().ok_or(Error::Unavailable)
46}
47
48pub struct Writer<E: Context, V: CodecShared> {
55 queue: Cell<E, V>,
56 notify: mpsc::Sender<()>,
57}
58
59impl<E: Context, V: CodecShared> Clone for Writer<E, V> {
60 fn clone(&self) -> Self {
61 Self {
62 queue: self.queue.clone(),
63 notify: self.notify.clone(),
64 }
65 }
66}
67
68impl<E: Context, V: CodecShared> Writer<E, V> {
69 pub async fn enqueue(&self, item: V) -> Result<u64, Error> {
76 let mut guard = self.queue.lock().await;
77 let (queue, pos) = take(&mut guard)?.enqueue(item).await?;
78 *guard = Some(queue);
79 drop(guard);
80
81 let _ = self.notify.try_send(());
85
86 debug!(position = pos, "writer: enqueued item");
87 Ok(pos)
88 }
89
90 pub async fn enqueue_bulk(
98 &self,
99 items: impl IntoIterator<Item = V>,
100 ) -> Result<Range<u64>, Error> {
101 let mut guard = self.queue.lock().await;
102 let mut queue = take(&mut guard)?;
103 let start = queue.size();
104 for item in items {
105 (queue, _) = queue.append(item).await?;
106 }
107 let end = queue.size();
108 if end > start {
109 queue = queue.commit().await?;
110 }
111 *guard = Some(queue);
112 drop(guard);
113
114 if start < end {
115 let _ = self.notify.try_send(());
116 }
117 debug!(start, end, "writer: enqueued bulk");
118 Ok(start..end)
119 }
120
121 pub async fn append(&self, item: V) -> Result<u64, Error> {
129 let mut guard = self.queue.lock().await;
130 let (queue, pos) = take(&mut guard)?.append(item).await?;
131 *guard = Some(queue);
132 drop(guard);
133 let _ = self.notify.try_send(());
134 debug!(position = pos, "writer: appended item");
135 Ok(pos)
136 }
137
138 pub async fn commit(&self) -> Result<(), Error> {
140 let mut guard = self.queue.lock().await;
141 let queue = take(&mut guard)?.commit().await?;
142 *guard = Some(queue);
143 Ok(())
144 }
145
146 pub async fn sync(&self) -> Result<(), Error> {
148 let mut guard = self.queue.lock().await;
149 let queue = take(&mut guard)?.sync().await?;
150 *guard = Some(queue);
151 Ok(())
152 }
153
154 pub async fn size(&self) -> Result<u64, Error> {
156 Ok(peek(&self.queue.lock().await)?.size())
157 }
158}
159
160pub struct Reader<E: Context, V: CodecShared> {
166 queue: Cell<E, V>,
167 notify: mpsc::Receiver<()>,
168}
169
170impl<E: Context, V: CodecShared> Reader<E, V> {
171 pub async fn recv(&mut self) -> Result<Option<(u64, V)>, Error> {
182 loop {
183 if let Some(item) = self.dequeue().await? {
185 return Ok(Some(item));
186 }
187
188 if self.notify.recv().await.is_none() {
191 return self.dequeue().await;
193 }
194 }
195 }
196
197 pub async fn try_recv(&mut self) -> Result<Option<(u64, V)>, Error> {
205 let _ = self.notify.try_recv();
207
208 self.dequeue().await
209 }
210
211 async fn dequeue(&self) -> Result<Option<(u64, V)>, Error> {
213 peek_mut(&mut self.queue.lock().await)?.dequeue().await
214 }
215
216 pub async fn ack(&self, position: u64) -> Result<(), Error> {
222 peek_mut(&mut self.queue.lock().await)?.ack(position)
223 }
224
225 pub async fn ack_up_to(&self, up_to: u64) -> Result<(), Error> {
231 peek_mut(&mut self.queue.lock().await)?.ack_up_to(up_to)
232 }
233
234 pub async fn ack_floor(&self) -> Result<u64, Error> {
236 Ok(peek(&self.queue.lock().await)?.ack_floor())
237 }
238
239 pub async fn read_position(&self) -> Result<u64, Error> {
241 Ok(peek(&self.queue.lock().await)?.read_position())
242 }
243
244 pub async fn is_empty(&self) -> Result<bool, Error> {
246 Ok(peek(&self.queue.lock().await)?.is_empty())
247 }
248
249 pub async fn reset(&self) -> Result<(), Error> {
251 peek_mut(&mut self.queue.lock().await)?.reset();
252 Ok(())
253 }
254}
255
256pub async fn init<E: Context, V: CodecShared>(
281 context: E,
282 cfg: Config<V::Cfg>,
283) -> Result<(Writer<E, V>, Reader<E, V>), Error> {
284 let queue = Arc::new(AsyncMutex::new(Some(Queue::init(context, cfg).await?)));
285 let (notify_tx, notify_rx) = mpsc::channel(1);
286
287 let writer = Writer {
288 queue: queue.clone(),
289 notify: notify_tx,
290 };
291
292 let reader = Reader {
293 queue,
294 notify: notify_rx,
295 };
296
297 Ok((writer, reader))
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303 use commonware_codec::RangeCfg;
304 use commonware_macros::{select, test_traced};
305 use commonware_runtime::{
306 BufferPooler, Clock, Runner, Spawner, Supervisor as _, buffer::paged::CacheRef,
307 deterministic,
308 };
309 use commonware_utils::{NZU16, NZU64, NZUsize};
310 use std::num::{NonZeroU16, NonZeroUsize};
311
312 const PAGE_SIZE: NonZeroU16 = NZU16!(1024);
313 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(10);
314
315 fn test_config(partition: &str, pooler: &impl BufferPooler) -> Config<(RangeCfg<usize>, ())> {
316 Config {
317 partition: partition.into(),
318 items_per_section: NZU64!(10),
319 compression: None,
320 codec_config: ((0..).into(), ()),
321 page_cache: CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE),
322 write_buffer: NZUsize!(4096),
323 replay_buffer: NZUsize!(4096),
324 }
325 }
326
327 #[test_traced]
328 fn test_shared_basic() {
329 let executor = deterministic::Runner::default();
330 executor.start(|context| async move {
331 let cfg = test_config("test_shared_basic", &context);
332 let (writer, mut reader) = init(context, cfg).await.unwrap();
333
334 let pos = writer.enqueue(b"hello".to_vec()).await.unwrap();
336 assert_eq!(pos, 0);
337
338 let (recv_pos, item) = reader.recv().await.unwrap().unwrap();
340 assert_eq!(recv_pos, 0);
341 assert_eq!(item, b"hello".to_vec());
342
343 reader.ack(recv_pos).await.unwrap();
345 assert!(reader.is_empty().await.unwrap());
346 });
347 }
348
349 #[test_traced]
350 fn test_shared_append_commit() {
351 let executor = deterministic::Runner::default();
352 executor.start(|context| async move {
353 let cfg = test_config("test_shared_append_commit", &context);
354 let (writer, mut reader) = init(context, cfg).await.unwrap();
355
356 for i in 0..5u8 {
358 let pos = writer.append(vec![i]).await.unwrap();
359 assert_eq!(pos, i as u64);
360 }
361
362 let (pos, item) = reader.recv().await.unwrap().unwrap();
364 assert_eq!(pos, 0);
365 assert_eq!(item, vec![0]);
366
367 writer.commit().await.unwrap();
369
370 for i in 1..5 {
372 let (pos, item) = reader.recv().await.unwrap().unwrap();
373 assert_eq!(pos, i);
374 assert_eq!(item, vec![i as u8]);
375 reader.ack(pos).await.unwrap();
376 }
377
378 reader.ack(0).await.unwrap();
379 assert!(reader.is_empty().await.unwrap());
380 });
381 }
382
383 #[test_traced]
384 fn test_shared_enqueue_bulk() {
385 let executor = deterministic::Runner::default();
386 executor.start(|context| async move {
387 let cfg = test_config("test_shared_bulk", &context);
388 let (writer, mut reader) = init(context, cfg).await.unwrap();
389
390 let range = writer
391 .enqueue_bulk((0..5u8).map(|i| vec![i]))
392 .await
393 .unwrap();
394 assert_eq!(range, 0..5);
395
396 for i in 0..5 {
397 let (pos, item) = reader.recv().await.unwrap().unwrap();
398 assert_eq!(pos, i);
399 assert_eq!(item, vec![i as u8]);
400 reader.ack(pos).await.unwrap();
401 }
402 assert!(reader.is_empty().await.unwrap());
403 });
404 }
405
406 #[test_traced]
407 fn test_shared_concurrent() {
408 let executor = deterministic::Runner::default();
409 executor.start(|context| async move {
410 let cfg = test_config("test_shared_concurrent", &context);
411 let (writer, mut reader) = init(context.child("storage"), cfg).await.unwrap();
412
413 let writer_handle = context.child("writer").spawn(|_ctx| async move {
415 for i in 0..10u8 {
416 writer.enqueue(vec![i]).await.unwrap();
417 }
418 writer
419 });
420
421 let mut received = Vec::new();
423 for _ in 0..10 {
424 let (pos, item) = reader.recv().await.unwrap().unwrap();
425 received.push((pos, item.clone()));
426 reader.ack(pos).await.unwrap();
427 }
428
429 for (i, (pos, item)) in received.iter().enumerate() {
431 assert_eq!(*pos, i as u64);
432 assert_eq!(*item, vec![i as u8]);
433 }
434
435 let _ = writer_handle.await.unwrap();
436 });
437 }
438
439 #[test_traced]
440 fn test_shared_select() {
441 let executor = deterministic::Runner::default();
442 executor.start(|context| async move {
443 let cfg = test_config("test_shared_select", &context);
444 let (writer, mut reader) = init(context.child("storage"), cfg).await.unwrap();
445
446 writer.enqueue(b"test".to_vec()).await.unwrap();
448
449 let result = select! {
451 item = reader.recv() => item,
452 _ = context.sleep(std::time::Duration::from_secs(1)) => {
453 panic!("timeout")
454 },
455 };
456
457 let (pos, item) = result.unwrap().unwrap();
458 assert_eq!(pos, 0);
459 assert_eq!(item, b"test".to_vec());
460
461 reader.ack(pos).await.unwrap();
462 });
463 }
464
465 #[test_traced]
466 fn test_shared_writer_dropped() {
467 let executor = deterministic::Runner::default();
468 executor.start(|context| async move {
469 let cfg = test_config("test_shared_writer_dropped", &context);
470 let (writer, mut reader) = init(context.child("storage"), cfg).await.unwrap();
471
472 writer.enqueue(b"item1".to_vec()).await.unwrap();
474 writer.enqueue(b"item2".to_vec()).await.unwrap();
475
476 let queue = writer.queue.clone();
478 drop(writer);
479
480 let (pos1, _) = reader.recv().await.unwrap().unwrap();
482 reader.ack(pos1).await.unwrap();
483
484 let (pos2, _) = reader.recv().await.unwrap().unwrap();
485 reader.ack(pos2).await.unwrap();
486
487 let result = reader.recv().await.unwrap();
489 assert!(result.is_none());
490
491 drop(reader);
492 let _ = Arc::try_unwrap(queue)
493 .unwrap_or_else(|_| panic!("queue should have a single reference"))
494 .into_inner();
495 });
496 }
497
498 #[test_traced]
499 fn test_shared_try_recv() {
500 let executor = deterministic::Runner::default();
501 executor.start(|context| async move {
502 let cfg = test_config("test_shared_try_recv", &context);
503 let (writer, mut reader) = init(context, cfg).await.unwrap();
504
505 let result = reader.try_recv().await.unwrap();
507 assert!(result.is_none());
508
509 writer.enqueue(b"item".to_vec()).await.unwrap();
511 let (pos, item) = reader.try_recv().await.unwrap().unwrap();
512 assert_eq!(pos, 0);
513 assert_eq!(item, b"item".to_vec());
514
515 reader.ack(pos).await.unwrap();
516 });
517 }
518
519 #[test_traced]
520 fn test_shared_multiple_writers() {
521 let executor = deterministic::Runner::default();
522 executor.start(|context| async move {
523 let cfg = test_config("test_shared_multi_writer", &context);
524 let (writer, mut reader) = init(context.child("storage"), cfg).await.unwrap();
525
526 let writer2 = writer.clone();
528
529 let handle1 =
531 context
532 .child("writer")
533 .with_attribute("index", 1)
534 .spawn(|_ctx| async move {
535 for i in 0..5u8 {
536 writer.enqueue(vec![i]).await.unwrap();
537 }
538 writer
539 });
540
541 let handle2 =
542 context
543 .child("writer")
544 .with_attribute("index", 2)
545 .spawn(|_ctx| async move {
546 for i in 5..10u8 {
547 writer2.enqueue(vec![i]).await.unwrap();
548 }
549 });
550
551 let mut received = Vec::new();
553 for _ in 0..10 {
554 let (pos, item) = reader.recv().await.unwrap().unwrap();
555 received.push(item[0]);
556 reader.ack(pos).await.unwrap();
557 }
558
559 received.sort();
561 assert_eq!(received, (0..10u8).collect::<Vec<_>>());
562
563 let _ = handle1.await.unwrap();
564 handle2.await.unwrap();
565 });
566 }
567}