Class BulkSetValueGuard

java.lang.Object
io.keikai.model.impl.BulkSetValueGuard

public final class BulkSetValueGuard extends Object
KEIKAI-679 / KEIKAI-721: thread-local guard that lets bulk callers (currently RangeImpl.setValue / setValues / clearContents) suppress the per-cell precedent-update walk inside SCell.setValue. The guard collects the cell refs that were set during the bulk loop; the caller pops and fires ModelUpdateUtil.handlePrecedentUpdate(io.keikai.model.SBookSeries, io.keikai.model.sys.dependency.Ref) for each unique ref after the loop, preserving the "dependents are notified" semantic (KEIKAI_0741Test) at a constant cost regardless of bulk size.

Without the guard, KEIKAI_0679Test#test (fill 322 cells via Range.setCellValue) and KEIKAI_0721Test (paste 46×7 cells) blow past their 6-s / 14-s waitResponse window because each per-cell handlePrecedentUpdate triggers an O(N) `getEvaluatedDependents` walk.

Since:
7.0.0
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static interface 
    Callback registered by CellImplRustAdv when it lazily opens a Rust `batchBegin` for a freshly-seen book inside the current bulk loop.
  • Method Summary

    Modifier and Type
    Method
    Description
    static boolean
    True when the current thread is inside a `push()` / `popAndFire()` bulk window.
    static void
    End the bulk loop and fire a single handlePrecedentUpdate covering the bounding region of all collected refs.
    static void
    Start a bulk visitor loop.
    static boolean
    Called by CellImplRustAdv.setValue.
    static boolean
    Called by CellImplRustAdv on the first `setValue` it sees per book within a bulk window.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • push

      public static void push()
      Start a bulk visitor loop. Subsequent SCell#setValue calls on this thread will collect their refs instead of firing handlePrecedentUpdate inline. Always pair with a finally-clause popAndFire(io.keikai.model.SBookSeries).
    • isActive

      public static boolean isActive()
      True when the current thread is inside a `push()` / `popAndFire()` bulk window. CellImplRustAdv uses this to lazily open a Rust batch on the first cell write per book — see registerRustBatch(long, RustBatchCloser).
    • registerRustBatch

      public static boolean registerRustBatch(long bookHandle, BulkSetValueGuard.RustBatchCloser closer)
      Called by CellImplRustAdv on the first `setValue` it sees per book within a bulk window. The first call per `bookHandle` is honoured; subsequent calls with the same handle return immediately. The closer is invoked once when the bulk window unwinds.

      Returns true iff this is the first time this handle was seen in the current bulk window — i.e. the caller should fire the Rust-side `batchBegin` now.

    • recordIfActive

      public static boolean recordIfActive(Ref ref)
      Called by CellImplRustAdv.setValue. Returns true if the cell's ref was collected (caller should skip per-cell precedent notification), false if no bulk loop is active.
    • popAndFire

      public static void popAndFire(SBookSeries bs)
      End the bulk loop and fire a single handlePrecedentUpdate covering the bounding region of all collected refs. Safe to call when nothing was recorded.

      Implementation note: per-cell calls would be O(322) for the KEIKAI_0679 fill workload. Each `handlePrecedentUpdate` walks the dependency table for the precedent ref, so collapsing to one call on the bounding region is O(1) walks — the dep table's region lookup naturally returns the union of dependents. Same-sheet/same- book refs are merged into one bounding-box ref per (book, sheet).