lona.process¶
Process and realm management intrinsics. These provide BEAM-style lightweight processes with message passing.
Process Creation¶
spawn¶
Create process in current realm.
Options:
| Key | Type | Default | Description |
|---|---|---|---|
:min-heap-size |
integer | 4096 | Initial heap bytes |
:max-heap-size |
integer | nil | Maximum heap (nil = unlimited) |
:priority |
0-255 | 100 | Scheduling priority |
:name |
symbol | nil | Register with name |
;; @todo
;; Spawn with priority option
(def p (spawn (fn* [] (sleep 1000)) %{:priority 50}))
(pid? p) ; => true
(= (:priority (process-info p)) 50) ; => true
;; Spawn with name auto-registers
(def p2 (spawn (fn* [] (sleep 1000)) %{:name 'named-process}))
(= (whereis 'named-process) p2) ; => true
spawn-link¶
Create linked process. Bidirectional crash notification.
spawn-monitor¶
Create monitored process. Unidirectional notification.
;; @todo
(def result (spawn-monitor (fn* [] :ok)))
(tuple? result) ; => true
(pid? (first result)) ; => true
(ref? (nth result 1)) ; => true
Cross-Realm Process Creation¶
spawn-in¶
Create process in descendant realm.
(spawn-in realm-id f) ; → pid
(spawn-in realm-id f arg1 arg2 ...) ; → pid, with arguments
(spawn-in realm-id f arg1 arg2 ... opts) ; → pid, with arguments and options
Arguments are passed to f when the process starts. If the last argument is a map
with spawn options (:min-heap-size, :priority), it is used as options, not passed
to f.
;; @todo
(def child-realm (realm-create %{:name 'spawn-in-test}))
(def p (spawn-in child-realm my-worker))
(pid? p) ; => true
(realm-terminate child-realm)
spawn-link-in¶
Create linked process in descendant realm.
;; @todo
(def child-realm (realm-create %{:name 'spawn-link-test}))
(def p (spawn-link-in child-realm my-worker))
(pid? p) ; => true
(realm-terminate child-realm)
spawn-monitor-in¶
Create monitored process in descendant realm.
(spawn-monitor-in realm-id f) ; → [pid monitor-ref]
(spawn-monitor-in realm-id f opts) ; → [pid monitor-ref]
;; @todo
(def child-realm (realm-create %{:name 'spawn-monitor-test}))
(def result (spawn-monitor-in child-realm my-worker))
(tuple? result) ; => true
(pid? (first result)) ; => true
(ref? (nth result 1)) ; => true
(realm-terminate child-realm)
Cross-Realm Function Execution¶
Function must be a var reference:
The function f must be defined via def and referenced by its var. Anonymous
functions and closures cannot be transferred across realm boundaries.
;; Works: var reference with arguments
(spawn-in child-realm my-worker arg1 arg2)
;; Error: anonymous function
(spawn-in child-realm (fn [] (do-work)))
;; Error: closure with captured bindings
(let [x 42]
(spawn-in child-realm (fn [] (use x))))
;; Instead, pass data as arguments
(let [x 42]
(spawn-in child-realm my-worker x)) ; x is deep-copied
Code sharing:
Parent realm's code pages are mapped read-only into child realms during realm creation. The spawned function must reside in these shared pages.
Arguments:
Arguments to the spawned function are deep-copied across the realm boundary, using the same serialization as inter-realm messages.
Scope:
spawn-in only works with descendant realms (child, grandchild, etc.):
- You cannot spawn into parent realms
- You cannot spawn into sibling or unrelated realms
- Attempting to spawn into a non-descendant returns [:error :not-descendant]
Process Identity¶
self¶
Current process PID.
self-realm¶
Current realm ID.
alive?¶
Check if process exists.
;; @todo
(alive? (self)) ; => true
;; Dead process returns false
(def p (spawn (fn* [] :done)))
(sleep 10)
(alive? p) ; => false
process-info¶
Get process information.
;; @todo
(def info (process-info (self)))
(map? info) ; => true
(contains? info :status) ; => true
(contains? info :heap-size) ; => true
(contains? info :mailbox-len) ; => true
(contains? info :priority) ; => true
(contains? info :links) ; => true
(contains? info :monitors) ; => true
Returns map with :status, :heap-size, :mailbox-len, :priority, :links, :monitors.
Messaging¶
send¶
Send message asynchronously.
- Intra-realm: deep copy, ~100-500 ns
- Inter-realm: seL4 IPC, ~1-10 µs
- Order preserved between same sender-receiver pair
send-sync¶
Send message and wait for acknowledgment.
;; @todo
;; send-sync with zero timeout returns :timeout if not acknowledged
(send-sync (self) :msg 0) ; => :timeout
send-named¶
Send to registered name.
;; @todo
(send-named 'nonexistent :msg) ; => :not-found
;; Success case: send to registered name
(register 'send-named-test)
(send-named 'send-named-test :hello) ; => :ok
(receive msg msg :after 100 :timeout) ; => :hello
(unregister 'send-named-test)
receive¶
Pattern-matched message reception with optional timeout. This is a VM-supported construct (not a simple macro) for efficient single-pass pattern matching.
Semantics:
- Scan mailbox messages in order
- For each message, try full pattern matching against all clauses (in order)
- First pattern that matches: remove message, bind variables, execute body
- If no pattern matches this message: skip it, continue to next message
- If no messages match: block waiting for new messages
- If
:afterspecified and timeout expires: execute timeout-body
Key properties:
- Single-pass matching — full pattern matching during scan, no double evaluation
- Selective receive — non-matching messages remain in mailbox (Erlang semantics)
- Guards —
whenclauses evaluated only if pattern structurally matches
(receive
%{:type :request :id id :payload p}
(handle-request id p)
%{:type :shutdown}
(cleanup-and-exit)
[:EXIT pid reason] when (not= reason :normal)
(handle-crash pid reason)
:after 5000
(handle-timeout))
Timeout returns immediately if mailbox empty:
Receive with pattern matching:
Selective receive (non-matching messages remain):
;; @todo
(send (self) :a)
(send (self) [:b 1])
(receive [:b x] x :after 100 :timeout) ; => 1
(receive :a :got-a :after 100 :timeout) ; => :got-a
Message ordering preserved:
;; @todo
(send (self) 1)
(send (self) 2)
(send (self) 3)
(receive x x :after 100 :timeout) ; => 1
(receive x x :after 100 :timeout) ; => 2
(receive x x :after 100 :timeout) ; => 3
Message Value Types¶
Intra-realm messages (same realm):
All Lonala values can be sent, including:
- Primitives, collections, binaries
- Functions and closures
- Capability wrappers (notification, endpoint, etc.)
Values are deep-copied to the receiver's heap.
Inter-realm messages (cross-realm):
All serializable values can be sent, including: - Primitives: nil, booleans, numbers, strings, symbols, keywords - Collections: lists, tuples, vectors, maps, sets (with serializable contents) - Binaries (reference-counted, not copied) - Capability wrappers — the kernel transfers the underlying capability
;; Granting a notification to another realm via message
(send worker-in-child-realm %{:task data :done-signal my-notif})
;; Receiver gets a copy of the notification capability
Cannot be sent inter-realm:
- Functions and closures (use var references via spawn-in instead)
- Bytebufs (mutable, not shareable)
- Vars (send the var's value, not the var itself)
Enforcement:
- Intra-realm: no enforcement (same VSpace, direct copy)
- Inter-realm: VM serializes message; kernel handles capability transfer via seL4 IPC
Linking¶
link¶
Create bidirectional link.
unlink¶
Remove link.
trap-exit¶
Enable/disable exit signal trapping.
(trap-exit true) ; Receive [:EXIT pid reason] as messages
(trap-exit false) ; Propagate crashes (default)
With trap-exit, linked process exits become messages:
;; @todo
(trap-exit true)
(def p (spawn-link (fn* [] (exit :test-exit))))
(receive
[:EXIT pid reason] [pid reason]
:after 1000 :timeout) ; => [p :test-exit]
Exit Signals¶
When linked process exits, linked processes receive:
Reasons: :normal, :killed, [:error type], or custom term.
Monitoring¶
monitor¶
Start monitoring process.
demonitor¶
Stop monitoring.
Monitor Messages¶
When monitored process exits:
;; @todo
(def p (spawn (fn* [] :done)))
(def mref (monitor p))
(receive [:DOWN mref _ reason] reason :after 1000 :timeout) ; => :normal
Lifecycle¶
exit¶
Terminate process or send exit signal.
(exit reason) ; Exit current process with reason
(exit pid reason) ; Send exit signal to another process
Exit Reasons¶
Exit reasons are ordinary Lonala values. Common patterns:
| Reason | Meaning | Cascades to links? |
|---|---|---|
:normal |
Clean exit | No |
:shutdown |
Clean shutdown | Yes (but expected) |
:killed |
Process was force-killed | Yes |
[:error type info] |
Error with details | Yes |
| Other term | Application-defined | Yes |
Exit Signals¶
When sending exit signal to another process:
| Signal Sent | What Receiver Sees | Trappable? |
|---|---|---|
:normal |
:normal |
Yes |
:kill |
:killed |
No (always terminates) |
:shutdown |
:shutdown |
Yes |
| Other | Same value | Yes |
Note: Sending :kill is transformed to :killed at the receiver.
Link Cascade Behavior¶
When process A (linked to B) exits with reason R:
- R is
:normal: B is NOT affected (clean exit doesn't cascade) - R is anything else: B receives exit signal with reason R
- If B is not trapping exits: B exits with same reason
- If B is trapping exits: B receives
[:EXIT pid-of-A R]message
Runtime Errors¶
Runtime errors (match failure, bad arguments, etc.) cause the process to exit with an error-shaped reason:
| Error | Exit Reason |
|---|---|
| Match failure | [:error :badmatch %{:value v}] |
| Bad argument | [:error :badarg %{:fn f :args args}] |
| Undefined var | [:error :undef %{:var v}] |
| Out of memory | [:error :oom] |
These are not special types — they're just values that supervisors can pattern match.
Registry¶
register¶
Register process with name.
;; @todo
(register 'my-test-process)
(pid? (whereis 'my-test-process)) ; => true
(= (whereis 'my-test-process) (self)) ; => true
unregister¶
Remove registration.
;; @todo
(register 'to-unregister)
(unregister 'to-unregister) ; => :ok
(whereis 'to-unregister) ; => nil
whereis¶
Hierarchical name lookup.
Searches: current realm → parent → grandparent → root.
;; @todo
;; whereis finds registered process
(def p (spawn (fn* [] (sleep 1000))))
(register 'whereis-test p)
(= (whereis 'whereis-test) p) ; => true
(unregister 'whereis-test)
whereis-local¶
Local realm lookup only.
Realm Management¶
realm-create¶
Create child realm.
Options:
| Key | Required | Description |
|---|---|---|
:name |
yes | Realm name |
:policy |
no | Resource policy |
:schedulers |
no | :auto or integer |
:shared |
no | Regions to share |
Shared regions format:
Policy:
realm-terminate¶
Terminate child realm.
Terminates all processes and child realms, reclaims resources.
realm-info¶
Get realm information.
Returns: :name, :status, :parent, :children, :policy, :resource-usage, :process-count.
;; @todo
(def info (realm-info (self-realm)))
(map? info) ; => true
(contains? info :name) ; => true
(contains? info :status) ; => true
(contains? info :process-count) ; => true
parent-realm¶
Get parent realm ID.
;; @todo
;; parent-realm returns realm-id or nil (if root)
(or (nil? (parent-realm)) (realm-id? (parent-realm))) ; => true
child-realms¶
Get child realm IDs.
Shared Memory¶
make-shared-region¶
Create shared memory region.
share-region¶
Grant region access to child realm.
Access: :read-only or :read-write
;; @todo
(def r (make-shared-region 4096 'share-test))
(def child (realm-create %{:name 'share-child}))
(share-region r child :read-only) ; => :ok
(realm-terminate child)
unshare-region¶
Revoke region access.
;; @todo
(def r (make-shared-region 4096 'unshare-test))
(def child (realm-create %{:name 'unshare-child}))
(share-region r child :read-write)
(unshare-region r child) ; => :ok
(realm-terminate child)
get-shared-region¶
Get region shared with this realm.
region-size¶
Get region size.
region-name¶
Get region name.
Resources¶
request-memory¶
Request additional memory from parent realm.
On success, the parent grants Untyped capabilities which are automatically installed
in the realm's CSpace. The VM runtime's allocator is notified and can use these for
subsequent allocations. Application code does not interact with the capabilities
directly — use lona.kernel for low-level capability manipulation.
return-memory¶
Return memory to parent realm.
The VM runtime releases Untyped capabilities back to the parent.
memory-pressure-handler¶
Register pressure callback.
Callback receives: :low, :critical, or :normal.
Notifications¶
make-notification¶
Create notification object.
Used with lona.kernel signal/wait/poll operations.
Appendix: Expected Derived Functions¶
The following are not intrinsics and should be implemented in Lonala:
Supervisor:
Implements restart strategies using spawn-link, trap-exit, receive. Semantics
follow Erlang/OTP supervisors: supports one-for-one, one-for-all, and rest-for-one
restart strategies with configurable restart intensity limits.
Other:
receive-nb(non-blocking receive)call(synchronous request-response withmake-ref)