An atomic LPC Function Body Rolls Back on Deliberate Error in an HTTP-Dispatched Call

Claim

In the eOS-Harness MVA running against DGD 1.7.9 plus the eOS-kernellib HTTP/1 platform at upstream c5b1dee, an LPC function declared atomic that mutates a counter and then calls error() rolls back the mutation as observed from outside the runtime over HTTP. The dispatch shape is: HTTP POST /increment-with-failure -> route handler catch(/usr/Test/atomic_demo->increment_with_failure()) -> the atomic function body mutates counter += 1 and then fires the deliberate error. The error reaches the handler's catch, the handler returns 200 OK with the captured error in the body, and the next GET /counter returns the pre-mutation value. The pattern repeats: each subsequent POST /increment-with-failure followed by GET /counter continues to show the pre-mutation value, confirming the rollback is not a one-shot effect of cold-boot state but an ongoing property of the atomic envelope. The same probe with the atomic modifier removed from increment_with_failure shows the increment persisting past the error: the counter advances from 0 to 1 across one POST and would continue to advance on subsequent POSTs.

Grounds

This is an Empirical Observation. The grounds are two captured smoke sequences run 2026-05-17 against the live MVA demonstration instance -- one with the atomic modifier present, one with it removed.

Setup (verifiable from repo state):

Atomic-modified source (the load-bearing function in src/usr/Test/atomic_demo.c):

atomic void increment_with_failure()
{
    counter += 1;
    error("deliberate failure for atomicity demonstration");
}

Boot sequence:

$ rm -f state/snapshot state/snapshot.old state/swap state/boot.log   (within the isolated runtime staging tree)
$ <materialize the isolated runtime staging tree>
$ cd <isolated runtime staging tree> && ./bin/dgd <instance config> > state/boot.log 2>&1 &
...
** DGD 1.7.9
** Initializing...
** Initialization complete.

** Initialization complete. reached in under one second. The Test domain's initd compiled atomic_demo plus sys/atomic_handler; the deferred call_out in sys/atomic_handler::create registered the URL roots counter and increment-with-failure with /usr/WWW/sys/httpd once the WWW domain's initd had finished.

Smoke A -- WITH the atomic modifier present:

$ ./tests/atomic-rollback-demo.sh
=== atomic-rollback smoke against http://127.0.0.1:8080 ===

Step 1: GET /counter
  counter=0

Step 2: POST /increment-with-failure
  deliberate-failure-fired: deliberate failure for atomicity demonstration

Step 3: GET /counter (after deliberate failure)
  counter=0

=== PASS: counter unchanged across deliberate-failure increment ===
         initial=0   final=0   (rollback verified)
$ echo $?
0

Re-running the smoke immediately:

$ ./tests/atomic-rollback-demo.sh
...
=== PASS: counter unchanged across deliberate-failure increment ===
         initial=0   final=0   (rollback verified)

The counter remains at 0 across repeated runs; each POST's deliberate failure rolls back the increment.

Boot-log trace of the deliberate error during smoke A (note the [atomic] annotation):

** deliberate failure for atomicity demonstration [atomic]
                               /usr/Test/atomic_demo
   31   increment_with_failure    /usr/Test/atomic_demo
** deliberate failure for atomicity demonstration [caught]
                                   /usr/WWW/obj/server#NNN
  429   receiveBytes                  /usr/HTTP/lib/Connection1
   99   receiveHeaders                /usr/HTTP/api/lib/Server1
  193   receiveRequest                /usr/WWW/obj/server
  143   dispatch                      /usr/WWW/obj/server
                                   /usr/Test/sys/atomic_handler
   85   handle                        /usr/Test/sys/atomic_handler
   65 * handleIncrementWithFailure    /usr/Test/sys/atomic_handler

The [atomic] line precedes the rollback frame for the atomic function body; the [caught] line records the catch in the handler. DGD records both because the error propagates from inside the atomic envelope to outside it.

Smoke B -- WITHOUT the atomic modifier (the modifier-less version of the same source, run earlier in the same session before the modifier was added):

$ ./tests/atomic-rollback-demo.sh   # plain `void increment_with_failure()`
...
Step 1: GET /counter
  counter=0

Step 2: POST /increment-with-failure
  deliberate-failure-fired: deliberate failure for atomicity demonstration

Step 3: GET /counter (after deliberate failure)
  counter=1

=== FAIL: counter changed across deliberate-failure increment ===
         initial=0   final=1   (rollback did NOT fire)

The same probe shape -- same handler, same dispatch path, same error -- produced counter=1 rather than counter=0. The boot log for smoke B carried [caught] on the error trace but did NOT carry [atomic]; DGD did not treat the function body as an atomic envelope.

Process identity check: a single DGD process served all requests in each smoke run. The boot log records no ** System halted. entry between requests. No statedump was invoked. No admin_console session was opened. The recompile from non-atomic to atomic between smoke B and smoke A was achieved by editing the source on disk and re-running the isolated-runtime materialization step plus cold-boot (the overlay replaces the isolated runtime staging tree's src/usr/Test/atomic_demo.c); the runtime instances of smoke A and smoke B are different DGD processes.

What the evidence establishes:

What the evidence does not establish:

What Would Revise It

Sources

Relations