On this page:
lock?
lock!
lock-acquire!
lock-release!
make-lock
5.3.1 A Warning About Deadlocks
5.3.2 Read-Write Locks
read-write-lock?
make-read-write-lock
read-write-lock-read-lock
read-write-lock-write-lock
8.12

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.

procedure

(lock? v)  boolean?

  v : any/c
A predicate for locks.

procedure

(lock! lock thunk)  any

  lock : lock?
  thunk : (-> any)
Acquires lock, calls thunk, then releases lock when control exits thunk and returns the results of calling thunk. A continuation barrier is installed around the call to thunk, meaning that while control may leave thunk abnormally (due to an exception, escape continuation, or other control operation) it cannot enter thunk abnormally.

Strongly prefer this form of locking over calling lock-acquire! and lock-release! manually, as it ensures that the acquired lock is always released.

Examples:
(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?
Acquires lock, blocking until lock is available. Call lock-release! afterward to ensure that lock is made available to other threads. Beware that every call to lock-acquire! must precede a call to lock-release!, see A Warning About Deadlocks.

procedure

(lock-release! lock)  void?

  lock : lock?
Releases lock, which the current thread must hold. Beware that every call to lock-release! must follow a call to lock-acquire!, see A Warning About Deadlocks.

procedure

(make-lock [#:reentrant? reentrant?])  lock?

  reentrant? : boolean? = #true
Constructs a new lock. If reentrant? is true (the default) then the lock is reentrant.

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.

Examples:
(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
A predicate for read-write locks.

procedure

(make-read-write-lock [#:reentrant? reentrant?])

  read-write-lock?
  reentrant? : boolean? = #true
Constructs a new read-write lock. If reentrant? is true (the default) then both the read and write locks are reentrant.

procedure

(read-write-lock-read-lock rw-lock)  lock?

  rw-lock : read-write-lock?
Returns the read lock of rw-lock.

procedure

(read-write-lock-write-lock rw-lock)  lock?

  rw-lock : read-write-lock?
Returns the write lock of rw-lock.