Mutex (Mutex v4.0.0)

Copy Markdown View Source

This is the main module in this application, it implements a mutex as a GenServer with a notification system to be able to await lock releases.

See README.md for usage instructions.

Summary

Types

A key can be any term.

Identifier for a mutex process.

Functions

Locks a key if it is available, or waits for the key to be freed before attempting again to lock it.

Awaits multiple keys at once. Returns once all the keys have been locked, timeout is :infinity.

Returns a specification to start a mutex under a supervisor.

Sets the process identified by pid as the new owner of the lock.

Tells the mutex to release all the keys owned by the calling process and returns immediately with :ok.

Attempts to lock a resource on the mutex and returns immediately with the result.

Attempts to lock a resource on the mutex and returns immediately with the lock, or raises a Mutex.LockError if the key is already locked.

Releases the given lock synchronously.

Releases the given lock synchronously, raising when the release fails.

Tells the mutex to free the given lock and immediately returns :ok without waiting for the actual release.

Starts a mutex with no process linking. Given options are passed as options for a GenServer, it's a good place to set the name for registering the process.

Starts a mutex linked to the calling process.

Alias for with_lock/3.

Awaits a lock for the given key, executes the given fun and releases the lock immediately.

Awaits a lock for the given keys, executes the given fun and releases the lock immediately.

Types

key()

@type key() :: term()

A key can be any term.

name()

@type name() :: GenServer.server()

Identifier for a mutex process.

Functions

await(mutex, key, timeout \\ 5000)

@spec await(mutex :: name(), key :: key(), timeout :: timeout()) :: Mutex.Lock.t()

Locks a key if it is available, or waits for the key to be freed before attempting again to lock it.

Returns the lock or fails with a timeout.

When the key is released, all awaiting processes compete to lock it again, and one of them, chosen in no particular order, acquires it. The other processes keep waiting.

Because of those repeated attempts, the timeout is a minimum: the function waits at least for the passed amount of milliseconds, but may take slightly longer to give up.

Default timeout is 5000 milliseconds. If the timeout is reached, the caller process exits as in GenServer.call/3. More information in the timeouts section.

Raises a Mutex.LockError when the calling process already owns the key, since waiting for its own key to be released would deadlock.

await_all(mutex, keys)

@spec await_all(mutex :: name(), keys :: [key()]) :: Mutex.Lock.t()

Awaits multiple keys at once. Returns once all the keys have been locked, timeout is :infinity.

If two processes are trying to lock [:user_1, :user_2] and [:user_2, :user_3] at the same time, this function ensures that no deadlock can happen and that one process will eventually lock all the keys.

More information at the end of the deadlocks section.

Keys must be unique, otherwise an ArgumentError is raised. When keys are built dynamically and may collide, deduplicate them first, for instance with Enum.uniq/1.

Raises a Mutex.LockError when the calling process already owns one of the keys. In that case, the keys acquired during the call are released before raising.

child_spec(arg)

Returns a specification to start a mutex under a supervisor.

See the "Child specification" section in the Supervisor module for more detailed information.

give_away(mutex, lock, pid, gift_data \\ nil)

(since 3.0.0)
@spec give_away(mutex :: name(), Mutex.Lock.t(), pid(), gift_data :: term()) :: :ok

Sets the process identified by pid as the new owner of the lock.

If successful, that new owner will be sent a {:"MUTEX-TRANSFER", from_pid, lock, gift_data} message. If it is not alive, the lock will be released.

Raises a Mutex.ReleaseError when the calling process does not own the lock.

This function only supports single key locks.

goodbye(mutex)

@spec goodbye(mutex :: name()) :: :ok

Tells the mutex to release all the keys owned by the calling process and returns immediately with :ok.

lock(mutex, key)

@spec lock(name :: name(), key :: key()) ::
  {:ok, Mutex.Lock.t()} | {:error, Mutex.LockError.t()}

Attempts to lock a resource on the mutex and returns immediately with the result.

When the key is already locked, the returned Mutex.LockError carries the owner pid in its :cause field. This lets callers tell whether they already own the key themselves:

case Mutex.lock(mutex, key) do
  {:ok, lock} ->
    handle_resource(lock)

  {:error, %Mutex.LockError{cause: {:locked, owner}}} when owner == self() ->
    :already_mine

  {:error, %Mutex.LockError{}} ->
    :taken
end

The owner pid is the owner at the time the mutex handled the request. When that pid is self() the information stays accurate, as a lock is only released or given away by its owner.

lock!(mutex, key)

@spec lock!(name :: name(), key :: key()) :: Mutex.Lock.t()

Attempts to lock a resource on the mutex and returns immediately with the lock, or raises a Mutex.LockError if the key is already locked.

release(mutex, lock)

@spec release(mutex :: name(), lock :: Mutex.Lock.t()) ::
  :ok | {:error, Mutex.ReleaseError.t()}

Releases the given lock synchronously.

Returns :ok once the keys of the lock are unlocked, making them available to other processes.

Returns {:error, error} with a Mutex.ReleaseError when a key of the lock is not locked by the calling process in the mutex.

release!(mutex, lock)

@spec release!(mutex :: name(), lock :: Mutex.Lock.t()) :: :ok

Releases the given lock synchronously, raising when the release fails.

Returns :ok once the keys of the lock are unlocked, making them available to other processes.

Raises a Mutex.ReleaseError when a key of the lock is not locked by the calling process in the mutex.

release_async(mutex, lock)

Tells the mutex to free the given lock and immediately returns :ok without waiting for the actual release.

Unlike release/2, this function will not raise if the calling process is not lock owner. In that case, the lock is not released and an error is logged.

Supports only single key locks.

start(opts \\ [])

@spec start(opts :: Keyword.t()) :: GenServer.on_start()

Starts a mutex with no process linking. Given options are passed as options for a GenServer, it's a good place to set the name for registering the process.

See start_link/1 for options.

start_link(opts \\ [])

@spec start_link(opts :: Keyword.t()) :: GenServer.on_start()

Starts a mutex linked to the calling process.

Accepts only t:GenServer.options().

See GenServer options.

under(mutex, key, timeout \\ :infinity, fun)

This function is deprecated. use with_lock/4 instead.

Alias for with_lock/4.

under_all(mutex, key, fun)

This function is deprecated. use with_lock/3 instead.

Alias for with_lock/3.

with_lock(mutex, key, timeout \\ :infinity, fun)

(since 3.0.0)
@spec with_lock(
  mutex :: name(),
  key :: key(),
  timeout :: timeout(),
  fun :: (-> any()) | (Mutex.Lock.t() -> any())
) :: any()

Awaits a lock for the given key, executes the given fun and releases the lock immediately.

If an exception is raised or thrown in the fun, the lock is automatically released.

If a function of arity 1 is given, it will be passed the lock. Otherwise the arity must be 0. You should not manually release the lock within the function.

The lock is awaited as in await/3, with the same timeout and error behaviors.

with_lock_all(mutex, keys, fun)

(since 3.0.0)
@spec with_lock_all(
  mutex :: name(),
  keys :: [key()],
  fun :: (-> any()) | (Mutex.Lock.t() -> any())
) ::
  any()

Awaits a lock for the given keys, executes the given fun and releases the lock immediately.

If an exception is raised or thrown in the fun, the lock is automatically released.

If a function of arity 1 is given, it will be passed the lock. Otherwise the arity must be 0. You should not manually release the lock within the function.

The keys are awaited as in await_all/2, with the same unique keys requirement and error behaviors.