Shared State and Synchronization

25.1 shared Variables with lock / rlock

shared objects (structs, arrays, maps) must be accessed inside lock/rlock blocks. The compiler enforces this.

Lock multiple objects at once (deadlock-free ordering): lock a, b { ... }.

struct Counter {
mut:
	value int
}

fn increment(shared c Counter) {
	for _ in 0 .. 1000 {
		lock c {               // exclusive write lock
			c.value++
		}
	}
}

fn main() {
	shared c := Counter{}
	mut ths := []thread{}
	for _ in 0 .. 4 {
		ths << spawn increment(shared c)
	}
	ths.wait()
	rlock c {                  // shared read lock
		println(c.value)       // 4000
	}
	val := rlock c { c.value } // lock as an expression
	_ = val
}

25.2 The sync Module

Atomics: import sync.stdatomic (add_i64, load_u64, ...) or atomic int fields: struct S { mut: n atomic int }.

import sync
mut mtx := sync.new_mutex()
mtx.lock()
// critical section
mtx.unlock()
mut rw := sync.new_rwmutex()
rw.rlock()
rw.runlock()
mut once := sync.new_once()
once.do(fn () { println('init once') })
mut sem := sync.new_semaphore_init(3)
sem.wait()
sem.post()

25.3 Concurrency Best Practices

Share memory by communicating (channels). Use shared + lock for state.

Keep lock regions short. Avoid nested locks across different call paths.

Never access a shared object outside lock/rlock. Join all threads before exit.