Skip to main content

gpui_kit_grid/
store.rs

1//! Row storage: a single-writer ingest task that owns the rows, the pinned
2//! rows and the progress, and publishes immutable snapshots.
3//!
4//! `Ingest` and `StoreSnapshot` have no GPUI types. `GridStore` only uses the
5//! GPUI background executor to run the ingest loop and to read the clock.
6
7mod 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
24/// Rows per page. Publishing a snapshot copies only the pages changed since
25/// the previous snapshot, and only while that snapshot is still held.
26pub const PAGE_SIZE: usize = 1024;
27
28/// The default interval between published snapshots and between a grid's
29/// pulls of them: the store publishes no more often than a grid pulls.
30pub(crate) const SNAPSHOT_INTERVAL: Duration = Duration::from_millis(8);
31
32/// The edge a pinned band sits at.
33#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34pub enum PinnedEdge {
35    /// Under the header, above the data rows.
36    Top,
37    /// At the bottom of the body, below the data rows.
38    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/// Progress of work the grid waits for: a load, a reload, a recalculation.
51///
52/// Written with [`GridWriter::set_progress`], in order with the data, and
53/// read from [`StoreSnapshot::progress`].
54#[derive(Clone, Debug, PartialEq)]
55pub struct GridProgress {
56    fraction: Option<f32>,
57    label: Option<Arc<str>>,
58}
59
60impl GridProgress {
61    /// Work of unknown length.
62    pub fn indeterminate() -> Self {
63        Self {
64            fraction: None,
65            label: None,
66        }
67    }
68
69    /// Work that is `fraction` done, clamped to `0.0..=1.0`; NaN is
70    /// indeterminate.
71    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    /// Sets the text that describes the work, e.g. "Loading trades".
79    pub fn with_label(mut self, label: impl Into<Arc<str>>) -> Self {
80        self.label = Some(label.into());
81        self
82    }
83
84    /// The done fraction in `0.0..=1.0`, or `None` for work of unknown length.
85    pub fn fraction(&self) -> Option<f32> {
86        self.fraction
87    }
88
89    /// The text that describes the work.
90    pub fn label(&self) -> Option<&str> {
91        self.label.as_deref()
92    }
93
94    /// Returns true if the length of the work is unknown.
95    pub fn is_indeterminate(&self) -> bool {
96        self.fraction.is_none()
97    }
98}
99
100/// The storage slot of a row plus its generation. A `RowId` held by a stale
101/// projection stops resolving once its row is removed.
102#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
103pub struct RowId {
104    ix: u32,
105    generation: u32,
106}
107
108impl RowId {
109    /// The slot index.
110    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
156/// The rows pinned at one edge and their change stamps, index for index.
157/// Bands are short and replaced whole, so a snapshot shares them by `Arc`.
158struct 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
181/// The clock change stamps and highlight ages are measured with. The store
182/// and every view over it read the same clock.
183pub trait Clock: Send + Sync + 'static {
184    /// The current instant.
185    fn now(&self) -> Instant;
186}
187
188/// GPUI's executor clock: real time in applications, a fake clock in tests,
189/// `web_time` on the web.
190impl Clock for BackgroundExecutor {
191    fn now(&self) -> Instant {
192        BackgroundExecutor::now(self)
193    }
194}
195
196/// Resolves a reload: a fresh snapshot from the server (`replace_all`)
197/// against live operations producers wrote while it was loading.
198///
199/// The default is [`ReplayLive`]. A policy that knows row versions can drop
200/// live operations older than the snapshot instead. The live operations
201/// include [`Op::SetPinnedRows`]; [`Op::SetProgress`] is never buffered.
202pub trait ReloadPolicy<R: GridRow>: Send + 'static {
203    /// Returns the operations to apply, in order. Runs on the ingest task.
204    fn resolve(&mut self, snapshot: Vec<Arc<R>>, live: Vec<Op<R>>) -> Vec<Op<R>>;
205}
206
207/// Replaces every row with the snapshot, then replays the live operations
208/// written meanwhile, so nothing newer than the snapshot is lost. Pinned rows
209/// written during the reload are replayed the same way.
210#[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/// An operation written by a producer.
223#[non_exhaustive]
224pub enum Op<R: GridRow> {
225    /// Insert or replace by key, highlighting changed columns.
226    Upsert(Arc<R>),
227    /// Insert or replace by key without highlight (initial load).
228    UpsertSilent(Arc<R>),
229    /// Highlight columns of an existing row without replacing it.
230    MarkChanged(R::Key, ColumnSet),
231    /// Remove by key.
232    Remove(R::Key),
233    /// Atomically replace every row, without highlight.
234    ReplaceAll(Vec<Arc<R>>),
235    /// Remove every row.
236    Clear,
237    /// A reload finished: resolved by the store's [`ReloadPolicy`].
238    FinishReload {
239        /// The rows the reload returned.
240        snapshot: Vec<Arc<R>>,
241        /// Operations producers wrote while the reload was running.
242        live: Vec<Op<R>>,
243    },
244    /// Replace the rows pinned at `edge`, highlighting changes by key. Data
245    /// rows, `ReplaceAll` and `Clear` never touch a band.
246    SetPinnedRows {
247        /// The band to replace.
248        edge: PinnedEdge,
249        /// The new rows, in order. A repeated key keeps its last row.
250        rows: Vec<Arc<R>>,
251    },
252    /// Show (`Some`) or clear (`None`) the store's progress. A running
253    /// reload never buffers it, so a [`ReloadPolicy`] does not see it.
254    SetProgress(Option<GridProgress>),
255}
256
257/// The key index published with every snapshot: a persistent hash map, so
258/// publishing is O(1) and a lookup is O(log32 n), about four hops at 100k
259/// rows. The ingest's write path uses its own `FxHashMap` (O(1)); this index
260/// changes only on inserts and removals, never on updates.
261type KeyIndex<K> =
262    imbl::GenericHashMap<K, RowId, FxBuildHasher, imbl::shared_ptr::DefaultSharedPtr>;
263
264/// A removed row while it fades out ([`HighlightPolicy::with_removal_fade`]):
265/// its last version, its removal stamp and its insertion sequence.
266struct 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
282/// The fading rows published with every snapshot, by their id before
283/// removal: a persistent hash map, so publishing is O(1). It holds at most
284/// the removals of one fade.
285type FadingMap<R> =
286    imbl::GenericHashMap<RowId, FadingRow<R>, FxBuildHasher, imbl::shared_ptr::DefaultSharedPtr>;
287
288/// What changed between two snapshots, accumulated per subscriber.
289#[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    /// Merges `other` into this set.
304    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    /// Returns true if rows were inserted, removed or replaced wholesale.
317    pub fn has_structure_change(&self) -> bool {
318        self.inserted > 0 || !self.removed.is_empty() || self.reset
319    }
320
321    /// Returns true if rows were inserted or every row was replaced: the
322    /// display order has to be rebuilt.
323    pub fn has_insertions_or_reset(&self) -> bool {
324        self.inserted > 0 || self.reset
325    }
326
327    /// Returns true if every row was replaced or removed (`replace_all`,
328    /// `clear`): a new data set, not a stream of changes.
329    pub fn is_reset(&self) -> bool {
330        self.reset
331    }
332
333    /// The number of inserted rows.
334    pub fn inserted(&self) -> usize {
335        self.inserted
336    }
337
338    /// The ids of removed rows (empty after a reset, which removes all).
339    ///
340    /// A row that fades out ([`HighlightPolicy::with_removal_fade`]) is here
341    /// at its removal, so the selection lets go of its key at once, and in
342    /// [`Self::faded`] when its fade ends.
343    pub fn removed(&self) -> &[RowId] {
344        &self.removed
345    }
346
347    /// The ids of fading rows the store dropped from its snapshots: their
348    /// fade ended, their key was written again, or a reset (`replace_all`,
349    /// `clear`) dropped every fading row. A view that shows fading rows
350    /// drops them from its projection. [`Self::removed`] reported them
351    /// already, at removal, so they are not a structure change.
352    pub fn faded(&self) -> &[RowId] {
353        &self.faded
354    }
355
356    /// Returns true if a replaced row has another
357    /// [`GridRow::parent_key`] than its previous version: a tree view
358    /// rebuilds on it. Reported even when no column changed; such a
359    /// replacement is in [`Self::updated`], without highlight.
360    pub fn has_parent_change(&self) -> bool {
361        self.parent
362    }
363
364    /// The columns whose values changed in data rows. Pinned rows do not add
365    /// to it: they are not in the display order. Neither does a replacement
366    /// that changes no column.
367    pub fn columns(&self) -> ColumnSet {
368        self.columns
369    }
370
371    /// The ids of rows replaced by a new row or marked changed, so a view
372    /// can tell whether any of them is on screen.
373    ///
374    /// A replacement that changes no column is here too, without highlight
375    /// and without [`Self::columns`]: data the columns do not show, such as
376    /// a tooltip text or a style input, may have changed, so a view repaints
377    /// the row if it is on screen, and the display order needs no work. The
378    /// same `Arc` written again is not a replacement.
379    pub fn updated(&self) -> &[RowId] {
380        &self.updated
381    }
382
383    /// Returns true if a band of pinned rows changed: rows, order or values.
384    /// A row replaced by a new `Arc` is a change even when no column
385    /// changed; a band of the same `Arc`s in the same order is not. The
386    /// display order needs no work for it, only a repaint.
387    pub fn has_pinned_change(&self) -> bool {
388        self.pinned
389    }
390
391    /// Returns true if the progress written through
392    /// [`GridWriter::set_progress`] changed.
393    pub fn has_progress_change(&self) -> bool {
394        self.progress
395    }
396
397    /// Returns true if nothing changed.
398    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
411/// An immutable view of every row at one store version.
412pub 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    /// The store version this snapshot was published at.
426    pub fn version(&self) -> u64 {
427        self.version
428    }
429
430    /// The number of live rows.
431    pub fn len(&self) -> usize {
432        self.len
433    }
434
435    /// Returns true if there are no rows.
436    pub fn is_empty(&self) -> bool {
437        self.len == 0
438    }
439
440    /// The number of slots, live or free. Row indices are below this.
441    pub fn capacity(&self) -> usize {
442        self.capacity
443    }
444
445    /// The instant stamps are measured from.
446    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    /// The row with this id, or `None` if it was removed since.
465    pub fn row(&self, id: RowId) -> Option<&Arc<R>> {
466        self.live_slot(id).and_then(|slot| slot.row.as_ref())
467    }
468
469    /// The id of the row with this key in this snapshot.
470    pub fn row_id(&self, key: &R::Key) -> Option<RowId> {
471        self.index.get(key).copied()
472    }
473
474    /// The row with this key in this snapshot.
475    pub fn get(&self, key: &R::Key) -> Option<&Arc<R>> {
476        self.row_id(key).and_then(|id| self.row(id))
477    }
478
479    /// The change stamps of the row's cells, if any is hot. Read a cell's
480    /// with [`RowStamps::get`].
481    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    /// The insertion sequence of the row: the stable tie-breaker for sorting.
488    pub fn seq(&self, id: RowId) -> u64 {
489        self.live_slot(id).map_or(0, |slot| slot.seq)
490    }
491
492    /// Every live row with its id, in storage order.
493    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    /// The rows pinned at `edge`, in order. Their keys are a space of their
511    /// own: a pinned row may share its key with a data row or with a row of
512    /// the other band.
513    pub fn pinned_rows(&self, edge: PinnedEdge) -> &[Arc<R>] {
514        &self.pinned[edge.ix()].rows
515    }
516
517    /// The change stamps of the cells of the `ix`-th row pinned at `edge`,
518    /// if any is hot.
519    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    /// The progress written through [`GridWriter::set_progress`].
528    pub fn progress(&self) -> Option<&GridProgress> {
529        self.progress.as_ref()
530    }
531
532    /// A removed row while it fades out
533    /// ([`HighlightPolicy::with_removal_fade`]): its last version and its
534    /// removal stamp. `id` is the row's id before removal (its old
535    /// generation), so it never resolves to a live row.
536    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    /// Every fading row with its id, removal stamp and insertion sequence
543    /// (the tie-breaker for sorting, as [`Self::seq`] of a live row), in no
544    /// particular order.
545    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    /// Bytes held by the change stamps of data rows, their allocations'
552    /// reference counts included (for the spike's memory measurement).
553    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
563/// The single writer of the rows. Applies operations in order, diffs every
564/// replacement, writes change stamps and publishes snapshots.
565pub struct Ingest<R: GridRow> {
566    pages: Vec<Arc<Page<R>>>,
567    capacity: usize,
568    /// Write path: O(1) key lookup for every operation.
569    index: FxHashMap<R::Key, RowId>,
570    /// Read path: published with snapshots; changes on inserts and removals.
571    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 column of `R`: the cells an insert flashes.
580    every: ColumnSet,
581    last_sweep_ms: u32,
582    pinned: [Arc<PinnedBand<R>>; 2],
583    progress: Option<GridProgress>,
584    /// Read path: removed rows while they fade, published with snapshots.
585    fading: FadingMap<R>,
586    /// Write path: the fading row of a removed key, so that a new row of
587    /// the key drops it at once.
588    fading_keys: FxHashMap<R::Key, RowId>,
589    /// Fading rows with their removal stamps, in removal order: the front
590    /// ends its fade first, so a batch checks one entry while no fade has
591    /// ended. A row dropped earlier stays here until it reaches the front.
592    fade_queue: VecDeque<(RowId, Stamp)>,
593}
594
595impl<R: GridRow> Ingest<R> {
596    /// Creates an empty store whose stamps are measured from `epoch`.
597    pub fn new(policy: HighlightPolicy, epoch: Instant) -> Self {
598        Self::with_reload_policy(policy, epoch, Box::new(ReplayLive))
599    }
600
601    /// Creates an empty store with a custom [`ReloadPolicy`].
602    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    /// The number of live rows.
630    pub fn len(&self) -> usize {
631        self.index.len()
632    }
633
634    /// Returns true if there are no rows.
635    pub fn is_empty(&self) -> bool {
636        self.index.is_empty()
637    }
638
639    /// The number of rows that currently own change stamps.
640    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    /// Applies `ops` in order at `now` and returns what changed. Then drops
668    /// the fading rows whose fade ended at `now` ([`ChangeSet::faded`]),
669    /// even when `ops` is empty.
670    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    /// Drops the fading rows whose fade ended and counts the version.
680    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                        // A policy cannot nest reloads.
712                        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    /// Replaces a band, diffing it against the previous one by key: changed
731    /// columns of a kept key are stamped `Updated` with their direction, a
732    /// new key flashes `Inserted`, and a kept key carries its stamps over.
733    /// The first fill of an empty band is not highlighted, like
734    /// `replace_all`. Costs O(band rows × columns); bands are short.
735    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                // A new row repaints the band even when no column changed,
773                // as a replaced data row does.
774                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        // A band of the same rows is replaced too, but publishes nothing,
793        // like an upsert of the same row.
794        self.pinned[edge.ix()] = Arc::new(PinnedBand { rows, stamps });
795        changes.pinned |= changed;
796    }
797
798    /// Removes the row of `key`. With a removal fade, its last version
799    /// fades out: it stays in snapshots by its old id until the fade ends.
800    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    /// Drops the fading row of `key` at once, if there is one: a new row of
824    /// the key shows without a ghost beside it.
825    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    /// Drops the fading rows whose fade ended at `now_ms`, earliest first.
833    /// O(1) while the earliest fade has not ended.
834    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            // Not there if its key was written again since.
841            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    /// Frees every slot and drops every fading row: a new data set does not
849    /// fade. Slots are kept and their generations bumped, so a `RowId` from
850    /// an older projection never resolves to a new row.
851    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            // The same row again: nothing to store, and no page to copy.
880            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            // A tree moves the row; its columns may all be the same.
890            changes.parent |= row.parent_key() != previous.parent_key();
891            // A new row is published even when no column changed: hooks may
892            // read data the columns do not show. Without changed columns it
893            // has no highlight and needs no work for the display order.
894            changes.updated.push(id);
895            if columns.is_empty() {
896                return;
897            }
898            changes.columns.union_with(columns);
899            if highlight {
900                // Each changed cell against the previous version in this
901                // very operation, so a chain A → B → C in one batch stamps
902                // the direction of B → C.
903                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    /// Stamps `columns` of a row with `stamp(column)` and drops the row's
937    /// stamps that no longer highlight.
938    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    /// Frees the stamps of rows whose highlight is over. Runs at most twice
960    /// per highlight duration and costs O(hot rows). A row with some hot
961    /// stamps keeps its expired ones until it is written again.
962    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        // Pinned rows are not in `hot`: bands are short, so they are scanned.
987        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    /// Publishes the current rows. Cheap: clones one `Arc` per page and per
1002    /// pinned band.
1003    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/// The stamp of a changed `column` of `row`, replacing `previous`: `Updated`
1019/// with the row's own [`GridRow::change_direction`].
1020#[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
1026/// Drops every row whose key repeats later in the band: the last one wins,
1027/// in its own position.
1028fn 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    /// While a reload runs, live operations wait here.
1061    reload: Option<ReloadBuffer<R>>,
1062    next_reload: u64,
1063    /// `GridWriter::flush` calls waiting for the next batch. When a flush
1064    /// registers, every operation written before it is in `queue` or already
1065    /// taken by the ingest. The ingest takes `queue` and the flushes under one
1066    /// lock and resolves them after publishing, so the batch that takes a
1067    /// flush covers its operations. No counter is needed.
1068    flushes: Vec<async_channel::Sender<u64>>,
1069    /// The ingest task is gone: flushes resolve to `None` at once.
1070    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    /// Queues `op` for the ingest, or buffers it while a reload runs; the
1090    /// progress is never buffered. Returns true if `op` was queued: the
1091    /// ingest has to wake.
1092    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    /// Ends the reload `id` with `snapshot`: queues both with the operations
1107    /// it buffered. Returns false if a newer reload superseded it.
1108    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
1117/// Lives inside the ingest future: once the task is dropped with the store,
1118/// it resolves the pending flushes to `None` and refuses new ones.
1119struct 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        // A dropped sender resolves its flush to `None`.
1129        drop(flushes);
1130    }
1131}
1132
1133/// Writes rows into a [`GridStore`] from any thread.
1134///
1135/// Operations are applied in order by the store's ingest task. Writing is a
1136/// short lock and a non-blocking wake-up; it never waits for the UI.
1137/// Operations written one by one may be published in different snapshots;
1138/// [`GridWriter::batch`] publishes several in one.
1139pub 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    /// Writes the operations of `write` as one batch: the store applies them
1162    /// in order and publishes them in one snapshot, so no snapshot has some
1163    /// of them without the others. Other writes may share that snapshot.
1164    /// Nothing is written until `write` returns; then the batch costs one
1165    /// lock and at most one wake-up.
1166    ///
1167    /// A running reload buffers the batch's writes until it finishes, as it
1168    /// buffers single ones, but not the batch's progress, which is never
1169    /// buffered. [`WriteBatch::finish_reload`] finishes a reload inside the
1170    /// batch: the reloaded rows and the batch's writes after it, such as
1171    /// clearing the progress, are published together.
1172    ///
1173    /// ```ignore
1174    /// writer.batch(|batch| {
1175    ///     batch.finish_reload(reload, rows);
1176    ///     batch.set_progress(None);
1177    /// });
1178    /// ```
1179    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    /// Starts a reload: until [`Reload::finish`], operations from every
1206    /// writer are buffered instead of applied. `finish(snapshot)` hands the
1207    /// snapshot and the buffered operations to the store's [`ReloadPolicy`]
1208    /// (by default the snapshot replaces every row and the buffered operations
1209    /// are replayed on top). Dropping the guard without `finish` cancels the
1210    /// reload and applies the buffered operations as usual. A newer reload
1211    /// supersedes an older one: the older `finish` is then ignored.
1212    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    /// Inserts or replaces a row by key and highlights the changed columns,
1230    /// each with its [`GridRow::change_direction`].
1231    ///
1232    /// A new row that changes no column is published all the same, without
1233    /// highlight, so hooks that read other data of the row see it. Writing
1234    /// the same `Arc` again changes nothing.
1235    pub fn upsert(&self, row: impl Into<Arc<R>>) {
1236        self.push(Op::Upsert(row.into()));
1237    }
1238
1239    /// Inserts or replaces a row by key without highlight.
1240    pub fn upsert_silent(&self, row: impl Into<Arc<R>>) {
1241        self.push(Op::UpsertSilent(row.into()));
1242    }
1243
1244    /// Highlights columns of an existing row without replacing it, without
1245    /// a direction: the values did not change.
1246    pub fn mark_changed(&self, key: R::Key, columns: ColumnSet) {
1247        self.push(Op::MarkChanged(key, columns));
1248    }
1249
1250    /// Removes a row by key. With a removal fade
1251    /// ([`HighlightPolicy::with_removal_fade`]), its last version stays in
1252    /// snapshots as a fading row ([`StoreSnapshot::fading_row`]) until the
1253    /// fade ends or the key is written again.
1254    pub fn remove(&self, key: R::Key) {
1255        self.push(Op::Remove(key));
1256    }
1257
1258    /// Atomically replaces every row, without highlight.
1259    pub fn replace_all(&self, rows: impl IntoIterator<Item = Arc<R>>) {
1260        self.push(Op::ReplaceAll(rows.into_iter().collect()));
1261    }
1262
1263    /// Removes every row.
1264    pub fn clear(&self) {
1265        self.push(Op::Clear);
1266    }
1267
1268    /// Replaces the rows pinned at `edge` (an empty `rows` clears the band).
1269    ///
1270    /// Applied in order with other writes; buffered by a running reload like
1271    /// them. Rows are diffed by key against the previous band: changed
1272    /// columns are highlighted and a new key flashes, except on the first
1273    /// fill of an empty band. A key repeated within `rows` keeps its last
1274    /// row. Pinned keys are a space of their own, apart from the data rows
1275    /// and the other band. `replace_all` and `clear` leave bands alone.
1276    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    /// Shows (`Some`) or clears (`None`) the store's progress.
1284    ///
1285    /// Applied in order with other writes, but never buffered by a reload:
1286    /// progress written during a reload shows while the reload runs, and
1287    /// progress cleared after [`Reload::finish`] is never cleared before the
1288    /// snapshot that applies the reload. It may be cleared in a later one;
1289    /// to clear it in that very snapshot, write both in one
1290    /// [`GridWriter::batch`] with [`WriteBatch::finish_reload`].
1291    pub fn set_progress(&self, progress: Option<GridProgress>) {
1292        self.push(Op::SetProgress(progress));
1293    }
1294
1295    /// Resolves once every operation written before the call is applied and
1296    /// published: with that store version (the version of the published
1297    /// snapshot that has them), or `None` if the store is gone.
1298    ///
1299    /// The operations waited for are fixed by the call, not by the first
1300    /// poll. Operations buffered by an unfinished reload are not waited for:
1301    /// they are not written to the store until [`Reload::finish`]. Each call
1302    /// wakes the ingest task once; an idle store resolves with its current
1303    /// version.
1304    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    /// Returns true once the store is gone; producers can stop writing.
1316    pub fn is_closed(&self) -> bool {
1317        self.shared.wake.is_closed()
1318    }
1319
1320    /// The latest published snapshot, for producers that need the current
1321    /// rows (e.g. to patch a row by key). Operations still queued are not in it.
1322    pub fn snapshot(&self) -> Arc<StoreSnapshot<R>> {
1323        self.hub.lock().snapshot.clone()
1324    }
1325}
1326
1327/// A running reload, from [`GridWriter::begin_reload`].
1328pub struct Reload<R: GridRow> {
1329    shared: Arc<Shared<R>>,
1330    id: u64,
1331    finished: bool,
1332}
1333
1334impl<R: GridRow> Reload<R> {
1335    /// Finishes the reload with the rows it returned. To publish other
1336    /// writes in the same snapshot, finish it in a [`GridWriter::batch`]
1337    /// with [`WriteBatch::finish_reload`] instead.
1338    pub fn finish(mut self, snapshot: impl IntoIterator<Item = Arc<R>>) {
1339        self.finished = true;
1340        let snapshot = snapshot.into_iter().collect();
1341        // Ignored if a newer reload superseded this one.
1342        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
1372/// Operations collected by [`GridWriter::batch`], written when its closure
1373/// returns and published in one snapshot. Each method writes what the
1374/// [`GridWriter`] method of the same name writes.
1375pub struct WriteBatch<R: GridRow> {
1376    shared: Arc<Shared<R>>,
1377    writes: Vec<BatchWrite<R>>,
1378}
1379
1380/// One write of a [`WriteBatch`], in the order written.
1381enum BatchWrite<R: GridRow> {
1382    Op(Op<R>),
1383    /// Holds the guard: a batch dropped unwritten cancels the reload, like
1384    /// a dropped [`Reload`].
1385    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    /// Inserts or replaces a row by key and highlights the changed columns.
1394    pub fn upsert(&mut self, row: impl Into<Arc<R>>) {
1395        self.push(Op::Upsert(row.into()));
1396    }
1397
1398    /// Inserts or replaces a row by key without highlight.
1399    pub fn upsert_silent(&mut self, row: impl Into<Arc<R>>) {
1400        self.push(Op::UpsertSilent(row.into()));
1401    }
1402
1403    /// Highlights columns of an existing row without replacing it.
1404    pub fn mark_changed(&mut self, key: R::Key, columns: ColumnSet) {
1405        self.push(Op::MarkChanged(key, columns));
1406    }
1407
1408    /// Removes a row by key.
1409    pub fn remove(&mut self, key: R::Key) {
1410        self.push(Op::Remove(key));
1411    }
1412
1413    /// Replaces every row, without highlight.
1414    pub fn replace_all(&mut self, rows: impl IntoIterator<Item = Arc<R>>) {
1415        self.push(Op::ReplaceAll(rows.into_iter().collect()));
1416    }
1417
1418    /// Removes every row.
1419    pub fn clear(&mut self) {
1420        self.push(Op::Clear);
1421    }
1422
1423    /// Replaces the rows pinned at `edge`; see [`GridWriter::set_pinned_rows`].
1424    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    /// Shows (`Some`) or clears (`None`) the store's progress; never
1432    /// buffered by a running reload.
1433    pub fn set_progress(&mut self, progress: Option<GridProgress>) {
1434        self.push(Op::SetProgress(progress));
1435    }
1436
1437    /// Finishes `reload` with the rows it returned, at this point of the
1438    /// batch: writes before it in the batch are buffered by the reload, and
1439    /// writes after it are published with the reloaded rows. Ignored if a
1440    /// newer reload superseded it, like [`Reload::finish`]. A reload of
1441    /// another store is finished there at once, outside the batch.
1442    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/// The ingest's batches that published a snapshot, and their times.
1466#[derive(Clone, Copy, Debug, Default)]
1467struct BatchCounters {
1468    count: u64,
1469    last: Duration,
1470    max: Duration,
1471}
1472
1473/// Counters of a store's write queue and ingest, for profiling
1474/// ([`GridStore::stats`]).
1475#[derive(Clone, Copy, Debug, Default)]
1476pub struct StoreStats {
1477    queue: QueueCounters,
1478    batches: BatchCounters,
1479}
1480
1481impl StoreStats {
1482    /// Operations written so far, by every writer: each upsert, removal,
1483    /// mark, replacement, band or progress, merged or buffered by a reload.
1484    pub fn writes(&self) -> u64 {
1485        self.queue.writes
1486    }
1487
1488    /// Writes merged into a waiting upsert of the same key instead of
1489    /// queued ([`WriteCoalescing::ByKey`]).
1490    pub fn coalesced_writes(&self) -> u64 {
1491        self.queue.coalesced
1492    }
1493
1494    /// Writes the ingest has taken, merged ones included. A reload's
1495    /// writes count when the reload ends.
1496    pub fn applied_writes(&self) -> u64 {
1497        self.queue.applied
1498    }
1499
1500    /// Entries waiting in the queue now.
1501    pub fn pending(&self) -> usize {
1502        self.queue.pending
1503    }
1504
1505    /// The most entries the queue has held at once.
1506    pub fn peak_pending(&self) -> usize {
1507        self.queue.peak_pending
1508    }
1509
1510    /// Ingest batches that published a snapshot.
1511    pub fn batches(&self) -> u64 {
1512        self.batches.count
1513    }
1514
1515    /// CPU time of the last batch that published a snapshot: applying its
1516    /// writes, freeing expired stamps and making the snapshot.
1517    pub fn last_batch(&self) -> Duration {
1518        self.batches.last
1519    }
1520
1521    /// CPU time of the slowest batch that published a snapshot.
1522    pub fn max_batch(&self) -> Duration {
1523        self.batches.max
1524    }
1525}
1526
1527/// Options of a [`GridStore`].
1528#[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    /// Sets the highlight policy, removal fade included. It belongs to the
1547    /// store, so every view over the store fades the same way.
1548    pub fn with_highlight(mut self, highlight: HighlightPolicy) -> Self {
1549        self.highlight = highlight;
1550        self
1551    }
1552
1553    /// Snapshots are published at most this often (default 8 ms, the same
1554    /// as a grid's default pull interval,
1555    /// [`GridTuning::with_pull_interval`](crate::GridTuning::with_pull_interval)),
1556    /// so writes arriving one by one are applied and published in batches.
1557    ///
1558    /// A published snapshot shares every page with the store, so the next
1559    /// batch copies each page it writes to, about 15 µs per page of 1024
1560    /// rows. Under a fast stream a batch writes to every page, and each
1561    /// publish costs a copy of the whole store: publishing more often than
1562    /// the grid pulls copies pages no grid reads. A shorter interval shows
1563    /// writes sooner in [`GridWriter::snapshot`] and [`GridWriter::flush`];
1564    /// a longer one halves the ingest at 100k updates/s (16 ms against 8)
1565    /// and delays a write on screen by up to the interval.
1566    pub fn with_publish_interval(mut self, interval: Duration) -> Self {
1567        self.publish_interval = interval;
1568        self
1569    }
1570
1571    /// How writes wait for the ingest (default [`WriteCoalescing::ByKey`]:
1572    /// an upsert takes the place of a waiting upsert of its key, so the
1573    /// queue grows with the keys written, not with the writes).
1574    pub fn with_write_coalescing(mut self, coalescing: WriteCoalescing) -> Self {
1575        self.write_coalescing = coalescing;
1576        self
1577    }
1578
1579    /// The highlight policy.
1580    pub fn highlight(&self) -> HighlightPolicy {
1581        self.highlight
1582    }
1583
1584    /// How writes wait for the ingest.
1585    pub fn write_coalescing(&self) -> WriteCoalescing {
1586        self.write_coalescing
1587    }
1588
1589    /// The minimum interval between published snapshots.
1590    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        // Close every subscription: their `changed()` returns false.
1606        self.hub.lock().subscribers.clear();
1607    }
1608}
1609
1610/// The rows of one or more grids. Cloning shares the store.
1611pub 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    /// Creates a store and starts its ingest task on the background executor.
1625    pub fn new(options: StoreOptions, cx: &App) -> Self {
1626        Self::builder(options).build(cx)
1627    }
1628
1629    /// A store with custom strategies: clock and reload policy.
1630    pub fn builder(options: StoreOptions) -> GridStoreBuilder<R> {
1631        GridStoreBuilder {
1632            options,
1633            clock: None,
1634            reload: Box::new(ReplayLive),
1635        }
1636    }
1637
1638    /// The current time of the store clock (stamps are measured with it).
1639    pub fn now(&self) -> Instant {
1640        self.inner.clock.now()
1641    }
1642
1643    /// A writer for producers on any thread.
1644    pub fn writer(&self) -> GridWriter<R> {
1645        GridWriter {
1646            shared: self.inner.shared.clone(),
1647            hub: self.inner.hub.clone(),
1648        }
1649    }
1650
1651    /// The latest published snapshot.
1652    pub fn snapshot(&self) -> Arc<StoreSnapshot<R>> {
1653        self.inner.hub.lock().snapshot.clone()
1654    }
1655
1656    /// The store options.
1657    pub fn options(&self) -> StoreOptions {
1658        self.inner.options
1659    }
1660
1661    /// Counters of the write queue and the ingest, for profiling.
1662    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    /// Subscribes to new snapshots. Changes accumulate until taken.
1669    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
1687/// Builds a [`GridStore`] with custom strategies.
1688pub 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    /// Measures stamps with `clock` instead of GPUI's executor clock.
1696    pub fn with_clock(mut self, clock: Arc<dyn Clock>) -> Self {
1697        self.clock = Some(clock);
1698        self
1699    }
1700
1701    /// Resolves reloads with `policy` instead of [`ReplayLive`].
1702    pub fn with_reload_policy(mut self, policy: impl ReloadPolicy<R>) -> Self {
1703        self.reload = Box::new(policy);
1704        self
1705    }
1706
1707    /// Starts the store on the app's background executor.
1708    pub fn build(self, cx: &App) -> GridStore<R> {
1709        self.build_on(cx.background_executor().clone())
1710    }
1711
1712    /// Starts the store on `executor`.
1713    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            // Part of the future even before its first poll, so dropping the
1738            // task with the store always releases the flushes.
1739            let guard = IngestGuard(shared.clone());
1740            async move {
1741                let _guard = guard;
1742                while wake_rx.recv().await.is_ok() {
1743                    // Every operation written before a taken flush is in the
1744                    // taken writes or in an earlier batch.
1745                    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                    // Profiling only: the store clock may be a fake one.
1751                    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                        // Free retired pages here, not under the lock.
1773                        drop(previous);
1774                    }
1775                    // After the publish: the version is the published one, or
1776                    // the current one if the batch changed nothing.
1777                    for flush in flushes {
1778                        let _ = flush.try_send(ingest.version);
1779                    }
1780                    if publish {
1781                        // Batch writes that arrive one by one: everything
1782                        // written during this pause is applied and published
1783                        // together.
1784                        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
1801/// A subscription to a [`GridStore`]'s snapshots.
1802pub 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    /// Waits for the next snapshot. Returns false once the store is gone.
1810    pub async fn changed(&self) -> bool {
1811        self.rx.recv().await.is_ok()
1812    }
1813
1814    /// Takes the latest snapshot and every change since the last take.
1815    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}