Packages

builtin
cbindgen
core
core.alloc
core.alloc.arena
core.alloc.atomic
core.alloc.fixed
core.alloc.gc
core.alloc.heap
core.alloc.log
core.alloc.memdebug
core.alloc.pool
core.alloc.ring
core.arg_parse
core.array
core.avl_tree
core.bucket_array
core.conv
core.doc
core.encoding
core.encoding.base64
core.encoding.csv
core.encoding.hex
core.encoding.json
core.encoding.kdl
core.encoding.osad
core.encoding.utf8
core.hash
core.hash.md5
core.hash.sha1
core.hash.sha256
core.heap
core.intrinsics
core.intrinsics.atomics
core.intrinsics.onyx
core.intrinsics.types
core.intrinsics.wasm
core.io
core.io.binary
core.iter
core.js
core.list
core.map
core.math
core.memory
core.misc
core.net
core.os
core.random
core.set
core.slice
core.string
core.sync
core.test
core.thread
core.time
main
runtime
runtime.info
runtime.platform
runtime.vars
simd

package core.sync

Barrier
Barrier :: struct {
    mutex: Mutex;
    cond: Condition_Variable;
    index: i32;
    generation: i32;
    thread_count: i32;
}

Represents a generational barrier, so the same barrier can be used safely multiple times.

Channel
Channel :: struct (T: type_expr) {
    _buf: [..] T;
    _mutex: Mutex;
    _condvar: Condition_Variable;
    _is_open: bool;
}
Methods
Channel.as_iter
Channel.as_iter :: (chan: &Channel) -> Iterator(chan.T)
Channel.close
Channel.close :: (chan: &Channel) -> void
Channel.make
Channel.make :: ($T: type_expr, allocator) -> Channel(T)
Channel.poll
Channel.poll :: (chan: &Channel) -> bool
Channel.recv
Channel.recv :: (chan: &Channel) -> ? chan.T
Channel.send
Channel.send :: (chan: &Channel, msg: chan.T) -> void
Condition_Variable
Condition_Variable :: struct {
    mutex: Mutex;
    queue: &Condition_Variable.Node;
}

A condition variable is used to implement a queue of threads waiting for a condition to be true. Each thread joins the queue using condition_wait. Then, another thread can signal that the condition has changed and can "wake up" the first thread in the queue using condition_signal. Alternatively, all threads can be woken up using condition_broadcast.

Condition variables are generally used to prevent spin checking a condition and waiting for it to change. Instead, the thread joins a wait-queue, and leave it up to another thread to wake it up to continue processing. However sadly, in WebAssembly this is not possible because with the atomic_wait and atomic_notify instructions, which currently are not supported by any runtime outside of the browser.

Mutex
Mutex :: struct {
    lock: i32;
    owner: i32;
}

A mutex represents a resource that can only be held by one thread at a time. It is used to create sections of code that only one thread can be in at a time.

Mutexes in WebAssembly are very cheap, because they simply use the atomic_cmpxchg intrinsic to operate. This only uses memory, so no real resource allocation is necessary.

lock has two states: 0, and 1. 0 means unlocked, 1 means locked

To lock it:

Try to store 1 if the value was already 0.
Otherwise, if it was already 1, wait until it goes to 0.

To unlock it:

Atomically set it to 0.
Notify at most 1 other thread about this change.
MutexGuard
MutexGuard :: struct (T: type_expr) {
    _: ARRAY TYPE;
}

Represents a "guarded" value, i.e. one that is protected by a mutex.

The only way to access the value inside is by using the with method and passing in a code block that accepts a pointer to the value. This way, there is no way to access the value without locking a mutex. (Unless of course, you store the pointer somewhere else, but then you are just being a bad citizen of the programming language ;) ).

Methods
MutexGuard.make
MutexGuard.make :: ($T: type_expr) -> MutexGuard(T)
MutexGuard.make :: (v: $T) -> MutexGuard(T)
MutexGuard.with
MutexGuard.with :: macro (__guard: &MutexGuard, body: Code) -> u32
Once
Once :: struct {
    done: bool;
    mutex: Mutex;
}

