Standard library · Time and concurrency

sync

Mutexes, condition variables, wait groups, read-write locks, semaphores and once, for coordinating threads.

import sync
On this page
  1. Types
  2. Functions

When several threads change the same variable they have to take turns, or the result depends on the order the threads happen to run in. A mutex is the simplest answer: each thread takes the lock before touching the data and releases it afterwards.

A wait group is for waiting until several pieces of work are done: call Add before starting them, Done at the end of each, and Wait on the main thread. Every tool in this package is created with a New... function and released with a Destroy... one.

Types

Mutex

struct Mutex
A mutex; only one thread holds it at a time.
NameTypeDescription
handlevoid*The operating system's lock.
sync-Mutex.salam
import sync

mut balance := 0
mut lock := sync.Mutex {}
mut group := sync.WaitGroup {}

func deposit():
    repeat 500:
        sync.Lock(lock)
        balance += 1
        sync.Unlock(lock)
    end
    sync.Done(group)
end

func main:
    lock = sync.NewMutex()
    group = sync.NewWaitGroup()
    sync.Wait(group)
    sync.Add(group, 4)
    a := spawn(deposit)
    b := spawn(deposit)
    c := spawn(deposit)
    d := spawn(deposit)
    sync.Wait(group)
    join(a)
    join(b)
    join(c)
    join(d)
    println "balance:", balance
    sync.DestroyWaitGroup(group)
    sync.Destroy(lock)
end
Outputbalance: 2000

CondVar

struct CondVar
A condition variable: threads sleep on it until another thread wakes them.
NameTypeDescription
handlevoid*The operating system's condition variable.
sync-CondVar.salam
import sync

mut ready := false
mut lock := sync.Mutex {}
mut cond := sync.CondVar {}

func producer():
    sync.SleepMs(20)
    sync.Lock(lock)
    ready = true
    sync.Broadcast(cond)
    sync.Unlock(lock)
end

func main:
    lock = sync.NewMutex()
    cond = sync.NewCondVar()
    start := sync.NowMs()
    sync.Lock(lock)
    println sync.WaitCondTimeout(cond, lock, 10)
    t := spawn(producer)
    until not ready:
        sync.WaitCond(cond, lock)
    end
    sync.Unlock(lock)
    join(t)
    println ready, sync.NowMs() - start >= 20
    sync.Signal(cond)
    sync.DestroyCondVar(cond)
    sync.Destroy(lock)
end
Outputtrue true true

WaitGroup

struct WaitGroup
A wait group: a count of unfinished work.
NameTypeDescription
counteri64*The number of unfinished pieces of work.
muvoid*The internal lock.
cvvoid*The internal condition variable.

The example for this item is shown with the ones above: sync-Mutex.salam

RWMutex

struct RWMutex
A read-write lock: several readers together, or one writer alone.
NameTypeDescription
statei64*The internal state: the readers, the writer and the waiting writers.
muvoid*The internal lock.
cvvoid*The internal condition variable.
sync-RWMutex.salam
import sync

func main:
    rw := sync.NewRWMutex()
    sync.RLock(rw)
    sync.RLock(rw)
    println "two readers together"
    sync.RUnlock(rw)
    sync.RUnlock(rw)
    sync.WLock(rw)
    println "one writer alone"
    sync.WUnlock(rw)
    sync.DestroyRWMutex(rw)

    sem := sync.NewSemaphore(2)
    sync.Acquire(sem)
    println sync.TryAcquire(sem), sync.TryAcquire(sem)
    sync.Release(sem)
    println sync.TryAcquire(sem)
    sync.DestroySemaphore(sem)

    once := sync.NewOnce()
    repeat 3:
        sync.Do(once, (): println "only once" end)
    end
    sync.DestroyOnce(once)
end
Outputtwo readers together one writer alone true false true only once

Semaphore

struct Semaphore
A semaphore: a limited number of permits that threads take and give back.
NameTypeDescription
permitsi64*The number of free permits.
muvoid*The internal lock.
cvvoid*The internal condition variable.

The example for this item is shown with the ones above: sync-RWMutex.salam

Once

struct Once
A "once": it runs a function only the first time.
NameTypeDescription
donei64*Whether the function has run.
muvoid*The internal lock.

The example for this item is shown with the ones above: sync-RWMutex.salam

Functions

NewMutex

func NewMutex(): Mutex
A new mutex.

The example for this item is shown with the ones above: sync-Mutex.salam

Lock

func Lock(m: Mutex): int
Takes the lock, waiting when another thread holds it.

The example for this item is shown with the ones above: sync-Mutex.salam

Unlock

func Unlock(m: Mutex): int
Releases the lock.

