Skip to content

v0.7.0

Latest

Choose a tag to compare

@github-actions github-actions released this 20 Oct 16:14

Breaking

  • getStatus() returns { running, tick, linkCount, componentCount }. running is true from
    the runAsync() call until its promise settles. It replaces state, and the SimState type and
    Node enum are gone. speed is gone too: for a ticks/s readout, sample tick against a clock.
    During a run it is at most one batch behind: about 1 ms on Node, 5 ms in WASM.
  • linkCount() and componentCount() are gone from both bindings: read them from
    getStatus().
  • On Node, a delta snapshot's ids is a Uint32Array, like its invalid, instead of a
    Buffer of little-endian u32s.
  • In WASM, runAsync() rejects while another run on the same simulation is in progress and not
    stopped, and run() throws then, as on Node. Both used to run, interleaved between batches.
  • Both bindings read boards and run configs with the same rules and messages:
    • A field that is undefined is absent, and null is refused. WASM used to read a null
      field or bound as absent.
    • Numbers must be exact non-negative integers where the board holds a u32 or a byte. Node used
      to truncate them, so a type of 1.5 built a NOT and a period of 2.7 became 2.
    • Unknown fields are ignored.
    • A run config must be an object. Node used to start an unbounded run for runAsync(5). A
      malformed config throws from runAsync() in WASM too, instead of rejecting its promise.
    • Fields are checked in order, component by component, so a board with several mistakes names
      the same one in both.
  • In WASM, link(id) throws for an id past the last link, as on Node. It used to return false.
  • In Rust:
    • Simulation::run returns nothing, and SimState, Simulation::state, Simulation::stop and
      the state and speed fields of Status are gone.
    • read_state(comp, offset, len) returns the bytes, and read_records(comp, since, max) returns
      them in Records with their ticks, instead of filling buffers; SimError::RecordTicks is
      gone.
    • InputRequest builds an owned submit_input request; inputs() lists the UserInputs.
    • Board, Simulation::new and StateLayout::of are no longer public: build a simulation
      with Simulation::from_descriptor, and read a layout with Simulation::state_layout.
    • SnapshotInfo drops changed: a delta's changed links are snapshot_ids().

Performance

  • In browsers, the WASM runAsync() no longer idles 4 ms between batches: it yields through a
    MessageChannel instead of a nested setTimeout(0), which browsers clamp to 4 ms. An unbounded
    run now ticks as fast as a blocking run(), about 1.5× its previous speed on a board that
    changes every tick. Under Node it yields with setImmediate, without setTimeout's 1 ms.

Fixed

  • A run with an ms bound ends within about a tick of it on any board, in the CLI and both
    bindings. The engine used to read the clock only after its first 1 021 ticks, and the Node addon
    every 4 093, so on a board at 5 000 ticks/s, run({ ms: 5 }) took 0.2 s in WASM and 0.6 s on
    Node.
  • runAsync() works in batches of about 5 ms in WASM and 1 ms on Node on any board, so stop(),
    snapshots and messages wait at most that long. On a board at 5 000 ticks/s, a WASM batch used
    to hold the thread for 0.2 s and a Node snapshot waited up to 0.9 s. On Node, stop() now takes
    effect at the end of a batch rather than at the next tick. Batch lengths vary by a tick, so state
    read between batches reaches every phase of a periodic signal, even on a board slow enough for a
    batch to be a few ticks.
  • During a WASM runAsync(), getStatus() reports the run, as on Node. It used to report
    Stopped. On Node, it reports the run from the runAsync() call until the promise settles; it
    used to report it only once the worker had picked the run up, and could stop before the promise
    settled.
  • readRecords allocates for the records it returns. It used to allocate the recorder's full
    capacity on every call, about 12 MiB for a large recorder with ticks; in WASM that could grow
    memory and detach every outstanding snapshot view.
  • Freeing a WASM simulation (destroy(), free() or garbage collection) during a runAsync()
    ends the run. It used to keep ticking forever, and kept a Node process alive.
  • A runAsync() or run() called straight after stop(), before the stopped run's promise has
    settled, starts. In WASM, the new runAsync() used to revive the stopped run, which then never
    ended. On Node, it was rarely rejected as a run already in progress.

Full Changelog: v0.6.0...v0.7.0