Skip to main content

cranpose_core/
state.rs

1// StateRecord uses Rc with Cell for single-threaded shared ownership in the snapshot system.
2#![allow(clippy::arc_with_non_send_sync)]
3
4use crate::collections::map::{HashMap, HashSet};
5use crate::debug_trace::debug_record_scope_invalidation;
6use std::any::Any;
7use std::cell::{Cell, RefCell};
8use std::fmt;
9use std::hash::Hash;
10use std::marker::PhantomData;
11use std::ops::Deref;
12use std::rc::{Rc, Weak as RcWeak};
13use std::sync::{Arc, Mutex, MutexGuard, Weak};
14
15use crate::snapshot_id_set::{SnapshotId, SnapshotIdSet};
16use crate::snapshot_pinning::lowest_pinned_snapshot;
17use crate::snapshot_v2::{
18    advance_global_snapshot, allocate_record_id, current_snapshot, AnySnapshot, GlobalSnapshot,
19};
20use crate::{
21    runtime, with_current_composer_opt, RecomposeScope, RecomposeScopeInner, RuntimeHandle,
22    ScopeId, StateId,
23};
24
25pub(crate) const PREEXISTING_SNAPSHOT_ID: SnapshotId = 1;
26
27const INVALID_SNAPSHOT_ID: SnapshotId = 0;
28
29/// Maximum snapshot ID used to mark records as invisible during initialization
30const SNAPSHOT_ID_MAX: SnapshotId = usize::MAX;
31
32#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Default)]
33pub struct ObjectId(pub(crate) usize);
34
35impl ObjectId {
36    pub(crate) fn new<T: ?Sized + 'static>(object: &Arc<T>) -> Self {
37        Self(Arc::as_ptr(object) as *const () as usize)
38    }
39
40    #[inline]
41    pub(crate) fn as_usize(self) -> usize {
42        self.0
43    }
44}
45
46/// A record in the state history chain.
47///
48/// # Thread Safety
49/// Contains `Cell<T>` which is not `Send`/`Sync`. This is safe because state records
50/// are accessed only from the UI thread via thread-local snapshot system. The `Rc`
51/// is used for cheap cloning and shared ownership within a single thread.
52pub struct StateRecord {
53    snapshot_id: Cell<SnapshotId>,
54    tombstone: Cell<bool>,
55    next: Cell<Option<Rc<StateRecord>>>,
56    value: RefCell<Option<Box<dyn Any>>>,
57}
58
59#[derive(Debug)]
60struct StateReadFailure {
61    state_id: ObjectId,
62    snapshot_id: SnapshotId,
63    fresh_snapshot_id: SnapshotId,
64    fresh_invalid: SnapshotIdSet,
65    record_chain: Vec<(SnapshotId, bool)>,
66}
67
68impl std::fmt::Display for StateReadFailure {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        write!(
71            f,
72            "Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied\n\
73             state={:?}, snapshot_id={}, fresh_snapshot_id={}, fresh_invalid={:?}\n\
74             record_chain={:?}",
75            self.state_id,
76            self.snapshot_id,
77            self.fresh_snapshot_id,
78            self.fresh_invalid,
79            self.record_chain
80        )
81    }
82}
83
84#[derive(Debug, Clone, Copy, Eq, PartialEq)]
85pub(crate) enum StateRecordValueError {
86    MissingOrWrongType { expected: &'static str },
87}
88
89impl StateRecord {
90    pub(crate) fn new<T: Any>(
91        snapshot_id: SnapshotId,
92        value: T,
93        next: Option<Rc<StateRecord>>,
94    ) -> Rc<Self> {
95        Rc::new(Self {
96            snapshot_id: Cell::new(snapshot_id),
97            tombstone: Cell::new(false),
98            next: Cell::new(next),
99            value: RefCell::new(Some(Box::new(value))),
100        })
101    }
102
103    #[inline]
104    pub(crate) fn snapshot_id(&self) -> SnapshotId {
105        self.snapshot_id.get()
106    }
107
108    #[inline]
109    pub(crate) fn set_snapshot_id(&self, id: SnapshotId) {
110        self.snapshot_id.set(id);
111    }
112
113    #[inline]
114    pub(crate) fn next(&self) -> Option<Rc<StateRecord>> {
115        self.next.take().inspect(|record| {
116            self.next.set(Some(Rc::clone(record)));
117        })
118    }
119
120    #[inline]
121    pub(crate) fn set_next(&self, next: Option<Rc<StateRecord>>) {
122        self.next.set(next);
123    }
124
125    #[inline]
126    pub(crate) fn is_tombstone(&self) -> bool {
127        self.tombstone.get()
128    }
129
130    #[inline]
131    pub(crate) fn set_tombstone(&self, tombstone: bool) {
132        self.tombstone.set(tombstone);
133    }
134
135    pub(crate) fn clear_value(&self) {
136        self.value.borrow_mut().take();
137    }
138
139    pub(crate) fn replace_value<T: Any>(&self, new_value: T) {
140        *self.value.borrow_mut() = Some(Box::new(new_value));
141    }
142
143    pub(crate) fn with_value<T: Any, R>(&self, f: impl FnOnce(&T) -> R) -> R {
144        self.try_with_value(f)
145            .unwrap_or_else(|| panic!("StateRecord value missing or wrong type"))
146    }
147
148    pub(crate) fn try_with_value<T: Any, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
149        let guard = self.value.borrow();
150        let value = guard.as_ref().and_then(|boxed| boxed.downcast_ref::<T>())?;
151        Some(f(value))
152    }
153
154    /// Clears the value from this record to free memory.
155    /// Used when marking records as reusable - clears the value to reduce memory usage.
156    #[cfg(test)]
157    pub(crate) fn clear_for_reuse(&self) {
158        self.clear_value();
159    }
160
161    /// Copies the value from the source record into this record.
162    ///
163    /// This is used during record reuse to copy valid data from a readable record
164    /// into a reused record, and during cleanup to preserve data in records being
165    /// marked as INVALID_SNAPSHOT.
166    ///
167    pub(crate) fn assign_value<T: Any + Clone>(
168        &self,
169        source: &StateRecord,
170    ) -> Result<(), StateRecordValueError> {
171        let cloned_value = source.try_with_value(|value: &T| value.clone()).ok_or(
172            StateRecordValueError::MissingOrWrongType {
173                expected: std::any::type_name::<T>(),
174            },
175        )?;
176        self.replace_value(cloned_value);
177        Ok(())
178    }
179}
180
181impl Drop for StateRecord {
182    fn drop(&mut self) {
183        // Prevent recursive drop of deep chains which can cause stack overflow.
184        // We iteratively detach and drop the next record if we are the sole owner.
185        let mut next = self.next.take();
186        while let Some(node) = next {
187            match Rc::try_unwrap(node) {
188                Ok(record) => {
189                    // We were the last owner. Take its next pointer to continue the loop.
190                    // The record itself will be dropped here, but since next is None,
191                    // it won't recurse.
192                    next = record.next.take();
193                }
194                Err(_) => {
195                    // Someone else holds a reference to this node.
196                    // The chain destruction stops here.
197                    break;
198                }
199            }
200        }
201    }
202}
203
204/// Owns the mutable head pointer for a state's record chain.
205///
206/// Snapshot code clones the current head, prepends new records, and swaps in
207/// replacement heads frequently. Centralizing those operations keeps the
208/// `RefCell<Rc<StateRecord>>` borrow protocol out of the higher-level state
209/// logic.
210struct CurrentRecord {
211    head: RefCell<Rc<StateRecord>>,
212}
213
214impl CurrentRecord {
215    fn new(head: Rc<StateRecord>) -> Self {
216        Self {
217            head: RefCell::new(head),
218        }
219    }
220
221    fn clone_head(&self) -> Rc<StateRecord> {
222        self.head.borrow().clone()
223    }
224
225    fn replace(&self, new_head: Rc<StateRecord>) {
226        *self.head.borrow_mut() = new_head;
227    }
228
229    fn prepend(&self, record: Rc<StateRecord>) {
230        let current_head = self.clone_head();
231        record.set_next(Some(current_head));
232        self.replace(record);
233    }
234}
235
236#[inline]
237fn record_is_valid_for(
238    record: &Rc<StateRecord>,
239    snapshot_id: SnapshotId,
240    invalid: &SnapshotIdSet,
241) -> bool {
242    if record.is_tombstone() {
243        return false;
244    }
245
246    let candidate = record.snapshot_id();
247    if candidate == INVALID_SNAPSHOT_ID || candidate > snapshot_id {
248        return false;
249    }
250
251    candidate == snapshot_id || !invalid.get(candidate)
252}
253
254pub(crate) fn readable_record_for(
255    head: &Rc<StateRecord>,
256    snapshot_id: SnapshotId,
257    invalid: &SnapshotIdSet,
258) -> Option<Rc<StateRecord>> {
259    // Find the highest valid record in the chain.
260    // We must scan the full chain because reused records may not be prepended
261    // as the head but still need to be found (e.g., after writes using record reuse).
262    let mut best: Option<Rc<StateRecord>> = None;
263    let mut cursor = Some(Rc::clone(head));
264
265    while let Some(record) = cursor {
266        if record_is_valid_for(&record, snapshot_id, invalid) {
267            let replace = best
268                .as_ref()
269                .map(|current| current.snapshot_id() < record.snapshot_id())
270                .unwrap_or(true);
271            if replace {
272                best = Some(Rc::clone(&record));
273            }
274        }
275        cursor = record.next();
276    }
277
278    best
279}
280
281/// Finds the youngest record in the chain, or the first one matching the predicate.
282///
283/// Searches the record chain starting from the given head:
284/// - If a record matches the predicate, returns it immediately
285/// - Otherwise, tracks the youngest record (highest snapshot_id) and returns it
286fn find_youngest_or<F>(head: &Rc<StateRecord>, predicate: F) -> Rc<StateRecord>
287where
288    F: Fn(&Rc<StateRecord>) -> bool,
289{
290    let mut current = Some(Rc::clone(head));
291    let mut youngest = Rc::clone(head);
292
293    while let Some(record) = current {
294        if predicate(&record) {
295            return record;
296        }
297        if youngest.snapshot_id() < record.snapshot_id() {
298            youngest = Rc::clone(&record);
299        }
300        current = record.next();
301    }
302
303    youngest
304}
305
306/// Finds a StateRecord that can be safely reused because no open snapshot can see it.
307///
308/// Returns a record that either:
309/// 1. Is marked as INVALID_SNAPSHOT (abandoned/tombstone)
310/// 2. Is obscured by a newer record (both are below the reuse limit)
311///
312/// The reuse limit is `lowest_pinned_snapshot - 1`, meaning any record with a snapshot ID
313/// at or below this value cannot be selected by any currently open snapshot.
314///
315/// Note: PREEXISTING records (snapshot_id=1) are never reused to maintain the ability
316/// for all snapshots to read the initial state.
317pub(crate) fn used_locked(head: &Rc<StateRecord>) -> Option<Rc<StateRecord>> {
318    let mut current = Some(Rc::clone(head));
319    let mut valid_record: Option<Rc<StateRecord>> = None;
320
321    // Calculate reuse limit: records below this ID are invisible to all open snapshots
322    let reuse_limit = lowest_pinned_snapshot()
323        .map(|lowest| lowest.saturating_sub(1))
324        .unwrap_or_else(|| allocate_record_id().saturating_sub(1));
325
326    let invalid = SnapshotIdSet::EMPTY;
327
328    while let Some(record) = current {
329        let current_id = record.snapshot_id();
330
331        // Never reuse PREEXISTING records - they must always be available as a fallback
332        if current_id == PREEXISTING_SNAPSHOT_ID {
333            current = record.next();
334            continue;
335        }
336
337        // Fast path: records marked INVALID_SNAPSHOT can be reused immediately
338        if current_id == INVALID_SNAPSHOT_ID {
339            return Some(record);
340        }
341
342        if record.is_tombstone() && current_id < reuse_limit {
343            return Some(record);
344        }
345
346        // Check if this record is valid for snapshots at or below the reuse limit
347        if record_is_valid_for(&record, reuse_limit, &invalid) {
348            if let Some(ref existing) = valid_record {
349                // We found two valid records below the reuse limit.
350                // This means one obscures the other - return the older one for reuse.
351                return Some(if current_id < existing.snapshot_id() {
352                    record
353                } else {
354                    Rc::clone(existing)
355                });
356            } else {
357                // First valid record below reuse limit - keep looking
358                valid_record = Some(record.clone());
359            }
360        }
361
362        current = record.next();
363    }
364
365    // No reusable record found
366    None
367}
368
369/// Creates a new overwritable record for a state object, reusing an existing record if possible.
370///
371/// The record is initially marked with SNAPSHOT_ID_MAX to make it invisible to all snapshots
372/// during initialization. The caller must:
373/// 1. Copy/set the desired value into the record
374/// 2. Set the final snapshot_id
375///
376/// Returns a record that is either:
377/// - A reused record (if `used_locked()` found one), marked with SNAPSHOT_ID_MAX
378/// - A newly created record, prepended to the state's record chain via `prepend_state_record()`
379pub(crate) fn new_overwritable_record_locked(state: &dyn StateObject) -> Rc<StateRecord> {
380    let state_head = state.first_record();
381
382    // Try to reuse an existing record
383    if let Some(reusable) = used_locked(&state_head) {
384        // Mark as invisible during initialization
385        reusable.set_snapshot_id(SNAPSHOT_ID_MAX);
386        return reusable;
387    }
388
389    // No reusable record found; create an invisible record and let the caller
390    // replace the unit initializer before publishing the final snapshot id.
391    let new_record = StateRecord::new(
392        SNAPSHOT_ID_MAX,
393        (),
394        None, // next will be set by prepend_state_record
395    );
396
397    // Prepend the new record to the state's chain
398    state.prepend_state_record(Rc::clone(&new_record));
399
400    new_record
401}
402
403/// Creates an overwritable record and ensures it is the head of the record chain.
404///
405/// This is used for global snapshot writes where the newest record must be at the head
406/// to keep tombstoning logic consistent. Reused records are unlinked from their current
407/// position before being prepended.
408pub(crate) fn new_overwritable_record_as_head_locked(state: &dyn StateObject) -> Rc<StateRecord> {
409    let head = state.first_record();
410
411    if let Some(reusable) = used_locked(&head) {
412        reusable.set_snapshot_id(SNAPSHOT_ID_MAX);
413
414        if !Rc::ptr_eq(&head, &reusable) {
415            let mut cursor = Some(Rc::clone(&head));
416            let mut unlinked = false;
417
418            while let Some(node) = cursor {
419                let next = node.next();
420                if let Some(next_record) = next {
421                    if Rc::ptr_eq(&next_record, &reusable) {
422                        node.set_next(reusable.next());
423                        unlinked = true;
424                        break;
425                    }
426                    cursor = Some(next_record);
427                } else {
428                    break;
429                }
430            }
431
432            if !unlinked {
433                debug_assert!(
434                    false,
435                    "new_overwritable_record_as_head_locked: reusable record not found in chain"
436                );
437                let new_record = StateRecord::new(SNAPSHOT_ID_MAX, (), None);
438                state.prepend_state_record(Rc::clone(&new_record));
439                return new_record;
440            }
441
442            state.prepend_state_record(Rc::clone(&reusable));
443        }
444
445        return reusable;
446    }
447
448    let new_record = StateRecord::new(SNAPSHOT_ID_MAX, (), None);
449    state.prepend_state_record(Rc::clone(&new_record));
450    new_record
451}
452
453/// Overwrites unused records in a state object's record chain with data from retained records.
454///
455/// This function implements Kotlin's `overwriteUnusedRecordsLocked` to reclaim memory by:
456/// 1. Finding records below the reuse limit (records invisible to all open snapshots)
457/// 2. Keeping the highest record below the reuse limit (so lowest pinned snapshot can see it)
458/// 3. Marking older obscured records as INVALID_SNAPSHOT and copying valid data into them
459///
460/// The valid data is copied from a "young" record (above reuse limit) to ensure that if
461/// an invalidated record is somehow accessed, it contains current valid data rather than
462/// cleared/garbage values.
463///
464/// Returns `true` if the state has multiple retained records and should stay in extraStateObjects,
465/// `false` if it can be removed from tracking.
466pub(crate) fn overwrite_unused_records_locked<T: Any + Clone>(state: &dyn StateObject) -> bool {
467    let head = state.first_record();
468    let mut current = Some(Rc::clone(&head));
469    let mut overwrite_record: Option<Rc<StateRecord>> = None;
470    let mut valid_record: Option<Rc<StateRecord>> = None;
471
472    // Calculate reuse limit: records below this ID are invisible to all open snapshots
473    // Mirrors Kotlin's: val reuseLimit = pinningTable.lowestOrDefault(nextSnapshotId)
474    let reuse_limit =
475        lowest_pinned_snapshot().unwrap_or_else(crate::snapshot_v2::peek_next_snapshot_id);
476
477    let mut retained_records = 0;
478
479    while let Some(record) = current {
480        let current_id = record.snapshot_id();
481
482        if current_id == INVALID_SNAPSHOT_ID {
483            // Already invalid, skip
484        } else if current_id < reuse_limit {
485            if valid_record.is_none() {
486                // If any records are below reuse_limit, we must keep the highest one
487                // so the lowest snapshot can select it
488                valid_record = Some(Rc::clone(&record));
489                retained_records += 1;
490            } else {
491                // We have two records below the reuse limit - one obscures the other
492                // Overwrite the older one (lower snapshot_id)
493                let Some(valid) = valid_record.as_ref() else {
494                    valid_record = Some(Rc::clone(&record));
495                    retained_records += 1;
496                    current = record.next();
497                    continue;
498                };
499                let record_to_overwrite = if current_id < valid.snapshot_id() {
500                    Rc::clone(&record)
501                } else {
502                    // Keep current as valid, overwrite the previous valid
503                    let to_overwrite = Rc::clone(valid);
504                    valid_record = Some(Rc::clone(&record));
505                    to_overwrite
506                };
507
508                // Lazily find a young record to copy data from
509                let source_record = overwrite_record.get_or_insert_with(|| {
510                    find_youngest_or(&head, |r| r.snapshot_id() >= reuse_limit)
511                });
512
513                // Mark the old record as invalid and copy valid data into it
514                record_to_overwrite.set_snapshot_id(INVALID_SNAPSHOT_ID);
515                if let Err(error) = record_to_overwrite.assign_value::<T>(source_record) {
516                    log::error!(
517                        "snapshot cleanup could not copy retained state record value for state {:?}: {:?}",
518                        state.object_id(),
519                        error
520                    );
521                }
522            }
523        } else {
524            // Record is above reuse limit - it's still visible and must be kept
525            retained_records += 1;
526        }
527
528        current = record.next();
529    }
530
531    // Return true if we have multiple records that must be retained
532    // (state should stay in extraStateObjects for future cleanup)
533    retained_records > 1
534}
535
536fn active_snapshot() -> AnySnapshot {
537    current_snapshot().unwrap_or_else(|| AnySnapshot::Global(GlobalSnapshot::get_or_create()))
538}
539
540pub(crate) trait MutationPolicy<T>: Send + Sync {
541    fn equivalent(&self, a: &T, b: &T) -> bool;
542    fn merge(&self, _previous: &T, _current: &T, _applied: &T) -> Option<T> {
543        None
544    }
545}
546
547pub(crate) struct NeverEqual;
548
549impl<T> MutationPolicy<T> for NeverEqual {
550    fn equivalent(&self, _a: &T, _b: &T) -> bool {
551        false
552    }
553}
554
555pub trait StateObject: Any {
556    fn object_id(&self) -> ObjectId;
557    fn first_record(&self) -> Rc<StateRecord>;
558    fn try_readable_record(
559        &self,
560        snapshot_id: SnapshotId,
561        invalid: &SnapshotIdSet,
562    ) -> Option<Rc<StateRecord>>;
563    fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord>;
564
565    /// Prepends a record to the head of the record chain.
566    /// This is used when reusing records - the record's next pointer is updated to point to the current head,
567    /// and the head is updated to point to the new record.
568    fn prepend_state_record(&self, record: Rc<StateRecord>);
569
570    fn merge_records(
571        &self,
572        _previous: Rc<StateRecord>,
573        _current: Rc<StateRecord>,
574        _applied: Rc<StateRecord>,
575    ) -> Option<Rc<StateRecord>> {
576        None
577    }
578
579    fn commit_merged_record(&self, _merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
580        Err("StateObject does not support merged record commits")
581    }
582    fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str>;
583
584    /// Overwrites unused records in this state's record chain with valid data.
585    ///
586    /// Returns `true` if the state has multiple retained records and should stay in extraStateObjects,
587    /// `false` if it can be removed from tracking.
588    fn overwrite_unused_records(&self) -> bool {
589        false // Default implementation for states that don't support cleanup
590    }
591
592    /// Downcast to Any for testing/debugging purposes.
593    fn as_any(&self) -> &dyn Any;
594}
595
596pub(crate) struct SnapshotMutableState<T> {
597    head: CurrentRecord,
598    policy: Arc<dyn MutationPolicy<T>>,
599    id: ObjectId,
600    weak_self: Mutex<Option<Weak<Self>>>,
601    apply_observers: Mutex<Vec<Box<dyn Fn() + 'static>>>,
602}
603
604impl<T> SnapshotMutableState<T> {
605    fn assert_chain_integrity(&self, caller: &str, snapshot_context: Option<SnapshotId>) {
606        if !should_check_chain_integrity() {
607            return;
608        }
609        let head = self.head.clone_head();
610        let mut cursor = Some(head);
611        let mut seen: HashSet<usize> = HashSet::default();
612        let mut ids = Vec::new();
613
614        while let Some(record) = cursor {
615            let addr = Rc::as_ptr(&record) as usize;
616            assert!(
617                seen.insert(addr),
618                "SnapshotMutableState::{} detected duplicate/cycle at record {:p} for state {:?} (snapshot_context={:?}, chain_ids={:?})",
619                caller,
620                Rc::as_ptr(&record),
621                self.id,
622                snapshot_context,
623                ids
624            );
625            ids.push(record.snapshot_id());
626            cursor = record.next();
627        }
628
629        assert!(
630            !ids.is_empty(),
631            "SnapshotMutableState::{} finished integrity scan with empty id list for state {:?} (snapshot_context={:?})",
632            caller,
633            self.id,
634            snapshot_context
635        );
636    }
637}
638
639fn should_check_chain_integrity() -> bool {
640    #[cfg(debug_assertions)]
641    {
642        true
643    }
644
645    #[cfg(not(debug_assertions))]
646    {
647        std::env::var_os("CRANPOSE_ASSERT_STATE_CHAIN").is_some()
648    }
649}
650
651impl<T: Clone + 'static> SnapshotMutableState<T> {
652    fn record_chain_debug(&self) -> Vec<(SnapshotId, bool)> {
653        let mut chain_ids = Vec::new();
654        let mut cursor = Some(self.first_record());
655        while let Some(record) = cursor {
656            chain_ids.push((record.snapshot_id(), record.is_tombstone()));
657            cursor = record.next();
658        }
659        chain_ids
660    }
661
662    fn readable_record_for_active_snapshot(&self) -> Result<Rc<StateRecord>, StateReadFailure> {
663        let snapshot = active_snapshot();
664        if let Some(state) = self.upgrade_self() {
665            snapshot.record_read(&*state);
666        }
667
668        let snapshot_id = snapshot.snapshot_id();
669        let invalid = snapshot.invalid();
670
671        if let Some(record) = self.readable_for(snapshot_id, &invalid) {
672            return Ok(record);
673        }
674
675        let fresh_snapshot = active_snapshot();
676        let fresh_id = fresh_snapshot.snapshot_id();
677        let fresh_invalid = fresh_snapshot.invalid();
678
679        if let Some(record) = self.readable_for(fresh_id, &fresh_invalid) {
680            return Ok(record);
681        }
682
683        let global = GlobalSnapshot::get_or_create();
684        let global_id = global.snapshot_id();
685        let global_invalid = global.invalid();
686
687        if let Some(record) = self.readable_for(global_id, &global_invalid) {
688            return Ok(record);
689        }
690
691        Err(StateReadFailure {
692            state_id: self.id,
693            snapshot_id,
694            fresh_snapshot_id: fresh_id,
695            fresh_invalid,
696            record_chain: self.record_chain_debug(),
697        })
698    }
699
700    fn readable_for(
701        &self,
702        snapshot_id: SnapshotId,
703        invalid: &SnapshotIdSet,
704    ) -> Option<Rc<StateRecord>> {
705        let head = self.first_record();
706        readable_record_for(&head, snapshot_id, invalid)
707    }
708
709    fn writable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
710        let readable = match self.readable_for(snapshot_id, invalid) {
711            Some(record) => record,
712            None => {
713                let current_head = self.head.clone_head();
714                let refreshed = readable_record_for(&current_head, snapshot_id, invalid);
715                let source = refreshed.unwrap_or_else(|| current_head.clone());
716
717                // Create a new record
718                // Record reuse is NOT used here to preserve history for conflict detection
719                // Reuse happens during cleanup (overwrite_unused_records_locked)
720                let cloned_value = source.with_value(|value: &T| value.clone());
721                let new_head = StateRecord::new(snapshot_id, cloned_value, Some(current_head));
722                self.head.replace(new_head.clone());
723                self.assert_chain_integrity("writable_record(recover)", Some(snapshot_id));
724                return new_head;
725            }
726        };
727
728        if readable.snapshot_id() == snapshot_id {
729            return readable;
730        }
731
732        let refreshed = {
733            let current_head = self.head.clone_head();
734            let refreshed = readable_record_for(&current_head, snapshot_id, invalid).unwrap_or_else(
735                || {
736                    panic!(
737                        "SnapshotMutableState::writable_record failed to locate refreshed readable record (state {:?}, snapshot_id={}, invalid={:?})",
738                        self.id, snapshot_id, invalid
739                    )
740                },
741            );
742
743            if refreshed.snapshot_id() == snapshot_id {
744                return refreshed;
745            }
746
747            Rc::clone(&refreshed)
748        };
749
750        let overwritable = new_overwritable_record_locked(self);
751        if let Err(error) = overwritable.assign_value::<T>(&refreshed) {
752            log::error!(
753                "snapshot writable record could not copy refreshed value for state {:?}: {:?}",
754                self.id,
755                error
756            );
757        }
758        overwritable.set_snapshot_id(snapshot_id);
759        overwritable.set_tombstone(false);
760
761        self.assert_chain_integrity("writable_record(reuse)", Some(snapshot_id));
762
763        overwritable
764    }
765
766    pub(crate) fn new_in_arc(initial: T, policy: Arc<dyn MutationPolicy<T>>) -> Arc<Self> {
767        let snapshot = active_snapshot();
768        let snapshot_id = snapshot.snapshot_id();
769
770        let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, initial.clone(), None);
771        let head = StateRecord::new(snapshot_id, initial, Some(tail));
772
773        let mut state = Arc::new(Self {
774            head: CurrentRecord::new(head),
775            policy,
776            id: ObjectId::default(),
777            weak_self: Mutex::new(None),
778            apply_observers: Mutex::new(Vec::new()),
779        });
780
781        let id = ObjectId::new(&state);
782        if let Some(state_inner) = Arc::get_mut(&mut state) {
783            state_inner.id = id;
784        }
785
786        *state.lock_weak_self() = Some(Arc::downgrade(&state));
787
788        // No need to advance the global snapshot for initial state creation
789
790        state
791    }
792
793    pub(crate) fn add_apply_observer(&self, observer: Box<dyn Fn() + 'static>) {
794        self.lock_apply_observers().push(observer);
795    }
796
797    fn notify_applied(&self) {
798        let observers = self.lock_apply_observers();
799        for observer in observers.iter() {
800            observer();
801        }
802    }
803
804    fn lock_weak_self(&self) -> MutexGuard<'_, Option<Weak<Self>>> {
805        self.weak_self
806            .lock()
807            .unwrap_or_else(|poisoned| poisoned.into_inner())
808    }
809
810    fn lock_apply_observers(&self) -> MutexGuard<'_, Vec<Box<dyn Fn() + 'static>>> {
811        self.apply_observers
812            .lock()
813            .unwrap_or_else(|poisoned| poisoned.into_inner())
814    }
815
816    fn upgrade_self(&self) -> Option<Arc<Self>> {
817        self.lock_weak_self()
818            .as_ref()
819            .and_then(|weak| weak.upgrade())
820    }
821
822    #[inline]
823    pub(crate) fn id(&self) -> ObjectId {
824        self.id
825    }
826
827    pub(crate) fn try_with_value<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
828        let record = self.readable_record_for_active_snapshot().ok()?;
829        record.try_with_value(f)
830    }
831
832    pub(crate) fn try_get(&self) -> Option<T> {
833        self.try_with_value(Clone::clone)
834    }
835
836    pub(crate) fn get(&self) -> T {
837        let record = self
838            .readable_record_for_active_snapshot()
839            .unwrap_or_else(|failure| panic!("{failure}"));
840        record.with_value(|value: &T| value.clone())
841    }
842
843    pub(crate) fn set(&self, new_value: T) -> bool {
844        // Debug-only check: warn if modifying state in event handler without proper snapshot
845        #[cfg(debug_assertions)]
846        {
847            let in_handler = crate::in_event_handler();
848            let in_snapshot = crate::in_applied_snapshot();
849            if in_handler && !in_snapshot {
850                log::warn!(
851                    target: "cranpose::state",
852                    "State modified in event handler without run_in_mutable_snapshot; \
853                     this can make updates invisible to other contexts. Wrap the handler \
854                     in run_in_mutable_snapshot() or dispatch_ui_event(). State: {:?}",
855                    self.id
856                );
857            }
858        }
859
860        let snapshot = active_snapshot();
861        let snapshot_id = snapshot.snapshot_id();
862
863        match &snapshot {
864            AnySnapshot::Global(global) => {
865                let invalid = snapshot.invalid();
866                let equivalent = self
867                    .readable_for(snapshot_id, &invalid)
868                    .map(|record| {
869                        record.with_value(|current: &T| self.policy.equivalent(current, &new_value))
870                    })
871                    .unwrap_or(false);
872                if equivalent {
873                    return false;
874                }
875
876                if global.has_pending_children() {
877                    panic!(
878                        "SnapshotMutableState::set attempted global write while pending children {:?} exist (state {:?}, snapshot_id={})",
879                        global.pending_children(),
880                        self.id,
881                        snapshot_id
882                    );
883                }
884
885                let mut written_state: Option<Arc<dyn StateObject>> = None;
886                if let Some(state) = self.upgrade_self() {
887                    let trait_object: Arc<dyn StateObject> = state.clone();
888                    snapshot.record_write(trait_object.clone());
889                    written_state = Some(trait_object);
890                }
891                mark_update_write(self.id);
892
893                let new_id = allocate_record_id();
894                let record = new_overwritable_record_as_head_locked(self);
895                record.replace_value(new_value);
896                record.set_snapshot_id(new_id);
897                record.set_tombstone(false);
898                advance_global_snapshot(new_id);
899                self.assert_chain_integrity("set(global-push)", Some(snapshot_id));
900
901                if !global.has_pending_children() {
902                    let mut cursor = record.next();
903                    while let Some(node) = cursor {
904                        if !node.is_tombstone() && node.snapshot_id() != PREEXISTING_SNAPSHOT_ID {
905                            node.clear_value();
906                            node.set_tombstone(true);
907                        }
908                        cursor = node.next();
909                    }
910                    self.assert_chain_integrity("set(global-tombstone)", Some(snapshot_id));
911                }
912
913                if let Some(modified) = written_state.as_ref() {
914                    crate::snapshot_v2::notify_apply_observers(
915                        std::slice::from_ref(modified),
916                        new_id,
917                    );
918                }
919            }
920            AnySnapshot::Mutable(_)
921            | AnySnapshot::NestedMutable(_)
922            | AnySnapshot::TransparentMutable(_) => {
923                let invalid = snapshot.invalid();
924                let equivalent = self
925                    .readable_for(snapshot_id, &invalid)
926                    .map(|record| {
927                        record.with_value(|current: &T| self.policy.equivalent(current, &new_value))
928                    })
929                    .unwrap_or(false);
930                if equivalent {
931                    return false;
932                }
933
934                if let Some(state) = self.upgrade_self() {
935                    let trait_object: Arc<dyn StateObject> = state.clone();
936                    snapshot.record_write(trait_object);
937                }
938                mark_update_write(self.id);
939
940                let record = self.writable_record(snapshot_id, &invalid);
941                record.replace_value(new_value);
942                self.assert_chain_integrity("set(child-writable)", Some(snapshot_id));
943            }
944            AnySnapshot::Readonly(_)
945            | AnySnapshot::NestedReadonly(_)
946            | AnySnapshot::TransparentReadonly(_) => {
947                panic!("Cannot write to a read-only snapshot");
948            }
949        }
950
951        // Retain the prior record chain so concurrent readers never observe freed nodes.
952        // Compose proper prunes when it can prove no readers exist; for now we keep
953        // the historical chain with tombstoned values to avoid use-after-free crashes
954        // under heavy UI load.
955        true
956    }
957}
958
959thread_local! {
960    static ACTIVE_UPDATES: RefCell<HashSet<ObjectId>> = RefCell::new(HashSet::default());
961    static PENDING_WRITES: RefCell<HashSet<ObjectId>> = RefCell::new(HashSet::default());
962}
963
964pub(crate) struct UpdateScope {
965    id: ObjectId,
966    finished: bool,
967}
968
969impl UpdateScope {
970    pub(crate) fn new(id: ObjectId) -> Self {
971        ACTIVE_UPDATES.with(|active| {
972            active.borrow_mut().insert(id);
973        });
974        PENDING_WRITES.with(|pending| {
975            pending.borrow_mut().remove(&id);
976        });
977        Self {
978            id,
979            finished: false,
980        }
981    }
982
983    pub(crate) fn finish(mut self) -> bool {
984        self.finished = true;
985        ACTIVE_UPDATES.with(|active| {
986            active.borrow_mut().remove(&self.id);
987        });
988        PENDING_WRITES.with(|pending| pending.borrow_mut().remove(&self.id))
989    }
990}
991
992impl Drop for UpdateScope {
993    fn drop(&mut self) {
994        if self.finished {
995            return;
996        }
997        ACTIVE_UPDATES.with(|active| {
998            active.borrow_mut().remove(&self.id);
999        });
1000        PENDING_WRITES.with(|pending| {
1001            pending.borrow_mut().remove(&self.id);
1002        });
1003    }
1004}
1005
1006fn mark_update_write(id: ObjectId) {
1007    ACTIVE_UPDATES.with(|active| {
1008        if active.borrow().contains(&id) {
1009            PENDING_WRITES.with(|pending| {
1010                pending.borrow_mut().insert(id);
1011            });
1012        }
1013    });
1014}
1015
1016impl<T: Clone + 'static> SnapshotMutableState<T> {
1017    /// Try to find a readable record, returning None if no valid record exists.
1018    fn try_readable_record(
1019        &self,
1020        snapshot_id: SnapshotId,
1021        invalid: &SnapshotIdSet,
1022    ) -> Option<Rc<StateRecord>> {
1023        self.readable_for(snapshot_id, invalid)
1024    }
1025}
1026
1027impl<T: Clone + 'static> StateObject for SnapshotMutableState<T> {
1028    fn object_id(&self) -> ObjectId {
1029        self.id
1030    }
1031
1032    fn first_record(&self) -> Rc<StateRecord> {
1033        self.head.clone_head()
1034    }
1035
1036    fn try_readable_record(
1037        &self,
1038        snapshot_id: SnapshotId,
1039        invalid: &SnapshotIdSet,
1040    ) -> Option<Rc<StateRecord>> {
1041        self.try_readable_record(snapshot_id, invalid)
1042    }
1043
1044    fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
1045        self.try_readable_record(snapshot_id, invalid)
1046            .unwrap_or_else(|| {
1047                panic!(
1048                    "SnapshotMutableState::readable_record returned null (state={:?}, snapshot_id={})",
1049                    self.id, snapshot_id
1050                )
1051            })
1052    }
1053
1054    fn prepend_state_record(&self, record: Rc<StateRecord>) {
1055        self.head.prepend(record);
1056    }
1057
1058    fn merge_records(
1059        &self,
1060        previous: Rc<StateRecord>,
1061        current: Rc<StateRecord>,
1062        applied: Rc<StateRecord>,
1063    ) -> Option<Rc<StateRecord>> {
1064        let Some(current_value) = current.try_with_value(|value: &T| value.clone()) else {
1065            log::error!(
1066                "SnapshotMutableState::merge_records current record value missing or wrong type (state {:?}, current_id={})",
1067                self.id,
1068                current.snapshot_id()
1069            );
1070            return None;
1071        };
1072        let Some(applied_value) = applied.try_with_value(|value: &T| value.clone()) else {
1073            log::error!(
1074                "SnapshotMutableState::merge_records applied record value missing or wrong type (state {:?}, applied_id={})",
1075                self.id,
1076                applied.snapshot_id()
1077            );
1078            return None;
1079        };
1080        if self.policy.equivalent(&current_value, &applied_value) {
1081            return Some(current);
1082        }
1083
1084        let Some(previous_value) = previous.try_with_value(|value: &T| value.clone()) else {
1085            log::error!(
1086                "SnapshotMutableState::merge_records previous record value missing or wrong type (state {:?}, previous_id={})",
1087                self.id,
1088                previous.snapshot_id()
1089            );
1090            return None;
1091        };
1092        let merged = self
1093            .policy
1094            .merge(&previous_value, &current_value, &applied_value)?;
1095
1096        Some(StateRecord::new(applied.snapshot_id(), merged, None))
1097    }
1098
1099    fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str> {
1100        let head = self.first_record();
1101        let mut cursor = Some(head);
1102        while let Some(record) = cursor {
1103            if record.snapshot_id() == child_id {
1104                let Some(cloned) = record.try_with_value(|value: &T| value.clone()) else {
1105                    log::error!(
1106                        "SnapshotMutableState::promote_record child record value missing or wrong type (state {:?}, child_id={})",
1107                        self.id,
1108                        child_id
1109                    );
1110                    return Err("child record value missing or wrong type");
1111                };
1112                let new_id = allocate_record_id();
1113                let current_head = self.head.clone_head();
1114                let new_head = StateRecord::new(new_id, cloned, Some(current_head));
1115                self.head.replace(new_head);
1116                advance_global_snapshot(new_id);
1117                self.notify_applied();
1118                self.assert_chain_integrity("promote_record", Some(child_id));
1119                return Ok(());
1120            }
1121            cursor = record.next();
1122        }
1123        log::error!(
1124            "SnapshotMutableState::promote_record missing child record (state {:?}, child_id={})",
1125            self.id,
1126            child_id
1127        );
1128        Err("missing child record")
1129    }
1130
1131    fn commit_merged_record(&self, merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
1132        let Some(value) = merged.try_with_value(|value: &T| value.clone()) else {
1133            log::error!(
1134                "SnapshotMutableState::commit_merged_record merged record value missing or wrong type (state {:?}, merged_id={})",
1135                self.id,
1136                merged.snapshot_id()
1137            );
1138            return Err("merged record value missing or wrong type");
1139        };
1140        let new_id = allocate_record_id();
1141        let current_head = self.head.clone_head();
1142        let new_head = StateRecord::new(new_id, value, Some(current_head));
1143        self.head.replace(new_head);
1144        advance_global_snapshot(new_id);
1145        self.notify_applied();
1146        self.assert_chain_integrity("commit_merged_record", Some(new_id));
1147        Ok(new_id)
1148    }
1149
1150    fn overwrite_unused_records(&self) -> bool {
1151        overwrite_unused_records_locked::<T>(self)
1152    }
1153
1154    fn as_any(&self) -> &dyn Any {
1155        self
1156    }
1157}
1158
1159pub(crate) struct MutableStateInner<T: Clone + 'static> {
1160    pub(crate) state: Arc<SnapshotMutableState<T>>,
1161    pub(crate) watchers: RefCell<HashMap<ScopeId, RcWeak<RecomposeScopeInner>>>,
1162    runtime: RuntimeHandle,
1163    state_id: Cell<Option<StateId>>,
1164}
1165
1166fn shrink_watchers_if_sparse(watchers: &mut HashMap<ScopeId, RcWeak<RecomposeScopeInner>>) {
1167    let len = watchers.len();
1168    let capacity = watchers.capacity();
1169    if capacity > len.saturating_mul(4).max(32) {
1170        watchers.shrink_to_fit();
1171    }
1172}
1173
1174impl<T: Clone + 'static> MutableStateInner<T> {
1175    pub(crate) fn new_with_policy(
1176        value: T,
1177        runtime: RuntimeHandle,
1178        policy: Arc<dyn MutationPolicy<T>>,
1179    ) -> Self {
1180        Self {
1181            state: SnapshotMutableState::new_in_arc(value, policy),
1182            watchers: RefCell::new(HashMap::default()),
1183            runtime,
1184            state_id: Cell::new(None),
1185        }
1186    }
1187
1188    pub(crate) fn install_snapshot_observer(&self, state_id: StateId) {
1189        self.state_id.set(Some(state_id));
1190        let runtime_handle = self.runtime.clone();
1191        self.state.add_apply_observer(Box::new(move || {
1192            let runtime = runtime_handle.clone();
1193            runtime_handle.enqueue_ui_task(Box::new(move || {
1194                runtime.with_state_arena(|arena| {
1195                    let _ = arena.with_typed_opt::<T, _>(state_id, |inner| {
1196                        inner.invalidate_watchers();
1197                    });
1198                });
1199            }));
1200        }));
1201    }
1202
1203    fn with_value<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1204        let value = self.state.get();
1205        f(&value)
1206    }
1207
1208    fn register_scope(&self, scope: &RecomposeScope) -> bool {
1209        let mut watchers = self.watchers.borrow_mut();
1210        match watchers.get(&scope.id()) {
1211            Some(existing) if existing.upgrade().is_some() => false,
1212            _ => {
1213                watchers.insert(scope.id(), scope.downgrade());
1214                true
1215            }
1216        }
1217    }
1218
1219    pub(crate) fn unregister_scope(&self, scope_id: ScopeId) {
1220        let mut watchers = self.watchers.borrow_mut();
1221        // Only prune a DEAD entry. Scope ids are derived from the scope
1222        // allocation's address, so a dropped scope's id can already belong
1223        // to a live replacement — its Drop-time unregister must not wipe
1224        // the new scope's registration.
1225        if watchers
1226            .get(&scope_id)
1227            .is_some_and(|weak| weak.upgrade().is_none())
1228        {
1229            watchers.remove(&scope_id);
1230            shrink_watchers_if_sparse(&mut watchers);
1231        }
1232    }
1233
1234    fn state_id(&self) -> Option<StateId> {
1235        self.state_id.get()
1236    }
1237
1238    fn invalidate_watchers(&self) {
1239        let watchers: Vec<RecomposeScope> = {
1240            let mut watchers = self.watchers.borrow_mut();
1241            let mut live = Vec::with_capacity(watchers.len());
1242            watchers.retain(|_, scope| {
1243                if let Some(inner) = scope.upgrade() {
1244                    live.push(RecomposeScope { inner });
1245                    true
1246                } else {
1247                    false
1248                }
1249            });
1250            shrink_watchers_if_sparse(&mut watchers);
1251            live
1252        };
1253
1254        for watcher in watchers {
1255            debug_record_scope_invalidation::<T>(watcher.id(), self.state_id.get());
1256            if let Some(state_id) = self.state_id.get() {
1257                watcher.invalidate_from_state(state_id);
1258            } else {
1259                watcher.invalidate();
1260            }
1261        }
1262    }
1263}
1264
1265fn register_current_state_scope<T: Clone + 'static>(inner: &MutableStateInner<T>) {
1266    let Some(Some(scope)) =
1267        with_current_composer_opt(|composer| composer.current_state_invalidation_scope())
1268    else {
1269        return;
1270    };
1271    if inner.register_scope(&scope) {
1272        if let Some(state_id) = inner.state_id() {
1273            scope.record_state_subscription(state_id);
1274        }
1275    }
1276}
1277
1278/// Cheap copyable read-only view of a state cell.
1279pub struct State<T: Clone + 'static> {
1280    id: StateId,
1281    runtime_id: runtime::RuntimeId,
1282    _marker: PhantomData<fn() -> T>,
1283}
1284
1285/// Cheap copyable mutable view of a state cell.
1286///
1287/// Ownership lives elsewhere: a composition slot, an [`OwnedMutableState`], or
1288/// the runtime for states created with [`crate::mutableStateOf`] /
1289/// [`MutableState::with_runtime`].
1290pub struct MutableState<T: Clone + 'static> {
1291    id: StateId,
1292    runtime_id: runtime::RuntimeId,
1293    _marker: PhantomData<fn() -> T>,
1294}
1295
1296/// Owning state handle for reclaimable state cells.
1297#[derive(Clone)]
1298pub struct OwnedMutableState<T: Clone + 'static> {
1299    state: MutableState<T>,
1300    _lease: Rc<runtime::StateHandleLease>,
1301    _marker: PhantomData<fn() -> T>,
1302}
1303
1304impl<T: Clone + 'static> PartialEq for State<T> {
1305    fn eq(&self, other: &Self) -> bool {
1306        self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1307    }
1308}
1309
1310impl<T: Clone + 'static> Eq for State<T> {}
1311
1312impl<T: Clone + 'static> PartialEq for MutableState<T> {
1313    fn eq(&self, other: &Self) -> bool {
1314        self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1315    }
1316}
1317
1318impl<T: Clone + 'static> Eq for MutableState<T> {}
1319
1320impl<T: Clone + 'static> Copy for State<T> {}
1321
1322impl<T: Clone + 'static> Clone for State<T> {
1323    fn clone(&self) -> Self {
1324        *self
1325    }
1326}
1327
1328impl<T: Clone + 'static> Copy for MutableState<T> {}
1329
1330impl<T: Clone + 'static> Clone for MutableState<T> {
1331    fn clone(&self) -> Self {
1332        *self
1333    }
1334}
1335
1336impl<T: Clone + 'static> State<T> {
1337    fn state_id(&self) -> StateId {
1338        self.id
1339    }
1340
1341    fn runtime_id(&self) -> runtime::RuntimeId {
1342        self.runtime_id
1343    }
1344
1345    fn runtime_handle(&self) -> RuntimeHandle {
1346        runtime::runtime_handle_by_id(self.runtime_id())
1347            .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1348    }
1349
1350    fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1351        runtime::runtime_handle_by_id(self.runtime_id())
1352    }
1353
1354    fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1355        self.runtime_handle()
1356            .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
1357    }
1358
1359    fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1360        self.runtime_handle_opt()?
1361            .try_with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))?
1362    }
1363
1364    fn subscribe_current_scope(&self) {
1365        self.with_inner(register_current_state_scope::<T>);
1366    }
1367
1368    pub fn is_alive(&self) -> bool {
1369        self.try_with_inner(|_| ()).is_some()
1370    }
1371
1372    pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1373        self.try_with_inner(|inner| inner.state.try_with_value(f))?
1374    }
1375
1376    pub fn try_value(&self) -> Option<T> {
1377        self.try_with_inner(|inner| inner.state.try_get())?
1378    }
1379
1380    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1381        self.subscribe_current_scope();
1382        self.with_inner(|inner| inner.with_value(f))
1383    }
1384
1385    pub fn value(&self) -> T {
1386        self.subscribe_current_scope();
1387        self.with_inner(|inner| inner.state.get())
1388    }
1389
1390    pub fn get(&self) -> T {
1391        self.value()
1392    }
1393}
1394
1395impl<T: Clone + 'static> MutableState<T> {
1396    pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1397        runtime.alloc_persistent_state(value)
1398    }
1399
1400    fn from_parts(id: StateId, runtime_id: runtime::RuntimeId) -> Self {
1401        Self {
1402            id,
1403            runtime_id,
1404            _marker: PhantomData,
1405        }
1406    }
1407
1408    pub(crate) fn from_lease(lease: &Rc<runtime::StateHandleLease>) -> Self {
1409        Self::from_parts(lease.id(), lease.runtime().id())
1410    }
1411
1412    fn state_id(&self) -> StateId {
1413        self.id
1414    }
1415
1416    fn runtime_id(&self) -> runtime::RuntimeId {
1417        self.runtime_id
1418    }
1419
1420    fn runtime_handle(&self) -> RuntimeHandle {
1421        runtime::runtime_handle_by_id(self.runtime_id())
1422            .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1423    }
1424
1425    fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1426        runtime::runtime_handle_by_id(self.runtime_id())
1427    }
1428
1429    fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1430        self.runtime_handle()
1431            .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
1432    }
1433
1434    fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1435        self.runtime_handle_opt()?
1436            .try_with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))?
1437    }
1438
1439    pub fn is_alive(&self) -> bool {
1440        self.try_with_inner(|_| ()).is_some()
1441    }
1442
1443    pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1444        self.try_with_inner(|inner| inner.state.try_with_value(f))?
1445    }
1446
1447    pub fn try_value(&self) -> Option<T> {
1448        self.try_with_inner(|inner| inner.state.try_get())?
1449    }
1450
1451    pub fn as_state(&self) -> State<T> {
1452        State {
1453            id: self.id,
1454            runtime_id: self.runtime_id,
1455            _marker: PhantomData,
1456        }
1457    }
1458
1459    pub fn try_retain(&self) -> Option<OwnedMutableState<T>> {
1460        let lease = self
1461            .runtime_handle_opt()?
1462            .retain_state_lease(self.state_id())?;
1463        Some(OwnedMutableState {
1464            state: *self,
1465            _lease: lease,
1466            _marker: PhantomData,
1467        })
1468    }
1469
1470    pub fn retain(&self) -> OwnedMutableState<T> {
1471        self.try_retain()
1472            .unwrap_or_else(|| panic!("state {:?} is no longer alive", self.state_id()))
1473    }
1474
1475    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1476        self.subscribe_current_scope();
1477        self.with_inner(|inner| inner.with_value(f))
1478    }
1479
1480    pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
1481        let runtime = self.runtime_handle();
1482        runtime.assert_ui_thread();
1483        runtime.with_state_arena(|arena| {
1484            arena.with_typed::<T, R>(self.state_id(), |inner| {
1485                let mut value = inner.state.get();
1486                let tracker = UpdateScope::new(inner.state.id());
1487                let result = f(&mut value);
1488                let wrote_elsewhere = tracker.finish();
1489                if !wrote_elsewhere && inner.state.set(value) {
1490                    inner.invalidate_watchers();
1491                }
1492                result
1493            })
1494        })
1495    }
1496
1497    pub fn replace(&self, value: T) {
1498        let Some(runtime) = self.runtime_handle_opt() else {
1499            log::debug!(
1500                "MutableState::replace skipped: runtime {:?} dropped",
1501                self.runtime_id()
1502            );
1503            return;
1504        };
1505        runtime.assert_ui_thread();
1506        let replaced = runtime
1507            .try_with_state_arena(|arena| {
1508                arena.with_typed_opt::<T, ()>(self.state_id(), |inner| {
1509                    if inner.state.set(value) {
1510                        inner.invalidate_watchers();
1511                    }
1512                })
1513            })
1514            .flatten();
1515        if replaced.is_none() {
1516            log::debug!(
1517                "MutableState::replace skipped: state cell released (slot={}, gen={})",
1518                self.state_id().slot(),
1519                self.state_id().generation(),
1520            );
1521        }
1522    }
1523
1524    pub fn set_value(&self, value: T) {
1525        self.replace(value);
1526    }
1527
1528    pub fn set(&self, value: T) {
1529        self.replace(value);
1530    }
1531
1532    pub fn value(&self) -> T {
1533        self.subscribe_current_scope();
1534        self.with_inner(|inner| inner.state.get())
1535    }
1536
1537    pub fn get(&self) -> T {
1538        self.value()
1539    }
1540
1541    pub fn get_non_reactive(&self) -> T {
1542        self.with_inner(|inner| inner.state.get())
1543    }
1544
1545    #[doc(hidden)]
1546    pub fn runtime_state_id(&self) -> StateId {
1547        self.state_id()
1548    }
1549
1550    #[doc(hidden)]
1551    pub fn subscribe_current_scope_only(&self) {
1552        self.subscribe_current_scope();
1553    }
1554
1555    fn subscribe_current_scope(&self) {
1556        self.with_inner(register_current_state_scope::<T>);
1557    }
1558
1559    #[cfg(test)]
1560    pub(crate) fn watcher_count(&self) -> usize {
1561        self.with_inner(|inner| inner.watchers.borrow().len())
1562    }
1563
1564    #[cfg(test)]
1565    pub(crate) fn watcher_capacity(&self) -> usize {
1566        self.with_inner(|inner| inner.watchers.borrow().capacity())
1567    }
1568
1569    #[cfg(test)]
1570    pub(crate) fn state_id_for_test(&self) -> StateId {
1571        self.state_id()
1572    }
1573
1574    #[cfg(test)]
1575    pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1576        self.as_state().subscribe_scope_for_test(scope);
1577    }
1578}
1579
1580impl<T: Clone + 'static> OwnedMutableState<T> {
1581    pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1582        let lease = runtime.alloc_state(value);
1583        Self {
1584            state: MutableState::from_lease(&lease),
1585            _lease: lease,
1586            _marker: PhantomData,
1587        }
1588    }
1589
1590    pub(crate) fn with_runtime_and_policy(
1591        value: T,
1592        runtime: RuntimeHandle,
1593        policy: Arc<dyn MutationPolicy<T>>,
1594    ) -> Self {
1595        let lease = runtime.alloc_state_with_policy(value, policy);
1596        Self {
1597            state: MutableState::from_lease(&lease),
1598            _lease: lease,
1599            _marker: PhantomData,
1600        }
1601    }
1602
1603    pub fn handle(&self) -> MutableState<T> {
1604        self.state
1605    }
1606
1607    pub fn as_state(&self) -> State<T> {
1608        self.state.as_state()
1609    }
1610}
1611
1612impl<T: Clone + 'static> Deref for OwnedMutableState<T> {
1613    type Target = MutableState<T>;
1614
1615    fn deref(&self) -> &Self::Target {
1616        &self.state
1617    }
1618}
1619
1620#[cfg(test)]
1621impl<T: Clone + 'static> State<T> {
1622    pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1623        self.with_inner(|inner| {
1624            if inner.register_scope(scope) {
1625                if let Some(state_id) = inner.state_id() {
1626                    scope.record_state_subscription(state_id);
1627                }
1628            }
1629        });
1630    }
1631}
1632
1633impl<T: fmt::Debug + Clone + 'static> fmt::Debug for MutableState<T> {
1634    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1635        if let Some(value) = self.try_value() {
1636            f.debug_struct("MutableState")
1637                .field("value", &value)
1638                .finish()
1639        } else {
1640            f.write_str("MutableState { value: <unavailable> }")
1641        }
1642    }
1643}
1644
1645#[derive(Clone)]
1646pub struct SnapshotStateList<T: Clone + 'static> {
1647    state: OwnedMutableState<Vec<T>>,
1648}
1649
1650impl<T: Clone + 'static> SnapshotStateList<T> {
1651    pub fn with_runtime<I>(values: I, runtime: RuntimeHandle) -> Self
1652    where
1653        I: IntoIterator<Item = T>,
1654    {
1655        let initial: Vec<T> = values.into_iter().collect();
1656        Self {
1657            state: OwnedMutableState::with_runtime(initial, runtime),
1658        }
1659    }
1660
1661    pub fn as_state(&self) -> State<Vec<T>> {
1662        self.state.as_state()
1663    }
1664
1665    pub fn as_mutable_state(&self) -> MutableState<Vec<T>> {
1666        self.state.handle()
1667    }
1668
1669    pub fn len(&self) -> usize {
1670        self.state.with(|values| values.len())
1671    }
1672
1673    pub fn is_empty(&self) -> bool {
1674        self.len() == 0
1675    }
1676
1677    pub fn to_vec(&self) -> Vec<T> {
1678        self.state.with(|values| values.clone())
1679    }
1680
1681    pub fn iter(&self) -> Vec<T> {
1682        self.to_vec()
1683    }
1684
1685    pub fn get(&self, index: usize) -> T {
1686        self.state.with(|values| values[index].clone())
1687    }
1688
1689    pub fn get_opt(&self, index: usize) -> Option<T> {
1690        self.state.with(|values| values.get(index).cloned())
1691    }
1692
1693    pub fn first(&self) -> Option<T> {
1694        self.get_opt(0)
1695    }
1696
1697    pub fn last(&self) -> Option<T> {
1698        self.state.with(|values| values.last().cloned())
1699    }
1700
1701    pub fn push(&self, value: T) {
1702        self.state.update(|values| values.push(value));
1703    }
1704
1705    pub fn extend<I>(&self, iter: I)
1706    where
1707        I: IntoIterator<Item = T>,
1708    {
1709        self.state.update(|values| values.extend(iter));
1710    }
1711
1712    pub fn insert(&self, index: usize, value: T) {
1713        self.state.update(|values| values.insert(index, value));
1714    }
1715
1716    pub fn set(&self, index: usize, value: T) -> T {
1717        self.state
1718            .update(|values| std::mem::replace(&mut values[index], value))
1719    }
1720
1721    pub fn remove(&self, index: usize) -> T {
1722        self.state.update(|values| values.remove(index))
1723    }
1724
1725    pub fn pop(&self) -> Option<T> {
1726        self.state.update(|values| values.pop())
1727    }
1728
1729    pub fn clear(&self) {
1730        self.state.replace(Vec::new());
1731    }
1732
1733    pub fn retain<F>(&self, mut predicate: F)
1734    where
1735        F: FnMut(&T) -> bool,
1736    {
1737        self.state
1738            .update(|values| values.retain(|value| predicate(value)));
1739    }
1740
1741    pub fn replace_with<I>(&self, iter: I)
1742    where
1743        I: IntoIterator<Item = T>,
1744    {
1745        self.state.replace(iter.into_iter().collect());
1746    }
1747}
1748
1749impl<T: fmt::Debug + Clone + 'static> fmt::Debug for SnapshotStateList<T> {
1750    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1751        let contents = self.to_vec();
1752        f.debug_struct("SnapshotStateList")
1753            .field("values", &contents)
1754            .finish()
1755    }
1756}
1757
1758#[derive(Clone)]
1759pub struct SnapshotStateMap<K, V>
1760where
1761    K: Clone + Eq + Hash + 'static,
1762    V: Clone + 'static,
1763{
1764    state: OwnedMutableState<HashMap<K, V>>,
1765}
1766
1767impl<K, V> SnapshotStateMap<K, V>
1768where
1769    K: Clone + Eq + Hash + 'static,
1770    V: Clone + 'static,
1771{
1772    pub fn with_runtime<I>(pairs: I, runtime: RuntimeHandle) -> Self
1773    where
1774        I: IntoIterator<Item = (K, V)>,
1775    {
1776        let map: HashMap<K, V> = pairs.into_iter().collect();
1777        Self {
1778            state: OwnedMutableState::with_runtime(map, runtime),
1779        }
1780    }
1781
1782    pub fn as_state(&self) -> State<HashMap<K, V>> {
1783        self.state.as_state()
1784    }
1785
1786    pub fn as_mutable_state(&self) -> MutableState<HashMap<K, V>> {
1787        self.state.handle()
1788    }
1789
1790    pub fn len(&self) -> usize {
1791        self.state.with(|map| map.len())
1792    }
1793
1794    pub fn is_empty(&self) -> bool {
1795        self.state.with(|map| map.is_empty())
1796    }
1797
1798    pub fn contains_key(&self, key: &K) -> bool {
1799        self.state.with(|map| map.contains_key(key))
1800    }
1801
1802    pub fn get(&self, key: &K) -> Option<V> {
1803        self.state.with(|map| map.get(key).cloned())
1804    }
1805
1806    pub fn to_hash_map(&self) -> HashMap<K, V> {
1807        self.state.with(|map| map.clone())
1808    }
1809
1810    pub fn insert(&self, key: K, value: V) -> Option<V> {
1811        self.state.update(|map| map.insert(key, value))
1812    }
1813
1814    pub fn extend<I>(&self, iter: I)
1815    where
1816        I: IntoIterator<Item = (K, V)>,
1817    {
1818        self.state.update(|map| map.extend(iter));
1819    }
1820
1821    pub fn remove(&self, key: &K) -> Option<V> {
1822        self.state.update(|map| map.remove(key))
1823    }
1824
1825    pub fn clear(&self) {
1826        self.state.replace(HashMap::default());
1827    }
1828
1829    pub fn retain<F>(&self, mut predicate: F)
1830    where
1831        F: FnMut(&K, &mut V) -> bool,
1832    {
1833        self.state.update(|map| map.retain(|k, v| predicate(k, v)));
1834    }
1835}
1836
1837impl<K, V> fmt::Debug for SnapshotStateMap<K, V>
1838where
1839    K: Clone + Eq + Hash + fmt::Debug + 'static,
1840    V: Clone + fmt::Debug + 'static,
1841{
1842    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1843        let contents = self.to_hash_map();
1844        f.debug_struct("SnapshotStateMap")
1845            .field("entries", &contents)
1846            .finish()
1847    }
1848}
1849
1850pub(crate) struct DerivedState<T: Clone + 'static> {
1851    compute: Rc<dyn Fn() -> T>,
1852    pub(crate) state: OwnedMutableState<T>,
1853}
1854
1855impl<T: Clone + 'static> DerivedState<T> {
1856    pub(crate) fn new(runtime: RuntimeHandle, compute: Rc<dyn Fn() -> T>) -> Self {
1857        let initial = compute();
1858        Self {
1859            compute,
1860            state: OwnedMutableState::with_runtime(initial, runtime),
1861        }
1862    }
1863
1864    pub(crate) fn set_compute(&mut self, compute: Rc<dyn Fn() -> T>) {
1865        self.compute = compute;
1866    }
1867
1868    pub(crate) fn recompute(&self) {
1869        let value = (self.compute)();
1870        self.state.set_value(value);
1871    }
1872}
1873
1874impl<T: fmt::Debug + Clone + 'static> fmt::Debug for State<T> {
1875    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1876        if let Some(value) = self.try_value() {
1877            f.debug_struct("State").field("value", &value).finish()
1878        } else {
1879            f.write_str("State { value: <unavailable> }")
1880        }
1881    }
1882}
1883
1884#[cfg(test)]
1885mod tests {
1886    use super::*;
1887
1888    /// Helper to create a chain of records for testing
1889    fn create_record_chain(ids: &[SnapshotId]) -> Rc<StateRecord> {
1890        let mut head: Option<Rc<StateRecord>> = None;
1891
1892        // Build chain in reverse order (last ID becomes the tail)
1893        for &id in ids.iter().rev() {
1894            head = Some(StateRecord::new(id, 0i32, head));
1895        }
1896
1897        head.expect("create_record_chain called with empty ids")
1898    }
1899
1900    struct ManualState {
1901        head: Rc<StateRecord>,
1902    }
1903
1904    impl ManualState {
1905        fn new(head: Rc<StateRecord>) -> Self {
1906            Self { head }
1907        }
1908    }
1909
1910    impl StateObject for ManualState {
1911        fn object_id(&self) -> ObjectId {
1912            ObjectId(999)
1913        }
1914
1915        fn first_record(&self) -> Rc<StateRecord> {
1916            Rc::clone(&self.head)
1917        }
1918
1919        fn try_readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Option<Rc<StateRecord>> {
1920            Some(Rc::clone(&self.head))
1921        }
1922
1923        fn readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Rc<StateRecord> {
1924            Rc::clone(&self.head)
1925        }
1926
1927        fn prepend_state_record(&self, _: Rc<StateRecord>) {}
1928
1929        fn promote_record(&self, _: SnapshotId) -> Result<(), &'static str> {
1930            Ok(())
1931        }
1932
1933        fn as_any(&self) -> &dyn Any {
1934            self
1935        }
1936    }
1937
1938    fn poison_mutex<T>(mutex: &Mutex<T>) {
1939        let poison_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1940            let _guard = mutex
1941                .lock()
1942                .unwrap_or_else(|poisoned| poisoned.into_inner());
1943            panic!("poison snapshot state mutex for recovery test");
1944        }));
1945
1946        assert!(poison_result.is_err());
1947    }
1948
1949    #[test]
1950    fn snapshot_mutable_state_recovers_poisoned_weak_self_lock() {
1951        let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1952
1953        poison_mutex(&state.weak_self);
1954
1955        assert_eq!(state.get(), 100);
1956        assert!(state.set(101));
1957        assert_eq!(state.get(), 101);
1958    }
1959
1960    #[test]
1961    fn snapshot_mutable_state_recovers_poisoned_apply_observer_lock() {
1962        let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1963        let calls = Rc::new(Cell::new(0usize));
1964        let observed_calls = Rc::clone(&calls);
1965
1966        poison_mutex(&state.apply_observers);
1967
1968        state.add_apply_observer(Box::new(move || {
1969            observed_calls.set(observed_calls.get() + 1);
1970        }));
1971        state.notify_applied();
1972
1973        assert_eq!(calls.get(), 1);
1974    }
1975
1976    #[test]
1977    fn snapshot_mutable_state_promote_missing_record_returns_error() {
1978        let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1979        let missing_snapshot = usize::MAX - 17;
1980
1981        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1982            StateObject::promote_record(&*state, missing_snapshot)
1983        }));
1984
1985        assert!(
1986            matches!(result, Ok(Err("missing child record"))),
1987            "missing child record should be reported through Result, got {result:?}"
1988        );
1989    }
1990
1991    #[test]
1992    fn snapshot_mutable_state_promote_wrong_record_type_returns_error() {
1993        let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1994        let child_snapshot = usize::MAX - 31;
1995        let wrong_record = StateRecord::new(child_snapshot, "wrong type", None);
1996        StateObject::prepend_state_record(&*state, wrong_record);
1997
1998        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1999            StateObject::promote_record(&*state, child_snapshot)
2000        }));
2001
2002        assert!(
2003            matches!(result, Ok(Err("child record value missing or wrong type"))),
2004            "wrong child record type should be reported through Result, got {result:?}"
2005        );
2006    }
2007
2008    #[test]
2009    fn snapshot_mutable_state_commit_wrong_record_type_returns_error() {
2010        let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2011        let merged = StateRecord::new(usize::MAX - 43, "wrong type", None);
2012
2013        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2014            StateObject::commit_merged_record(&*state, merged)
2015        }));
2016
2017        assert!(
2018            matches!(result, Ok(Err("merged record value missing or wrong type"))),
2019            "wrong merged record type should be reported through Result, got {result:?}"
2020        );
2021    }
2022
2023    #[test]
2024    fn snapshot_mutable_state_merge_wrong_record_type_returns_none() {
2025        let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2026        let previous = StateRecord::new(usize::MAX - 51, 1i32, None);
2027        let current = StateRecord::new(usize::MAX - 52, "wrong type", None);
2028        let applied = StateRecord::new(usize::MAX - 53, 2i32, None);
2029
2030        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2031            StateObject::merge_records(&*state, previous, current, applied)
2032        }));
2033
2034        match result {
2035            Ok(None) => {}
2036            Ok(Some(_)) => panic!("wrong merge record type unexpectedly produced a merged record"),
2037            Err(_) => panic!("wrong merge record type should not panic"),
2038        }
2039    }
2040
2041    #[test]
2042    fn test_used_locked_finds_invalid_snapshot() {
2043        // Create a chain with an INVALID_SNAPSHOT record
2044        let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2045        let invalid_rec = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, Some(tail));
2046        let head = StateRecord::new(10, 0i32, Some(invalid_rec.clone()));
2047
2048        let result = used_locked(&head);
2049        assert!(result.is_some());
2050        assert_eq!(result.unwrap().snapshot_id(), INVALID_SNAPSHOT_ID);
2051    }
2052
2053    #[test]
2054    fn test_used_locked_finds_obscured_record() {
2055        // Reset pinning state for clean test
2056        crate::snapshot_pinning::reset_pinning_table();
2057
2058        // Pin a high snapshot to set a known reuse limit
2059        // This ensures records 2 and 5 are both below (reuse_limit = 10 - 1 = 9)
2060        let pin_handle = crate::snapshot_pinning::track_pinning(10, &SnapshotIdSet::EMPTY);
2061
2062        // Create a chain with two old records below the reuse limit
2063        let oldest = StateRecord::new(2, 0i32, None);
2064        let newer = StateRecord::new(5, 0i32, Some(oldest.clone()));
2065        let head = StateRecord::new(100, 0i32, Some(newer));
2066
2067        let result = used_locked(&head);
2068
2069        // Should find the older of the two records below reuse limit
2070        assert!(result.is_some());
2071        let reused = result.unwrap();
2072        assert_eq!(
2073            reused.snapshot_id(),
2074            2,
2075            "Should return the oldest obscured record"
2076        );
2077
2078        // Clean up
2079        crate::snapshot_pinning::release_pinning(pin_handle);
2080    }
2081
2082    #[test]
2083    fn test_used_locked_no_reusable_record() {
2084        // Reset pinning state
2085        crate::snapshot_pinning::reset_pinning_table();
2086
2087        // Create a chain where all records are recent (above reuse limit)
2088        // Use very high IDs to ensure they're above any reuse limit
2089        let high_id = allocate_record_id() + 1000;
2090        let head = create_record_chain(&[high_id, high_id + 1, high_id + 2]);
2091
2092        let result = used_locked(&head);
2093        assert!(
2094            result.is_none(),
2095            "Should find no reusable records when all are recent"
2096        );
2097    }
2098
2099    #[test]
2100    fn test_used_locked_single_old_record() {
2101        // Reset pinning state
2102        crate::snapshot_pinning::reset_pinning_table();
2103
2104        // Create a chain with only one old record (should not be reused)
2105        let old = StateRecord::new(2, 0i32, None);
2106        let head = StateRecord::new(100, 0i32, Some(old));
2107
2108        let result = used_locked(&head);
2109        // With only ONE record below reuse limit, it's still valid and should not be reused
2110        assert!(result.is_none(), "Single old record should not be reused");
2111    }
2112
2113    #[test]
2114    fn test_readable_record_for_preexisting() {
2115        let head = create_record_chain(&[PREEXISTING_SNAPSHOT_ID]);
2116        let invalid = SnapshotIdSet::EMPTY;
2117
2118        let result = readable_record_for(&head, 10, &invalid);
2119        assert!(result.is_some());
2120        assert_eq!(result.unwrap().snapshot_id(), PREEXISTING_SNAPSHOT_ID);
2121    }
2122
2123    #[test]
2124    fn test_readable_record_for_picks_highest_valid() {
2125        let head = create_record_chain(&[10, 5, PREEXISTING_SNAPSHOT_ID]);
2126        let invalid = SnapshotIdSet::EMPTY;
2127
2128        // Reading at snapshot 10 should return record 10
2129        let result = readable_record_for(&head, 10, &invalid);
2130        assert!(result.is_some());
2131        assert_eq!(result.unwrap().snapshot_id(), 10);
2132
2133        // Reading at snapshot 7 should skip record 10 and return record 5
2134        let result = readable_record_for(&head, 7, &invalid);
2135        assert!(result.is_some());
2136        assert_eq!(result.unwrap().snapshot_id(), 5);
2137    }
2138
2139    #[test]
2140    fn test_new_overwritable_record_locked_reuses_invalid() {
2141        // Create a state with an INVALID record in the chain
2142        let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2143
2144        // Manually insert an INVALID record into the chain
2145        let current_head = state.first_record();
2146        let invalid_rec = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, current_head.next());
2147        current_head.set_next(Some(invalid_rec.clone()));
2148
2149        let result = new_overwritable_record_locked(&*state);
2150
2151        // Should reuse the INVALID record
2152        assert!(Rc::ptr_eq(&result, &invalid_rec));
2153        assert_eq!(result.snapshot_id(), SNAPSHOT_ID_MAX);
2154    }
2155
2156    #[test]
2157    fn test_new_overwritable_record_locked_creates_new() {
2158        crate::snapshot_pinning::reset_pinning_table();
2159
2160        // Pin snapshot 1 to prevent PREEXISTING (id=1) from being reusable
2161        // This ensures the reuse limit is above 1, so PREEXISTING won't be obscured
2162        let _pin_handle = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2163
2164        // Create a state with all recent records (no reusable ones)
2165        let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2166        let old_head = state.first_record();
2167
2168        let result = new_overwritable_record_locked(&*state);
2169
2170        // Should create a new record
2171        assert_eq!(result.snapshot_id(), SNAPSHOT_ID_MAX);
2172
2173        // Should be prepended to the chain (becomes new head)
2174        let new_head = state.first_record();
2175        assert!(
2176            Rc::ptr_eq(&new_head, &result),
2177            "new_head ({:p}) should equal result ({:p})",
2178            Rc::as_ptr(&new_head),
2179            Rc::as_ptr(&result)
2180        );
2181
2182        // The new record should point to the old head
2183        assert!(result.next().is_some());
2184        assert!(Rc::ptr_eq(&result.next().unwrap(), &old_head));
2185    }
2186
2187    #[test]
2188    fn test_writable_record_reuses_invalid_record() {
2189        crate::snapshot_pinning::reset_pinning_table();
2190
2191        let state = SnapshotMutableState::new_in_arc(7i32, Arc::new(NeverEqual));
2192
2193        // Inject an INVALID record that should be reused on next write.
2194        let head = state.first_record();
2195        let invalid = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, head.next());
2196        head.set_next(Some(invalid.clone()));
2197
2198        let snapshot_id = allocate_record_id();
2199        let result = state.writable_record(snapshot_id, &SnapshotIdSet::EMPTY);
2200
2201        assert!(
2202            Rc::ptr_eq(&result, &invalid),
2203            "Expected writable_record to reuse the INVALID record"
2204        );
2205        assert_eq!(result.snapshot_id(), snapshot_id);
2206        result.with_value(|value: &i32| {
2207            assert_eq!(*value, 7, "Reused record should copy the readable value");
2208        });
2209        assert!(!result.is_tombstone());
2210    }
2211
2212    #[test]
2213    fn test_writable_record_creates_new_when_reuse_disallowed() {
2214        crate::snapshot_pinning::reset_pinning_table();
2215        let pin = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2216
2217        let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2218        let original_head = state.first_record();
2219        let preexisting = original_head
2220            .next()
2221            .expect("preexisting record should exist for newly created state");
2222
2223        let snapshot_id = allocate_record_id();
2224        let result = state.writable_record(snapshot_id, &SnapshotIdSet::EMPTY);
2225
2226        assert!(
2227            !Rc::ptr_eq(&result, &original_head),
2228            "Should not reuse the current head when reuse is disallowed"
2229        );
2230        assert!(
2231            !Rc::ptr_eq(&result, &preexisting),
2232            "Should not reuse the PREEXISTING record"
2233        );
2234        assert_eq!(result.snapshot_id(), snapshot_id);
2235        result.with_value(|value: &i32| assert_eq!(*value, 42));
2236
2237        let new_head = state.first_record();
2238        assert!(
2239            Rc::ptr_eq(&new_head, &result),
2240            "Newly created record should become the head of the chain"
2241        );
2242
2243        crate::snapshot_pinning::release_pinning(pin);
2244    }
2245
2246    #[test]
2247    fn test_state_record_clear_for_reuse() {
2248        let record = StateRecord::new(10, 42i32, None);
2249
2250        // Verify value exists before clearing
2251        record.with_value(|val: &i32| {
2252            assert_eq!(*val, 42);
2253        });
2254
2255        // Clear the record for reuse
2256        record.clear_for_reuse();
2257
2258        // Value should be cleared (will panic if we try to access it)
2259        // Just verify snapshot_id is unchanged
2260        assert_eq!(record.snapshot_id(), 10);
2261    }
2262
2263    #[test]
2264    fn test_overwrite_unused_records_no_old_records() {
2265        crate::snapshot_pinning::reset_pinning_table();
2266
2267        // Create state first to establish snapshot IDs
2268        let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2269
2270        // Pin snapshot 1 so reuse limit is 1, making both initial records (1 and 2) above it
2271        // This ensures PREEXISTING won't be overwritten
2272        let _pin = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2273
2274        let should_retain = state.overwrite_unused_records();
2275
2276        // With both records above/at reuse limit, we have 2 retained
2277        assert!(
2278            should_retain,
2279            "Should retain multiple records when none are old enough"
2280        );
2281
2282        // No records should be marked as INVALID
2283        let mut cursor = Some(state.first_record());
2284        while let Some(record) = cursor {
2285            assert_ne!(record.snapshot_id(), INVALID_SNAPSHOT_ID);
2286            cursor = record.next();
2287        }
2288    }
2289
2290    #[test]
2291    fn test_overwrite_unused_records_basic_cleanup() {
2292        // Test that old records get marked invalid when newer ones exist
2293        crate::snapshot_pinning::reset_pinning_table();
2294
2295        // Create simple manual chain to avoid snapshot ID allocation complexity
2296        let rec1 = StateRecord::new(100, 1i32, None);
2297        let rec2 = StateRecord::new(200, 2i32, Some(rec1.clone()));
2298        let rec3 = StateRecord::new(300, 3i32, Some(rec2.clone()));
2299
2300        // Mock state object for testing
2301        struct TestState {
2302            head: Rc<StateRecord>,
2303        }
2304        impl StateObject for TestState {
2305            fn object_id(&self) -> ObjectId {
2306                ObjectId(999)
2307            }
2308            fn first_record(&self) -> Rc<StateRecord> {
2309                Rc::clone(&self.head)
2310            }
2311            fn try_readable_record(
2312                &self,
2313                _: SnapshotId,
2314                _: &SnapshotIdSet,
2315            ) -> Option<Rc<StateRecord>> {
2316                Some(Rc::clone(&self.head))
2317            }
2318            fn readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Rc<StateRecord> {
2319                Rc::clone(&self.head)
2320            }
2321            fn prepend_state_record(&self, _: Rc<StateRecord>) {}
2322            fn promote_record(&self, _: SnapshotId) -> Result<(), &'static str> {
2323                Ok(())
2324            }
2325            fn as_any(&self) -> &dyn Any {
2326                self
2327            }
2328        }
2329
2330        let test_state = TestState { head: rec3.clone() };
2331
2332        // Pin at 1000 so all three records (100, 200, 300) are below reuse limit
2333        let _pin = crate::snapshot_pinning::track_pinning(1000, &SnapshotIdSet::EMPTY);
2334
2335        let result = overwrite_unused_records_locked::<i32>(&test_state);
2336
2337        // Should keep highest (300), mark others invalid
2338        assert_eq!(rec3.snapshot_id(), 300);
2339        assert_eq!(rec2.snapshot_id(), INVALID_SNAPSHOT_ID);
2340        assert_eq!(rec1.snapshot_id(), INVALID_SNAPSHOT_ID);
2341
2342        // Only one record retained (300), so should return false
2343        assert!(!result);
2344    }
2345
2346    #[test]
2347    fn test_overwrite_unused_records_single_record_only() {
2348        crate::snapshot_pinning::reset_pinning_table();
2349
2350        let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2351
2352        // Remove the PREEXISTING record by setting next to None
2353        let head = state.first_record();
2354        head.set_next(None);
2355
2356        let should_retain = state.overwrite_unused_records();
2357
2358        // With only one record, should return false
2359        assert!(!should_retain, "Single record should return false");
2360    }
2361
2362    #[test]
2363    fn snapshot_state_try_get_reports_missing_visible_record_without_panicking() {
2364        crate::snapshot_pinning::reset_pinning_table();
2365
2366        let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2367        let head = state.first_record();
2368        head.set_snapshot_id(SNAPSHOT_ID_MAX);
2369        head.set_next(None);
2370
2371        assert_eq!(state.try_get(), None);
2372    }
2373
2374    #[test]
2375    fn test_overwrite_unused_records_clears_values() {
2376        crate::snapshot_pinning::reset_pinning_table();
2377
2378        let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2379        let old_rec1 = StateRecord::new(2, 999i32, Some(tail.clone()));
2380        let old_rec2 = StateRecord::new(3, 888i32, Some(old_rec1.clone()));
2381        let head = StateRecord::new(150, 42i32, Some(old_rec2.clone()));
2382        let state = ManualState::new(head.clone());
2383
2384        // Verify value exists before cleanup
2385        old_rec1.with_value(|val: &i32| {
2386            assert_eq!(*val, 999);
2387        });
2388
2389        let _pin = crate::snapshot_pinning::track_pinning(100, &SnapshotIdSet::EMPTY);
2390        overwrite_unused_records_locked::<i32>(&state);
2391
2392        // The invalidated record should have its value cleared
2393        assert_eq!(old_rec1.snapshot_id(), INVALID_SNAPSHOT_ID);
2394        // Value access would panic, so we just verify it was marked invalid
2395    }
2396
2397    #[test]
2398    fn test_overwrite_unused_records_mixed_old_and_new() {
2399        crate::snapshot_pinning::reset_pinning_table();
2400
2401        // Create mixed chain: recent (50) -> old (5) -> old (2) -> PREEXISTING
2402        let preexisting = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2403        let rec2 = StateRecord::new(2, 100i32, Some(preexisting.clone()));
2404        let rec5 = StateRecord::new(5, 100i32, Some(rec2.clone()));
2405        let rec50 = StateRecord::new(50, 100i32, Some(rec5.clone()));
2406        let head = StateRecord::new(120, 100i32, Some(rec50.clone()));
2407        let state = ManualState::new(head.clone());
2408
2409        // Pin snapshot 40 so reuse limit is ~40, making 2 and 5 old but 50 recent
2410        let _pin = crate::snapshot_pinning::track_pinning(40, &SnapshotIdSet::EMPTY);
2411
2412        let should_retain = overwrite_unused_records_locked::<i32>(&state);
2413        assert!(should_retain);
2414
2415        // rec50 is above reuse limit - should stay valid
2416        assert_eq!(rec50.snapshot_id(), 50);
2417        // rec5 is highest below reuse limit - should stay valid
2418        assert_eq!(rec5.snapshot_id(), 5);
2419        // rec2 is older and below reuse limit - should be invalidated
2420        assert_eq!(rec2.snapshot_id(), INVALID_SNAPSHOT_ID);
2421    }
2422
2423    #[test]
2424    fn test_readable_record_for_skips_invalid_set() {
2425        let head = create_record_chain(&[10, 5, PREEXISTING_SNAPSHOT_ID]);
2426        let invalid = SnapshotIdSet::new().set(5);
2427
2428        // Reading at snapshot 10 should skip record 5 (in invalid set)
2429        let result = readable_record_for(&head, 10, &invalid);
2430        assert!(result.is_some());
2431        assert_eq!(result.unwrap().snapshot_id(), 10);
2432
2433        // Reading at snapshot 7 should skip 5 and fall back to PREEXISTING
2434        let result = readable_record_for(&head, 7, &invalid);
2435        assert!(result.is_some());
2436        assert_eq!(result.unwrap().snapshot_id(), PREEXISTING_SNAPSHOT_ID);
2437    }
2438
2439    // ========== Tests for assign_value() ==========
2440
2441    #[test]
2442    fn test_assign_value_copies_int() {
2443        let source = StateRecord::new(10, 42i32, None);
2444        let target = StateRecord::new(20, 0i32, None);
2445
2446        target.assign_value::<i32>(&source).expect("copy int value");
2447
2448        // Verify the value was copied
2449        target.with_value(|val: &i32| {
2450            assert_eq!(*val, 42);
2451        });
2452
2453        // Verify source is unchanged
2454        source.with_value(|val: &i32| {
2455            assert_eq!(*val, 42);
2456        });
2457
2458        // Verify snapshot IDs are unchanged
2459        assert_eq!(source.snapshot_id(), 10);
2460        assert_eq!(target.snapshot_id(), 20);
2461    }
2462
2463    #[test]
2464    fn test_assign_value_copies_string() {
2465        let source = StateRecord::new(10, "hello".to_string(), None);
2466        let target = StateRecord::new(20, "world".to_string(), None);
2467
2468        target
2469            .assign_value::<String>(&source)
2470            .expect("copy string value");
2471
2472        // Verify the value was copied
2473        target.with_value(|val: &String| {
2474            assert_eq!(val, "hello");
2475        });
2476
2477        // Verify source is unchanged
2478        source.with_value(|val: &String| {
2479            assert_eq!(val, "hello");
2480        });
2481    }
2482
2483    #[test]
2484    fn test_assign_value_reports_cleared_source() {
2485        let source = StateRecord::new(10, 42i32, None);
2486        let target = StateRecord::new(20, 0i32, None);
2487
2488        source.clear_value();
2489
2490        assert_eq!(
2491            target.assign_value::<i32>(&source),
2492            Err(StateRecordValueError::MissingOrWrongType {
2493                expected: std::any::type_name::<i32>(),
2494            })
2495        );
2496        assert_eq!(target.with_value(|val: &i32| *val), 0);
2497    }
2498
2499    #[test]
2500    fn test_assign_value_overwrites_existing_value() {
2501        let source = StateRecord::new(10, 100i32, None);
2502        let target = StateRecord::new(20, 999i32, None);
2503
2504        // Verify target has initial value
2505        target.with_value(|val: &i32| {
2506            assert_eq!(*val, 999);
2507        });
2508
2509        // Assign from source
2510        target
2511            .assign_value::<i32>(&source)
2512            .expect("overwrite int value");
2513
2514        // Verify target now has source's value
2515        target.with_value(|val: &i32| {
2516            assert_eq!(*val, 100);
2517        });
2518    }
2519
2520    #[test]
2521    fn test_assign_value_with_custom_type() {
2522        #[derive(Clone, PartialEq, Debug)]
2523        struct Point {
2524            x: f64,
2525            y: f64,
2526        }
2527
2528        let source = StateRecord::new(10, Point { x: 1.5, y: 2.5 }, None);
2529        let target = StateRecord::new(20, Point { x: 0.0, y: 0.0 }, None);
2530
2531        target
2532            .assign_value::<Point>(&source)
2533            .expect("copy point value");
2534
2535        target.with_value(|val: &Point| {
2536            assert_eq!(val, &Point { x: 1.5, y: 2.5 });
2537        });
2538    }
2539
2540    #[test]
2541    fn test_assign_value_self_assignment() {
2542        let record = StateRecord::new(10, 42i32, None);
2543
2544        // Self-assignment should work (though not useful in practice)
2545        record
2546            .assign_value::<i32>(&record)
2547            .expect("self-assign int value");
2548
2549        record.with_value(|val: &i32| {
2550            assert_eq!(*val, 42);
2551        });
2552    }
2553
2554    #[test]
2555    fn test_assign_value_with_vec() {
2556        let source = StateRecord::new(10, vec![1, 2, 3, 4, 5], None);
2557        let target = StateRecord::new(20, Vec::<i32>::new(), None);
2558
2559        target
2560            .assign_value::<Vec<i32>>(&source)
2561            .expect("copy vec value");
2562
2563        target.with_value(|val: &Vec<i32>| {
2564            assert_eq!(val, &vec![1, 2, 3, 4, 5]);
2565        });
2566
2567        // Verify it's a deep copy (modifying source won't affect target)
2568        source.replace_value(vec![10, 20]);
2569        target.with_value(|val: &Vec<i32>| {
2570            assert_eq!(val, &vec![1, 2, 3, 4, 5]);
2571        });
2572    }
2573}