Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Concurrency and parallelism

There is no async/await or event loop in Plenty. The separate async/await exploration includes a standalone spike, not an implemented language feature or an adoption decision.

Concurrency is explicit. Native workers can overlap without an interpreter lock; ordinary loops and comprehensions remain serial. Ownership transfers and checked loans still apply across threads. Shared immutable storage does not permit unsynchronized mutation; std.atomic values are updated through shared references with atomic instructions, and a mutex grants one lock holder at a time exclusive access to its value. A Shared[T] handle gives several owners, such as pool jobs, shared access to one value; the last owner, on any thread, drops it.

The Go-style scoped-task exploration assesses a possible lightweight alternative, including a fixed-stack native experiment. It introduces no implemented task API or change to these contracts.

Worker eligibility is a closed-world compiler check, not a user-defined marker protocol. It examines concrete stored types, reachable helper and callback bodies, captures, results, and destructors, including finite recursive type graphs. Scalars, strings, ranges, collections, classes, enums, and Shared handles are eligible when their contents and effects are eligible. A shared value’s destructor is checked as if any holder’s thread could run it. Stored concrete callbacks retain their eligibility evidence. Thin Callable values are certified against every matching function address in the program.

Each native operation has a declared effect. Files, directory streams and child pipes cross threads under the ordinary ownership and loan rules; changing the working directory is rejected. Foreign pointers/calls, uncertified indirect callable effects, and generator frames are conservatively excluded. Imports do not grant thread permission. Ordinary checked standard-stream I/O is usable, with scheduler-dependent ordering. Owned transferred values cannot contain references or borrowed environments.

Scoped threads can borrow parent storage for the task’s lexical scope. Channels and executor jobs require wholly owned storable values. Executors and futures cannot enter worker code, including through containers or messages: worker-side submission and future waiting are unsupported.

Allocation guarantees exclude argument expressions, worker bodies, and user cleanup. Error wrappers add no allocation. Blocking operations offer no strict waiter fairness, and application communication can deadlock. Normal scope exits join workers; fatal traps terminate without unwinding. A nonterminating worker prevents its scope or executor from finishing. Worker stack exhaustion terminates the process rather than becoming a job error.