TruLang
v1.0.0
Architectural Whitepaper & Deep Dive

How TruLang Works Under the Hood

Just as building production apps in Node.js requires understanding the Event Loop, mastering TruLang requires understanding its Dual-Region Memory Engine, Zero-Lock Service Actors, and Hybrid FFI Worker Pool.

βœ“ 0.0 ms GC Pauses (O(1) Bump Arena) βœ“ Zero-Lock Single-Threaded Actors βœ“ Dual-Mode (Bytecode VM + AOT Native)

1. The Core Paradigm Shift: Node.js vs TruLang

In Node.js, developers learn early on that "Never block the Event Loop" is the cardinal rule. In Node.js, all JavaScript runs on a single main thread, while a generational Garbage Collector (V8) periodically pauses execution (Stop-the-World) to scan heaps and compact live references.

🟒 Node.js Architecture

  • Concurrency: Single-Threaded Event Loop with libuv thread pool.
  • Memory Model: V8 Generational GC (Scavenger + Mark-Sweep-Compact).
  • GC Overhead: 15 – 45 ms Stop-the-World GC pauses under heavy load.
  • Memory Footprint: 40 – 80 MB RSS base overhead per process.
  • Failure Mode: An unhandled exception or memory leak crashes the entire Node process.
  • Blocking Risk: Heavy JSON serialization or crypto starves incoming socket events.

πŸ”· TruLang Architecture

  • Concurrency: Zero-Lock Service Actors with lock-free bounded mailboxes.
  • Memory Model: Dual-Region (Region A bump arena + Region B acyclic heap).
  • GC Overhead: 0.0 ms Garbage Collection pauses (reclaimed in $O(1)$).
  • Memory Footprint: 2.5 MB RSS (~6,400 microservices per 16GB node).
  • Failure Mode: Per-request memory budget isolation; overflows fail fast without crashing actors.
  • Blocking Risk: Foreign I/O (SQL, HTTP) automatically offloaded to worker pool.
+---------------------------------------------------------------------------------------------------+ | TruLang Architecture at a Glance | +---------------------------------------------------------------------------------------------------+ | | | [ Ingress TCP / HTTP ] | | β”‚ | | β–Ό | | [ Bounded Mailbox ] ──(If Full: 429 Too Many Requests Backpressure) | | β”‚ | | β–Ό | | β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” | | β”‚ SERVICE ACTOR (Main Thread) β”‚ β”‚ HYBRID FFI WORKER POOL β”‚ | | β”‚ β”‚ β”‚ (Background Thread Pool) β”‚ | | β”‚ 1. Receive Message -> Reset Region A Arena (O(1)) β”‚ β”‚ β”‚ | | β”‚ 2. Allocate Request Objects via Fast Pointer Bump β”‚ β”‚ β€’ PostgreSQL Pool Queries β”‚ | | β”‚ 3. Execute Pure Business Logic without Lock Contention │──>β”‚ β€’ HTTP/REST Client Calls β”‚ | | β”‚ 4. Promote Long-Lived State to Region B via persist() β”‚<──│ β€’ Heavy Crypto & Image Ops β”‚ | | β”‚ 5. Send Response -> Bulk Free Region A Arena (O(1)) β”‚ β”‚ β”‚ | | β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ | | β”‚ | | β–Ό Lockless Seqlock Atomic Shm (0 overhead) | | [ Independent Memory Supervisor (/tmp/trulang_shm_*.dat) ] | +---------------------------------------------------------------------------------------------------+

2. The Dual-Region Memory Model in Depth

Traditional languages force an agonizing trade-off: either accept unpredictable GC pauses (Java, Go, Node.js) or endure complex borrow checker rules and manual memory lifetimes (Rust, C++). TruLang eliminates this trade-off by categorizing memory into two distinct operational regions:

REGION A Execution Arena (Transient)

A contiguous, linear pre-allocated memory chunk. Every allocation within a request (local variables, parsed DTOs, string concatenations, arrays) is simply an 8-byte aligned pointer increment:

