package core:sync
Overview
Synchronization primitives
This package implements various synchronization primitives that can be used to synchronize threads' access to shared memory.
To limit or control the threads' access to shared memory typically the following approaches are used:
Locks Lock-free
When using locks, sections of the code that access shared memory (also known as critical sections) are guarded by locks, allowing limited access to threads and blocking the execution of any other threads.
In lock-free programming the data itself is organized in such a way that threads don't intervene much. It can be done via segmenting the data between threads, and/or by using atomic operations.
Index
Types (23)
Variables (0)
This section is empty.
Procedures (104)
- atomic_add
- atomic_add_explicit
- atomic_and
- atomic_and_explicit
- atomic_compare_exchange_strong
- atomic_compare_exchange_strong_explicit
- atomic_compare_exchange_weak
- atomic_compare_exchange_weak_explicit
- atomic_cond_broadcast
- atomic_cond_signal
- atomic_cond_wait
- atomic_cond_wait_with_timeout
- atomic_exchange
- atomic_exchange_explicit
- atomic_load
- atomic_load_explicit
- atomic_mutex_guard
- atomic_mutex_lock
- atomic_mutex_try_lock
- atomic_mutex_unlock
- atomic_nand
- atomic_nand_explicit
- atomic_or
- atomic_or_explicit
- atomic_recursive_mutex_guard
- atomic_recursive_mutex_lock
- atomic_recursive_mutex_try_lock
- atomic_recursive_mutex_unlock
- atomic_rw_mutex_guard
- atomic_rw_mutex_lock
- atomic_rw_mutex_shared_guard
- atomic_rw_mutex_shared_lock
- atomic_rw_mutex_shared_unlock
- atomic_rw_mutex_try_lock
- atomic_rw_mutex_try_shared_lock
- atomic_rw_mutex_unlock
- atomic_sema_post
- atomic_sema_wait
- atomic_sema_wait_with_timeout
- atomic_signal_fence
- atomic_store
- atomic_store_explicit
- atomic_sub
- atomic_sub_explicit
- atomic_thread_fence
- atomic_xor
- atomic_xor_explicit
- auto_reset_event_signal
- auto_reset_event_wait
- barrier_init
- barrier_wait
- benaphore_guard
- benaphore_lock
- benaphore_try_lock
- benaphore_unlock
- cond_broadcast
- cond_signal
- cond_wait
- cond_wait_with_timeout
- cpu_relax
- current_thread_id
- futex_broadcast
- futex_signal
- futex_wait
- futex_wait_with_timeout
- mutex_guard
- mutex_lock
- mutex_try_lock
- mutex_unlock
- once_do_with_data
- once_do_with_data_contextless
- once_do_without_data
- once_do_without_data_contextless
- one_shot_event_signal
- one_shot_event_wait
- park
- park_with_timeout
- recursive_benaphore_guard
- recursive_benaphore_lock
- recursive_benaphore_try_lock
- recursive_benaphore_unlock
- recursive_mutex_guard
- recursive_mutex_lock
- recursive_mutex_try_lock
- recursive_mutex_unlock
- rw_mutex_guard
- rw_mutex_lock
- rw_mutex_shared_guard
- rw_mutex_shared_lock
- rw_mutex_shared_unlock
- rw_mutex_try_lock
- rw_mutex_try_shared_lock
- rw_mutex_unlock
- sema_post
- sema_wait
- sema_wait_with_timeout
- ticket_mutex_guard
- ticket_mutex_lock
- ticket_mutex_unlock
- unpark
- wait_group_add
- wait_group_done
- wait_group_wait
- wait_group_wait_with_timeout
Procedure Groups (14)
Types
Atomic_Cond ¶
Atomic_Cond :: struct { state: Futex, }
Atomic_Cond implements a condition variable, a rendezvous point for threads waiting for signalling the occurence of an event
An Atomic_Cond must not be copied after first use
Related Procedures With Parameters
- atomic_cond_broadcast
- atomic_cond_signal
- atomic_cond_wait
- atomic_cond_wait_with_timeout
- broadcast (procedure groups)
- signal (procedure groups)
- wait (procedure groups)
- wait_with_timeout (procedure groups)
Atomic_Memory_Order ¶
Atomic_Memory_Order :: .Atomic_Memory_Order
Describes memory ordering for an atomic operation.
Modern CPU's contain multiple cores and caches specific to those cores. When a core performs a write to memory, the value is written to cache first. The issue is that a core doesn't typically see what's inside the caches of other cores. In order to make operations consistent CPU's implement mechanisms that synchronize memory operations across cores by asking other cores or by pushing data about writes to other cores.
Due to how these algorithms are implemented, the stores and loads performed by one core may seem to happen in a different order to another core. It also may happen that a core reorders stores and loads (independent of how compiler put them into the machine code). This can cause issues when trying to synchronize multiple memory locations between two cores. Which is why CPU's allow for stronger memory ordering guarantees if certain instructions or instruction variants are used.
In Odin there are 5 different memory ordering guaranties that can be provided to an atomic operation:
Relaxed
: The memory access (load or store) is unordered with respect to
other memory accesses. This can be used to implement an atomic counter.
Multiple threads access a single variable, but it doesn't matter when
exactly it gets incremented, because it will become eventually consistent.
Consume
: No loads or stores dependent on a memory location can be
reordered before a load with consume memory order. If other threads released
the same memory, it becomes visible.
Acquire
: No loads or stores on a memory location can be reordered before a
load of that memory location with acquire memory ordering. If other threads
release the same memory, it becomes visible.
Release
: No loads or stores on a memory location can be reordered after a
store of that memory location with release memory ordering. All threads that
acquire the same memory location will see all writes done by the current
thread.
Acq_Rel
: Acquire-release memory ordering: combines acquire and release
memory orderings in the same operation.
Seq_Cst
: Sequential consistency. The strongest memory ordering. A load will
always be an acquire operation, a store will always be a release operation,
and in addition to that all threads observe the same order of writes.
Non-explicit atomics will always be sequentially consistent.
Atomic_Memory_Order :: enum { Relaxed = 0, // Unordered Consume = 1, // Monotonic Acquire = 2, Release = 3, Acq_Rel = 4, Seq_Cst = 5, }
Note(i386, x64): x86 has a very strong memory model by default. It
guarantees that all writes are ordered, stores and loads aren't reordered. In
a sense, all operations are at least acquire and release operations. If lock
prefix is used, all operations are sequentially consistent. If you use explicit
atomics, make sure you have the correct atomic memory order, because bugs likely
will not show up in x86, but may show up on e.g. arm. More on x86 memory
ordering can be found
here
Atomic_Mutex ¶
Atomic_Mutex :: struct { state: Atomic_Mutex_State, }
An Atomic_Mutex is a mutual exclusion lock The zero value for a Atomic_Mutex is an unlocked mutex
An Atomic_Mutex must not be copied after first use
Related Procedures With Parameters
- atomic_cond_wait
- atomic_cond_wait_with_timeout
- atomic_mutex_guard
- atomic_mutex_lock
- atomic_mutex_try_lock
- atomic_mutex_unlock
- guard (procedure groups)
- lock (procedure groups)
- try_lock (procedure groups)
- unlock (procedure groups)
- wait (procedure groups)
- wait_with_timeout (procedure groups)
Atomic_Mutex_State ¶
Atomic_Mutex_State :: enum Futex { Unlocked = 0, Locked = 1, Waiting = 2, }
Atomic_RW_Mutex ¶
Atomic_RW_Mutex :: struct { state: Atomic_RW_Mutex_State, mutex: Atomic_Mutex, sema: Atomic_Sema, }
An Atomic_RW_Mutex is a reader/writer mutual exclusion lock The lock can be held by any arbitrary number of readers or a single writer The zero value for an Atomic_RW_Mutex is an unlocked mutex
An Atomic_RW_Mutex must not be copied after first use
Related Procedures With Parameters
- atomic_rw_mutex_guard
- atomic_rw_mutex_lock
- atomic_rw_mutex_shared_guard
- atomic_rw_mutex_shared_lock
- atomic_rw_mutex_shared_unlock
- atomic_rw_mutex_try_lock
- atomic_rw_mutex_try_shared_lock
- atomic_rw_mutex_unlock
- guard (procedure groups)
- lock (procedure groups)
- shared_guard (procedure groups)
- shared_lock (procedure groups)
- shared_unlock (procedure groups)
- try_lock (procedure groups)
- try_shared_lock (procedure groups)
- unlock (procedure groups)
Atomic_Recursive_Mutex ¶
An Atomic_Recursive_Mutex is a recursive mutual exclusion lock The zero value for a Recursive_Mutex is an unlocked mutex
An Atomic_Recursive_Mutex must not be copied after first use
Related Procedures With Parameters
- atomic_recursive_mutex_guard
- atomic_recursive_mutex_lock
- atomic_recursive_mutex_try_lock
- atomic_recursive_mutex_unlock
- guard (procedure groups)
- lock (procedure groups)
- try_lock (procedure groups)
- unlock (procedure groups)
Atomic_Sema ¶
Atomic_Sema :: struct { count: Futex, }
When waited upon, blocks until the internal count is greater than zero, then subtracts one. Posting to the semaphore increases the count by one, or the provided amount.
An Atomic_Sema must not be copied after first use
Related Procedures With Parameters
- atomic_sema_post
- atomic_sema_wait
- atomic_sema_wait_with_timeout
- post (procedure groups)
- wait (procedure groups)
- wait_with_timeout (procedure groups)
Auto_Reset_Event ¶
Auto_Reset_Event :: struct { // status == 0: Event is reset and no threads are waiting // status == 1: Event is signalled // status == -N: Event is reset and N threads are waiting status: i32, sema: Sema, }
Auto-reset event.
Represents a thread synchronization primitive that, when signalled, releases one single waiting thread and then resets automatically to a state where it can be signalled again.
When a thread calls auto_reset_event_wait
, its execution will be blocked,
until the event is signalled by another thread. The call to
auto_reset_event_signal
wakes up exactly one thread waiting for the event.
Related Procedures With Parameters
Barrier ¶
Barrier.
A barrier is a synchronization primitive enabling multiple threads to synchronize the beginning of some computation.
When barrier_wait
procedure is called by any thread, that thread will block
the execution, until all threads associated with the barrier reach the same
point of execution and also call barrier_wait
.
When a barrier is initialized, a thread_count
parameter is passed, signifying
the amount of participant threads of the barrier. The barrier also keeps track
of an internal atomic counter. When a thread calls barrier_wait
, the internal
counter is incremented. When the internal counter reaches thread_count
, it is
reset and all threads waiting on the barrier are unblocked.
This type of synchronization primitive can be used to synchronize "staged"
workloads, where the workload is split into stages, and until all threads have
completed the previous threads, no thread is allowed to start work on the next
stage. In this case, after each stage, a barrier_wait
shall be inserted in the
thread procedure.
Example:
THREAD_COUNT :: 4 threads: [THREAD_COUNT]^thread.Thread sync.barrier_init(barrier, THREAD_COUNT) for _, i in threads { threads[i] = thread.create_and_start(proc(t: ^thread.Thread) { // Same messages will be printed together but without any interleaving fmt.println("Getting ready!") sync.barrier_wait(barrier) fmt.println("Off their marks they go!") }) } for t in threads { thread.destroy(t) }
Related Procedures With Parameters
Benaphore ¶
Benaphore.
A benaphore is a combination of an atomic variable and a semaphore that can improve locking efficiency in a no-contention system. Acquiring a benaphore lock doesn't call into an internal semaphore, if no other thread is in the middle of a critical section.
Once a lock on a benaphore is acquired by a thread, no other thread is allowed into any critical sections, associted with the same benaphore, until the lock is released.
Related Procedures With Parameters
- benaphore_guard
- benaphore_lock
- benaphore_try_lock
- benaphore_unlock
- guard (procedure groups)
- lock (procedure groups)
- try_lock (procedure groups)
- unlock (procedure groups)
Cond ¶
Cond :: struct {
impl: _Cond,
}
A condition variable.
Cond
implements a condition variable, a rendezvous point for threads waiting
for signalling the occurence of an event. Condition variables are used in
conjuction with mutexes to provide a shared access to one or more shared
variable.
A typical usage of condition variable is as follows. A thread that intends to modify a shared variable shall:
1. Acquire a lock on a mutex.
2. Modify the shared memory.
3. Release the lock.
3. Call cond_signal
or cond_broadcast
.
A thread that intends to wait on a shared variable shall:
1. Acquire a lock on a mutex.
2. Call cond_wait
or cond_wait_with_timeout
(will release the mutex).
3. Check the condition and keep waiting in a loop if not satisfied with result.
Note: A condition variable must not be copied after first use (e.g., after
waiting on it the first time). This is because, in order to coordinate with
other threads, all threads must watch the same memory address to know when the
lock has been released. Trying to use a copy of the lock at a different memory
address will result in broken and unsafe behavior. For this reason, condition
variables are marked as #no_copy
.
Related Procedures With Parameters
- cond_broadcast
- cond_signal
- cond_wait
- cond_wait_with_timeout
- broadcast (procedure groups)
- signal (procedure groups)
- wait (procedure groups)
- wait_with_timeout (procedure groups)
Futex ¶
Futex :: distinct u32
Fast userspace mutual exclusion lock.
Futex is a fast userspace mutual exclusion lock, that uses a pointer to a 32-bit value as an identifier of the queue of waiting threads. The value pointed to by that pointer can be used to store extra data.
IMPORTANT: A futex must not be copied after first use (e.g., after waiting on it the first time, or signalling it). This is because, in order to coordinate with other threads, all threads must watch the same memory address. Trying to use a copy of the lock at a different memory address will result in broken and unsafe behavior.
Related Procedures With Parameters
- futex_broadcast
- futex_signal
- futex_wait
- futex_wait_with_timeout
- broadcast (procedure groups)
- signal (procedure groups)
- wait (procedure groups)
- wait_with_timeout (procedure groups)
Mutex ¶
Mutex :: struct {
impl: _Mutex,
}
Mutual exclusion lock.
A Mutex is a mutual exclusion lock It can be used to prevent more than one thread from entering the critical section, and thus prevent access to same piece of memory by multiple threads, at the same time.
Mutex's zero-initializzed value represents an initial, unlocked state.
If another thread tries to acquire the lock, while it's already held (typically by another thread), the thread's execution will be blocked, until the lock is released. Code or memory that is "surrounded" by a mutex lock and unlock operations is said to be "guarded by a mutex".
Note: A Mutex must not be copied after first use (e.g., after locking it the
first time). This is because, in order to coordinate with other threads, all
threads must watch the same memory address to know when the lock has been
released. Trying to use a copy of the lock at a different memory address will
result in broken and unsafe behavior. For this reason, Mutexes are marked as
#no_copy
.
Note: If the current thread attempts to lock a mutex, while it's already
holding another lock, that will cause a trivial case of deadlock. Do not use
Mutex
in recursive functions. In case multiple locks by the same thread are
desired, use Recursive_Mutex
.
Related Procedures With Parameters
- cond_wait
- cond_wait_with_timeout
- mutex_guard
- mutex_lock
- mutex_try_lock
- mutex_unlock
- guard (procedure groups)
- lock (procedure groups)
- try_lock (procedure groups)
- unlock (procedure groups)
- wait (procedure groups)
- wait_with_timeout (procedure groups)
Once ¶
Once action.
Once
a synchronization primitive, that only allows a single entry into a
critical section from a single thread.
Related Procedures With Parameters
One_Shot_Event ¶
One_Shot_Event :: struct { state: Futex, }
One-shot event.
A one-shot event is an associated token which is initially not present:
The one_shot_event_wait
blocks the current thread until the event
is made available
The one_shot_event_signal
procedure automatically makes the token
available if its was not already.
Related Procedures With Parameters
Parker ¶
Parker :: struct { state: Futex, }
A Parker is an associated token which is initially not present:
The park
procedure blocks the current thread unless or until the token
is available, at which point the token is consumed.
The park_with_timeout
procedures works the same as park
but only
blocks for the specified duration.
The unpark
procedure automatically makes the token available if it
was not already.
Related Procedures With Parameters
RW_Mutex ¶
RW_Mutex :: struct {
impl: _RW_Mutex,
}
Read-write mutual exclusion lock.
An RW_Mutex
is a reader/writer mutual exclusion lock. The lock can be held by
any number of readers or a single writer.
This type of synchronization primitive supports two kinds of lock operations:
Exclusive lock (write lock) Shared lock (read lock)
When an exclusive lock is acquired by any thread, all other threads, attempting to acquire either an exclusive or shared lock, will be blocked from entering the critical sections associated with the read-write mutex, until the exclusive owner of the lock releases the lock.
When a shared lock is acquired by any thread, any other thread attempting to acquire a shared lock will also be able to enter all the critical sections associated with the read-write mutex. However threads attempting to acquire an exclusive lock will be blocked from entering those critical sections, until all shared locks are released.
Note: A read-write mutex must not be copied after first use (e.g., after
acquiring a lock). This is because, in order to coordinate with other threads,
all threads must watch the same memory address to know when the lock has been
released. Trying to use a copy of the lock at a different memory address will
result in broken and unsafe behavior. For this reason, mutexes are marked as
#no_copy
.
Note: A read-write mutex is not recursive. Do not attempt to acquire an exclusive lock more than once from the same thread, or an exclusive and shared lock on the same thread. Taking a shared lock multiple times is acceptable.
Related Procedures With Parameters
- rw_mutex_guard
- rw_mutex_lock
- rw_mutex_shared_guard
- rw_mutex_shared_lock
- rw_mutex_shared_unlock
- rw_mutex_try_lock
- rw_mutex_try_shared_lock
- rw_mutex_unlock
- guard (procedure groups)
- lock (procedure groups)
- shared_guard (procedure groups)
- shared_lock (procedure groups)
- shared_unlock (procedure groups)
- try_lock (procedure groups)
- try_shared_lock (procedure groups)
- unlock (procedure groups)
Recursive_Benaphore ¶
Recursive benaphore.
A recursive benaphore is just like a plain benaphore, except it allows reentrancy into the critical section.
When a lock is acquired on a benaphore, all other threads attempting to acquire a lock on the same benaphore will be blocked from any critical sections, associated with the same benaphore.
When a lock is acquired on a benaphore by a thread, that thread is allowed to acquire another lock on the same benaphore. When a thread has acquired the lock on a benaphore, the benaphore will stay locked until the thread releases the lock as many times as it has been locked by the thread.
Related Procedures With Parameters
- recursive_benaphore_guard
- recursive_benaphore_lock
- recursive_benaphore_try_lock
- recursive_benaphore_unlock
- guard (procedure groups)
- lock (procedure groups)
- try_lock (procedure groups)
- unlock (procedure groups)
Recursive_Mutex ¶
Recursive_Mutex :: struct {
impl: _Recursive_Mutex,
}
Recursive mutual exclusion lock.
Recurisve mutex is just like a plain mutex, except it allows reentrancy. In order for a thread to release the mutex for other threads, the mutex needs to be unlocked as many times, as it was locked.
When a lock is acquired on a recursive mutex, all other threads attempting to acquire a lock on the same mutex will be blocked from any critical sections, associated with the same recrusive mutex.
When a lock is acquired on a recursive mutex by a thread, that thread is allowed to acquire another lock on the same mutex. When a thread has acquired the lock on a recursive mutex, the recursive mutex will stay locked until the thread releases the lock as many times as it has been locked by the thread.
Note: A recursive mutex must not be copied after first use (e.g., after
acquiring a lock). This is because, in order to coordinate with other threads,
all threads must watch the same memory address to know when the lock has been
released. Trying to use a copy of the lock at a different memory address will
result in broken and unsafe behavior. For this reason, mutexes are marked as
#no_copy
.
Related Procedures With Parameters
- recursive_mutex_guard
- recursive_mutex_lock
- recursive_mutex_try_lock
- recursive_mutex_unlock
- guard (procedure groups)
- lock (procedure groups)
- try_lock (procedure groups)
- unlock (procedure groups)
Sema ¶
Sema :: struct {
impl: _Sema,
}
Semaphore.
When waited upon, semaphore blocks until the internal count is greater than zero, then decrements the internal counter by one. Posting to the semaphore increases the count by one, or the provided amount.
This type of synchronization primitives can be useful for implementing queues.
The internal counter of the semaphore can be thought of as the amount of items
in the queue. After a data has been pushed to the queue, the thread shall call
sema_post()
procedure, increasing the counter. When a thread takes an item
from the queue to do the job, it shall call sema_wait()
, waiting on the
semaphore counter to become non-zero and decreasing it, if necessary.
Note: A semaphore must not be copied after first use (e.g., after posting
to it). This is because, in order to coordinate with other threads, all threads
must watch the same memory address to know when the lock has been released.
Trying to use a copy of the lock at a different memory address will result in
broken and unsafe behavior. For this reason, semaphores are marked as #no_copy
.
Related Procedures With Parameters
- sema_post
- sema_wait
- sema_wait_with_timeout
- post (procedure groups)
- wait (procedure groups)
- wait_with_timeout (procedure groups)
Ticket_Mutex ¶
Ticket lock.
A ticket lock is a mutual exclusion lock that uses "tickets" to control which thread is allowed into a critical section.
This synchronization primitive works just like spinlock, except that it implements a "fairness" guarantee, making sure that each thread gets a roughly equal amount of entries into the critical section.
This type of synchronization primitive is applicable for short critical sections in low-contention systems, as it uses a spinlock under the hood.
Related Procedures With Parameters
- ticket_mutex_guard
- ticket_mutex_lock
- ticket_mutex_unlock
- guard (procedure groups)
- lock (procedure groups)
- unlock (procedure groups)
Wait_Group ¶
Wait group.
Wait group is a synchronization primitive used by the waiting thread to wait, until all working threads finish work.
The waiting thread first sets the number of working threads it will expect to
wait for using wait_group_add
call, and start waiting using wait_group_wait
call. When worker threads complete their work, each of them will call
wait_group_done
, and after all working threads have called this procedure,
the waiting thread will resume execution.
For the purpose of keeping track whether all working threads have finished their work, the wait group keeps an internal atomic counter. Initially, the waiting thread might set it to a certain non-zero amount. When each working thread completes the work, the internal counter is atomically decremented until it reaches zero. When it reaches zero, the waiting thread is unblocked. The counter is not allowed to become negative.
Note: Just like any synchronization primitives, a wait group cannot be
copied after first use. See documentation for Mutex
or Cond
.
Related Procedures With Parameters
- wait_group_add
- wait_group_done
- wait_group_wait
- wait_group_wait_with_timeout
- wait (procedure groups)
- wait_with_timeout (procedure groups)
Constants
Atomic_RW_Mutex_State_Half_Width ¶
Atomic_RW_Mutex_State_Half_Width :: size_of(Atomic_RW_Mutex_State) * 8 / 2
Atomic_RW_Mutex_State_Is_Writing ¶
Atomic_RW_Mutex_State_Is_Writing :: Atomic_RW_Mutex_State(1)
Atomic_RW_Mutex_State_Reader ¶
Atomic_RW_Mutex_State_Reader :: Atomic_RW_Mutex_State(1) << Atomic_RW_Mutex_State_Half_Width
Atomic_RW_Mutex_State_Reader_Mask ¶
Atomic_RW_Mutex_State_Reader_Mask :: Atomic_RW_Mutex_State(1 << (Atomic_RW_Mutex_State_Half_Width - 1) - 1) << Atomic_RW_Mutex_State_Half_Width
Atomic_RW_Mutex_State_Writer ¶
Atomic_RW_Mutex_State_Writer :: Atomic_RW_Mutex_State(1) << 1
Atomic_RW_Mutex_State_Writer_Mask ¶
Atomic_RW_Mutex_State_Writer_Mask :: Atomic_RW_Mutex_State(1 << (Atomic_RW_Mutex_State_Half_Width - 1) - 1) << 1
Variables
This section is empty.
Procedures
atomic_add ¶
atomic_add :: intrinsics.atomic_add
Atomically add a value to the value stored in memory.
This procedure loads a value from memory, adds the specified value to it, and stores it back as an atomic operation. This operation is an atomic equivalent of the following:
dst^ += val
The memory ordering of this operation is sequentially-consistent.
atomic_add_explicit ¶
atomic_add_explicit :: intrinsics.atomic_add_explicit
Atomically add a value to the value stored in memory.
This procedure loads a value from memory, adds the specified value to it, and stores it back as an atomic operation. This operation is an atomic equivalent of the following:
dst^ += val
The memory ordering of this operation is as specified by the order
parameter.
atomic_and ¶
atomic_and :: intrinsics.atomic_and
Atomically replace the memory location with the result of AND operation with the specified value.
This procedure loads a value from memory, calculates the result of AND operation between the loaded value and the specified value, and stores it back into the same memory location as an atomic operation. This operation is an atomic equivalent of the following:
dst^ &= val
The memory ordering of this operation is sequentially-consistent.
atomic_and_explicit ¶
atomic_and_explicit :: intrinsics.atomic_and_explicit
Atomically replace the memory location with the result of AND operation with the specified value.
This procedure loads a value from memory, calculates the result of AND operation between the loaded value and the specified value, and stores it back into the same memory location as an atomic operation. This operation is an atomic equivalent of the following:
dst^ &= val
The memory ordering of this operation is as specified by the order
parameter.
atomic_compare_exchange_strong ¶
atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong
Atomically compare and exchange the value with a memory location.
This procedure checks if the value pointed to by the dst
parameter is equal
to old
, and if they are, it stores the value new
into the memory location,
all done in a single atomic operation. This procedure returns the old value
stored in a memory location and a boolean value signifying whether old
was
equal to new
.
This procedure is an atomic equivalent of the following operation:
old_dst := dst^ if old_dst == old { dst^ = new return old_dst, true } else { return old_dst, false }
The strong version of compare exchange always returns true, when the returned
old value stored in location pointed to by dst
and the old
parameter are
equal.
Atomic compare exchange has two memory orderings: One is for the read-modify-write operation, if the comparison succeeds, and the other is for the load operation, if the comparison fails. The memory ordering for both of of these operations is sequentially-consistent.
atomic_compare_exchange_strong_explicit ¶
atomic_compare_exchange_strong_explicit :: intrinsics.atomic_compare_exchange_strong_explicit
Atomically compare and exchange the value with a memory location.
This procedure checks if the value pointed to by the dst
parameter is equal
to old
, and if they are, it stores the value new
into the memory location,
all done in a single atomic operation. This procedure returns the old value
stored in a memory location and a boolean value signifying whether old
was
equal to new
.
This procedure is an atomic equivalent of the following operation:
old_dst := dst^ if old_dst == old { dst^ = new return old_dst, true } else { return old_dst, false }
The strong version of compare exchange always returns true, when the returned
old value stored in location pointed to by dst
and the old
parameter are
equal.
Atomic compare exchange has two memory orderings: One is for the
read-modify-write operation, if the comparison succeeds, and the other is for
the load operation, if the comparison fails. The memory ordering for these
operations is as specified by success
and failure
parameters respectively.
atomic_compare_exchange_weak ¶
atomic_compare_exchange_weak :: intrinsics.atomic_compare_exchange_weak
Atomically compare and exchange the value with a memory location.
This procedure checks if the value pointed to by the dst
parameter is equal
to old
, and if they are, it stores the value new
into the memory location,
all done in a single atomic operation. This procedure returns the old value
stored in a memory location and a boolean value signifying whether old
was
equal to new
.
This procedure is an atomic equivalent of the following operation:
old_dst := dst^ if old_dst == old { // may return false here dst^ = new return old_dst, true } else { return old_dst, false }
The weak version of compare exchange may return false, even if dst^ == old
.
On some platforms running weak compare exchange in a loop is faster than a
strong version.
Atomic compare exchange has two memory orderings: One is for the read-modify-write operation, if the comparison succeeds, and the other is for the load operation, if the comparison fails. The memory ordering for both of these operations is sequentially-consistent.
atomic_compare_exchange_weak_explicit ¶
atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_explicit
Atomically compare and exchange the value with a memory location.
This procedure checks if the value pointed to by the dst
parameter is equal
to old
, and if they are, it stores the value new
into the memory location,
all done in a single atomic operation. This procedure returns the old value
stored in a memory location and a boolean value signifying whether old
was
equal to new
.
This procedure is an atomic equivalent of the following operation:
old_dst := dst^ if old_dst == old { // may return false here dst^ = new return old_dst, true } else { return old_dst, false }
The weak version of compare exchange may return false, even if dst^ == old
.
On some platforms running weak compare exchange in a loop is faster than a
strong version.
Atomic compare exchange has two memory orderings: One is for the
read-modify-write operation, if the comparison succeeds, and the other is for
the load operation, if the comparison fails. The memory ordering for these
operations is as specified by the success
and failure
parameters
respectively.
atomic_cond_broadcast ¶
atomic_cond_broadcast :: proc "contextless" (c: ^Atomic_Cond) {…}
atomic_cond_signal ¶
atomic_cond_signal :: proc "contextless" (c: ^Atomic_Cond) {…}
atomic_cond_wait ¶
atomic_cond_wait :: proc "contextless" (c: ^Atomic_Cond, m: ^Atomic_Mutex) {…}
atomic_cond_wait_with_timeout ¶
atomic_cond_wait_with_timeout :: proc "contextless" (c: ^Atomic_Cond, m: ^Atomic_Mutex, duration: time.Duration) -> (ok: bool) {…}
atomic_exchange ¶
atomic_exchange :: intrinsics.atomic_exchange
Atomically exchange the value in a memory location, with the specified value.
This procedure loads a value from the specified memory location, and stores the specified value into that memory location. Then the loaded value is returned, all done in a single atomic operation. This operation is an atomic equivalent of the following:
tmp := dst^ dst^ = val return tmp
The memory ordering of this operation is sequentially-consistent.
atomic_exchange_explicit ¶
atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit
Atomically exchange the value in a memory location, with the specified value.
This procedure loads a value from the specified memory location, and stores the specified value into that memory location. Then the loaded value is returned, all done in a single atomic operation. This operation is an atomic equivalent of the following:
tmp := dst^ dst^ = val return tmp
The memory ordering of this operation is as specified by the order
parameter.
atomic_load ¶
atomic_load :: intrinsics.atomic_load
Atomically load a value from memory.
This procedure loads a value from a memory location in such a way that the received value is not a partial read. The memory ordering of this operation is sequentially-consistent.
atomic_load_explicit ¶
atomic_load_explicit :: intrinsics.atomic_load_explicit
Atomically load a value from memory with explicit memory ordering.
This procedure loads a value from a memory location in such a way that the
received value is not a partial read. The memory ordering of this operation
is as specified by the order
parameter.
atomic_mutex_guard ¶
atomic_mutex_guard :: proc "contextless" (m: ^Atomic_Mutex) -> bool {…}
Example:
if atomic_mutex_guard(&m) {
...
}
atomic_mutex_lock ¶
atomic_mutex_lock :: proc "contextless" (m: ^Atomic_Mutex) {…}
atomic_mutex_lock locks m
atomic_mutex_try_lock ¶
atomic_mutex_try_lock :: proc "contextless" (m: ^Atomic_Mutex) -> bool {…}
atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure
atomic_mutex_unlock ¶
atomic_mutex_unlock :: proc "contextless" (m: ^Atomic_Mutex) {…}
atomic_mutex_unlock unlocks m
atomic_nand ¶
atomic_nand :: intrinsics.atomic_nand
Atomically replace the memory location with the result of NAND operation with the specified value.
This procedure loads a value from memory, calculates the result of NAND operation between the loaded value and the specified value, and stores it back into the same memory location as an atomic operation. This operation is an atomic equivalent of the following:
dst^ = ~(dst^ & val)
The memory ordering of this operation is sequentially-consistent.
atomic_nand_explicit ¶
atomic_nand_explicit :: intrinsics.atomic_nand_explicit
Atomically replace the memory location with the result of NAND operation with the specified value.
This procedure loads a value from memory, calculates the result of NAND operation between the loaded value and the specified value, and stores it back into the same memory location as an atomic operation. This operation is an atomic equivalent of the following:
dst^ = ~(dst^ & val)
The memory ordering of this operation is as specified by the order
parameter.
atomic_or ¶
atomic_or :: intrinsics.atomic_or
Atomically replace the memory location with the result of OR operation with the specified value.
This procedure loads a value from memory, calculates the result of OR operation between the loaded value and the specified value, and stores it back into the same memory location as an atomic operation. This operation is an atomic equivalent of the following:
dst^ |= val
The memory ordering of this operation is sequentially-consistent.
atomic_or_explicit ¶
atomic_or_explicit :: intrinsics.atomic_or_explicit
Atomically replace the memory location with the result of OR operation with the specified value.
This procedure loads a value from memory, calculates the result of OR operation between the loaded value and the specified value, and stores it back into the same memory location as an atomic operation. This operation is an atomic equivalent of the following:
dst^ |= val
The memory ordering of this operation is as specified by the order
parameter.
atomic_recursive_mutex_guard ¶
atomic_recursive_mutex_guard :: proc "contextless" (m: ^Atomic_Recursive_Mutex) -> bool {…}
Example:
if atomic_recursive_mutex_guard(&m) {
...
}
atomic_recursive_mutex_lock ¶
atomic_recursive_mutex_lock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) {…}
atomic_recursive_mutex_try_lock ¶
atomic_recursive_mutex_try_lock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) -> bool {…}
atomic_recursive_mutex_unlock ¶
atomic_recursive_mutex_unlock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) {…}
atomic_rw_mutex_guard ¶
atomic_rw_mutex_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool {…}
Example:
if atomic_rw_mutex_guard(&m) {
...
}
atomic_rw_mutex_lock ¶
atomic_rw_mutex_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {…}
atomic_rw_mutex_lock locks rw for writing (with a single writer) If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
atomic_rw_mutex_shared_guard ¶
atomic_rw_mutex_shared_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool {…}
Example:
if atomic_rw_mutex_shared_guard(&m) {
...
}
atomic_rw_mutex_shared_lock ¶
atomic_rw_mutex_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {…}
atomic_rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
atomic_rw_mutex_shared_unlock ¶
atomic_rw_mutex_shared_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {…}
atomic_rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
atomic_rw_mutex_try_lock ¶
atomic_rw_mutex_try_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {…}
atomic_rw_mutex_try_lock tries to lock rw for writing (with a single writer)
atomic_rw_mutex_try_shared_lock ¶
atomic_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {…}
atomic_rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
atomic_rw_mutex_unlock ¶
atomic_rw_mutex_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {…}
atomic_rw_mutex_unlock unlocks rw for writing (with a single writer)
atomic_sema_post ¶
atomic_sema_post :: proc "contextless" (s: ^Atomic_Sema, count: int = 1) {…}
atomic_sema_wait ¶
atomic_sema_wait :: proc "contextless" (s: ^Atomic_Sema) {…}
atomic_sema_wait_with_timeout ¶
atomic_sema_wait_with_timeout :: proc "contextless" (s: ^Atomic_Sema, duration: time.Duration) -> bool {…}
atomic_signal_fence ¶
atomic_signal_fence :: intrinsics.atomic_signal_fence
Establish memory ordering between a current thread and a signal handler.
This procedure establishes memory ordering between a thread and a signal
handler, that run on the same thread, without an associated atomic operation.
This procedure is equivalent to atomic_thread_fence
, except it doesn't
issue any CPU instructions for memory ordering.
atomic_store ¶
atomic_store :: intrinsics.atomic_store
Atomically store a value into memory.
This procedure stores a value to a memory location in such a way that no other thread is able to see partial reads. This operation is sequentially-consistent.
atomic_store_explicit ¶
atomic_store_explicit :: intrinsics.atomic_store_explicit
Atomically store a value into memory with explicit memory ordering.
This procedure stores a value to a memory location in such a way that no other
thread is able to see partial reads. The memory ordering of this operation is
as specified by the order
parameter.
atomic_sub ¶
atomic_sub :: intrinsics.atomic_sub
Atomically subtract a value from the value stored in memory.
This procedure loads a value from memory, subtracts the specified value from it, and stores the result back as an atomic operation. This operation is an atomic equivalent of the following:
dst^ -= val
The memory ordering of this operation is sequentially-consistent.
atomic_sub_explicit ¶
atomic_sub_explicit :: intrinsics.atomic_sub_explicit
Atomically subtract a value from the value stored in memory.
This procedure loads a value from memory, subtracts the specified value from it, and stores the result back as an atomic operation. This operation is an atomic equivalent of the following:
dst^ -= val
The memory ordering of this operation is as specified by the order
parameter.
atomic_thread_fence ¶
atomic_thread_fence :: intrinsics.atomic_thread_fence
Establish memory ordering.
This procedure establishes memory ordering, without an associated atomic operation.
atomic_xor ¶
atomic_xor :: intrinsics.atomic_xor
Atomically replace the memory location with the result of XOR operation with the specified value.
This procedure loads a value from memory, calculates the result of XOR operation between the loaded value and the specified value, and stores it back into the same memory location as an atomic operation. This operation is an atomic equivalent of the following:
dst^ ~= val
The memory ordering of this operation is sequentially-consistent.
atomic_xor_explicit ¶
atomic_xor_explicit :: intrinsics.atomic_xor_explicit
Atomically replace the memory location with the result of XOR operation with the specified value.
This procedure loads a value from memory, calculates the result of XOR operation between the loaded value and the specified value, and stores it back into the same memory location as an atomic operation. This operation is an atomic equivalent of the following:
dst^ ~= val
The memory ordering of this operation is as specified by the order
parameter.
auto_reset_event_signal ¶
auto_reset_event_signal :: proc "contextless" (e: ^Auto_Reset_Event) {…}
Signal an auto-reset event.
This procedure signals an auto-reset event, waking up exactly one waiting thread.
auto_reset_event_wait ¶
auto_reset_event_wait :: proc "contextless" (e: ^Auto_Reset_Event) {…}
Wait on an auto-reset event.
This procedure blocks the execution of the current thread, until the event is signalled by another thread.
barrier_init ¶
Initialize a barrier.
This procedure initializes the barrier for the specified amount of participant threads.
barrier_wait ¶
Block the current thread until all threads have rendezvoused.
This procedure blocks the execution of the current thread, until all threads
have reached the same point in the execution of the thread proc. Multiple calls
to barrier_wait
are allowed within the thread procedure.
benaphore_guard ¶
Guard the current scope with a lock on a benaphore.
This procedure acquires a lock on a benaphore. The lock is automatically released at the end of callee's scope. If the benaphore was already locked, this procedure also blocks until the lock can be acquired.
When a lock has been acquired, all threads attempting to acquire a lock will be blocked from entering any critical sections associated with the same benaphore, until the lock is released.
This procedure always returns true
. This makes it easy to define a critical
section by putting the function inside the if
statement.
Example:
if benaphore_guard(&m) { ... }
benaphore_lock ¶
benaphore_lock :: proc "contextless" (b: ^Benaphore) {…}
Acquire a lock on a benaphore.
This procedure acquires a lock on the specified benaphore. If the lock on a benaphore is already held, this procedure also blocks the execution of the current thread, until the lock could be acquired.
Once a lock is acquired, all threads attempting to take a lock will be blocked from entering any critical sections associated with the same benaphore, until until the lock is released.
benaphore_try_lock ¶
Try to acquire a lock on a benaphore.
This procedure tries to acquire a lock on the specified benaphore. If it was
already locked, then the returned value is false
, otherwise the lock is
acquired and the procedure returns true
.
If the lock is acquired, all threads that attempt to acquire a lock will be blocked from entering any critical sections associated with the same benaphore, until the lock is released.
benaphore_unlock ¶
benaphore_unlock :: proc "contextless" (b: ^Benaphore) {…}
Release a lock on a benaphore.
This procedure releases a lock on the specified benaphore. If any of the threads are waiting on the lock, exactly one thread is allowed into a critical section associated with the same benaphore.
cond_broadcast ¶
cond_broadcast :: proc "contextless" (c: ^Cond) {…}
Wake up all threads that wait on a condition variable.
This procedure causes all threads waiting on the condition variable to wake up.
cond_signal ¶
cond_signal :: proc "contextless" (c: ^Cond) {…}
Wake up one thread that waits on a condition variable.
This procedure causes exactly one thread waiting on the condition variable to wake up.
cond_wait ¶
Wait until the condition variable is signalled and release the associated mutex.
This procedure blocks the current thread until the specified condition variable is signalled, or until a spurious wakeup occurs. In addition, if the condition has been signalled, this procedure releases the lock on the specified mutex.
The mutex must be held by the calling thread, before calling the procedure.
Note: This procedure can return on a spurious wake-up, even if the condition variable was not signalled by a thread.
cond_wait_with_timeout ¶
cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {…}
Wait until the condition variable is signalled or timeout is reached and release the associated mutex.
This procedure blocks the current thread until the specified condition variable is signalled, a timeout is reached, or until a spurious wakeup occurs. In addition, if the condition has been signalled, this procedure releases the lock on the specified mutex.
If the timeout was reached, this procedure returns false
. Otherwise it returns
true
.
Before this procedure is called the mutex must be held by the calling thread.
cpu_relax ¶
cpu_relax :: intrinsics.cpu_relax
This procedure may lower CPU consumption or yield to a hyperthreaded twin processor. It's exact function is architecture specific, but the intent is to say that you're not doing much on a CPU.
current_thread_id ¶
current_thread_id :: proc "contextless" () -> int {…}
Obtain the current thread ID.
futex_broadcast ¶
futex_broadcast :: proc "contextless" (f: ^Futex) {…}
Wake up multiple threads waiting on a futex.
futex_signal ¶
futex_signal :: proc "contextless" (f: ^Futex) {…}
Wake up a single thread waiting on a futex.
futex_wait ¶
Sleep if the futex contains the expected value until it's signalled.
If the value of the futex is expected
, this procedure blocks the execution of
the current thread, until the futex is woken up, or until a spurious wakeup
occurs.
futex_wait_with_timeout ¶
futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {…}
Sleep if the futex contains the expected value until it's signalled or the timeout is reached.
If the value of the futex is expected
, this procedure blocks the execution of
the current thread, until the futex is signalled, a timeout is reached, or
until a spurious wakeup occurs.
This procedure returns false
if the timeout was reached, true
otherwise.
mutex_guard ¶
Guard the current scope with a lock on a mutex.
This procedure acquires a mutex lock. The lock is automatically released at the end of callee's scope. If the mutex was already locked, this procedure also blocks until the lock can be acquired.
When a lock has been acquired, all threads attempting to acquire a lock will be blocked from entering any critical sections associated with the mutex, until the lock is released.
This procedure always returns true
. This makes it easy to define a critical
section by putting the function inside the if
statement.
Example:
if mutex_guard(&m) { ... }
mutex_lock ¶
mutex_lock :: proc "contextless" (m: ^Mutex) {…}
Acquire a lock on a mutex.
This procedure acquires a lock with the specified mutex. If the mutex has been already locked by any thread, this procedure also blocks until the lock can be acquired.
Once the lock is acquired, all other threads that attempt to acquire a lock will be blocked from entering any critical sections associated with the same mutex, until the the lock is released.
Note: If the mutex is already locked by the current thread, a call to this procedure will block indefinately. Do not use this in recursive procedures.
mutex_try_lock ¶
Try to acquire a lock on a mutex.
This procedure tries to acquire a lock on the specified mutex. If it was already
locked, then the returned value is false
, otherwise the lock is acquired and
the procedure returns true
.
If the lock is acquired, all threads that attempt to acquire a lock will be blocked from entering any critical sections associated with the same mutex, until the lock is released.
mutex_unlock ¶
mutex_unlock :: proc "contextless" (m: ^Mutex) {…}
Release a lock on a mutex.
This procedure releases the lock associated with the specified mutex. If the mutex was not locked, this operation is a no-op.
When the current thread, that holds a lock to the mutex calls mutex_unlock
,
this allows one other thread waiting on the mutex to enter any critical sections
associated with the mutex. If there are no threads waiting on the mutex, the
critical sections will remain open.
once_do_with_data ¶
Call a function with data once.
once_do_with_data_contextless ¶
once_do_with_data_contextless :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) {…}
Call a contextless function with data once.
once_do_without_data ¶
once_do_without_data :: proc(o: ^Once, fn: proc()) {…}
Call a function with no data once.
once_do_without_data_contextless ¶
once_do_without_data_contextless :: proc(o: ^Once, fn: proc "contextless" ()) {…}
Call a contextless function with no data once.
one_shot_event_signal ¶
one_shot_event_signal :: proc "contextless" (e: ^One_Shot_Event) {…}
Make event available.
one_shot_event_wait ¶
one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) {…}
Block until the event is made available.
This procedure blocks the execution of the current thread, until the event is made available.
park ¶
park :: proc "contextless" (p: ^Parker) {…}
Blocks until the token is available.
This procedure blocks the execution of the current thread, until a token is made available.
Note: This procedure assumes this is only called by the thread that owns the Parker.
park_with_timeout ¶
Blocks until the token is available with timeout.
This procedure blocks the execution of the current thread until a token is made available, or until the timeout has expired, whatever happens first.
Note: This procedure assumes this is only called by the thread that owns the Parker.
recursive_benaphore_guard ¶
recursive_benaphore_guard :: proc "contextless" (m: ^Recursive_Benaphore) -> bool {…}
Guard the current scope with a recursive benaphore.
This procedure acquires a lock on the specified recursive benaphores and automatically releases it at the end of the callee's scope. If the recursive benaphore was already held by a another thread, this procedure also blocks until the lock can be acquired.
When the lock is acquired all other threads attempting to take a lock will be blocked from entering any critical sections associated with the same benaphore, until the lock is released.
This procedure always returns true
, which makes it easy to define a critical
section by calling this procedure inside an if
statement.
Example:
if recursive_benaphore_guard(&m) { ... }
recursive_benaphore_lock ¶
recursive_benaphore_lock :: proc "contextless" (b: ^Recursive_Benaphore) {…}
Acquire a lock on a recursive benaphore.
This procedure acquires a lock on a recursive benaphore. If the benaphore is held by another thread, this function blocks until the lock can be acquired.
Once a lock is acquired, all other threads attempting to acquire a lock will be blocked from entering any critical sections associated with the same recursive benaphore, until the lock is released.
recursive_benaphore_try_lock ¶
recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) -> bool {…}
Try to acquire a lock on a recursive benaphore.
This procedure attempts to acquire a lock on recursive benaphore. If the
benaphore is already held by a different thread, this procedure returns false
.
Otherwise the lock is acquired and the procedure returns true
.
If the lock is acquired, all other threads attempting to acquire a lock will be blocked from entering any critical sections assciated with the same recursive benaphore, until the lock is released.
recursive_benaphore_unlock ¶
recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) {…}
Release a lock on a recursive benaphore.
This procedure releases a lock on the specified recursive benaphore. It also causes the critical sections associated with the same benaphore, to become open for other threads for entering.
recursive_mutex_guard ¶
recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool {…}
Guard the scope with a recursive mutex lock.
This procedure acquires a lock on the specified recursive mutex and automatically releases it at the end of the callee's scope. If the recursive mutex was already held by a another thread, this procedure also blocks until the lock can be acquired.
When the lock is acquired all other threads attempting to take a lock will be blocked from entering any critical sections associated with the same mutex, until the lock is released.
This procedure always returns true
, which makes it easy to define a critical
section by calling this procedure inside an if
statement.
Example:
if recursive_mutex_guard(&m) { ... }
recursive_mutex_lock ¶
recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {…}
Acquire a lock on a recursive mutex.
This procedure acquires a lock on the specified recursive mutex. If the lock is acquired by a different thread, this procedure also blocks until the lock can be acquired.
When the lock is acquired, all other threads attempting to acquire a lock will be blocked from entering any critical sections associated with the same mutex, until the lock is released.
recursive_mutex_try_lock ¶
recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {…}
Try to acquire a lock on a recursive mutex.
This procedure attempts to acquire a lock on the specified recursive mutex. If
the recursive mutex is locked by other threads, this procedure returns false
.
Otherwise it locks the mutex and returns true
.
If the lock is acquired, all other threads attempting to obtain a lock will be blocked from entering any critical sections associated with the same mutex, until the lock is released.
recursive_mutex_unlock ¶
recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {…}
Release a lock on a recursive mutex.
This procedure releases a lock on the specified recursive mutex. It also causes the critical sections associated with the same mutex, to become open for other threads for entering.
rw_mutex_guard ¶
Guard the current scope with an exclusive lock on a read-write mutex.
This procedure acquires an exclusive lock on the specified read-write mutex. This procedure automatically releases the lock at the end of the callee's scope. If the mutex was already locked by readers or a writer, this procedure blocks, until a lock can be acquired.
When an exclusive lock is acquired, all other threads attempting to acquire an exclusive lock will be blocked from entering any critical sections associated with the same read-write mutex, until the exclusive lock is released.
This procedure always returns true
, which makes it easy to define a critical
section by running this procedure inside an if
statement.
Example:
if rw_mutex_guard(&m) { ... }
rw_mutex_lock ¶
rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {…}
Acquire an exclusive lock.
This procedure acquires an exclusive lock on the specified read-write mutex. If the lock is already held by any thread, this procedure also blocks until the lock can be acquired.
After a lock has been acquired, any thread attempting to acquire any lock will be blocked from entering any critical sections associated with the same read-write mutex, until the exclusive lock is released.
rw_mutex_shared_guard ¶
Guard the current scope with a shared lock on a read-write mutex.
This procedure acquires a shared lock on the specified read-write mutex. This procedure automatically releases the lock at the end of the callee's scope. If the mutex already has an associated exclusive lock, this procedure blocks, until a lock can be acquired.
When a shared lock is obtained, all other threads attempting to obtain an exclusive lock will be blocked from any critical sections, associated with the same read-write mutex, until all shared locks are released.
This procedure always returns true
, which makes it easy to define a critical
section by running this procedure inside an if
statement.
Example:
if rw_mutex_guard(&m) { ... }
rw_mutex_shared_lock ¶
rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {…}
Acquire a shared lock on a read-write mutex.
This procedure acquires a shared lock on the specified read-write mutex. If the mutex already has an exclusive lock held, this procedure also blocks until the lock can be acquired.
After the shared lock is obtained, all threads attempting to acquire an exclusive lock will be blocked from entering any critical sections associated with the same read-write mutex, until all shared locks associated with the specified read-write mutex are released.
rw_mutex_shared_unlock ¶
rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {…}
Release the shared lock on a read-write mutex.
This procedure releases shared lock on the specified read-write mutex. When all shared locks are released, all critical sections associated with the same read-write mutex become open to other threads.
rw_mutex_try_lock ¶
Try to acquire an exclusive lock on a read-write mutex.
This procedure tries to acquire an exclusive lock on the specified read-write
mutex. If the mutex was already locked, the procedure returns false
. Otherwise
it acquires the exclusive lock and returns true
.
If the lock has been acquired, all threads attempting to acquire any lock will be blocked from entering any critical sections associated with the same read-write mutex, until the exclusive locked is released.
rw_mutex_try_shared_lock ¶
Try to acquire a shared lock on a read-write mutex.
This procedure attempts to acquire a lock on the specified read-write mutex. If
the mutex already has an exclusive lock held, this procedure returns false
.
Otherwise, it acquires the lock on the mutex and returns true
.
If the shared lock has been acquired, it causes all threads attempting to acquire the exclusive lock to be blocked from entering any critical sections associated with the same read-write mutex, until all shared locks are released.
rw_mutex_unlock ¶
rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {…}
Release an exclusive lock.
This procedure releases an exclusive lock associated with the specified read-write mutex.
When the exclusive lock is released, all critical sections, associated with the same read-write mutex, become open to other threads.
sema_post ¶
Increment the internal counter on a semaphore by the specified amount.
This procedure increments the internal counter of the semaphore. If any of the
threads were waiting on the semaphore, up to count
of threads will continue
the execution and enter the critical section.
sema_wait ¶
sema_wait :: proc "contextless" (s: ^Sema) {…}
Wait on a semaphore until the internal counter is non-zero.
This procedure blocks the execution of the current thread, until the semaphore counter is non-zero, and atomically decrements it by one, once the wait has ended.
sema_wait_with_timeout ¶
Wait on a semaphore until the internal counter is non-zero or a timeout is reached.
This procedure blocks the execution of the current thread, until the semaphore
counter is non-zero, and if so atomically decrements it by one, once the wait
has ended. If the specified timeout is reached, the function returns false
,
otherwise it returns true
.
ticket_mutex_guard ¶
ticket_mutex_guard :: proc "contextless" (m: ^Ticket_Mutex) -> bool {…}
Guard the current scope with a lock on a ticket mutex.
This procedure acquires a lock on a ticket mutex. The lock is automatically released at the end of callee's scope. If the mutex was already locked, this procedure also blocks until the lock can be acquired.
When a lock has been acquired, all threads attempting to acquire a lock will be blocked from entering any critical sections associated with the ticket mutex, until the lock is released.
This procedure always returns true
. This makes it easy to define a critical
section by putting the function inside the if
statement.
Example:
if ticket_mutex_guard(&m) { ... }
ticket_mutex_lock ¶
ticket_mutex_lock :: proc "contextless" (m: ^Ticket_Mutex) {…}
Acquire a lock on a ticket mutex.
This procedure acquires a lock on a ticket mutex. If the ticket mutex is held by another thread, this procedure also blocks the execution until the lock can be acquired.
Once the lock is acquired, any thread calling ticket_mutex_lock
will be
blocked from entering any critical sections associated with the same ticket
mutex, until the lock is released.
ticket_mutex_unlock ¶
ticket_mutex_unlock :: proc "contextless" (m: ^Ticket_Mutex) {…}
Release a lock on a ticket mutex.
This procedure releases the lock on a ticket mutex. If any of the threads are waiting to acquire the lock, exactly one of those threads is unblocked and allowed into the critical section.
wait_group_add ¶
wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) {…}
Increment an internal counter of a wait group.
This procedure atomically increments a number to the specified wait group's internal counter by a specified amount. This operation can be done on any thread.
wait_group_done ¶
wait_group_done :: proc "contextless" (wg: ^Wait_Group) {…}
Signal work done by a thread in a wait group.
This procedure decrements the internal counter of the specified wait group and wakes up the waiting thread. Once the internal counter reaches zero, the waiting thread resumes execution.
wait_group_wait ¶
wait_group_wait :: proc "contextless" (wg: ^Wait_Group) {…}
Wait for all worker threads in the wait group.
This procedure blocks the execution of the current thread, until the specified wait group's internal counter reaches zero.
wait_group_wait_with_timeout ¶
wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: time.Duration) -> bool {…}
Wait for all worker threads in the wait group, or until timeout is reached.
This procedure blocks the execution of the current thread, until the specified wait group's internal counter reaches zero, or until the timeout is reached.
This procedure returns false
, if the timeout was reached, true
otherwise.
Procedure Groups
broadcast ¶
broadcast :: proc{ cond_broadcast, atomic_cond_broadcast, futex_broadcast, }
guard ¶
guard :: proc{ mutex_guard, rw_mutex_guard, recursive_mutex_guard, ticket_mutex_guard, benaphore_guard, recursive_benaphore_guard, atomic_mutex_guard, atomic_recursive_mutex_guard, atomic_rw_mutex_guard, }
Example:
if guard(&m) {
...
}
lock ¶
lock :: proc{ mutex_lock, rw_mutex_lock, recursive_mutex_lock, ticket_mutex_lock, benaphore_lock, recursive_benaphore_lock, atomic_mutex_lock, atomic_recursive_mutex_lock, atomic_rw_mutex_lock, }
lock locks m
once_do ¶
once_do :: proc{ once_do_without_data, once_do_without_data_contextless, once_do_with_data, once_do_with_data_contextless, }
Call a function once.
The once_do
procedure group calls a specified function, if it wasn't already
called from the perspective of a specific Once
struct.
post ¶
post :: proc{ sema_post, atomic_sema_post, }
shared_guard ¶
shared_guard :: proc{ rw_mutex_shared_guard, atomic_rw_mutex_shared_guard, }
Example:
if shared_guard(&m) {
...
}
shared_lock ¶
shared_lock :: proc{ rw_mutex_shared_lock, atomic_rw_mutex_shared_lock, }
shared_lock locks rw for reading (with arbitrary number of readers)
shared_unlock ¶
shared_unlock :: proc{ rw_mutex_shared_unlock, atomic_rw_mutex_shared_unlock, }
shared_unlock unlocks rw for reading (with arbitrary number of readers)
signal ¶
signal :: proc{ cond_signal, atomic_cond_signal, futex_signal, }
try_lock ¶
try_lock :: proc{ mutex_try_lock, rw_mutex_try_lock, recursive_mutex_try_lock, benaphore_try_lock, recursive_benaphore_try_lock, atomic_mutex_try_lock, atomic_recursive_mutex_try_lock, atomic_rw_mutex_try_lock, }
try_lock tries to lock m, will return true on success, and false on failure
try_shared_lock ¶
try_shared_lock :: proc{ rw_mutex_try_shared_lock, atomic_rw_mutex_try_shared_lock, }
try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
unlock ¶
unlock :: proc{ mutex_unlock, rw_mutex_unlock, recursive_mutex_unlock, ticket_mutex_unlock, benaphore_unlock, recursive_benaphore_unlock, atomic_mutex_unlock, atomic_recursive_mutex_unlock, atomic_rw_mutex_unlock, }
unlock locks m
wait ¶
wait :: proc{ cond_wait, sema_wait, atomic_cond_wait, atomic_sema_wait, futex_wait, wait_group_wait, }
Source Files
- atomic.odin
- doc.odin
- extended.odin
- primitives.odin
- primitives_atomic.odin
- primitives_internal.odin
- sync_util.odin
- (hidden platform specific files)
Generation Information
Generated with odin version dev-2024-12 (vendor "odin") Windows_amd64 @ 2024-12-02 21:12:12.870885500 +0000 UTC