5.3 Locks
(require rebellion/concurrency/lock) | package: rebellion |
A lock, also called a mutex, is a synchronization primitive that protects access to a resource shared between threads. To protect a value with a lock, require that threads first acquire the lock before interacting with the value and release the lock afterward. Locks are inherently not kill safe, as a thread that dies while holding a lock will never release it.
Typically, threads cannot acquire locks twice in a row: a thread must release an acquired lock before attempting to acquire it again. However, reentrant locks can be acquired multiple times by the same thread. Reentrant locks allow code to acquire a lock before calling other functions that acquire the same lock.
Strongly prefer this form of locking over calling lock-acquire! and lock-release! manually, as it ensures that the acquired lock is always released.
(define counter 0) (define counter-lock (make-lock)) (define (get-and-inc!) (lock! counter-lock (λ () (define previous counter) (set! counter (add1 previous)) previous)))
> (get-and-inc!) 0
> (get-and-inc!) 1
> counter 2
procedure
(lock-acquire! lock) → void?
lock : lock?
procedure
(lock-release! lock) → void?
lock : lock?
5.3.1 A Warning About Deadlocks
Acquiring a lock without releasing it is dangerous and can lead to deadlocks. For this reason, strongly prefer calling lock! instead of lock-acquire! and lock-release!. The lock! function automatically ensures that acquired locks are always released. If you must use lock-acquire! and lock-release!, consider using dynamic-wind and a continuation barrier to ensure that the lock is acquired and released correctly.
(define counter 0) (define counter-lock (make-lock)) (define (get-and-update! updater) (lock-acquire! counter-lock) (dynamic-wind void (λ () (call-with-continuation-barrier (λ () (define previous counter) (set! counter (updater previous)) previous))) (λ () (lock-release! counter-lock))))
> (get-and-update! add1) 0
> (get-and-update! add1) 1
> counter 2
5.3.2 Read-Write Locks
A read-write lock is a pair of locks, one for read-only access to a resource and one for write access to a resource. The read lock can be held by multiple threads concurrently so long as nobody is writing. The write lock can only be held by one thread at a time.
procedure
(read-write-lock? v) → boolean?
v : any/c
procedure
(make-read-write-lock [#:reentrant? reentrant?])
→ read-write-lock? reentrant? : boolean? = #true
procedure
(read-write-lock-read-lock rw-lock) → lock?
rw-lock : read-write-lock?
procedure
(read-write-lock-write-lock rw-lock) → lock?
rw-lock : read-write-lock?