← Catalog

RFC-003AcceptedLanguage

Memory Model & Concurrency

Depends: RFC-000RFC-001RFC-002

Blocks: RFC-004RFC-006RFC-007

1. Abstract

This RFC defines Aura’s memory and concurrency model: tracing GC, reference vs value semantics, M:N lightweight tasks, channels, async/await, shared-memory synchronization, happens-before rules, and the policy that data races are bugs—detected in development, not licensed as silent undefined behavior.

Runtime implementation details (scheduler, collector algorithm) are expanded in RFC-006; this document is the language-level contract.

Toolchain today (2026-07-16): class instances as GC heap refs (MVP alloc + free-all); struct by-value; single-threaded execution. Tasks, channels, async/await, race detector, and concurrent GC are not implemented — declared design only until later milestones.

2. Motivation

2.1 Problem statement

Service authors need cheap concurrency and safe memory without Rust-style ownership. Classic JVM models provide GC and threads but historically weak tools for structured concurrency; Go provides tasks and channels with a simple model. Aura combines GC + tasks with a modern async surface and explicit race policy.

2.2 Why now

Compiler lowering, stdlib sync primitives, and diagnostics all depend on a single concurrency story.

2.3 Success metrics

MetricTarget
Data-race policyDocumented; detector in dev; not “optimizable UB”
Task scalabilityLarge numbers of blocked tasks with small stacks (stackless/async style)
LatencyGC and scheduler behaviors documented with knobs (RFC-006)
ExpressivenessConcurrent servers without manual OS thread pools

3. Goals

  • Clear concurrency story for backends and CLIs.
  • Safe-by-default memory via GC (no UAF in safe code).
  • Async I/O first-class with structured patterns.
  • Shared state possible with explicit locks/atomics/channels.
  • Portable memory ordering story for atomics.

4. Non-goals

  • Full CUDA/GPU model in v1.
  • Distributed actor cluster protocol (library later).
  • Borrow checker / ownership as the primary model.
  • Hard real-time GC guarantees in v1.

5. Prior art & alternatives

ModelNotesDecision
Go GC + goroutinesSimple, provenAdopt spirit
JVM threads + executorsMatureHeavier default
Rust ownershipStrong races freedomReject for user lang
Actor-onlyIsolationOptional library, not sole model
Single-threaded event loopSimpleToo limited alone

6. Design

6.1 Overview

code
┌─────────────────────────────────────────────┐
│  Process                                    │
│  ┌─────────────┐    ┌─────────────────────┐ │
│  │ GC Heap     │    │ Task scheduler (M:N)│ │
│  │  objects    │◄──►│  tasks / async      │ │
│  │  structs*   │    │  I/O reactor        │ │
│  └─────────────┘    └─────────────────────┘ │
│         ▲                    ▲              │
│         │    channels/locks  │              │
└─────────┴────────────────────┴──────────────┘
* structs may live in heap boxes or stack/registers when escaped analysis allows
  • Memory: tracing GC for class instances and boxed values.
  • Concurrency: many tasks multiplexed onto OS worker threads.
  • Communication: prefer channels & isolation; locks for shared mutability.

6.2 Memory management strategy

Optionv1
Tracing GCYes — default
RC/ARC primaryNo
Ownership/borrow primaryNo
Hybrid arenasOptional later for buffers
RegionsFuture

Decision: Tracing GC. Phased algorithm (RFC-006): free-all MVP (shipped) → precise stop-the-world mark-sweep next → concurrent collector later.

Safe Aura guarantees:

  • No use-after-free / double-free for GC-managed objects.
  • Finalizers: discouraged; prefer explicit Close/using patterns (stdlib).

6.3 Value semantics & references

KindSemantics
class instancesReference identity; GC-managed
primitivesValue; copy
structValue copy on assign/pass (unless boxed)
arrays / stringsReference; String is immutable
  • Interior mutability: fields of classes mutable per var/val; no separate Cell required for GC objects.
  • Pinning: needed at FFI boundaries for buffers (RFC-006).

6.4 Threading model

  • Logical concurrency unit: task (green, M:N).
  • OS threads: worker pool inside runtime; user may spawn blocking OS threads via stdlib for rare cases (spawnBlocking).
  • Scheduler placement, work-stealing → RFC-006.

6.5 Async model

aura
async fun loadUser(id: Id): User { ... }

fun main() {
  spawn { backgroundSync() }

  val user = await loadUser(42)
  // structured: prefer scopes
  taskScope {
    val a = async { fetchA() }
    val b = async { fetchB() }
    use(await a, await b)
  }
}
TopicRule
CoroutinesStackless async state machines (LLVM-friendly)
awaitSuspends task; does not block OS worker if I/O is async-aware
CancellationStructured scopes cancel children; CancelledError / cooperative checks
Structured concurrencyEncouraged via taskScope (not mandatory); global fire-and-forget spawn allowed but lintable

