1mod queue;
8
9use std::{collections::VecDeque, future::Future, sync::Arc, time::Duration};
10
11use gpui_kit::{App, BackgroundExecutor, Task};
12use parking_lot::Mutex;
13use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
14use web_time::Instant;
15
16pub use queue::WriteCoalescing;
17
18use crate::{
19 highlight::{HighlightPolicy, RowChange, RowStamps, SparseStamps, Stamp},
20 model::{ColumnIx, ColumnSet, GridColumns, GridRow},
21};
22use queue::{QueueCounters, WriteQueue};
23
24pub const PAGE_SIZE: usize = 1024;
27
28pub(crate) const SNAPSHOT_INTERVAL: Duration = Duration::from_millis(8);
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34pub enum PinnedEdge {
35 Top,
37 Bottom,
39}
40
41impl PinnedEdge {
42 const fn ix(self) -> usize {
43 match self {
44 Self::Top => 0,
45 Self::Bottom => 1,
46 }
47 }
48}
49
50#[derive(Clone, Debug, PartialEq)]
55pub struct GridProgress {
56 fraction: Option<f32>,
57 label: Option<Arc<str>>,
58}
59
60impl GridProgress {
61 pub fn indeterminate() -> Self {
63 Self {
64 fraction: None,
65 label: None,
66 }
67 }
68
69 pub fn new(fraction: f32) -> Self {
72 Self {
73 fraction: (!fraction.is_nan()).then(|| fraction.clamp(0.0, 1.0)),
74 label: None,
75 }
76 }
77
78 pub fn with_label(mut self, label: impl Into<Arc<str>>) -> Self {
80 self.label = Some(label.into());
81 self
82 }
83
84 pub fn fraction(&self) -> Option<f32> {
86 self.fraction
87 }
88
89 pub fn label(&self) -> Option<&str> {
91 self.label.as_deref()
92 }
93
94 pub fn is_indeterminate(&self) -> bool {
96 self.fraction.is_none()
97 }
98}
99
100#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
103pub struct RowId {
104 ix: u32,
105 generation: u32,
106}
107
108impl RowId {
109 pub fn ix(self) -> usize {
111 self.ix as usize
112 }
113}
114
115struct Slot<R> {
116 row: Option<Arc<R>>,
117 generation: u32,
118 seq: u64,
119 stamps: Option<SparseStamps>,
120}
121
122impl<R> Clone for Slot<R> {
123 fn clone(&self) -> Self {
124 Self {
125 row: self.row.clone(),
126 generation: self.generation,
127 seq: self.seq,
128 stamps: self.stamps.clone(),
129 }
130 }
131}
132
133impl<R> Default for Slot<R> {
134 fn default() -> Self {
135 Self {
136 row: None,
137 generation: 0,
138 seq: 0,
139 stamps: None,
140 }
141 }
142}
143
144struct Page<R> {
145 slots: Vec<Slot<R>>,
146}
147
148impl<R> Clone for Page<R> {
149 fn clone(&self) -> Self {
150 Self {
151 slots: self.slots.clone(),
152 }
153 }
154}
155
156struct PinnedBand<R> {
159 rows: Vec<Arc<R>>,
160 stamps: Vec<Option<SparseStamps>>,
161}
162
163impl<R> Clone for PinnedBand<R> {
164 fn clone(&self) -> Self {
165 Self {
166 rows: self.rows.clone(),
167 stamps: self.stamps.clone(),
168 }
169 }
170}
171
172impl<R> Default for PinnedBand<R> {
173 fn default() -> Self {
174 Self {
175 rows: Vec::new(),
176 stamps: Vec::new(),
177 }
178 }
179}
180
181pub trait Clock: Send + Sync + 'static {
184 fn now(&self) -> Instant;
186}
187
188impl Clock for BackgroundExecutor {
191 fn now(&self) -> Instant {
192 BackgroundExecutor::now(self)
193 }
194}
195
196pub trait ReloadPolicy<R: GridRow>: Send + 'static {
203 fn resolve(&mut self, snapshot: Vec<Arc<R>>, live: Vec<Op<R>>) -> Vec<Op<R>>;
205}
206
207#[derive(Clone, Copy, Debug, Default)]
211pub struct ReplayLive;
212
213impl<R: GridRow> ReloadPolicy<R> for ReplayLive {
214 fn resolve(&mut self, snapshot: Vec<Arc<R>>, mut live: Vec<Op<R>>) -> Vec<Op<R>> {
215 let mut ops = Vec::with_capacity(live.len() + 1);
216 ops.push(Op::ReplaceAll(snapshot));
217 ops.append(&mut live);
218 ops
219 }
220}
221
222#[non_exhaustive]
224pub enum Op<R: GridRow> {
225 Upsert(Arc<R>),
227 UpsertSilent(Arc<R>),
229 MarkChanged(R::Key, ColumnSet),
231 Remove(R::Key),
233 ReplaceAll(Vec<Arc<R>>),
235 Clear,
237 FinishReload {
239 snapshot: Vec<Arc<R>>,
241 live: Vec<Op<R>>,
243 },
244 SetPinnedRows {
247 edge: PinnedEdge,
249 rows: Vec<Arc<R>>,
251 },
252 SetProgress(Option<GridProgress>),
255}
256
257type KeyIndex<K> =
262 imbl::GenericHashMap<K, RowId, FxBuildHasher, imbl::shared_ptr::DefaultSharedPtr>;
263
264struct FadingRow<R> {
267 row: Arc<R>,
268 removed: Stamp,
269 seq: u64,
270}
271
272impl<R> Clone for FadingRow<R> {
273 fn clone(&self) -> Self {
274 Self {
275 row: self.row.clone(),
276 removed: self.removed,
277 seq: self.seq,
278 }
279 }
280}
281
282type FadingMap<R> =
286 imbl::GenericHashMap<RowId, FadingRow<R>, FxBuildHasher, imbl::shared_ptr::DefaultSharedPtr>;
287
288#[derive(Clone, Debug, Default, PartialEq, Eq)]
290pub struct ChangeSet {
291 inserted: usize,
292 removed: Vec<RowId>,
293 faded: Vec<RowId>,
294 updated: Vec<RowId>,
295 columns: ColumnSet,
296 reset: bool,
297 parent: bool,
298 pinned: bool,
299 progress: bool,
300}
301
302impl ChangeSet {
303 pub fn merge(&mut self, other: &ChangeSet) {
305 self.inserted += other.inserted;
306 self.removed.extend_from_slice(&other.removed);
307 self.faded.extend_from_slice(&other.faded);
308 self.updated.extend_from_slice(&other.updated);
309 self.columns.union_with(other.columns);
310 self.reset |= other.reset;
311 self.parent |= other.parent;
312 self.pinned |= other.pinned;
313 self.progress |= other.progress;
314 }
315
316 pub fn has_structure_change(&self) -> bool {
318 self.inserted > 0 || !self.removed.is_empty() || self.reset
319 }
320
321 pub fn has_insertions_or_reset(&self) -> bool {
324 self.inserted > 0 || self.reset
325 }
326
327 pub fn is_reset(&self) -> bool {
330 self.reset
331 }
332
333 pub fn inserted(&self) -> usize {
335 self.inserted
336 }
337
338 pub fn removed(&self) -> &[RowId] {
344 &self.removed
345 }
346
347 pub fn faded(&self) -> &[RowId] {
353 &self.faded
354 }
355
356 pub fn has_parent_change(&self) -> bool {
361 self.parent
362 }
363
364 pub fn columns(&self) -> ColumnSet {
368 self.columns
369 }
370
371 pub fn updated(&self) -> &[RowId] {
380 &self.updated
381 }
382
383 pub fn has_pinned_change(&self) -> bool {
388 self.pinned
389 }
390
391 pub fn has_progress_change(&self) -> bool {
394 self.progress
395 }
396
397 pub fn is_empty(&self) -> bool {
399 self.inserted == 0
400 && self.removed.is_empty()
401 && self.faded.is_empty()
402 && self.updated.is_empty()
403 && self.columns.is_empty()
404 && !self.reset
405 && !self.parent
406 && !self.pinned
407 && !self.progress
408 }
409}
410
411pub struct StoreSnapshot<R: GridRow> {
413 version: u64,
414 pages: Vec<Arc<Page<R>>>,
415 index: KeyIndex<R::Key>,
416 capacity: usize,
417 len: usize,
418 epoch: Instant,
419 pinned: [Arc<PinnedBand<R>>; 2],
420 progress: Option<GridProgress>,
421 fading: FadingMap<R>,
422}
423
424impl<R: GridRow> StoreSnapshot<R> {
425 pub fn version(&self) -> u64 {
427 self.version
428 }
429
430 pub fn len(&self) -> usize {
432 self.len
433 }
434
435 pub fn is_empty(&self) -> bool {
437 self.len == 0
438 }
439
440 pub fn capacity(&self) -> usize {
442 self.capacity
443 }
444
445 pub fn epoch(&self) -> Instant {
447 self.epoch
448 }
449
450 fn slot(&self, ix: usize) -> Option<&Slot<R>> {
451 if ix >= self.capacity {
452 return None;
453 }
454 self.pages
455 .get(ix / PAGE_SIZE)
456 .and_then(|page| page.slots.get(ix % PAGE_SIZE))
457 }
458
459 fn live_slot(&self, id: RowId) -> Option<&Slot<R>> {
460 self.slot(id.ix())
461 .filter(|slot| slot.generation == id.generation && slot.row.is_some())
462 }
463
464 pub fn row(&self, id: RowId) -> Option<&Arc<R>> {
466 self.live_slot(id).and_then(|slot| slot.row.as_ref())
467 }
468
469 pub fn row_id(&self, key: &R::Key) -> Option<RowId> {
471 self.index.get(key).copied()
472 }
473
474 pub fn get(&self, key: &R::Key) -> Option<&Arc<R>> {
476 self.row_id(key).and_then(|id| self.row(id))
477 }
478
479 pub fn stamps(&self, id: RowId) -> Option<RowStamps<'_>> {
482 self.live_slot(id)
483 .and_then(|slot| slot.stamps.as_ref())
484 .map(SparseStamps::view)
485 }
486
487 pub fn seq(&self, id: RowId) -> u64 {
489 self.live_slot(id).map_or(0, |slot| slot.seq)
490 }
491
492 pub fn iter(&self) -> impl Iterator<Item = (RowId, &Arc<R>)> {
494 self.pages.iter().enumerate().flat_map(|(page_ix, page)| {
495 page.slots
496 .iter()
497 .enumerate()
498 .filter_map(move |(slot_ix, slot)| {
499 slot.row.as_ref().map(|row| {
500 let id = RowId {
501 ix: (page_ix * PAGE_SIZE + slot_ix) as u32,
502 generation: slot.generation,
503 };
504 (id, row)
505 })
506 })
507 })
508 }
509
510 pub fn pinned_rows(&self, edge: PinnedEdge) -> &[Arc<R>] {
514 &self.pinned[edge.ix()].rows
515 }
516
517 pub fn pinned_stamps(&self, edge: PinnedEdge, ix: usize) -> Option<RowStamps<'_>> {
520 self.pinned[edge.ix()]
521 .stamps
522 .get(ix)?
523 .as_ref()
524 .map(SparseStamps::view)
525 }
526
527 pub fn progress(&self) -> Option<&GridProgress> {
529 self.progress.as_ref()
530 }
531
532 pub fn fading_row(&self, id: RowId) -> Option<(&Arc<R>, Stamp)> {
537 self.fading
538 .get(&id)
539 .map(|fading| (&fading.row, fading.removed))
540 }
541
542 pub fn fading_rows(&self) -> impl Iterator<Item = (RowId, &Arc<R>, Stamp, u64)> {
546 self.fading
547 .iter()
548 .map(|(id, fading)| (*id, &fading.row, fading.removed, fading.seq))
549 }
550
551 pub fn stamp_bytes(&self) -> usize {
554 self.pages
555 .iter()
556 .flat_map(|page| page.slots.iter())
557 .filter_map(|slot| slot.stamps.as_ref())
558 .map(SparseStamps::heap_bytes)
559 .sum()
560 }
561}
562
563pub struct Ingest<R: GridRow> {
566 pages: Vec<Arc<Page<R>>>,
567 capacity: usize,
568 index: FxHashMap<R::Key, RowId>,
570 published_index: KeyIndex<R::Key>,
572 free: Vec<u32>,
573 next_seq: u64,
574 version: u64,
575 hot: FxHashSet<u32>,
576 epoch: Instant,
577 policy: HighlightPolicy,
578 reload: Box<dyn ReloadPolicy<R>>,
579 every: ColumnSet,
581 last_sweep_ms: u32,
582 pinned: [Arc<PinnedBand<R>>; 2],
583 progress: Option<GridProgress>,
584 fading: FadingMap<R>,
586 fading_keys: FxHashMap<R::Key, RowId>,
589 fade_queue: VecDeque<(RowId, Stamp)>,
593}
594
595impl<R: GridRow> Ingest<R> {
596 pub fn new(policy: HighlightPolicy, epoch: Instant) -> Self {
598 Self::with_reload_policy(policy, epoch, Box::new(ReplayLive))
599 }
600
601 pub fn with_reload_policy(
603 policy: HighlightPolicy,
604 epoch: Instant,
605 reload: Box<dyn ReloadPolicy<R>>,
606 ) -> Self {
607 Self {
608 pages: Vec::new(),
609 capacity: 0,
610 index: FxHashMap::default(),
611 published_index: KeyIndex::default(),
612 free: Vec::new(),
613 next_seq: 0,
614 version: 0,
615 hot: FxHashSet::default(),
616 epoch,
617 policy,
618 reload,
619 every: ColumnSet::first(R::Column::all().len()),
620 last_sweep_ms: 0,
621 pinned: Default::default(),
622 progress: None,
623 fading: FadingMap::default(),
624 fading_keys: FxHashMap::default(),
625 fade_queue: VecDeque::new(),
626 }
627 }
628
629 pub fn len(&self) -> usize {
631 self.index.len()
632 }
633
634 pub fn is_empty(&self) -> bool {
636 self.index.is_empty()
637 }
638
639 pub fn hot_rows(&self) -> usize {
641 self.hot.len()
642 }
643
644 fn now_ms(&self, now: Instant) -> u32 {
645 now.saturating_duration_since(self.epoch).as_millis() as u32
646 }
647
648 fn slot_mut(&mut self, ix: usize) -> &mut Slot<R> {
649 let page = Arc::make_mut(&mut self.pages[ix / PAGE_SIZE]);
650 &mut page.slots[ix % PAGE_SIZE]
651 }
652
653 fn allocate(&mut self) -> usize {
654 if let Some(ix) = self.free.pop() {
655 return ix as usize;
656 }
657 let ix = self.capacity;
658 if ix % PAGE_SIZE == 0 {
659 let mut slots = Vec::with_capacity(PAGE_SIZE);
660 slots.resize_with(PAGE_SIZE, Slot::default);
661 self.pages.push(Arc::new(Page { slots }));
662 }
663 self.capacity += 1;
664 ix
665 }
666
667 pub fn apply(&mut self, ops: impl IntoIterator<Item = Op<R>>, now: Instant) -> ChangeSet {
671 let now_ms = self.now_ms(now);
672 let mut changes = ChangeSet::default();
673 for op in ops {
674 self.apply_op(op, now_ms, &mut changes);
675 }
676 self.finish_batch(now_ms, changes)
677 }
678
679 fn finish_batch(&mut self, now_ms: u32, mut changes: ChangeSet) -> ChangeSet {
681 self.drop_faded(now_ms, &mut changes);
682 if !changes.is_empty() {
683 self.version += 1;
684 }
685 changes
686 }
687
688 fn apply_op(&mut self, op: Op<R>, now_ms: u32, changes: &mut ChangeSet) {
689 {
690 match op {
691 Op::Upsert(row) => self.upsert(row, now_ms, true, changes),
692 Op::UpsertSilent(row) => self.upsert(row, now_ms, false, changes),
693 Op::MarkChanged(key, columns) => {
694 if let Some(&id) = self.index.get(&key) {
695 let stamp = Stamp::new(now_ms, RowChange::Updated);
696 self.stamp(id.ix(), columns, now_ms, |_| stamp);
697 changes.columns.union_with(columns);
698 changes.updated.push(id);
699 }
700 }
701 Op::Remove(key) => self.remove(key, now_ms, changes),
702 Op::ReplaceAll(rows) => {
703 self.reset(changes);
704 for row in rows {
705 self.upsert(row, now_ms, false, changes);
706 }
707 }
708 Op::Clear => self.reset(changes),
709 Op::FinishReload { snapshot, live } => {
710 for op in self.reload.resolve(snapshot, live) {
711 if !matches!(op, Op::FinishReload { .. }) {
713 self.apply_op(op, now_ms, changes);
714 }
715 }
716 }
717 Op::SetPinnedRows { edge, rows } => {
718 self.set_pinned_rows(edge, rows, now_ms, changes);
719 }
720 Op::SetProgress(progress) => {
721 if self.progress != progress {
722 self.progress = progress;
723 changes.progress = true;
724 }
725 }
726 }
727 }
728 }
729
730 fn set_pinned_rows(
736 &mut self,
737 edge: PinnedEdge,
738 rows: Vec<Arc<R>>,
739 now_ms: u32,
740 changes: &mut ChangeSet,
741 ) {
742 let written = rows.len();
743 let rows = keep_last_of_each_key(rows);
744 if cfg!(debug_assertions) && rows.len() < written {
745 log::warn!(
746 "gpui-kit-grid: {} rows pinned at {edge:?} repeat a key; the last row of each key is kept",
747 written - rows.len()
748 );
749 }
750 let previous = self.pinned[edge.ix()].clone();
751 let previous_ix: FxHashMap<&R::Key, usize> = previous
752 .rows
753 .iter()
754 .enumerate()
755 .map(|(ix, row)| (row.key(), ix))
756 .collect();
757 let first_fill = previous.rows.is_empty();
758 let mut changed = rows.len() != previous.rows.len();
759 let (policy, every) = (self.policy, self.every);
760 let columns_of = R::Column::all();
761 let stamps = rows
762 .iter()
763 .enumerate()
764 .map(|(ix, row)| {
765 let Some(&old_ix) = previous_ix.get(row.key()) else {
766 changed = true;
767 let flash = Stamp::new(now_ms, RowChange::Inserted);
768 return (!first_fill).then(|| SparseStamps::whole_row(flash));
769 };
770 let old = &previous.rows[old_ix];
771 let mut columns = ColumnSet::EMPTY;
772 let replaced = !Arc::ptr_eq(old, row);
775 if replaced {
776 row.changed_columns(old, &mut columns);
777 }
778 changed |= replaced || old_ix != ix;
779 let carried = &previous.stamps[old_ix];
780 if columns.is_empty() {
781 return carried.clone();
782 }
783 SparseStamps::written(
784 carried.as_ref(),
785 columns,
786 every,
787 |stamp| !policy.is_highlight_over(stamp, now_ms),
788 |column| updated::<R>(now_ms, row, old, columns_of[column.get()]),
789 )
790 })
791 .collect();
792 self.pinned[edge.ix()] = Arc::new(PinnedBand { rows, stamps });
795 changes.pinned |= changed;
796 }
797
798 fn remove(&mut self, key: R::Key, now_ms: u32, changes: &mut ChangeSet) {
801 let Some(id) = self.index.remove(&key) else {
802 return;
803 };
804 self.published_index.remove(&key);
805 let slot = self.slot_mut(id.ix());
806 let row = slot.row.take();
807 let seq = slot.seq;
808 slot.stamps = None;
809 slot.generation = slot.generation.wrapping_add(1);
810 self.free.push(id.ix);
811 self.hot.remove(&id.ix);
812 changes.removed.push(id);
813 if let Some(row) = row
814 && self.policy.has_removal_fade()
815 {
816 let removed = Stamp::removal(now_ms);
817 self.fading.insert(id, FadingRow { row, removed, seq });
818 self.fading_keys.insert(key, id);
819 self.fade_queue.push_back((id, removed));
820 }
821 }
822
823 fn drop_fading_key(&mut self, key: &R::Key, changes: &mut ChangeSet) {
826 if let Some(id) = self.fading_keys.remove(key) {
827 self.fading.remove(&id);
828 changes.faded.push(id);
829 }
830 }
831
832 fn drop_faded(&mut self, now_ms: u32, changes: &mut ChangeSet) {
835 while let Some(&(id, removed)) = self.fade_queue.front() {
836 if !self.policy.is_removal_over(removed, now_ms) {
837 break;
838 }
839 self.fade_queue.pop_front();
840 if let Some(fading) = self.fading.remove(&id) {
842 self.fading_keys.remove(fading.row.key());
843 changes.faded.push(id);
844 }
845 }
846 }
847
848 fn reset(&mut self, changes: &mut ChangeSet) {
852 changes.reset = true;
853 if !self.fading.is_empty() {
854 changes.faded.extend(self.fading.keys().copied());
855 self.fading = FadingMap::default();
856 self.fading_keys.clear();
857 }
858 self.fade_queue.clear();
859 for ix in 0..self.capacity {
860 let slot = self.slot_mut(ix);
861 if slot.row.is_some() {
862 slot.generation = slot.generation.wrapping_add(1);
863 }
864 slot.row = None;
865 slot.stamps = None;
866 }
867 self.free = (0..self.capacity as u32).rev().collect();
868 self.index.clear();
869 self.published_index = KeyIndex::default();
870 self.hot.clear();
871 }
872
873 fn upsert(&mut self, row: Arc<R>, now_ms: u32, highlight: bool, changes: &mut ChangeSet) {
874 if let Some(&id) = self.index.get(row.key()) {
875 let ix = id.ix();
876 let current = self.pages[ix / PAGE_SIZE].slots[ix % PAGE_SIZE]
877 .row
878 .as_ref();
879 if current.is_some_and(|current| Arc::ptr_eq(current, &row)) {
881 return;
882 }
883 let slot = self.slot_mut(ix);
884 let Some(previous) = slot.row.replace(row.clone()) else {
885 return;
886 };
887 let mut columns = ColumnSet::EMPTY;
888 row.changed_columns(&previous, &mut columns);
889 changes.parent |= row.parent_key() != previous.parent_key();
891 changes.updated.push(id);
895 if columns.is_empty() {
896 return;
897 }
898 changes.columns.union_with(columns);
899 if highlight {
900 let all = R::Column::all();
904 self.stamp(ix, columns, now_ms, |column| {
905 updated::<R>(now_ms, &row, &previous, all[column.get()])
906 });
907 }
908 return;
909 }
910
911 if !self.fading_keys.is_empty() {
912 self.drop_fading_key(row.key(), changes);
913 }
914 let ix = self.allocate();
915 let seq = self.next_seq;
916 self.next_seq += 1;
917 let key = row.key().clone();
918 let slot = self.slot_mut(ix);
919 slot.row = Some(row);
920 slot.seq = seq;
921 slot.stamps = None;
922 let id = RowId {
923 ix: ix as u32,
924 generation: slot.generation,
925 };
926 self.published_index.insert(key.clone(), id);
927 self.index.insert(key, id);
928 changes.inserted += 1;
929 if highlight {
930 let flash = SparseStamps::whole_row(Stamp::new(now_ms, RowChange::Inserted));
931 self.slot_mut(ix).stamps = Some(flash);
932 self.hot.insert(ix as u32);
933 }
934 }
935
936 fn stamp(
939 &mut self,
940 ix: usize,
941 columns: ColumnSet,
942 now_ms: u32,
943 stamp: impl FnMut(ColumnIx) -> Stamp,
944 ) {
945 let (policy, every) = (self.policy, self.every);
946 let slot = self.slot_mut(ix);
947 slot.stamps = SparseStamps::written(
948 slot.stamps.as_ref(),
949 columns,
950 every,
951 |stamp| !policy.is_highlight_over(stamp, now_ms),
952 stamp,
953 );
954 if slot.stamps.is_some() {
955 self.hot.insert(ix as u32);
956 }
957 }
958
959 pub fn sweep(&mut self, now: Instant) {
963 let now_ms = self.now_ms(now);
964 if now_ms.saturating_sub(self.last_sweep_ms) < self.policy.duration_ms() / 2 {
965 return;
966 }
967 self.last_sweep_ms = now_ms;
968 let policy = self.policy;
969 let is_hot = |stamp| !policy.is_highlight_over(stamp, now_ms);
970 let expired: Vec<u32> = self
971 .hot
972 .iter()
973 .copied()
974 .filter(|&ix| {
975 let page = &self.pages[ix as usize / PAGE_SIZE];
976 page.slots[ix as usize % PAGE_SIZE]
977 .stamps
978 .as_ref()
979 .is_none_or(|stamps| stamps.is_cold(is_hot))
980 })
981 .collect();
982 for ix in expired {
983 self.slot_mut(ix as usize).stamps = None;
984 self.hot.remove(&ix);
985 }
986 let expired = |stamps: &Option<SparseStamps>| {
988 stamps.as_ref().is_some_and(|stamps| stamps.is_cold(is_hot))
989 };
990 for band in &mut self.pinned {
991 if band.stamps.iter().any(expired) {
992 for stamps in &mut Arc::make_mut(band).stamps {
993 if expired(stamps) {
994 *stamps = None;
995 }
996 }
997 }
998 }
999 }
1000
1001 pub fn snapshot(&self) -> Arc<StoreSnapshot<R>> {
1004 Arc::new(StoreSnapshot {
1005 version: self.version,
1006 pages: self.pages.clone(),
1007 index: self.published_index.clone(),
1008 capacity: self.capacity,
1009 len: self.index.len(),
1010 epoch: self.epoch,
1011 pinned: self.pinned.clone(),
1012 progress: self.progress.clone(),
1013 fading: self.fading.clone(),
1014 })
1015 }
1016}
1017
1018#[inline]
1021fn updated<R: GridRow>(now_ms: u32, row: &R, previous: &R, column: R::Column) -> Stamp {
1022 let direction = row.change_direction(previous, column);
1023 Stamp::with_direction(now_ms, RowChange::Updated, direction)
1024}
1025
1026fn keep_last_of_each_key<R: GridRow>(rows: Vec<Arc<R>>) -> Vec<Arc<R>> {
1029 if rows.len() < 2 {
1030 return rows;
1031 }
1032 let mut last_ix: FxHashMap<&R::Key, usize> =
1033 FxHashMap::with_capacity_and_hasher(rows.len(), FxBuildHasher);
1034 for (ix, row) in rows.iter().enumerate() {
1035 last_ix.insert(row.key(), ix);
1036 }
1037 if last_ix.len() == rows.len() {
1038 return rows;
1039 }
1040 rows.iter()
1041 .enumerate()
1042 .filter(|(ix, row)| last_ix[row.key()] == *ix)
1043 .map(|(_, row)| row.clone())
1044 .collect()
1045}
1046
1047struct Shared<R: GridRow> {
1048 pending: Mutex<Pending<R>>,
1049 wake: async_channel::Sender<()>,
1050}
1051
1052impl<R: GridRow> Shared<R> {
1053 fn wake(&self) {
1054 let _ = self.wake.try_send(());
1055 }
1056}
1057
1058struct Pending<R: GridRow> {
1059 queue: WriteQueue<R>,
1060 reload: Option<ReloadBuffer<R>>,
1062 next_reload: u64,
1063 flushes: Vec<async_channel::Sender<u64>>,
1069 closed: bool,
1071}
1072
1073struct ReloadBuffer<R: GridRow> {
1074 id: u64,
1075 live: Vec<Op<R>>,
1076}
1077
1078impl<R: GridRow> Pending<R> {
1079 fn new(coalescing: WriteCoalescing) -> Self {
1080 Self {
1081 queue: WriteQueue::new(coalescing),
1082 reload: None,
1083 next_reload: 0,
1084 flushes: Vec::new(),
1085 closed: false,
1086 }
1087 }
1088
1089 fn write(&mut self, op: Op<R>) -> bool {
1093 match &mut self.reload {
1094 Some(reload) if !matches!(op, Op::SetProgress(_)) => {
1095 self.queue.count_buffered();
1096 reload.live.push(op);
1097 false
1098 }
1099 _ => {
1100 self.queue.push(op);
1101 true
1102 }
1103 }
1104 }
1105
1106 fn finish_reload(&mut self, id: u64, snapshot: Vec<Arc<R>>) -> bool {
1109 let Some(reload) = self.reload.take_if(|reload| reload.id == id) else {
1110 return false;
1111 };
1112 self.queue.push_finished_reload(snapshot, reload.live);
1113 true
1114 }
1115}
1116
1117struct IngestGuard<R: GridRow>(Arc<Shared<R>>);
1120
1121impl<R: GridRow> Drop for IngestGuard<R> {
1122 fn drop(&mut self) {
1123 let flushes = {
1124 let mut pending = self.0.pending.lock();
1125 pending.closed = true;
1126 std::mem::take(&mut pending.flushes)
1127 };
1128 drop(flushes);
1130 }
1131}
1132
1133pub struct GridWriter<R: GridRow> {
1140 shared: Arc<Shared<R>>,
1141 hub: Arc<Mutex<Hub<R>>>,
1142}
1143
1144impl<R: GridRow> Clone for GridWriter<R> {
1145 fn clone(&self) -> Self {
1146 Self {
1147 shared: self.shared.clone(),
1148 hub: self.hub.clone(),
1149 }
1150 }
1151}
1152
1153impl<R: GridRow> GridWriter<R> {
1154 fn push(&self, op: Op<R>) {
1155 let queued = self.shared.pending.lock().write(op);
1156 if queued {
1157 self.shared.wake();
1158 }
1159 }
1160
1161 pub fn batch(&self, write: impl FnOnce(&mut WriteBatch<R>)) {
1180 let mut batch = WriteBatch {
1181 shared: self.shared.clone(),
1182 writes: Vec::new(),
1183 };
1184 write(&mut batch);
1185 if batch.writes.is_empty() {
1186 return;
1187 }
1188 let mut pending = self.shared.pending.lock();
1189 let mut queued = false;
1190 for write in batch.writes {
1191 queued |= match write {
1192 BatchWrite::Op(op) => pending.write(op),
1193 BatchWrite::FinishReload(mut reload, snapshot) => {
1194 reload.finished = true;
1195 pending.finish_reload(reload.id, snapshot)
1196 }
1197 };
1198 }
1199 drop(pending);
1200 if queued {
1201 self.shared.wake();
1202 }
1203 }
1204
1205 pub fn begin_reload(&self) -> Reload<R> {
1213 let mut pending = self.shared.pending.lock();
1214 let id = pending.next_reload;
1215 pending.next_reload += 1;
1216 let live = pending
1217 .reload
1218 .take()
1219 .map(|reload| reload.live)
1220 .unwrap_or_default();
1221 pending.reload = Some(ReloadBuffer { id, live });
1222 Reload {
1223 shared: self.shared.clone(),
1224 id,
1225 finished: false,
1226 }
1227 }
1228
1229 pub fn upsert(&self, row: impl Into<Arc<R>>) {
1236 self.push(Op::Upsert(row.into()));
1237 }
1238
1239 pub fn upsert_silent(&self, row: impl Into<Arc<R>>) {
1241 self.push(Op::UpsertSilent(row.into()));
1242 }
1243
1244 pub fn mark_changed(&self, key: R::Key, columns: ColumnSet) {
1247 self.push(Op::MarkChanged(key, columns));
1248 }
1249
1250 pub fn remove(&self, key: R::Key) {
1255 self.push(Op::Remove(key));
1256 }
1257
1258 pub fn replace_all(&self, rows: impl IntoIterator<Item = Arc<R>>) {
1260 self.push(Op::ReplaceAll(rows.into_iter().collect()));
1261 }
1262
1263 pub fn clear(&self) {
1265 self.push(Op::Clear);
1266 }
1267
1268 pub fn set_pinned_rows(&self, edge: PinnedEdge, rows: impl IntoIterator<Item = Arc<R>>) {
1277 self.push(Op::SetPinnedRows {
1278 edge,
1279 rows: rows.into_iter().collect(),
1280 });
1281 }
1282
1283 pub fn set_progress(&self, progress: Option<GridProgress>) {
1292 self.push(Op::SetProgress(progress));
1293 }
1294
1295 pub fn flush(&self) -> impl Future<Output = Option<u64>> + Send + 'static {
1305 let (done, resolved) = async_channel::bounded(1);
1306 let mut pending = self.shared.pending.lock();
1307 if !pending.closed {
1308 pending.flushes.push(done);
1309 }
1310 drop(pending);
1311 self.shared.wake();
1312 async move { resolved.recv().await.ok() }
1313 }
1314
1315 pub fn is_closed(&self) -> bool {
1317 self.shared.wake.is_closed()
1318 }
1319
1320 pub fn snapshot(&self) -> Arc<StoreSnapshot<R>> {
1323 self.hub.lock().snapshot.clone()
1324 }
1325}
1326
1327pub struct Reload<R: GridRow> {
1329 shared: Arc<Shared<R>>,
1330 id: u64,
1331 finished: bool,
1332}
1333
1334impl<R: GridRow> Reload<R> {
1335 pub fn finish(mut self, snapshot: impl IntoIterator<Item = Arc<R>>) {
1339 self.finished = true;
1340 let snapshot = snapshot.into_iter().collect();
1341 let queued = self.shared.pending.lock().finish_reload(self.id, snapshot);
1343 if queued {
1344 self.shared.wake();
1345 }
1346 }
1347}
1348
1349impl<R: GridRow> Drop for Reload<R> {
1350 fn drop(&mut self) {
1351 if self.finished {
1352 return;
1353 }
1354 let mut pending = self.shared.pending.lock();
1355 if pending
1356 .reload
1357 .as_ref()
1358 .is_some_and(|reload| reload.id == self.id)
1359 {
1360 let live = pending
1361 .reload
1362 .take()
1363 .map(|reload| reload.live)
1364 .unwrap_or_default();
1365 pending.queue.push_cancelled_reload(live);
1366 drop(pending);
1367 self.shared.wake();
1368 }
1369 }
1370}
1371
1372pub struct WriteBatch<R: GridRow> {
1376 shared: Arc<Shared<R>>,
1377 writes: Vec<BatchWrite<R>>,
1378}
1379
1380enum BatchWrite<R: GridRow> {
1382 Op(Op<R>),
1383 FinishReload(Reload<R>, Vec<Arc<R>>),
1386}
1387
1388impl<R: GridRow> WriteBatch<R> {
1389 fn push(&mut self, op: Op<R>) {
1390 self.writes.push(BatchWrite::Op(op));
1391 }
1392
1393 pub fn upsert(&mut self, row: impl Into<Arc<R>>) {
1395 self.push(Op::Upsert(row.into()));
1396 }
1397
1398 pub fn upsert_silent(&mut self, row: impl Into<Arc<R>>) {
1400 self.push(Op::UpsertSilent(row.into()));
1401 }
1402
1403 pub fn mark_changed(&mut self, key: R::Key, columns: ColumnSet) {
1405 self.push(Op::MarkChanged(key, columns));
1406 }
1407
1408 pub fn remove(&mut self, key: R::Key) {
1410 self.push(Op::Remove(key));
1411 }
1412
1413 pub fn replace_all(&mut self, rows: impl IntoIterator<Item = Arc<R>>) {
1415 self.push(Op::ReplaceAll(rows.into_iter().collect()));
1416 }
1417
1418 pub fn clear(&mut self) {
1420 self.push(Op::Clear);
1421 }
1422
1423 pub fn set_pinned_rows(&mut self, edge: PinnedEdge, rows: impl IntoIterator<Item = Arc<R>>) {
1425 self.push(Op::SetPinnedRows {
1426 edge,
1427 rows: rows.into_iter().collect(),
1428 });
1429 }
1430
1431 pub fn set_progress(&mut self, progress: Option<GridProgress>) {
1434 self.push(Op::SetProgress(progress));
1435 }
1436
1437 pub fn finish_reload(&mut self, reload: Reload<R>, snapshot: impl IntoIterator<Item = Arc<R>>) {
1443 if !Arc::ptr_eq(&reload.shared, &self.shared) {
1444 reload.finish(snapshot);
1445 return;
1446 }
1447 let snapshot = snapshot.into_iter().collect();
1448 self.writes.push(BatchWrite::FinishReload(reload, snapshot));
1449 }
1450}
1451
1452struct Subscriber {
1453 id: u64,
1454 changes: ChangeSet,
1455 wake: async_channel::Sender<()>,
1456}
1457
1458struct Hub<R: GridRow> {
1459 snapshot: Arc<StoreSnapshot<R>>,
1460 subscribers: Vec<Subscriber>,
1461 next_subscriber: u64,
1462 batches: BatchCounters,
1463}
1464
1465#[derive(Clone, Copy, Debug, Default)]
1467struct BatchCounters {
1468 count: u64,
1469 last: Duration,
1470 max: Duration,
1471}
1472
1473#[derive(Clone, Copy, Debug, Default)]
1476pub struct StoreStats {
1477 queue: QueueCounters,
1478 batches: BatchCounters,
1479}
1480
1481impl StoreStats {
1482 pub fn writes(&self) -> u64 {
1485 self.queue.writes
1486 }
1487
1488 pub fn coalesced_writes(&self) -> u64 {
1491 self.queue.coalesced
1492 }
1493
1494 pub fn applied_writes(&self) -> u64 {
1497 self.queue.applied
1498 }
1499
1500 pub fn pending(&self) -> usize {
1502 self.queue.pending
1503 }
1504
1505 pub fn peak_pending(&self) -> usize {
1507 self.queue.peak_pending
1508 }
1509
1510 pub fn batches(&self) -> u64 {
1512 self.batches.count
1513 }
1514
1515 pub fn last_batch(&self) -> Duration {
1518 self.batches.last
1519 }
1520
1521 pub fn max_batch(&self) -> Duration {
1523 self.batches.max
1524 }
1525}
1526
1527#[derive(Clone, Copy, Debug)]
1529pub struct StoreOptions {
1530 highlight: HighlightPolicy,
1531 publish_interval: Duration,
1532 write_coalescing: WriteCoalescing,
1533}
1534
1535impl Default for StoreOptions {
1536 fn default() -> Self {
1537 Self {
1538 highlight: HighlightPolicy::default(),
1539 publish_interval: SNAPSHOT_INTERVAL,
1540 write_coalescing: WriteCoalescing::default(),
1541 }
1542 }
1543}
1544
1545impl StoreOptions {
1546 pub fn with_highlight(mut self, highlight: HighlightPolicy) -> Self {
1549 self.highlight = highlight;
1550 self
1551 }
1552
1553 pub fn with_publish_interval(mut self, interval: Duration) -> Self {
1567 self.publish_interval = interval;
1568 self
1569 }
1570
1571 pub fn with_write_coalescing(mut self, coalescing: WriteCoalescing) -> Self {
1575 self.write_coalescing = coalescing;
1576 self
1577 }
1578
1579 pub fn highlight(&self) -> HighlightPolicy {
1581 self.highlight
1582 }
1583
1584 pub fn write_coalescing(&self) -> WriteCoalescing {
1586 self.write_coalescing
1587 }
1588
1589 pub fn publish_interval(&self) -> Duration {
1591 self.publish_interval
1592 }
1593}
1594
1595struct StoreInner<R: GridRow> {
1596 shared: Arc<Shared<R>>,
1597 hub: Arc<Mutex<Hub<R>>>,
1598 options: StoreOptions,
1599 clock: Arc<dyn Clock>,
1600 _ingest: Task<()>,
1601}
1602
1603impl<R: GridRow> Drop for StoreInner<R> {
1604 fn drop(&mut self) {
1605 self.hub.lock().subscribers.clear();
1607 }
1608}
1609
1610pub struct GridStore<R: GridRow> {
1612 inner: Arc<StoreInner<R>>,
1613}
1614
1615impl<R: GridRow> Clone for GridStore<R> {
1616 fn clone(&self) -> Self {
1617 Self {
1618 inner: self.inner.clone(),
1619 }
1620 }
1621}
1622
1623impl<R: GridRow> GridStore<R> {
1624 pub fn new(options: StoreOptions, cx: &App) -> Self {
1626 Self::builder(options).build(cx)
1627 }
1628
1629 pub fn builder(options: StoreOptions) -> GridStoreBuilder<R> {
1631 GridStoreBuilder {
1632 options,
1633 clock: None,
1634 reload: Box::new(ReplayLive),
1635 }
1636 }
1637
1638 pub fn now(&self) -> Instant {
1640 self.inner.clock.now()
1641 }
1642
1643 pub fn writer(&self) -> GridWriter<R> {
1645 GridWriter {
1646 shared: self.inner.shared.clone(),
1647 hub: self.inner.hub.clone(),
1648 }
1649 }
1650
1651 pub fn snapshot(&self) -> Arc<StoreSnapshot<R>> {
1653 self.inner.hub.lock().snapshot.clone()
1654 }
1655
1656 pub fn options(&self) -> StoreOptions {
1658 self.inner.options
1659 }
1660
1661 pub fn stats(&self) -> StoreStats {
1663 let queue = self.inner.shared.pending.lock().queue.counters();
1664 let batches = self.inner.hub.lock().batches;
1665 StoreStats { queue, batches }
1666 }
1667
1668 pub fn subscribe(&self) -> StoreSubscription<R> {
1670 let (wake, rx) = async_channel::bounded(1);
1671 let mut hub = self.inner.hub.lock();
1672 let id = hub.next_subscriber;
1673 hub.next_subscriber += 1;
1674 hub.subscribers.push(Subscriber {
1675 id,
1676 changes: ChangeSet::default(),
1677 wake,
1678 });
1679 StoreSubscription {
1680 hub: self.inner.hub.clone(),
1681 id,
1682 rx,
1683 }
1684 }
1685}
1686
1687pub struct GridStoreBuilder<R: GridRow> {
1689 options: StoreOptions,
1690 clock: Option<Arc<dyn Clock>>,
1691 reload: Box<dyn ReloadPolicy<R>>,
1692}
1693
1694impl<R: GridRow> GridStoreBuilder<R> {
1695 pub fn with_clock(mut self, clock: Arc<dyn Clock>) -> Self {
1697 self.clock = Some(clock);
1698 self
1699 }
1700
1701 pub fn with_reload_policy(mut self, policy: impl ReloadPolicy<R>) -> Self {
1703 self.reload = Box::new(policy);
1704 self
1705 }
1706
1707 pub fn build(self, cx: &App) -> GridStore<R> {
1709 self.build_on(cx.background_executor().clone())
1710 }
1711
1712 pub fn build_on(self, executor: BackgroundExecutor) -> GridStore<R> {
1714 let Self {
1715 options,
1716 clock,
1717 reload,
1718 } = self;
1719 let clock: Arc<dyn Clock> = clock.unwrap_or_else(|| Arc::new(executor.clone()));
1720 let (wake, wake_rx) = async_channel::bounded(1);
1721 let shared = Arc::new(Shared {
1722 pending: Mutex::new(Pending::new(options.write_coalescing)),
1723 wake,
1724 });
1725 let mut ingest = Ingest::<R>::with_reload_policy(options.highlight, clock.now(), reload);
1726 let hub = Arc::new(Mutex::new(Hub {
1727 snapshot: ingest.snapshot(),
1728 subscribers: Vec::new(),
1729 next_subscriber: 0,
1730 batches: BatchCounters::default(),
1731 }));
1732 let task = executor.spawn({
1733 let shared = shared.clone();
1734 let hub = hub.clone();
1735 let clock = clock.clone();
1736 let timer = executor.clone();
1737 let guard = IngestGuard(shared.clone());
1740 async move {
1741 let _guard = guard;
1742 while wake_rx.recv().await.is_ok() {
1743 let (writes, flushes) = {
1746 let mut pending = shared.pending.lock();
1747 let writes = pending.queue.take();
1748 (writes, std::mem::take(&mut pending.flushes))
1749 };
1750 let started = Instant::now();
1752 let now = clock.now();
1753 let changes = ingest.apply_writes(writes, now);
1754 ingest.sweep(now);
1755 let publish = !changes.is_empty();
1756 if publish {
1757 let snapshot = ingest.snapshot();
1758 let elapsed = started.elapsed();
1759 let previous = {
1760 let mut hub = hub.lock();
1761 let batches = &mut hub.batches;
1762 batches.count += 1;
1763 batches.last = elapsed;
1764 batches.max = batches.max.max(elapsed);
1765 let previous = std::mem::replace(&mut hub.snapshot, snapshot);
1766 for subscriber in &mut hub.subscribers {
1767 subscriber.changes.merge(&changes);
1768 let _ = subscriber.wake.try_send(());
1769 }
1770 previous
1771 };
1772 drop(previous);
1774 }
1775 for flush in flushes {
1778 let _ = flush.try_send(ingest.version);
1779 }
1780 if publish {
1781 timer.timer(options.publish_interval).await;
1785 }
1786 }
1787 }
1788 });
1789 GridStore {
1790 inner: Arc::new(StoreInner {
1791 shared,
1792 hub,
1793 options,
1794 clock,
1795 _ingest: task,
1796 }),
1797 }
1798 }
1799}
1800
1801pub struct StoreSubscription<R: GridRow> {
1803 hub: Arc<Mutex<Hub<R>>>,
1804 id: u64,
1805 rx: async_channel::Receiver<()>,
1806}
1807
1808impl<R: GridRow> StoreSubscription<R> {
1809 pub async fn changed(&self) -> bool {
1811 self.rx.recv().await.is_ok()
1812 }
1813
1814 pub fn take(&self) -> (Arc<StoreSnapshot<R>>, ChangeSet) {
1816 let mut hub = self.hub.lock();
1817 let snapshot = hub.snapshot.clone();
1818 let changes = hub
1819 .subscribers
1820 .iter_mut()
1821 .find(|subscriber| subscriber.id == self.id)
1822 .map(|subscriber| std::mem::take(&mut subscriber.changes))
1823 .unwrap_or_default();
1824 (snapshot, changes)
1825 }
1826}
1827
1828impl<R: GridRow> Drop for StoreSubscription<R> {
1829 fn drop(&mut self) {
1830 let id = self.id;
1831 self.hub
1832 .lock()
1833 .subscribers
1834 .retain(|subscriber| subscriber.id != id);
1835 }
1836}