Represents something will only happen once.

Methods
Once.exec
Once.exec :: (o: &Once, f: () -> $R) -> void
Once.exec :: (o: &Once, ctx: $Ctx, f: (Ctx) -> $R) -> void
Semaphore
Semaphore :: struct {
    mutex: Mutex;
    counter: i32;
}

A semaphore represents a counter that can only be incremented and decremented by one thread at a time. "Waiting" on a semaphore means decrementing the counter by 1 if it is greater than 0, otherwise waiting until the counter is incremented. "Posting" on a semaphore means incrementing the counter by a certain value, in turn releasing other threads that might have been waiting for the value to change.

Semaphores are generally used for controlling access to shared resources. For a contrived example, say only 4 threads can use a given network connection at a time. A semaphore would be created with a value of 4. When a thread wants to use the network connection, it would use semaphore_wait to obtain the resource, or wait if the network is currently available. When it is done using the network, it would call semaphore_post to release the resource, allowing another thread to use it.

_MutexGuard
_MutexGuard :: struct (T: type_expr) {
    mutex: Mutex;
    value: T;
}
barrier_destroy
barrier_destroy :: (b: &Barrier) -> void

Destroys a generational barrier.

barrier_init
barrier_init :: (b: &Barrier, thread_count: i32) -> void

Initializes a new generational barrier with thread_count threads.

barrier_wait
barrier_wait :: (b: &Barrier) -> void

Signals that a thread has reached the barrier. The last thread to reach the barrier will wake up all other threads.

condition_broadcast
condition_broadcast :: (c: &Condition_Variable) -> void

Wakes up all threads from the wait-queue.

condition_destroy
condition_destroy :: (c: &Condition_Variable) -> void

Destroys a condition variable.

condition_init
condition_init :: (c: &Condition_Variable) -> void

Initializes a new condition variable.

condition_signal
condition_signal :: (c: &Condition_Variable) -> void

Wakes up one thread from the wait-queue.

condition_wait
condition_wait :: (c: &Condition_Variable, m: &Mutex) -> void

Enters the thread in the wait-queue of the condition variable. If m is not null, the mutex will first be released before entering the queue, and then relocked before returning.

critical_section
critical_section :: macro (m: &Mutex, body: Code) -> i32

Abstracts the pattern decribed in scoped_mutex by automatically calling scoped_mutex in the block of code given.

m: sync.Mutx;
sync.mutex_init(&m);

sync.critical_section(&m) {
   // Everything here is done by one thread at a time.
}
mutex_destroy
mutex_destroy :: (m: &Mutex) -> void

Destroys a mutex.

mutex_init
mutex_init :: (m: &Mutex) -> void

Initializes a new mutex.

mutex_lock
mutex_lock :: (m: &Mutex) -> void

Locks a mutex. If the mutex is currently held by another thread, this function enters a spin loop until the mutex is unlocked. In a JavaScript based implementation, the __atomic_wait intrinsic is used to avoid having to spin loop.

mutex_unlock
mutex_unlock :: (m: &Mutex) -> void

Unlocks a mutex, if the calling thread currently holds the mutex. In a JavaScript based implementation, the __atomic_notify intrinsic is used to wake up one waiting thread.

scoped_mutex
scoped_mutex :: macro (m: &Mutex) -> void

Helpful macro for making a particular block be protected by a macro.

m: sync.Mutx;
sync.mutex_init(&m);

{
   sync.scoped_mutex(&m);
   // Everything here is done by one thread at a time.
}
semaphore_destroy
semaphore_destroy :: (s: &Semaphore) -> void

Destroys a semaphore.

semaphore_init
semaphore_init :: (s: &Semaphore, value: i32) -> void

Initializes a semaphore with the specified value.

semaphore_post
semaphore_post :: (s: &Semaphore, count: i32) -> void

Increment the counter in a semaphore by count.

semaphore_wait
semaphore_wait :: (s: &Semaphore) -> void

Waits until the thread is able to decrement one from the semaphore.