6.6 Shared-state concurrency

Primitives (stdlib):

APIRole
Mutex<T> / RwLock<T>Critical sections
Channel<T> / SelectMessage passing
Atomic*Lock-free counters/flags
Once / LazyInit

Default style: share-memory-by-communicating when practical; locks when needed.

There is no borrow-based Send/Sync enforcement. Documentation and optional attributes may mark thread-hostile types. Race detector covers misuse.

6.7 Memory consistency model

  • Sequentially consistent atomics as default API for simplicity.
  • Acquire/release variants available for experts.
  • Happens-before edges: unlock→lock same mutex; channel send→receive; task spawn→start; async resume edges; volatile/atomic ops per their orderings.
  • Data race definition: concurrent conflicting accesses to the same non-atomic location where at least one is a write, without happens-before.

Data race policy:

ApproachRejected / Accepted
Silent UB (C/C++)Rejected
“Catch fire”Rejected
Language-level “race is a bug”; values may be torn/stale; runtime may detectAccepted
Dev race detector (like Go)Required for MVP tooling story
Prod detector always-onOptional flag / sampling

Aura does not promise that racy programs have sequential semantics; it promises races are not a free optimization license to delete safety checks elsewhere, and tools help find them.

6.8 FFI & foreign memory

  • Foreign memory is not GC-managed unless copied/bridged.
  • Buffers passed to C must remain valid for the call (pin / explicit lifetime scope).
  • Allocator hooks for custom native buffers → RFC-006.
  • unsafe required for raw pointer dereference.

6.9 Examples

aura
fun worker(c: Channel<Int>) {
  for (n in c) {
    println(n)
  }
}

fun main() {
  val c = Channel<Int>()
  spawn { worker(c) }
  c.send(1)
  c.send(2)
  c.close()
}
aura
async fun handle(conn: Conn) {
  val req = await conn.readRequest()
  val res = await route(req)
  await conn.writeResponse(res)
}

6.10 Error model / edge cases

TopicPolicy
DeadlockNot prevented statically; timeouts in stdlib; detector optional later
Panic/exception across tasksIsolated by default; join surfaces error; spawn needs supervision policy
Cancellation leaksScopes + finally / defer (if introduced)
GC during FFIDocumented; pin buffers

6.11 Compatibility & migration

  • Scheduler tuning flags may change performance but not language semantics without edition.
  • Strengthening race detection must not break race-free programs.

7. Open questions

#QuestionOptionsOwnerStatus
1GC algorithmImmix / CMS / Go-likeRuntimeResolved — phased STW mark-sweep next; concurrent later (RFC-006)
2Structured concurrency mandatory?encourageLangResolved — encourage, not require
3Preemptive vs cooperative task switchcooperative await + safepoint hybridRuntimeResolved (direction); tuning open
4String mutabilityimmutableLangResolved
5spawn supervision defaultslog + join surfaces errorLangResolved — log + join surfaces error; no auto-restart

8. Rationale & trade-offs

Go-like tasks + GC maximize concurrency productivity for servers. Stackless async integrates cleanly with LLVM and avoids huge per-task stacks. Rejecting ownership keeps the type system focused on nullability and classes. Rejecting silent UB for races aligns with “safety as a product value” while remaining implementable without a borrow checker. Cost: GC pauses and the need for discipline (and a detector) around shared mutation.

9. Unresolved / future work

  • Formal memory model appendix (axiomatic)
  • Profiler/tracing integration
  • Optional ownership annotations for buffers
  • Actor library design

10. Security & safety considerations

  • UAF/double-free in safe code: mitigated by GC.
  • Races as security bugs (TOCTOU, torn reads of pointers—mitigated if references are atomic-sized and GC-safe; still logical bugs).
  • FFI is the primary memory-safety escape hatch.
  • Side channels (Spectre) out of scope unless noted.

11. Implementation plan (optional)

PhaseScopeExit criteria
M0Single-threaded + async I/OHTTP echo / CLI
M1Multi-task schedulerConcurrent load test
M2Race detector + atomics/locksDetector finds planted races

12. References

  • Go memory model; Go race detector
  • Java Memory Model (happens-before concepts)
  • Kotlin coroutines / structured concurrency (Trio, Swift TaskGroup inspiration)
  • RFC-000, RFC-001, RFC-006

Changelog

DateAuthorChange
2026-07-16Lock GC phased path + spawn supervision defaults
2026-07-16Status → Accepted — Review: GC + tasks language contract locked; algo/scheduler detail in 006
2026-07-16Note GC MVP vs full concurrency model
2026-07-15Initial skeleton
2026-07-15Solid draft: GC, M:N tasks, race policy, async
2026-07-15Lock string immutability, structured concurrency encourage