The example for this item is shown with the ones above: sync-Mutex.salam

Destroy

func Destroy(m: Mutex)
Releases the mutex.

The example for this item is shown with the ones above: sync-Mutex.salam

SleepMs

func SleepMs(ms: int)
Puts the current thread to sleep for a number of milliseconds.

The example for this item is shown with the ones above: sync-CondVar.salam

NewCondVar

func NewCondVar(): CondVar
A new condition variable.

The example for this item is shown with the ones above: sync-CondVar.salam

WaitCond

func WaitCond(cv: CondVar, m: Mutex)
Releases the lock and sleeps until it is signalled; the lock is held again on waking. Always recheck the condition in a loop.

The example for this item is shown with the ones above: sync-CondVar.salam

WaitCondTimeout

func WaitCondTimeout(cv: CondVar, m: Mutex, ms: i64): bool
Like WaitCond, but for at most ms milliseconds. It returns false when the time runs out.

The example for this item is shown with the ones above: sync-CondVar.salam

NowMs

func NowMs(): i64
The current time in milliseconds, for working out deadlines.

The example for this item is shown with the ones above: sync-CondVar.salam

Signal

func Signal(cv: CondVar)
Wakes one of the waiting threads.

The example for this item is shown with the ones above: sync-CondVar.salam

Broadcast

func Broadcast(cv: CondVar)
Wakes every waiting thread.

The example for this item is shown with the ones above: sync-CondVar.salam

DestroyCondVar

func DestroyCondVar(cv: CondVar)
Releases the condition variable.

The example for this item is shown with the ones above: sync-CondVar.salam

NewWaitGroup

func NewWaitGroup(): WaitGroup
A new wait group with a count of zero.

The example for this item is shown with the ones above: sync-Mutex.salam

Add

func Add(wg: WaitGroup, delta: i64)
Increases the count of work by delta.

The example for this item is shown with the ones above: sync-Mutex.salam

Done

func Done(wg: WaitGroup)
Reports one piece of work as finished.

The example for this item is shown with the ones above: sync-Mutex.salam

Wait

func Wait(wg: WaitGroup)
Waits until the count reaches zero; if it is already zero it returns at once.

The example for this item is shown with the ones above: sync-Mutex.salam

DestroyWaitGroup

func DestroyWaitGroup(wg: WaitGroup)
Releases the wait group.

The example for this item is shown with the ones above: sync-Mutex.salam

NewRWMutex

func NewRWMutex(): RWMutex
A new read-write lock.

The example for this item is shown with the ones above: sync-RWMutex.salam

RLock

func RLock(rw: RWMutex)
Takes the lock for reading, waiting while a writer is working or waiting.

The example for this item is shown with the ones above: sync-RWMutex.salam

RUnlock

func RUnlock(rw: RWMutex)
Releases a read lock.

The example for this item is shown with the ones above: sync-RWMutex.salam

WLock

func WLock(rw: RWMutex)
Takes the lock for writing and waits for every reader to leave. A waiting writer comes before new readers.

The example for this item is shown with the ones above: sync-RWMutex.salam

WUnlock

func WUnlock(rw: RWMutex)
Releases the write lock.

The example for this item is shown with the ones above: sync-RWMutex.salam

DestroyRWMutex

func DestroyRWMutex(rw: RWMutex)
Releases the lock.

The example for this item is shown with the ones above: sync-RWMutex.salam

NewSemaphore

func NewSemaphore(permits: i64): Semaphore
A semaphore with the given number of permits.

The example for this item is shown with the ones above: sync-RWMutex.salam

Acquire

func Acquire(sem: Semaphore)
Takes one permit, waiting when none is left.

The example for this item is shown with the ones above: sync-RWMutex.salam

TryAcquire

func TryAcquire(sem: Semaphore): bool
Takes a permit if one is free and returns true; it never waits.

The example for this item is shown with the ones above: sync-RWMutex.salam

Release

func Release(sem: Semaphore)
Gives one permit back and wakes one waiting thread.

The example for this item is shown with the ones above: sync-RWMutex.salam

DestroySemaphore

func DestroySemaphore(sem: Semaphore)
Releases the semaphore.

The example for this item is shown with the ones above: sync-RWMutex.salam

NewOnce

func NewOnce(): Once
A new "once".

The example for this item is shown with the ones above: sync-RWMutex.salam

Do

func Do(o: Once, f: func())
Runs the function only if this is the first time; other threads wait until it finishes.

The example for this item is shown with the ones above: sync-RWMutex.salam

DestroyOnce

func DestroyOnce(o: Once)
Releases the "once".

The example for this item is shown with the ones above: sync-RWMutex.salam