Sync v0.1.0
Installation
Add Sync to the [Dependencies] table of your package's Rux.toml:
[Dependencies]
Sync = { Namespace = "Rux", Version = "0.1.0" }
Standard packages are declared by hand: the registry does not serve them, so rux add Rux/Sync cannot add them.
Dependencies
Core0.1.0FreeBSD0.1.0 (FreeBSD only)Linux0.1.0 (Linux only)macOS0.1.0 (macOS only)Memory0.1.0Time0.1.0Windows0.1.0 (Windows only)
Guide
What it provides
| Module | Covers |
|---|---|
Lock | Lock, a bare lock, and Once, a once-only initialization; both can be module variables |
Mutex | Mutex<T> with its MutexGuard<T>, and Condition to wait on while holding one |
RwLock | RwLock<T> with ReadGuard<T> and WriteGuard<T>: many readers or one writer |
Counters | Semaphore, Barrier and WaitGroup |
Channel | Channel<T>, a bounded queue any number of threads send to and receive from |
The module names describe where each declaration comes from; they are not part of an import path.
Example
import Sync::Mutex;
import Thread::{ JoinHandle, Spawn };
func AddOne(total: Mutex<uint64>) -> () {
let guard = total.Lock();
*guard.Get() = *guard.Get() + 1u64;
}
func Main() -> int {
let total = Mutex<uint64>(0u64);
var first = Spawn(AddOne, total.Clone()) catch { else => return 1 };
var second = Spawn(AddOne, total.Clone()) catch { else => return 1 };
first.Join();
second.Join();
let guard = total.Lock();
return *guard.Get() == 2u64 ? 0 : 2;
}
Design
Every type here waits the same way: a thread sleeps on a 32-bit word only while the word still holds the value it last saw, and the system checks that as it puts the thread to sleep, so a change that lands just before the sleep is never missed. That is futex on Linux, _umtx_op on FreeBSD, __ulock_wait on macOS and WaitOnAddress on Windows. Taking a free lock and releasing one nobody waits for are each one atomic instruction; only contention costs a system call.
Lock and Once live where they are declared and need no destroying, so a module variable can be one, starting as Lock::Unlocked or Once::Incomplete, and every thread names it directly. The other types are handles to storage their clones share: Clone gives another handle to move to another thread, and the storage, with the value a Mutex<T>, RwLock<T> or Channel<T> holds, is destroyed when the last handle and the last guard are gone. A guard is the only way to reach a guarded value, and it releases its lock when it is dropped. The value's address a guard hands out is valid only while the guard holds the lock.
None of the locks is reentrant, and none queues its waiters in order: RwLock can keep a writer waiting behind a steady stream of readers. A Condition wait can return without a notification, so it is always written in a loop that checks the condition. Running out of memory while creating one of these objects is a program error, as is misuse the type cannot recover from, such as marking a WaitGroup done more times than work was added.
Index
Types
| Name | Summary |
|---|---|
Channel<T> | A bounded queue of values passed between threads, any number of which may send and receive. |
Semaphore | A count of permits that threads take and give back, waiting while none is left. |
Barrier | A meeting point a fixed number of threads wait at until all of them have arrived, then pass together. |
WaitGroup | A count of unfinished work that threads add to and mark done, and that others wait to see reach zero. |
Lock | A lock that guards nothing in particular: whatever the code between Acquire and Release touches. |
Once | Runs one initialization once, however many threads ask for it at once. |
Mutex<T> | A value guarded by a lock, shared between the threads that hold a handle to it. |
MutexGuard<T> | The lock on a Mutex<T>, held for as long as the guard lives, and the way to reach the value. |
Condition | A condition threads wait on while holding a Mutex<T>, until another thread says it may hold. |
RwLock<T> | A value guarded by a lock that many readers or one writer may hold, shared between the threads that hold a handle. |
ReadGuard<T> | A shared hold on an RwLock<T>: the value may be read, and other readers may hold it too. |
WriteGuard<T> | The exclusive hold on an RwLock<T>: the value may be read and written, and nobody else holds the lock. |