Module sync

Module sync 

Source
Expand description

Dynamic-linking (Dl*) synchronization for the WASIX super::Linker.

Instance groups run on different OS threads and share one super::LinkerState behind an std::sync::RwLock. Operations that change “who exists” or “what every group must agree on” must coordinate with both that lock and a stop-the-world style broadcast of concrete mutations (DlOperation). This module holds the primitives that make that safe.

§Locks and responsibilities

  • Instance-group mutex (lock_instance_group_state!, Linker::instance_group_state): Per-super::Linker handle to this thread’s [InstanceGroupState]. Many linker entry points take it first so they can call into group-local state and, when needed, run cooperative DL helpers with the right Store / [FunctionEnv].

  • Topology lease (LinkerShared holds topology_lock::TopologyCoordinator privately): A single-writer gate for topology-changing work (new instance groups, module loads, export resolution that can allocate shared slots, etc.). The lease is acquired in a cooperative loop with backoff and pending-DL cooperation (see LinkerShared::acquire_topology_token, LinkerShared::write_linker_state_with_topology). TopologyToken may move to another thread (spawn handoff).

  • Shared linker state (inside LinkerShared, not exposed as a field): Global module table, symbol records, and the buses used to broadcast DlOperation and barriers. Writers must follow the cooperative patterns below—not raw lock calls.

  • Pending-DL handshake (dl_operation_pending, barriers, wakeup signals): While an instigator runs LinkerShared::synchronize_link_operation, follower threads must enter [Linker::do_pending_link_operations] (or helpers) so everyone rendezvouses. That is why contended access to [LinkerState] cannot spin blindly.

§Lock ordering (intended)

When topology applies: topology token first, then lock [LinkerState] for write via the APIs in this module—not the inverse. Never try to acquire a topology lease from inside code that already holds [LinkerState] for write without a deliberate, reviewed plan (easy deadlock).

§Why you must never lock LinkerState directly

Do not call linker_state.read(), write(), or try_write() on [Linker]’s [RwLock] from normal instance-group linker paths. Doing so skips the cooperative path and can deadlock the whole process: another thread may hold the write lock while waiting at a DL barrier for this thread to execute LinkerShared::do_pending_link_operations_internal, which requires the same group context and cannot run if this thread is stuck in a naive blocking write().

Use instead:

Narrow exceptions (e.g. one-off bootstrap in super::Linker::new before other groups exist) belong in tightly scoped code and should still avoid contending paths that overlap DL sync.

Modules§

linker_shared 🔒
Shared dynamic-link state for every super::super::Linker clone.
topology_lock 🔒
Single-writer gate for WASIX linker topology changes.

Macros§

lock_instance_group_state 🔒

Structs§

LinkerStateWriteBackoff 🔒
Spin, then yield, then capped exponential sleep — for cooperative linker-state retries.

Enums§

DlOperation 🔒