8.5. Extern "C" symbols
Defines the signatures of the symbols exposed by librokr.a. Compiler-generated code and the standard library call these symbols.
On the rok side they are declared with the runtime ABI extern "rok" (5.9.2), which the toolchain places in rok::core::rt as pub(pkg). In the blocks below a fn value is RokFn (8.5.0a) and all other values are as in the C ABI. str is struct { const uint8_t *ptr; size_t len; }; runtime functions taking or returning str follow the platform C ABI for a 16-byte struct (8.6.3), like RokFn: by value in registers where the ABI allows, never split between registers and the stack, and by reference on Windows x64. A program may declare a runtime symbol itself (extern "rok" / extern "C"); its declaration must have the signature given here, and a different one is an error.
Important
Every symbol starts with the rok_ prefix, to avoid symbol collisions with external libraries and user code.
8.5.0. Thread-safety classes
Each symbol definition carries a Thread-safety annotation. It is part of the contract between compiler-emitted code and the runtime and states in which calling contexts the symbol may be called safely. There are six classes.
| Class | Meaning |
|---|---|
thread-safe | Safe to call concurrently from multiple threads (the runtime synchronizes internally) |
caller-thread-only | Bound to the calling thread's context - another thread cannot act on behalf of that caller |
process-init | Exactly once at process start, before entering mt |
process-shutdown | Exactly once at process exit, after mt ends |
per-thread-init | Once at the start of each worker thread, by that thread for itself |
per-thread-shutdown | Once at the end of each worker thread, by that thread for itself |
caller-thread-only means the function's effects are tied to the caller's TLS slots or stack frame, so another thread cannot act on behalf of that caller. It does not mean the function is non-reentrant; multiple threads may call it concurrently, each for itself.
8.5.0a. RokFn - C ABI form of function values
The C representation used when passing a rok function value {code, env} (4.4) to a runtime symbol.
typedef struct {
void *code; // code pointer that takes env as its first argument
void *env; // capture environment (NULL for named functions)
} RokFn;
RokFn is passed by value as a C struct. The actual register assignment follows the target C ABI's rules for 16-byte structs.
8.5.1. GC area
rok_gc_alloc_slow
The TLAB slow path from Allocation. Thread-safety: thread-safe.
extern "rok" {
fn rok_gc_alloc_slow(size: usize, align: usize, type_id: u32) -> GcBuf<u8>;
}
The arguments and return value mean:
size: the total object size: the 16-byte header plus the payload, aligned.align: the object's alignment requirement, usually 8, larger when#[repr(align(N))]is specified.type_id: the object's type ID.- Returns: a GC reference (
GcBuf<u8>, the runtime ABI's GC pointer) to the object, starting at the header; the function fills in type_id and gc_flags.
It never returns null. Out of memory is fatal (rok_oom), and a request above isize::MAX is the fatal error "allocation too large" (exit 101).
rok_alloc_raw / rok_realloc_raw
Allocates raw memory outside the GC (stdlib 1.4 alloc). Thread-safety: thread-safe.
extern "rok" {
fn rok_alloc_raw(size: usize, align: usize, zeroed: i32) -> usize;
fn rok_realloc_raw(ptr: usize, old_size: usize, align: usize, new_size: usize) -> usize;
}
If zeroed is nonzero, the allocated memory is zero-filled. rok_alloc_raw returns null (0) if size is 0, if align is not a power of two, or if allocation fails; it does not abort. rok_realloc_raw also returns null on failure. Failure handling is the caller's (rok::bare::alloc) responsibility.
rok_gc_wb_slow
The write barrier slow path from Barriers and root tracking. Thread-safety: caller-thread-only (records into the wb log buffer in TLS).
extern "rok" {
fn rok_gc_wb_slow(obj: Ptr<u8>);
}
The argument obj is a pointer to the object containing the modified slot; there is no return value. Effect: adds obj to the remembered set and flushes when the log is full.
rok_gc_register_type
Registers a type's ScanTable. Thread-safety: process-init.
extern "rok" {
fn rok_gc_register_type(type_id: u32, scan_table: ConstPtr<u8>);
}
Called at process start, before entering mt, so no synchronization is needed.
rok_gc_pin / rok_gc_unpin
Pinning of FFI objects (see Object layout and memory spaces and extern blocks (FFI declarations)). Thread-safety: thread-safe.
extern "rok" {
fn rok_gc_pin(obj: Ptr<u8>) -> Ptr<u8>; // returns the pinned object (same address)
fn rok_gc_unpin(obj: Ptr<u8>);
}
A GC pointer passed to an extern "C" function or a blocking runtime call is not moved even without pinning, because the argument stays in a root until the call ends (7.4.1). Pinning is needed only when C code keeps the pointer after the call. Pin sets PINNED on the object, excluding it from evacuation and keeping it alive; unpin releases it.
rok_gc_collect
An explicit GC trigger, rarely used. Thread-safety: thread-safe.
extern "rok" {
fn rok_gc_collect();
}
Performs a full collection and returns after it finishes.
rok_gc_secret_new / rok_gc_secret_wipe
Secret storage for rok::core::crypto::Secret (stdlib 2.21.5). Thread-safety: thread-safe.
extern "rok" {
fn rok_gc_secret_new(len: usize) -> GcBuf<u8>;
fn rok_gc_secret_wipe(obj: GcBuf<u8>);
}
rok_gc_secret_new allocates a LOS object of type id 1 (no references) with WIPE set (8.1.3). Its zeroed payload holds len bytes rounded up to a multiple of 8. It may collect first, and a request above isize::MAX is the fatal error "allocation too large". rok_gc_secret_wipe overwrites the whole payload with volatile stores followed by a compiler fence; it neither allocates nor blocks, so it is no safepoint (8.4). The collector overwrites the payload the same way before it frees the object.
8.5.2. Runtime area
rok_runtime_init
Initializes the runtime. Thread-safety: process-init.
extern "rok" {
fn rok_runtime_init();
}
Called right after process start, before main(). Effects: creates the worker pool, mmaps the GC spaces and initializes the I/O driver. The compiler-generated C-ABI main calls rok.register_types right after this call (8.2.13).
The runtime additionally exports the thread-local block rok_tls (8.3.8).
rok_runtime_shutdown
Shuts down the runtime. Thread-safety: process-shutdown.
extern "rok" {
fn rok_runtime_shutdown();
}
Called right after main() ends. Every other rok thread stops for good at its next safepoint, after finishing any runtime or C code it is in. Then the worker pool shuts down and outstanding resources are released.
rok_exit
Terminates the process immediately. Thread-safety: thread-safe.
extern "rok" {
fn rok_exit(code: i32) -> !;
}
rok::std::env::exit calls this symbol. finally and Drop are not run (4.16.4).
String helpers
Internal helpers backing FromStr and str::bytes (stdlib 2.15). Thread-safety: thread-safe. str arguments use the struct form of the preamble.
extern "rok" {
fn rok_str_eq(a: str, b: str) -> i32; // 1 if equal, else 0
fn rok_str_byte(s: str, i: usize) -> u8; // i-th byte (bounds check is the caller's)
fn rok_str_slice(s: str, start: usize, end: usize, at: str) -> str; // s[start:end]. panics at location at if the range / UTF-8 boundary is wrong
fn rok_str_parse_int(s: str, radix: u32, signed: i32, bits: u32, out: GcBuf<i64>) -> i32;
// 0 ok, 1 empty, 2 invalid digit, 3 out of range; value bits in out[0]
fn rok_str_parse_float(s: str, out: GcBuf<f64>) -> i32; // 0 ok, 2 invalid, 3 out of range (finite digits past the type's range); value in out[0]
}
rok_runtime_yield
The yield function of the Safepoint protocol, i.e. the slow path of the safepoint poll. Thread-safety: caller-thread-only (called by the calling mutator as a result of polling its own yield_flag).
extern "rok" {
fn rok_runtime_yield();
}
rok_enter_native / rok_leave_native
Slow paths of the native bracket (8.4.10). Thread-safety: caller-thread-only.
extern "rok" {
fn rok_enter_native(); // entering C: this thread counts as PARKED
fn rok_leave_native(); // back from C: wait out a running collection
}
The compiler inlines the fast path around every extern "C" call: a seq_cst store of PARKED / RUNNING to the mutator state, then a check of the yield flag. Only when the flag is set does it call these.
rok_native_reenter / rok_native_resume
The entry and exit of every extern "C" fn definition, so that a C callback runs as a mutator. Thread-safety: caller-thread-only.
extern "rok" {
fn rok_native_reenter() -> u32; // if the thread was PARKED (inside C), leave native; returns whether it was
fn rok_native_resume(parked: u32); // re-enter native if reenter left it
}
rok_thread_init / rok_thread_shutdown
Worker thread lifecycle. Thread-safety: rok_thread_init is per-thread-init, rok_thread_shutdown is per-thread-shutdown.
extern "rok" {
fn rok_thread_init();
fn rok_thread_shutdown();
}
Called at the start and end of each worker thread to initialize or tear down the TLS of the TLS slots.
Internal symbol families
These families are runtime-internal helpers of the reference runtime and are not part of the ABI contract: rok_fmt_*, rok_str_* helpers, rok_char_*, rok_nat_*, rok_stdio_*, rok_time_*, rok_gc_stats_*, rok_buf_*, rok_cstr_len.
Functions that are not safepoints
On the rok side they are declared in an extern "rok" block marked #[no_safepoint] (5.9.2).
The following runtime functions are guaranteed never to allocate and never to block, so a call to them is not a safepoint (8.4.2, 8.9.12): rok_str_len, rok_str_byte, rok_str_cmp, rok_str_eq, rok_str_eq_at, rok_str_find, rok_str_rfind, rok_str_char_count, rok_str_hash, rok_str_bytes_buf, rok_buf_copy, the rok_char_* property and case functions, rok_fmt_len, rok_f64_*, rok_f32_*.
8.5.3. Coroutine area
rok_coro_frame_alloc
Allocates a coroutine frame. Thread-safety: thread-safe.
extern "rok" {
fn rok_coro_frame_alloc(size: usize, type_id: u32) -> GcBuf<u8>;
}
Nearly identical to rok_gc_alloc_slow, but allows frame-specific handling.
rok_coro_park / rok_coro_unpark
The compiler does not emit these two symbols (await uses the parking registry of rok::core::task). The runtime provides them for C and low-level code. The address of frame (any GC object) is the parking key, and rok_coro_unpark(frame) wakes every waker parked on that key. Calling them outside a task executor aborts.
Thread-safety: rok_coro_park is caller-thread-only (parks the calling coroutine itself), rok_coro_unpark is thread-safe (may be called from another thread).
extern "rok" {
fn rok_coro_park(frame: Ptr<u8>, waker: Ptr<u8>);
fn rok_coro_unpark(frame: Ptr<u8>);
}
rok_coro_park suspends the coroutine and registers the waker. rok_coro_unpark resumes the coroutine by pushing it onto the task queue.
Here waker is an opaque pointer (Ptr<u8>) to a bundle of wake function and context, i.e. the runtime's Waker object (2.20.2). The internal representation of Waker is not exposed across the C ABI boundary, so the waker argument has type Ptr<u8> everywhere, including 7.8.8 (likewise for rok_io_register and rok_timer_register). The exact layout of the pointed-to object is determined by the coroutine implementation.
8.5.4. Scheduler area
The task runtime (spawn, join, cancel, executor) is rok code in rok::core::task (stdlib 2.18). The runtime provides only the executor slot, thread and parking symbols below.
rok_task_worker_* / rok_time_now_ns
Functions that store and retrieve the current thread's executor worker (stdlib 2.18) in the TLS slot rok_tls_task_worker (8.3.1), plus a clock. Thread-safety: caller-thread-only (rok_time_now_ns is thread-safe).
extern "rok" {
fn rok_task_worker_present() -> i32; // 1 if this thread is a worker
fn rok_task_worker_get() -> usize; // worker object address, 0 if none
fn rok_task_worker_set(w: usize); // a live GC object or 0. Pinned while installed
fn rok_task_worker_clear(); // = rok_task_worker_set(0)
fn rok_time_now_ns() -> i64; // monotonic clock (ns)
}
rok_thread_spawn
Creates a 1:1 OS thread. Thread-safety: thread-safe.
extern "rok" {
fn rok_thread_spawn(f: RokFn, stack_size: usize, id: u64) -> usize;
fn rok_thread_new_id() -> u64;
fn rok_thread_id() -> u64; // worker id of the current thread
fn rok_thread_sleep_ns(ns: i64);
fn rok_thread_yield_now();
}
rok_thread_spawn runs the rok closure f (a RokFn, passed by value) on an OS thread (stack_size 0 means the default). id is a value obtained from rok_thread_new_id and becomes the new thread's rok_tls_worker_id. The return value is a join handle, or 0 if creation fails. env is a GC root until the thread finishes.
rok_task_hooks_install / rok_task_key
The executor of rok::core::task installs hooks into the runtime. The runtime's I/O driver, timer, and rok_coro_park / rok_coro_unpark use these hooks to wake and suspend tasks. Thread-safety: thread-safe.
extern "rok" {
fn rok_task_hooks_install(
io: extern "C" fn(fd: i32, interest: u32, waker: Ptr<u8>),
timer: extern "C" fn(deadline_ns: u64, waker: Ptr<u8>),
park: extern "C" fn(key: Ptr<u8>, waker: Ptr<u8>),
unpark: extern "C" fn(key: Ptr<u8>),
restart: extern "C" fn(), // restart executor work lost with a failed REPL cell
); // the first installation wins
unsafe fn rok_task_key(obj: Ptr<u8>) -> usize; // GC object address as a park key
}
rok_task_key uses the address as a key, so it sets NOMOVE (8.1.3) on the object. The object is never moved afterwards. Identity comparisons used only within a call use buf_same (stdlib 2.9), which pins nothing.
rok_sync_wait / rok_sync_wake
Waiting and waking for synchronization primitives (Mutex, Condvar, Once). Thread-safety: thread-safe.
extern "rok" {
fn rok_sync_wait(buf: Ptr<u8>, index: usize, expected: i32, timeout_ns: i64); // wait while the GcBuf<i32> element equals expected. timeout_ns < 0 means forever
fn rok_sync_wake(buf: Ptr<u8>, index: usize); // wake all threads waiting on that element
}
rok_sync_wait may return without a reason to wake, so the caller re-checks its condition. While waiting, the thread is considered stopped with respect to the GC.
rok_thread_join
Thread-safety: thread-safe.
extern "rok" {
fn rok_thread_join(handle: usize) -> i32; // 0 on success
}
rok_thread_detach / rok_cpu_count
Thread-safety: thread-safe.
extern "rok" {
fn rok_thread_detach(handle: usize) -> i32; // OS resources are released when it ends; can no longer be joined. 0 on success
fn rok_cpu_count() -> usize; // number of online CPUs (at least 1) - size of the par pool
}
rok_static_init_begin / rok_static_init_end
Back the lazy initialization of statics whose initializer is not const (5.3). Thread-safety: thread-safe.
extern "rok" {
fn rok_static_init_begin(state: Ptr<u8>, at: ConstPtr<u8>, at_len: usize) -> i32; // 1: caller runs the initializer, 0: already ready
fn rok_static_init_end(state: Ptr<u8>); // publish initialization completion
}
state is the state byte of that static (0 new, 1 in progress, 2 ready). If another thread is initializing, rok_static_init_begin waits until it finishes, during which the thread is considered stopped with respect to the GC. at / at_len is the path:line:col of the code reading the static; an initializer that reads its own static panics there (4.14.13).
rok_gc_add_root
extern "rok" {
fn rok_gc_add_root(word: ConstPtr<usize>); // register a static's reference word as a GC root until process exit
}
Startup code generated by the compiler calls this for every static that holds a GC reference.
rok_os_* - OS helpers (internal, subject to change)
Internal symbols backing the standard library rok::std (fs, io, env, process). They are not covered by ABI stability and may change, so user code does not call them directly.
rok_os_strerror rok_os_open rok_os_close rok_os_read rok_os_read_all
rok_os_write rok_os_write_all rok_os_fsync rok_os_ftruncate rok_os_lseek
rok_os_dup rok_os_fchmod rok_os_flock rok_os_copy_fd rok_os_stat
rok_os_mkdir rok_os_unlink rok_os_rename rok_os_link rok_os_opendir
rok_os_readdir rok_os_closedir rok_os_getcwd rok_os_chdir rok_os_realpath
rok_os_stdin_line rok_os_stdin_read rok_os_flush_stdout
rok_os_argc rok_os_arg rok_os_env_at rok_os_getenv rok_os_setenv
rok_os_spawn rok_os_waitpid rok_os_kill rok_os_getpid rok_os_getppid
rok_os_abort
rok_deque_* - work-stealing deque
The lock-free Chase-Lev deque of the task scheduler (7.6.2). The deque and its ring are GC objects traced by the GC, so elements that exist only inside a deque are kept alive. Thread-safety: push / pop only from the owning thread; steal and len from any thread.
extern "rok" {
fn rok_deque_new() -> GcBuf<u8>; // empty deque (GC object)
unsafe fn rok_deque_push(d: Ptr<u8>, item: usize); // owner: push at the end (GC reference)
unsafe fn rok_deque_pop(d: Ptr<u8>, out: Ptr<u8>, index: usize) -> i32; // owner: newest element into out[index], 1 if any
unsafe fn rok_deque_steal(d: Ptr<u8>, out: Ptr<u8>, index: usize) -> i32; // oldest element into out[index], 1 if any
unsafe fn rok_deque_len(d: Ptr<u8>) -> usize; // element count (approximate under contention)
}
out is a GcBuf holding GC references; the result is written at index. These functions contain no safepoints.
8.5.5. I/O / Timer area
rok_io_register / rok_timer_register
Let C or low-level code register a task to be woken when an fd is ready or at a point in time. Internally they use the reactor (7.7) and the executor's timer. Calling them outside a task executor aborts.
Thread-safety: thread-safe.
extern "rok" {
fn rok_io_register(fd: i32, interest: u32, waker: Ptr<u8>);
fn rok_timer_register(deadline: u64, waker: Ptr<u8>);
}
fd: the file descriptor.interest:1= read,2= write (the two may be ORed).waker: a single-elementGcBuf<Waker>. Registration is edge-triggered and wakes only once (spurious wakeups are possible). If the fd is already ready or cannot be watched, it wakes immediately.deadline: an absolute time (ns) on therok_time_now_nsclock. When that time is reached, the waker is woken once.
rok_io_* - reactor (internal, subject to change)
Internal symbols making up the reactor of the I/O driver. They are not covered by ABI stability and may change. Thread-safety: thread-safe.
extern "rok" {
fn rok_io_poller() -> i32; // the process's kqueue / epoll fd (created on first call)
fn rok_io_watch(fd: i32) -> i32; // register fd for read / write edge-triggered (re-registering is harmless), 0 or -errno
fn rok_io_poll(out: Ptr<u8>, max: usize) -> i32; // blocks until something changes (parked for the GC), event count or -errno
}
rok_io_poll writes up to max events into out, a GcBuf<i64>, as fd * 2 + direction (read 0, write 1).
rok_net_* - socket helpers (internal, subject to change)
Internal symbols backing the standard library rok::std::net (stdlib 3.2). They are not covered by ABI stability and may change, so user code does not call them directly. Sockets are always created non-blocking, and failures are returned as -errno.
rok_net_socket rok_net_bind rok_net_listen rok_net_connect
rok_net_sock_error rok_net_accept rok_net_addr rok_net_send
rok_net_send_to rok_net_recv_from rok_net_shutdown rok_net_setopt
rok_net_multicast_v4 rok_net_wait rok_net_resolve rok_net_gai_strerror
rok_net_wait is a poll(2) with which a blocking call waits for readiness (a native section for the GC); rok_net_resolve is getaddrinfo.
8.5.6. Error propagation (no symbols)
throw / try / catch from Error control flow have no runtime symbols. A throwing function returns the error as part of its return value (8.6.5), and the call site checks that value and branches (8.9.6). There is therefore no runtime call to throw an error or fetch the current error, and there are no TLS slots or jmp_buf for error handling.
8.5.7. Diagnostic area
Exit codes below (101 etc.) reach the host as plain failure on wasm32-wasi (4.16.4).
rok_panic
Thread-safety: thread-safe (callable from any thread; the process exits immediately).
extern "rok" {
fn rok_panic(msg: ConstPtr<u8>, msg_len: usize); // does not return
}
An unrecoverable panic; fatal. This function does not return: it prints the message to stderr and terminates the process with exit code 101. catch and finally are not run.
rok_panic_str
Thread-safety: thread-safe.
extern "rok" {
fn rok_panic_str(msg: str) -> !; // msg is (ptr, len)
}
Same as rok_panic but takes the message as a str. It prints no location and is used only for panics internal to the runtime.
rok_panic_at
Thread-safety: thread-safe.
extern "rok" {
#[track_caller]
fn rok_panic_at(msg: str) -> !;
}
The declaration is #[track_caller] (6.2.12), so the symbol takes the caller's location as a hidden trailing str argument ("path:line:col"): rok_panic_at(msg, at).
Prints in the format of 4.14.13 and exits with code 101. Core's panic and compiler-generated panics (bounds checks, overflow, division by zero, etc.) call this symbol.
rok_uncaught_error / rok_uncaught_error_at
extern "rok" {
fn rok_uncaught_error(what: str) -> !; // uncaught exception: print what to stderr, exit code 1
fn rok_uncaught_error_at(what: str, at: str) -> !; // followed by a " --> <at>" line
}
Handle errors propagated out of the entry point (4.16.4). The declarations live in core.
rok_oom
Thread-safety: thread-safe.
extern "rok" {
fn rok_oom() -> !;
}
Prints fatal error: out of memory to stderr and exits with code 101. Allocation failures inside the runtime and core's oom() both go through this symbol. The runtime cannot call rok functions by name, so on allocation failure it uses this symbol directly instead of calling the function in the oom_fn slot.
Stack overflow
When a thread's stack overflows, the runtime flushes stdout (if possible), prints the following to stderr and aborts (SIGABRT). Unlike a panic, there is no unwinding and no finally execution.
thread '<name>' has overflowed its stack
<name> is the thread name given with thread::Builder::name (also set as the OS thread name, 7. Runtime). This covers the main thread (rok_runtime_init), rok_thread_spawn threads (including executor workers, and deep await chains), and C threads that called rok_thread_init. The runtime records the guard range below each thread's stack in the TLS slot rok_tls_stack_guard (8.3.1) and installs an alternate signal stack (sigaltstack). Only SIGSEGV / SIGBUS within that range is reported as a stack overflow; other faults follow the default behavior. On wasm a module cannot catch the trap; the host reports it (the shipped JS host prints the message above).
8.5.8. Symbol naming conventions
The area of each symbol prefix is summarized below.
| Prefix | Area |
|---|---|
rok_gc_* | GC |
rok_runtime_* | Runtime in general |
rok_thread_* | OS threads |
rok_coro_* | Coroutines |
rok_task_* | Tasks |
rok_io_* | I/O |
rok_timer_* | Timers |
rok_panic | Diagnostics |
This prefix convention avoids symbol collisions with external libraries and user code. The tables of 8.5.1 - 8.5.7 list the normative runtime symbols; the runtime may export further implementation-defined rok_* symbols (e.g. rok_fmt_*, rok_os_*, rok_stdio_*, rok_static_init_*).
8.5.9. Compatibility of function signatures
Each function signature is part of the ABI. The change policy is:
- minor: only compatible changes are allowed; adding arguments is handled through function wrappers.
- major: breaking changes are possible, and the ABI version hash changes.
See the stability policy in the overview for details.
8.5.10. Calling convention
All functions above use the target platform's C ABI calling convention, e.g. System V x86_64, AAPCS aarch64, MS x64 Windows.
8.5.11. Example: slow call from the TLAB fast path
; LLVM IR (when the fast path fails)
%t = call ptr @llvm.threadlocal.address(ptr @rok_tls)
%cursor_p = getelementptr i8, ptr %t, i64 8 ; tlab_cursor at p
%cursor = load i8*, i8** %cursor_p
%new_cursor = getelementptr i8, i8* %cursor, i64 24
%limit_p = getelementptr i8, ptr %t, i64 16 ; tlab_limit at 2p
%limit = load i8*, i8** %limit_p
%check = icmp ugt i8* %new_cursor, %limit
br i1 %check, label %slow, label %fast
slow:
%obj = call i8* @rok_gc_alloc_slow(i64 24, i64 8, i32 12345)
br label %cont
fast:
store i8* %new_cursor, i8** %cursor_p
br label %cont
cont:
%obj_phi = phi i8* [%obj_slow, %slow], [%cursor, %fast]
; ... continue
The example shows the LLVM IR structure combining the fast path and the slow path.
8.5.12. Auxiliary symbols (appendix, non-normative)
Important
This section is non-normative. The symbols below are auxiliary symbols exported by the reference runtime (rokr). They are not part of the main ABI tables (8.5.1 ~ 8.5.11) and carry no stability promise; the runtime may export further implementation-defined rok_* symbols (8.5.8).
Signatures use the same notation as the main text (Ptr<T> = mutable raw pointer, ConstPtr<T> = read-only raw pointer). String arguments are NUL-terminated C strings.
extern "rok" {
// memory
fn rok_free(ptr: Ptr<u8>); // null ignored
fn rok_load_i32(ptr: ConstPtr<i32>) -> i32;
fn rok_load_i64(ptr: ConstPtr<i64>) -> i64;
fn rok_load_f64(ptr: ConstPtr<f64>) -> f64;
fn rok_store_i32(ptr: Ptr<i32>, val: i32);
fn rok_store_i64(ptr: Ptr<i64>, val: i64);
fn rok_store_f64(ptr: Ptr<f64>, val: f64);
// C strings
fn rok_str_len(s: ConstPtr<u8>) -> usize; // null → 0
// GC
fn rok_gc_collect_young(); // force a minor collection
fn rok_gc_weak_new(target: Ptr<u8>) -> Ptr<u8>; // a weak cell (type id 3) to target
fn rok_gc_weak_get(cell: Ptr<u8>) -> Ptr<u8>; // the target, or null once freed
// process exit
fn rok_set_exit_hook(hook: extern "C" fn(i32) | null);
// called with the exit status, on the ending thread, before a panic,
// uncaught error, exit or (Windows) stack overflow ends the process;
// the process ends when it returns
// REPL host (session save / restore)
fn rok_session_hold(timeout_ns: i64) -> i32; // stop the world at a quiet moment: no poll / blocking job
// running, no thread stopped inside a lock; 0: not within
// timeout_ns (still requested)
fn rok_session_unhold(); // withdraw a pending or granted hold
fn rok_session_resume(); // resume the world after a hold
fn rok_session_enter(); // executor threads' gates around a poll / blocking job
fn rok_session_leave();
fn rok_session_pass();
fn rok_lock_notes() -> i32; // 1 in a session host
fn rok_lock_note(word: GcBuf<i32>, kind: i32) -> i32; // kind: 0 Mutex, 1 read, 2 write, 3 running Once; 1 = noted
fn rok_lock_drop(word: GcBuf<i32>);
fn rok_world_threads(out: Ptr<u64>, cap: usize) -> usize; // world stopped: OS ids of the other mutators (Windows)
fn rok_session_restored(lost: ConstPtr<u64>, n: usize); // session put back; n == SIZE_MAX: every other thread is gone
fn rok_thread_lost(worker_id: u64) -> i32; // 1 if that thread was found gone after a failed cell
fn rok_io_quiesce(); // Windows: cancel in-flight I/O before memory is put back
fn rok_watch_writes(); // Windows: write-watch the heap; call before runtime init
static rok_page_epoch: u64; // Windows: bumped when watched pages change without a write
// or a watched block is released
}
The following rules apply.
- User code and the standard library must not depend on these symbols. Their signatures may change or be removed without notice.
- They share the
rok_prefix but are not included in the area table of 8.5.8. To avoid name collisions, normative symbols of the specification do not reuse these names. rok_panicdoes not belong to this appendix. The signaturerok_panic(msg: ConstPtr<u8>, msg_len: usize)in 8.5.7 is authoritative, and an implementation exporting a different signature is non-conformant.