struct

Syn::Core::Mutex

Inherits Syn::Core::Lockable / Struct / Value / Object

A Fiber aware, mutually exclusive lock.

Prevents two or more fibers to access the same non concurrent piece of code (e.g. mutating a shared object) at the same time.

There are three types of mutexes.

  • Checked mutexes memorize the fiber that locked the mutex; trying to lock the mutex twice from the same fiber will raise an exception; trying to unlock from another mutex will also raise an exception.

    Checked mutexes are the default type, and the only safe type of mutex.

  • Unchecked mutexes don't check for errors; trying to lock twice from the same fiber will deadlock; any fiber is allowed to unlock the mutex.

    If the mutex is to protect a shared data structure, and its usage is restricted to an object's internals, you may consider unchecked mutexes for a limited performance gain.

  • Reentrant mutexes also memorize the fiber that locked the mutex; the same fiber is allowed to re-lock the mutex up to 255 times (afterwards an exception will be raised). Only the fiber that locked the mutex is allowed to unlock the mutex, but it must unlock it has many times as it previously locked it to actually unlock it.

    There are probably valid use cases for reentrant mutexes, but ideally you shouldn't never have to use them (there are better synchronization concepts for complex use cases). Yet, they might be useful temporary as a quick workaround to deadlocks?

This is a smaller and fully contained alternative to the ::Mutex class in stdlib that doesn't allocate extraneous objects. It also exposes the quick #try_lock? that won't block.

TODO: check whether the owning fiber is still alive or not (i.e. EOWNERDEAD)

Constructors

new(type : Type = :checked)
Source

Instance methods

lock(timeout : Time::Span) : Bool

Identical to #lock but aborts if the lock couldn't be acquired until timeout is reached, in which case it returns false (failed to acquire lock). Returns true if the lock was acquired.

Source
lock

Acquires the lock, suspending the current fiber until the lock can be acquired.

If the mutex is unchecked, trying to re-lock while the current fiber is already holding the lock will result in a deadlock. If checked it will raise an Error. If reentrant, the counter will be incremented and the method will return.

Source
synchronize(timeout : Time::Span, &) : Bool

Identical to #synchronize but aborts if the lock couldn't be acquired until timeout is reached, in which case it returns false.

NOTE: unlike #synchronize it doesn't return the block's value!

Source
synchronize

Acquires the lock, yields, then releases the lock, even if the block raised an exception.

Source
try_lock?

Returns true if the lock could be acquired, otherwise immediately returns false without blocking.

Merely returns false whenever the lock if already held. Doesn't check if the current fiber currently holds the lock, doesn't raise and also doesn't increment the counter for reentrant mutexes (unless it's the initial lock).

Source
unlock

Releases the lock.

If unchecked, any fiber can unlock the mutex and the mutex doesn't even need to be locked.

If checked or reentrant, the mutex must have been locked and only the fiber holding the lock is allowed otherwise Error exceptions will be raised. If reentrant the counter will be decremented and the lock only released when the counter reaches zero (i.e. you must call #unlock as many times as #lock was called.

Source

Nested types