// Linear Bump Pointer Allocation (O(1))
void* ptr = arena->memory + arena->offset;
arena->offset += (size + 7) & ~7;
return ptr;
  • ⚑ Speed: Zero malloc overhead, instant $O(1)$ allocation.
  • 🧹 Reclamation: When the request exits, arena->offset = 0. The entire arena is reclaimed in 1 single CPU cycle ($O(1)$).
  • 🎯 GC Pause: Exactly 0.0 ms. No mark-sweep traversal.
  • πŸ“ˆ Elastic Bursts: Tiered budgets (e.g. 16 MB max 64 MB) allow controlled transient bursts, failing fast if the ceiling is exceeded.
REGION B Persistent Heap (Long-Lived)

A managed heap for data that must survive beyond the lifecycle of a single requestβ€”such as actor in-memory caches, session tables, or metric counters:

// Explicit or Automatic Promotion
cache.insert(user_id, persist(user_dto));
// TruLang deep-clones user_dto into Region B,
// decoupling it from Region A bulk reset.
  • πŸ”’ Safety: Enforces an Acyclic Directed Graph (DAG) invariant; circular references are rejected at runtime.
  • πŸ€– Auto-Promotion Pass: The compiler automatically infers when data is stored into persistent actor state and inserts Opcode::Persist.
  • πŸ›‘οΈ Zero Memory Leak: Verified with 50,000 requests under continuous concurrency (0 bytes RSS drift).
🧠 How TruLang Decides: Region A vs. Region B Allocation Decision Engine

Developers never manually annotate complex lifetimes. TruLang enforces a deterministic 3-tier allocation decision rule:

1. Default Rule (Region A)
All local variables, parsed JSON payloads, database rows, and intermediate strings begin in Region A. Reclaimed in a single CPU instruction ((1)$) with 0.0 ms GC.
2. Explicit persist(x)
Promotes data to Region B with DAG cycle detection (detect_value_cycles). Guarantees zero circular reference leaks in persistent actor storage.
3. Compiler Escape Analysis
Assignments from transient scopes into actor fields (e.g. cache.insert(k, v)) are automatically detected by the compiler, which inserts Opcode::Persist.
πŸ›‘οΈ Directional Invariant: Region B can never point to Region A (UAF is impossible). Read Full Memory Model Spec →

3. Step-by-Step Lifecycle of a TruLang Request

Here is exactly what happens under the hood when a client invokes an endpoint on a TruLang Service:

1
Ingress & Mailbox Backpressure Check

Incoming requests land in the Service Actor's bounded mailbox queue. If the queue is at capacity (e.g. during a sudden downstream outage), TruLang rejects the request immediately with ActorError::MailboxFull (HTTP 429), protecting the process from runaway queue bloat and out-of-memory crashes.

2
Region A Arena Zero-Offset Reset

Before handling the message, the runtime ensures the actor's Region A arena offset is reset to zero (offset = 0). This instant reset takes < 1 nanosecond and guarantees pristine, unfragmented memory for the request.

3
Single-Threaded Actor Execution

The actor executes the request handler sequentially. All allocations (JSON parsing, query string decodes, intermediate maps, response objects) bump the Region A pointer. Because only one request executes on this actor at a time, there is zero mutex lock contention and zero data race risk.

4
Foreign I/O Offloaded to Worker Pool

If the handler queries a PostgreSQL database or issues an HTTP GET, the call is dispatched to the background WorkerPool. The actor mailbox remains responsive, and results stream directly back into Region A bump memory upon completion.

5
State Promotion & Instant 0.0 ms Teardown

If any cached entity is stored in service state, it is promoted to Region B. The response is written to the client socket, and the actor executes FreeRegionA. All memory used by the request vanishes instantlyβ€”without running a Garbage Collection sweep.

4. Zero-Lock Service Actors & Mailbox Mechanics

