Rebus.Bus (rebus v0.4.0)

View Source

Requests and releases well-known bus names, such as "org.example.Calculator".

A connection to a message bus is known by its unique name, such as ":1.42", for its whole life. A service also asks the bus for a well-known name, which clients address it by. request_name/4 asks for one and release_name/3 gives it back. Both are calls to the bus driver, org.freedesktop.DBus, with the driver's numeric answers turned into atoms.

Example

{:ok, conn} = Rebus.connect(:session)
{:ok, _ref} = Rebus.Service.export(conn, "/org/example/Calculator", [calculator])

{:ok, :primary_owner} = Rebus.Bus.request_name(conn, "org.example.Calculator")

receive do
  {:rebus_name, ^conn, :acquired, "org.example.Calculator"} -> :ok
end

Export before requesting

Export a service's objects before requesting its name. The bus announces a new owner with NameOwnerChanged as soon as the name is granted, and a client waiting for that, or one the bus activated, may call immediately. The connection replies to calls on unknown paths with org.freedesktop.DBus.Error.UnknownObject.

Ownership notices

The bus tells the owning connection when it gains or loses a name, with the NameAcquired and NameLost signals. request_name/4 has them delivered to its :owner, the calling process by default, as

{:rebus_name, conn, :acquired | :lost, name}

A name can change hands at any time after it is requested. A connection that asks with :replace_existing takes it from an owner that allowed replacement, and a connection waiting in the queue gets it when the owner releases it or disconnects. Each change arrives as a notice, so an owner learns that it lost the name and a queued connection that it gained it.

Notices for a name continue until release_name/3 releases it, the owner exits, or the connection closes. A first request answered :exists sets up no notices, and requesting a name again for the same owner does not duplicate them.

Make one request or release of a given name at a time on a connection. When two overlap, the earlier one's cleanup can stop the notices the later one set up.

Summary

Types

Failure reasons returned by request_name/4 and release_name/3.

A flag for request_name/4.

The message an owner receives when its connection gains or loses a name.

The bus's answer to release_name/3.

The bus's answer to request_name/4.

Functions

Releases the well-known name name, or leaves its queue.

Asks the bus for the well-known name name.

Types

error_reason()

@type error_reason() ::
  :invalid_name
  | :invalid_flags
  | :invalid_owner
  | :invalid_timeout
  | :not_a_bus
  | :invalid_bus_reply
  | {:bus_error, binary()}
  | Rebus.error_reason()

Failure reasons returned by request_name/4 and release_name/3.

  • :invalid_name, :invalid_flags, :invalid_owner and :invalid_timeout - an invalid argument. Nothing was sent.
  • :not_a_bus - the connection was opened with bus: false and has no bus to ask. Nothing was sent.
  • {:bus_error, error_name} - the bus refused the request, for example with org.freedesktop.DBus.Error.AccessDenied where its policy does not let this connection own the name.
  • :invalid_bus_reply - the bus answered with something other than one of the documented values.
  • Any other reason is a failure of the call to the bus, as for Rebus.call/3. :timeout is delivery-ambiguous: the bus may have applied the request.

flag()

@type flag() :: :allow_replacement | :replace_existing | :do_not_queue

A flag for request_name/4.

  • :allow_replacement - let a later request with :replace_existing take the name from this connection.
  • :replace_existing - take the name from its current owner, if that owner allowed replacement.
  • :do_not_queue - answer :exists rather than waiting in the queue for a name that cannot be taken now.

name_notice()

@type name_notice() :: {:rebus_name, pid(), :acquired | :lost, binary()}

The message an owner receives when its connection gains or loses a name.

release_result()

@type release_result() :: :released | :non_existent | :not_owner

The bus's answer to release_name/3.

  • :released - the connection owned the name, or waited in its queue, and no longer does.
  • :non_existent - nobody owned the name.
  • :not_owner - the name is owned by another connection, and this one was not in its queue.

request_result()

@type request_result() :: :primary_owner | :in_queue | :exists | :already_owner

The bus's answer to request_name/4.

  • :primary_owner - the connection now owns the name.
  • :in_queue - the name is owned elsewhere and the connection waits in its queue. It becomes the owner, and its owner receives an :acquired notice, when the name comes free.
  • :exists - the name is owned elsewhere and the connection did not queue for it.
  • :already_owner - the connection already owned the name.

Functions

release_name(conn, name, opts \\ [])

@spec release_name(pid(), binary(), keyword()) ::
  {:ok, release_result()} | {:error, error_reason()}

Releases the well-known name name, or leaves its queue.

The name's ownership notices end once the bus has answered, for every owner that request_name/4 gave the connection. A :lost notice the bus sent before its answer still arrives.

Options

  • :timeout - milliseconds for the whole request. Defaults to 5000.

Return values

Examples

{:ok, :released} = Rebus.Bus.release_name(conn, "org.example.Calculator")

request_name(conn, name, flags \\ [], opts \\ [])

@spec request_name(pid(), binary(), [flag()], keyword()) ::
  {:ok, request_result()} | {:error, error_reason()}

Asks the bus for the well-known name name.

flags is a list of flag/0 values and defaults to [], which asks for the name, queues for it if another connection owns it, and lets nobody take it away.

Options

  • :owner - the local process that receives the name's name_notice/0 messages, or nil for none. Defaults to the caller.
  • :timeout - milliseconds for the whole request. Defaults to 5000.

Return values

Examples

{:ok, :primary_owner} = Rebus.Bus.request_name(conn, "org.example.Calculator")

{:ok, :exists} =
  Rebus.Bus.request_name(other_conn, "org.example.Calculator", [:do_not_queue])