4.84. kink/thread/MUTEX

Provides non-reentrant mutex locks.

4.84.1. type mutex

A mutex is a non-reentrant mutual exclusion lock. A mutex can also be used as a conditional variable on itself.

For a mutex, at the most one thread can hold the mutex.

If a thread is holding the mutex, the thread cannot acquire the mutex again. Namely, it is not reentrant.

Example: a simple semaphore:

:MUTEX.require_from('kink/thread/')

:new_semaphore <- {(:Capa)
  :V = new_val('Count' 0)
  :Mut = MUTEX.new
  new_val(
    'acquire' {
      Mut.with_lock{
        Mut.wait_until{ V.Count < Capa }
        V:Count <- V.Count + 1
      }
    }
    'release' {
      Mut.with_lock{
        V:Count <- V.Count - 1
        Mut.notify_all
      }
    }
  )
}

§§ Design note: Why non-reentrant?

A mutex is designed to be non-reentrant, because a non-reentrant mutex:

• Can detect imbalanced lock-unlock correspondence earlier, and

• Makes a programmer more careful about a critical section boundary

4.84.1.1. Mutex.lock

Mutex.lock acquires the Mutex.

Precondition:

• The current thread must NOT hold the mutex

If the Mutex is held by another thread, the current thread blocks until it is available.

4.84.1.2. Mutex.unlock

Mutex.unlock releases the mutex.

Precondition:

• The current thread must hold the Mutex

4.84.1.3. Mutex.with_lock($thunk)

Mutex.with_lock calls $thunk holding the Mutex. This is a convenient wrapper method for .lock and .unlock.

Precondition:

• The current thread must NOT hold the lock

Mutex.with_lock acquires the Mutex before invoking $thunk, and releases the Mutex after $thunk returns a val, raising an exception, or escaping (see kink/CONTROL.escape).

4.84.1.4. Mutex.wait(...[Timeout_seconds = plus infinity])

`wait` blocks the thread until waken up.

Preconditions:

• The current thread must hold `Mutex`

• If `Timeout_seconds` is specified, it must be a non-negative num

Calling .wait method releases `Mutex` until it gets waken up. When the thread gets waken up, the thread acquires `Mutex` again.

The thread gets waken when:

• Another thread calls .notify_all method for `Mutex`,

• `Timeout_seconds` passes, or

• “Spurious wakeup” happens; namely without any trigger within your program

Because of existence of “spurious wakeup,” you should always test the status which is waited for.

See POSIX specification for “spurious wakeup.”

http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html

4.84.1.5. Mutex.wait_until($ok_thunk?)

Mutex.wait_until blocks the thread until waken up, and $ok_thunk? returns true.

This is a convenient wrapper method for .wait.

Precondition:

• The current thread must hold a lease of the lock.

Note that $ok_thunk? is called in the current thread while holding the Mutex.

4.84.1.6. Mutex.notify_all

Mutex.notify_all makes all the threads which is waiting for the Mutex via .wait method waken up when the Mutex is available.

Precondition:

• The current thread must hold a lease of the lock.

4.84.2. MUTEX.is?(Val)

MUTEX.is? returns whether the Val is a mutex.

4.84.3. MUTEX.new

MUTEX.new makes a new mutex.