In traditional multi-threaded languages (Go, Java, C#), shared mutable state requires sync.Mutex, rwlock, or synchronized blocks. Under high concurrency, threads spend up to 70% of CPU time waiting on lock acquisitions and cache line invalidations.

Why TruLang Uses the Zero-Lock Actor Model

In TruLang, every service declaration defines an independent Actor with its own private memory domain and mailbox queue:

service AccountService {
    memory_budget request = 16 MB max 64 MB;
    memory_budget persistent = 256 MB;

    let mut balances: Map<string, i64> = Map::new();

    fn transfer(from: string, to: string, amount: i64) -> bool {
        // Executed sequentially inside the Actor!
        // NO mutex.lock() needed! NO data race possible!
        let cur_from = balances.get(from);
        if cur_from >= amount {
            balances.insert(from, cur_from - amount);
            balances.insert(to, balances.get(to) + amount);
            return true;
        }
        return false;
    }
}

5. The Hybrid Thread-Pooled FFI Bridge (`crates/ffi`)

A common pitfall with pure actor architectures is Mailbox Starvation: if an actor executes a synchronous blocking foreign call (such as a slow SQL query or network socket read), its mailbox freezes and cannot process incoming messages.

How TruLang Solves Mailbox Starvation

TruLang's crates/ffi introduces a Hybrid Thread Pool:

  • Worker Pool Offloading: Foreign blocking functions (db.query, http.get) are automatically dispatched to dedicated OS background worker threads.
  • Deterministic RAII Destructors: Opaque foreign resources (e.g. connection pools) are held via ExternalHandle<T>. When the handle goes out of scope or the actor terminates, the native C/Rust destructor runs automatically with zero leaks.
  • Region A Marshaling: Results returned by native libraries stream directly into the caller's Region A bump arena, enforcing memory budget ceilings so external drivers cannot bypass quotas.

6. Dual-Mode Execution: Dev VM vs Production Native AOT

To maximize both developer productivity and production throughput, TruLang provides a seamless Dual-Mode Execution Strategy:

Execution Mode CLI Command Cold-Start Time Throughput Primary Use Case
Development Mode trulang run file.tr < 3 ms ~780,000 req/s Local development, testing, live debugging, CLI scripting.
Native JIT Mode trulang run --jit file.tr ~300 ms (JIT compile) 1,150,000+ req/s On-the-fly native benchmark evaluation and test validation.
Production AOT Mode trulang build --release file.tr Instant (0 ms) 1,200,000+ req/s Production container images, Kubernetes pods, standalone binaries.

7. Out-of-Process Atomic Seqlock Telemetry (`crates/supervisor`)

In high-performance services, monitoring metrics (Prometheus scraping, memory sampling) often introduces latency jitter and CPU overhead on worker threads. TruLang decouples monitoring completely into an independent out-of-process supervisor:

Lockless Atomic Seqlock Protocol
// Worker Process writes telemetry without taking locks:
sequence.store(seq + 1, Release);
region_a_used.store(bytes, Relaxed);
burst_count.store(count, Relaxed);
sequence.store(seq + 2, Release);

// External Monitor reads atomically without pausing the worker:
do {
    s1 = sequence.load(Acquire);
    read_telemetry_snapshot(&data);
    s2 = sequence.load(Acquire);
} while (s1 != s2 || (s1 & 1) != 0);

8. Developer Mental Model: Best Practices for Building TruLang Apps

πŸ’‘ Rule 1: Think in Request Scopes

Do not worry about freeing intermediate strings, arrays, or objects created while handling a request. Allocate freelyβ€”the entire request arena is reclaimed automatically in $O(1)$ constant time as soon as the function returns.

πŸ’‘ Rule 2: Keep Persistent State Truly Shared

Only promote data to Region B if it needs to be accessed across multiple requests (such as an in-memory session cache or database connection pool). All transient request payloads belong in Region A.

πŸ’‘ Rule 3: Size Budgets with Headroom

Use elastic budgets (e.g. memory_budget request = 16 MB max 64 MB;). The base budget (16 MB) keeps memory footprint tiny during normal traffic, while the max ceiling (64 MB) gracefully accommodates occasional large payloads without crashing.