[English](concurrency.md) | [Português](concurrency.pt_BR.md) # CONCURRENCY.md — Kof Concurrency Model **Status:** Implemented on all 3 targets, real concurrency on all 3 (JVM virtual threads + JS async/await/Promise + Native pthread) — 0.2.6-beta 03/09 **Version:** 0.4.0-beta **Date:** September 3, 2026 (CONC003 closed — JS is no longer sequential) --- ## 0. Implemented (0.2.6-beta) ### `spawn` — statement ```kof spawn processarFila() // function call as a task spawn { // inline block (lambda with no captures) println("background") } ``` Implemented semantics: - the task runs concurrently (JVM: virtual threads; Native: OS threads via `pthread_create`); - the program **waits for the tasks before exiting** (implicit join — counter + shutdown hook on the JVM; `pthread_join` on Native); - the function's return value is discarded (fire-and-forget); - exceptions in the task are printed to stderr (they do not bring down the program); - ~~**Native**: diagnostic `CONC001`~~ — ✅ closed 31/08: `pthread_create` + trampoline + `await`/`pthread_join` + thread-safe allocator (futex lock) + implicit join in main (history: "spawn: not supported on the Native target yet" — documented gap, never hidden); - isolation by value: the task receives the arguments; no primitive shared state in the language. ### Implemented 0.1.0 → 0.2.6-beta - `spawn` statement + `val r = spawn f()` + `await r` with typed `Handle` and unboxing (`KofAwaitTest` 7/7, `KofConcurrency2Test` 10/10) — JVM; JS **real async** via GraalJS `async`/`await`/`Promise`, closed 03/09 (`CONC003`, see section 4); full Native pthread (CONC001 closed) - non-blocking `done(h)`/`poll(h)`, `cancel(h)`/`cancelled()` (cooperative cancel) and `selectAny(h1, h2, …)` — JVM + Native (1ms polling, `KofConcurrency2Test`); JS via handle `{done,value,error,promise}` + `Promise.race` (`cancelled()` always `0` in JS — no thread-local, see section 4); Android follows `AND001` - `awaitTimeout(r, ms)` — value if the task finishes within the deadline; otherwise throws an exception (catchable via `try/catch`) — JVM (`Future.get(ms)`) + Native (1ms polling with deadline) + JS (cooperative polling via `await Promise.resolve()`, truly fires against a slower task — `KofConcurrency2Test.awaitTimeoutSlowTaskJs`) - `channel()` — thread-safe FIFO with `c.send(v)`/`c.receive()` — JVM (`LinkedBlockingQueue`, blocking `put`/`take`) + Native (linked list + futex mutex + 1ms polling) + JS (queue of pending resolvers — `receive()` on an empty channel truly blocks until a later `send()`, `KofConcurrency2Test.channelBlocksBeforeSendJs`) - Lambdas with capture via `BoxN` already support `spawn { println(x) }` — including capturing a mutated variable from an outer scope, on all 3 targets - `kof.mq` publish/subscribe/queue — **3 targets** (JVM in-memory; Native asm 01/09, MQ001 closed; JS in-process); `kof.time interval/cancel` — JVM+Native ### Not exposed No platform API (Thread/Runnable/Executor) is visible in the language. `Thread.startVirtualThread` is an internal detail of the JVM runtime. ### Next iterations (P2) - ~~typed producer/consumer queues (`kof.concurrent.Queue`)~~ — ✅ 31/08, channel with real blocking 03/09: `channel()` with `send`/`receive` (JVM blocking `LinkedBlockingQueue` + Native futex FIFO + JS queue of pending resolvers); - ~~`CONC003` — real async on the JS target~~ — ✅ 03/09: GraalJS `async`/`await`/`Promise`, `KofJsRunner` drains the microtask queue (`kofActiveTasks`), see section 4; - native scheduler (threads on the Native target — depends on futex/clone); - multiple `select` with timeout (`selectAny` already ✅ without timeout; combining it with a deadline is the next step); - real `cancelled()` in JS — today always `0` (known limitation: no thread-local for the "current task" context in interleaved async functions in the embedded GraalJS, see section 4). Concurrency is a capability of the **language/stdlib**, not a collection of platform APIs. The programmer expresses **intention**: ```text concurrent tasks ``` and not: ```text Thread / ExecutorService / CompletableFuture / pthread / epoll / libuv ``` The decision of how to execute (virtual thread, platform thread, event loop, worker) belongs to the **target/runtime**. --- ## 2. Semantics (what the language promises) ### 2.1 Tasks A task is a unit of concurrent execution with: - explicit start (function or block); - implicit termination (end of the body); - optional result (observable return value); - propagable failure (the task's exception is observable). Conceptually: ```text task ``` ### 2.2 Isolation The proposed Kof model is **isolation by value** (like the actor model, without the ceremony): - each task has its own execution context; - communication occurs through **values exchanged explicitly** (parameters, returns, queues); - **no mutable shared memory** as the primary model (eliminates data races by construction); - the runtime can freely scale tasks across OS threads. This is NOT decided yet — it is the proposed direction. Alternative considered: shared memory with explicit synchronization (rejected as the primary model because it reproduces the complexity of threads). ### 2.3 Communication Exchange of values between tasks through: - parameters and returns ("join" style); - queues (producer/consumer) — abstraction planned in the stdlib (`kof.concurrent.Queue`); - structured callbacks (not as the primary model). ### 2.4 Synchronization - By construction (isolation); - by values (return/queue); - never by locks as a primary API. --- ## 3. Syntax (chosen: `spawn` — 0.2.6-beta) **Implemented on all 3 targets, real concurrency on all of them:** ```kof spawn task() spawn { println("background") } val handle = spawn tarefa() // typed Handle — 0.1.0 val result = await handle // unboxing + clean exception — 0.1.0 ``` `spawn`/`await` work on JVM (virtual threads), JS (GraalJS `async`/`await`/`Promise`, 03/09) and Native (OS threads via `pthread_create`, 31/08). **JS-only restriction (`CONC003-JS-01`):** a regular lambda passed to `list.map`/`filter`/`reduce` (or a UI/timer/mq handler) cannot use `await`/`spawn expr`/`channel.receive()` — only the body of a `spawn { ... }` can. It is a compilation error, not silently wrong behavior: the reason is that JS's `Array.prototype.map/filter/reduce` is synchronous and does not know how to handle a callback that returns a `Promise` — without this restriction, the result would become an `Array>` disguised as `List`, corrupting data with no error at all. JVM/Native do not have this restriction. Rejected: `async { }` (confuses with async/await). Pending decisions: - how to express queues/pub-sub (`kof.concurrent.Queue` planned); - error model (the exception already propagates via `await` with unwrap `ExecutionException` — see `KofAwaitTest`). **Do not implement syntax before the semantics above are validated.** — validated 0.1.0. --- ## 4. Per-Target Mapping (0.2.6-beta) The same Kof semantics uses different implementations: | Target | Implementation | Status | |--------|---------------|--------| | JVM 21+ | Virtual Threads (JVM scheduler) | ✅ `await`/`Handle` + `kof.mq` | | Native x86_64 | OS threads: `pthread_create` + trampoline + `await`/`pthread_join` + `done`/`poll`/`cancel`/`cancelled`/`selectAny` + thread-safe allocator (futex) | ✅ 31/08 (`CONC001` closed) | | Native riscv64/aarch64 | OS threads: `clone(220)` + stack per `mmap` + wait via futex on `handle->done` (`nat/NativeRiscvSpawn.java`) — **only** `spawn`/`await`/implicit join | ⚠️ partial: `poll`/`done`/`cancel`/`cancelled`/`selectAny`/`awaitTimeout` **missing** (`CONC001` gate at compile-time since 11/09 — see note below) | | JS (GraalJS) | native `async`/`await`/`Promise` — async coloring by fixpoint in the compiler (`JsBackend.computeAsyncColoring`), handle `{done,value,error,promise}`, channels with a queue of pending resolvers, `KofJsRunner` drains the microtask queue (`kofActiveTasks`) | ✅ 03/09 (`CONC003` closed) | | KofScript | JVM via KofScriptGlobals | ✅ | The Kof code does not change between targets; on x86_64 there is no longer a gap of `spawn`/`await` nor of the helpers (`poll`/`done`/`cancel`/`cancelled`/ `selectAny`/`awaitTimeout` — `CONC001` closed, including the residual), nor on JS (`CONC003` closed). On riscv64/aarch64 `spawn`/`await` exists (`clone` 220 + futex), but the helpers **do not** — and that absence is diagnosed at compile-time: `ExpressionStaticCallLowerer` emits **`CONC001`** for `poll`/`done`/`cancel`/`cancelled`/`selectAny`/ `awaitTimeout` on those targets (issue #91, 11/09; before there was no gate — `NativeRiscvCrossOps.resolveCalleeNameRiscv` fell into the generic `sanitizeName` and the error appeared only at **link** time, as an undefined symbol, the same pattern as bug 59). Proof: `KofConcurrency2Test.crossMissingConcurrencyHelpersReportConc001`. What remains is porting the symbols, not the diagnostic (Native lane). In JS specifically: only lambdas created directly at a `spawn` site ("task-lambdas") can become an `async function`; see restriction `CONC003-JS-01` in section 3. `cancelled()` in JS always returns `0` (known limitation, no equivalent thread-local for "current task" in interleaved async functions in the embedded GraalJS). --- ## 4.5 Supervisor (kof.supervisor — issue #83, 11/09, experimental) OTP supervision core written **in Kof** (host injected by `import kof.supervisor` — `android-host` mechanism, zero VM change): ```kof import kof.supervisor supervisor("net") // new object per system .child("conn", Fabrica(), "permanent") // permanent|transient|temporary .restartLimit(5) // maximum restarts before escalating .escalate(Handler) // KofEscalate callback (optional) .start() // starts the workers .stop(2000) // controlled shutdown (cancel + deadline) .stats() // KofSupStats(started,restarts,dropped,vivos) ``` - **Failure observation:** a `vigiar` loop per child (dedicated `spawn`) does `await` on the worker's handle inside `try/catch (String)` — the original cause reaches the supervisor (`done()`+poll is not used: polling a failed handle is fragile on targets without preemption). - **Individual restart with clean state:** the `KofWorkerFactory` creates a **new** `KofWorker` on each restart (DD-OTP-06) — it never re-runs the object that failed. - **Policies (DD-OTP-04):** `permanent` crashes→always restarts (terminating normally is an anomaly); `transient` terminates-normally→stops, fails→restarts; `temporary` never restarts (discarded). - **Limit + escalation (DD-OTP-07/08):** `restartLimit(max)` → when exceeded, calls `escalate.disparou(id,motivo,reinicios)`; without a handler, it STOPS restarting and warns on stdout (R6 — never a silent infinite loop). - **Controlled shutdown:** `stop(deadlineMs)` cancels cooperatively (`cancelled()` flag on targets with threads) and waits for the deadline; children that ignore the cancel are reported. - **Parity (rule 6):** JVM ✅ · KofScript ✅ · Native x86 ✅ (since §129 closed, 15/09: the handler chain is TLS per-thread and a `throw` in a task publishes the cause on the handle; `await`/`selectAny` rethrow it in the consumer) · Native riscv/aarch = `OTP001` (raw `clone`, no TLS) · JS = `OTP002` (§132: event-loop does not schedule task-of-task) — the cross/JS cases blocked at compile-time with a diagnostic. ## 5. Concurrent I/O Code like: ```kof loadUser(id: UUID): User { return database.users.find(id) } ``` does not require the user to know whether internally it used: ```text blocking I/O non-blocking I/O virtual thread epoll event loop worker ``` That decision belongs to the target/runtime. --- ## 6. Dependencies (0.2.6-beta) - ✅ Lambdas with capture via `BoxN` — implemented (necessary for idiomatic `spawn { ... }`); - queues in the stdlib (`kof.concurrent.Queue` — planned, `kof.mq` already provides pub/sub); - per-task exception model — ✅ unwrap `ExecutionException` on `await` (JVM); - ✅ OS threads on Native — `pthread_create` + trampoline + futex (31/08, `CONC001` closed on x86_64); `scheduler.every/at` scheduler on Native still follows `SCHED001`. ## 7. Implementation Phases 1. Semantics validated (this document); 2. `spawn`/`async` primitive on the JVM (virtual threads when available) — ✅; 3. Native Scheduler — ✅ (pthread, `CONC001` closed); 4. Queues/pub-sub in the stdlib — ✅ (`channel()`, `kof.mq`); 5. KofJS — ✅ 03/09 (`CONC003` closed, see section 4). ## 8. Non-Goals - Exposing `Thread`, `ExecutorService`, `pthread`, `epoll` as a language API; - locks as the primary model; - `CompletableFuture`-style APIs leaking to the user.