Packages

A wrapper for Erlang's :global module with documentation.

Current section

Files

Jump to
global lib global.ex
Raw

lib/global.ex

defmodule Global do
# Simple delegations
@doc """
Deletes the lock `id` synchronously.
"""
@spec del_lock(:global.id) :: true
defdelegate del_lock(id), to: :global
@doc """
Deletes the lock `id` synchronously.
"""
@spec del_lock(:global.id, [atom]) :: true
defdelegate del_lock(id, nodes), to: :global
@doc """
This function can be used as a name resolving function for `register/3` and `re_register/3`.
It unregisters both pids, and sends the message `{:global_name_conflict, name, other_pid}`
to both processes.
"""
@spec notify_all(atom, pid, pid) :: pid
defdelegate notify_all(name, pid1, pid2), to: :global, as: :notify_all_name
@doc """
This function can be used as a name resolving function for `register/3` and `re_register/3`.
It randomly chooses one of the pids for registration and kills the other one.
"""
@spec random_exit(atom, pid, pid) :: pid
defdelegate random_exit(name, pid1, pid2), to: :global, as: :random_exit_name
@doc """
This function can be used as a name resolving function for `register/3` and
`re_register/3`. It randomly chooses one of the pids for registration, and sends
the message `{:global_name_conflict, name}` to the other pid.
"""
@spec random_notify(atom, pid, pid) :: pid
defdelegate random_notify(name, pid1, pid2), to: :global, as: :random_notify_name
@doc """
Returns a lists of all globally registered names.
"""
@spec registered() :: [atom]
# Provide same API as `Process`
defdelegate registered(), to: :global, as: :registered_names
@doc """
Sends the message `msg` to the pid globally registered as `name`.
Failure: If `name` is not a globally registered name, the calling function will
exit with reason `{:badarg, {name, msg}}`.
"""
@spec send(any, any) :: pid
defdelegate send(name, msg), to: :global
@doc """
Sets a lock on the specified nodes (or on all nodes if none are specified) on
`resource_id` for `lock_requester_id` (see `:global.id` typespec). If a lock
already exists on `resource_id` for another requester than `lock_requester_id`,
and `retries` is not equal to 0, the process sleeps for a while and will try to
execute the action later. When `retries` attempts have been made, `false` is
returned, otherwise `true`. If `retries` is `:infinity`, `true` is eventually
returned (unless the lock is never released).
If no value for `retries` is given, `:infinity` is used.
This function is completely synchronous.
If a process which holds a lock dies, or the node goes down, the locks held by
the process are deleted.
The global name server keeps track of all processes sharing the same lock, that is,
if two processes set the same lock, both processes must delete the lock.
This function does not address the problem of a deadlock. A deadlock can never
occur as long as processes only lock one resource at a time. But if some processes
try to lock two or more resources, a deadlock may occur. It is up to the application
to detect and rectify a deadlock.
## NOTE
Some values of `resource_id` should be avoided or Erlang/OTP will not work properly.
A list of resources to avoid: `:global`, `:dist_ac`, `:mnesia_table_lock`, `:mnesia_adjust_log_writes`,
`:pg2`.
"""
@spec set_lock(:global.id) :: true | false
defdelegate set_lock(id), to: :global
@doc """
Sets a lock on the specified nodes (or on all nodes if none are specified) on
`resource_id` for `lock_requester_id` (see `:global.id` typespec). If a lock
already exists on `resource_id` for another requester than `lock_requester_id`,
and `retries` is not equal to 0, the process sleeps for a while and will try to
execute the action later. When `retries` attempts have been made, `false` is
returned, otherwise `true`. If `retries` is `:infinity`, `true` is eventually
returned (unless the lock is never released).
If no value for `retries` is given, `:infinity` is used.
This function is completely synchronous.
If a process which holds a lock dies, or the node goes down, the locks held by
the process are deleted.
The global name server keeps track of all processes sharing the same lock, that is,
if two processes set the same lock, both processes must delete the lock.
This function does not address the problem of a deadlock. A deadlock can never
occur as long as processes only lock one resource at a time. But if some processes
try to lock two or more resources, a deadlock may occur. It is up to the application
to detect and rectify a deadlock.
## NOTE
Some values of `resource_id` should be avoided or Erlang/OTP will not work properly.
A list of resources to avoid: `:global`, `:dist_ac`, `:mnesia_table_lock`, `:mnesia_adjust_log_writes`,
`:pg2`.
"""
@spec set_lock(:global.id, [atom]) :: true | false
defdelegate set_lock(id, nodes), to: :global
@doc """
Sets a lock on the specified nodes (or on all nodes if none are specified) on
`resource_id` for `lock_requester_id` (see `:global.id` typespec). If a lock
already exists on `resource_id` for another requester than `lock_requester_id`,
and `retries` is not equal to 0, the process sleeps for a while and will try to
execute the action later. When `retries` attempts have been made, `false` is
returned, otherwise `true`. If `retries` is `:infinity`, `true` is eventually
returned (unless the lock is never released).
If no value for `retries` is given, `:infinity` is used.
This function is completely synchronous.
If a process which holds a lock dies, or the node goes down, the locks held by
the process are deleted.
The global name server keeps track of all processes sharing the same lock, that is,
if two processes set the same lock, both processes must delete the lock.
This function does not address the problem of a deadlock. A deadlock can never
occur as long as processes only lock one resource at a time. But if some processes
try to lock two or more resources, a deadlock may occur. It is up to the application
to detect and rectify a deadlock.
## NOTE
Some values of `resource_id` should be avoided or Erlang/OTP will not work properly.
A list of resources to avoid: `:global`, `:dist_ac`, `:mnesia_table_lock`, `:mnesia_adjust_log_writes`,
`:pg2`.
"""
@spec set_lock(:global.id, [atom], integer | :infinity) :: true | false
defdelegate set_lock(id, nodes, retries), to: :global
@doc """
Synchronizes the global name server with all nodes known to this node. These are
the nodes which are returned from `:erlang.nodes()`. When this function returns,
the global name server will receive global information from all nodes. This function
can be called when new nodes are added to the network.
"""
@spec sync() :: :ok | {:error, any}
defdelegate sync(), to: :global
@doc """
Removes the globally registered name `name` from the network of Erlang nodes.
"""
@spec unregister(atom) :: :ok
defdelegate unregister(name), to: :global, as: :unregister_name
# API changes
@doc """
Sets a lock on `id` (using `set_lock/3`). If this succeeds, `fun.()` is evaluated
and the result `res` is returned. Returns `:aborted` if the lock attempt failed.
If Retries is set to `:infinity`, the transaction will not abort.
`:infinity` is the default setting and will be used if no value is given for `retries`.
"""
@spec trans(:global.id, [atom], integer | :infinity) :: any | :aborted
def trans(id, nodes \\ Node.list, retries \\ :infinity, fun) do
:global.trans(id, fun, nodes, retries)
end
@doc """
Atomically changes the registered name `name` on all nodes to refer to `pid`.
The `resolve` function has the same behavior as in `register_name/2,3`.
"""
# Provide same API as `Process.register`
@spec re_register(pid, atom, ((atom, pid, pid) -> pid)) :: :yes | :no
def re_register(pid, name, resolve \\ &__MODULE__.random_exit/3) do
:global.re_register_name(name, pid, resolve)
end
@doc """
Globally associates the name `name` with a pid, that is, Globally notifies all
nodes of a new global name in a network of Erlang nodes.
When new nodes are added to the network, they are informed of the globally registered
names that already exist. The network is also informed of any global names in
newly connected nodes. If any name clashes are discovered, the `resolve` function
is called. Its purpose is to decide which pid is correct. If the function crashes,
or returns anything other than one of the pids, the name is unregistered. This
function is called once for each name clash.
### WARNING
*If you plan to change code without restarting your system, you must use an external
function (&Module.function/arity from anoter module) as the resolve function; if
you use a local function you can never replace the code for the module that the
function belongs to.*
There are three pre-defined resolve functions: `random_exit/3`, `random_notify/3`,
and `notify_all/3`. If no `resolve` function is defined, `random_exit/3` is
used. This means that one of the two registered processes will be selected as
correct while the other is killed.
This function is completely synchronous. This means that when this function returns,
the name is either registered on all nodes or none.
The function returns `:yes` if successful, `:no` if it fails. For example, `:no`
is returned if an attempt is made to register an already registered process or
to register a process with a name that is already in use.
If a process with a registered name dies, or the node goes down, the name is
unregistered on all nodes.
"""
# Provide same API as `Process`
@spec register(pid, atom, ((atom, pid, pid) -> pid)) :: :yes | :no
def register(pid, name, resolve \\ &__MODULE__.random_exit/3) do
:global.register_name(name, pid, resolve)
end
@doc """
Returns the pid with the globally registered name `name`. Returns `nil` if the
name is not globally registered.
"""
# Provide same API as `Process`
@spec whereis(atom) :: pid | port | nil
def whereis(name) do
case :global.whereis_name(name) do
pid when is_pid(pid) -> pid
:undefined -> nil
end
end
end