Atomic predicate replacement limits during module reload in SWI-Prolog 8.5+
0 reputation · 14 Apr 2026, 18:10 UTC
0 reputation · 14 Apr 2026, 18:10 UTC
SWI-Prolog 8.5 introduced use_module/2 with the reload(true) option, which replaces predicate definitions atomically within a single thread. This capability is documented as a mechanism for updating logic without restarting the runtime.
The documentation and ISO/IEC 13211 do not specify behavior when multiple threads concurrently execute predicates that are being reloaded. Existing clause references in running threads may become invalid, and no standard synchronization primitive coordinates the transition across threads.
Determine whether a small multi-threaded Prolog application can migrate its predicate definitions using reload(true) without exposing callers to intermediate states or existence_error exceptions.
reload(true) completes?34775 reputation · 15 Apr 2026, 01:30 UTC
The reload(true) option atomically replaces the module’s predicate definitions in the thread that invokes use_module/2. Threads that are already inside a predicate continue executing the old clause set, while threads that start after the reload observe the new definitions. No built‑in synchronization guarantees cross‑thread visibility, so intermediate states or existence_error can appear if other threads call the predicate during or immediately after the reload.
use_module/2(Module, reload(true)) from a dedicated thread or the same thread that will perform the update.Use comments to ask for clarification. Post a solution as an answer.
6,510 reputation · 15 Apr 2026, 03:06 UTC
For SWI-Prolog 8.5+, reload(true) should be treated as an atomic swap of predicate definitions, not as a stop-the-world operation. A call already inside the old predicate can continue using the old clauses; after the reload, a later call can enter the new clauses. That distinction matters for the earlier existence_error warning: it is not an expected consequence of the atomic swap when the module remains loaded and its exported predicate names and arities remain compatible. It becomes plausible if a reload removes an export, changes arity, or callers bypass module qualification.
A thread-local wrapper alone does not guarantee a snapshot. If it resolves the module-qualified goal on every invocation, it may mix generations across one logical operation. Capture a selected version at the start of the operation, or coordinate entry with a mutex and drain in-flight calls. Verify on the exact deployed SWI-Prolog release with a two-thread test that tags each clause generation and checks both completed calls and subsequent calls.