Threading

The four threading rules — capture on the main thread, everything after it off-thread, autosave never touching a Unity object, and callbacks that never block.

Advanced NexusForge Persistence 0.2.0 Updated

The four rules#

  1. Capture on the main thread. Reading live Unity objects from a worker thread is a data race. The projector takes the framework's dispatcher and refuses on any other thread, naming the reason.
  2. Everything after capture is off-thread. The document is an independent snapshot, so serialization, compression, encryption, integrity and the write are pure work over bytes.
  3. Autosave never touches a Unity object. It asks the dispatcher for a capture on the main thread, then saves the snapshot on a worker.
  4. Unity callbacks never block. OnDisable, OnApplicationPause and scene changes capture synchronously and save asynchronously, because those callbacks cannot await and the object may not exist a frame later.

Where the work actually runs#

text
main thread    capture (projection) ----+
                                       |
worker         serialize -> compress -> encrypt -> integrity -> write -> verify -> promote

Storage.OffloadWritesToThreadPool moves the second row off your thread. It is off by default because existing code that mutates its model immediately after calling save keeps working. A test asserts all three facts: the default stays on the caller's thread, the option leaves it, and the model is still read on the caller's thread either way.

Getting back to the main thread#

csharp
dispatcher.Post(() => transform.position = restored);
await dispatcher.RunAsync(() => Task.CompletedTask);

IMainThreadDispatcher is a seam: UnityMainThreadDispatcher drives work from a player-loop system in a build and from EditorApplication.update in the Editor, with no GameObject, scene object or coroutine required. InlineMainThreadDispatcher exists for tests and headless hosts.

Restoring is also main-thread only#

UnityObjectApplier.TryRestore refuses off-thread for the same reason capture does: it writes to live objects.

Next#

  • Performance — the numbers, and why the offload is measured in behaviour rather than time.
  • Large saves — what to do when the snapshot itself is big.
  • Limitations — the constraints stated as constraints.

Something wrong on this page? Every page here describes behaviour that is checked in the repository. If a page and the package disagree, the package wins.

Report a documentation problem · Frequently asked questions · Troubleshooting