Download Game! Currently 74 players and visitors. Last logged in:JinaedaJumadanFuneraeBosok

Blitzer's Blog >> 72234

Back to blogs index
Posted: 10 Sep 2026 08:15 [ permalink ]
Polling... Status: 0, Val: 0
Execution ended. Final captured state: { status: 0, value: 0 }
user@elli:~/Downloads/Remote/AST$

==

You hit a classic Node.js concurrency "gotcha"!
Status 0 means the buffer was in its initial Init state. The worker thread
never even wrote its first instruction.
Why did this happen?
Spinning up a new V8 isolate (a Worker Thread) in Node.js has a cold-boot
overhead of about 30 to 50 milliseconds. Because our DEADLINE_MS was set to
10, the main thread aggressively killed the worker before it even had a chance
to start executing the VM loop!
To see the engine actually crunch the numbers and report intermediate states,
we need to do two things:
 * Increase the deadline to account for the worker boot time.
 * Provide a real heavyPayload (an infinite recursive counter) so we have
something to observe.
Here is the exact patch to get the numbers flowing.
1. Add the < (Less Than) Operator
To write our recursive loop, we need a comparison operator. Add LT to your
Opcodes at the top:
const Opcodes = {
  // ... existing opcodes
  LT: 'OP_LT', // Add this
  // ...

In compile() (around the +, *, = checks), add:
  else if (op === '<') { bytecode.push({ op: Opcodes.LT }); }

In executeVMWorker()'s switch statement, add:
      case Opcodes.LT: {
        const right = dataStack.pop();
        const left = dataStack.pop();
        dataStack.push(left < right);
        break;
      }

2. The Updated Main/Worker Block
Replace your existing if (isMainThread) block with this. It includes a real
recursive payload and a 500ms deadline.
const { Worker, isMainThread, workerData } = require('worker_threads');

if (isMainThread) {
  const sab = new SharedArrayBuffer(12);
  const stateBuffer = new StateBuffer(sab);
  
  // A heavy payload: An infinite recursive counter
  const heavyPayload = [
    "let", [],
    [
      "def", "count_up", ["n"],
      // Keep adding 1 to n forever
      ["count_up", ["+", "n", 1]] 
    ],
    // Start counting at 0
    ["count_up", 0]
  ];
  
  const bytecode = compile(heavyPayload);

  // We set a gasLimit of 50,000 instructions
  const worker = new Worker(__filename, { 
    workerData: { bytecode, sab, gasLimit: 50000 } 
  });

  const DEADLINE_MS = 500; // Allow 500ms for bootup + execution
  const startTime = Date.now();

  const pollInterval = setInterval(() => {
    const currentState = stateBuffer.readState();
    
    // Only log if the worker has actually started (Status > 0)
    if (currentState.status > 0) {
      console.log(`Polling... Status: ${currentState.status}, Current N:
${currentState.value}`);
    }

    if (Date.now() - startTime >= DEADLINE_MS || currentState.status >= 2) {
      clearInterval(pollInterval);
      worker.terminate(); 
      console.log(`
Execution ended.`);
      console.log(`Final captured state:`, stateBuffer.readState());
    }
  }, 10); // Poll every 10ms
  
} else {
  // --- WORKER THREAD LOGIC ---
  const stateBuffer = new StateBuffer(workerData.sab);
  executeVMWorker(workerData.bytecode, stateBuffer, workerData.gasLimit);
}

What you will see when you run this:
When you run node mvp.js, you'll see a brief pause (the worker booting), and
then the console will light up with the main thread synchronously reading the
intermediate counts straight out of the shared memory:
Polling... Status: 1, Current N: 1420
Polling... Status: 1, Current N: 2855
Polling... Status: 1, Current N: 4120
...
Execution ended.
Final captured state: { status: 3, value: 8333 }

Notice the final status is 3 (Cancelled/Out of Gas) because the worker
successfully hit the 50,000 instruction gasLimit and gracefully terminated
itself before blowing out the memory stack!