# `Parent.GenServer`
[🔗](https://github.com/sasa1977/parent/blob/0.13.0/lib/parent/gen_server.ex#L1)

GenServer with parenting capabilities powered by `Parent`.

This behaviour can be useful in situations where `Parent.Supervisor` won't suffice.

## Example

The following example is roughly similar to a standard
[callback-based Supervisor](https://hexdocs.pm/elixir/Supervisor.html#module-module-based-supervisors):

    defmodule MyApp.Supervisor do
      # Automatically defines child_spec/1
      use Parent.GenServer

      def start_link(init_arg),
        do: Parent.GenServer.start_link(__MODULE__, init_arg, name: __MODULE__)

      @impl GenServer
      def init(_init_arg) do
        Parent.start_all_children!(children)
        {:ok, initial_state}
      end
    end

The expression `use Parent.GenServer` will also inject `use GenServer` into your code. Your
parent process is a GenServer, and this behaviour doesn't try to hide it. Except when starting
the process, you work with the parent exactly as you work with any GenServer, using the same
functions, such as `GenServer.call/3`, and providing the same callbacks, such as `init/1`, or
`handle_call/3`.

## Interacting with the parent from the outside

You can issue regular `GenServer` calls and casts, and send messages to the parent, which can
be handled by corresponding `GenServer` callbacks. In addition, you can use functions from
the `Parent.Client` module to manipulate or query the parent state from other processes. As a
good practice, it's advised to wrap such invocations in the module which implements
`Parent.GenServer`.

## Interacting with children inside the parent

From within the parent process, you can interact with the child processes using functions from
the `Parent` module. All child processes should be started using `Parent` functions, such as
`Parent.start_child/2`, because otherwise `Parent` won't be aware of these processes and won't
be able to fulfill its guarantees.

Note that you can start children from any callback, not just during `init/1`. In addition, you
don't need to start all children at once. Therefore, `Parent.GenServer` can prove useful when
you need to make some runtime decisions:

      {:ok, child1} = Parent.start_child(child1_spec)

      if some_condition_met?,
        do: Parent.start_child(child2_spec)

      Parent.start_child(child3_spec)

However, bear in mind that this code won't be executed again if the processes are restarted.

## Handling child termination

If a child process terminates and isn't restarted, the `c:handle_stopped_children/2` callback is
invoked. The default implementation does nothing.

The following example uses `c:handle_stopped_children/2` to start a child task and report if it
it crashes:

      defmodule MyJob do
        use Parent.GenServer, restart: :temporary

        def start_link(arg), do: Parent.GenServer.start_link(__MODULE__, arg)

        @impl GenServer
        def init(_) do
          {:ok, _} = Parent.start_child(%{
            id: :job,
            start: {Task, :start_link, [fn -> job(arg) end]},
            restart: :temporary,

            # handle_stopped_children won't be invoked without this
            ephemeral?: true
          })
          {:ok, nil}
        end

        @impl Parent.GenServer
        def handle_stopped_children(%{job: info}, state) do
          if info.reason != :normal do
            # report job failure
          end

          {:stop, reason, state}
        end
      end

`handle_stopped_children` can be useful to implement arbitrary custom behaviour, such as
restarting after a delay, and using incremental backoff periods between two consecutive starts.

For example, this is how you could introduce a delay between two consecutive starts:

    def handle_stopped_children(stopped_children, state) do
      Process.send_after(self, {:restart, stopped_children}, delay)
      {:noreply, state}
    end

    def handle_info({:restart, stopped_children}, state) do
      Parent.return_children(stopped_children)
      {:noreply, state}
    end

Keep in mind that `handle_stopped_children` is only invoked if the child crashed on its own,
and if it's not going to be restarted.

If the child was explicitly stopped via a `Parent` function, such as `Parent.shutdown_child/1`,
this callback will not be invoked. The same holds for `Parent.Client` functions. If you want
to unconditionally react to a termination of a child process, setup a monitor with `Process.monitor`
and add a corresponding `handle_info` clause.

If the child was taken down because its lifecycle is bound to some other process, the
corresponding `handle_stopped_children` won't be invoked. For example, if process A is bound to
process B, and process B crashes, only one `handle_stopped_children` will be invoked (for the
crash of process B). However, the corresponding `info` will contain the list of all associated
siblings that have been taken down, and `stopped_children` will include information necessary to
restart all of these siblings. Refer to `Parent` documentation for details on lifecycles binding.

## Parent termination

The behaviour takes down the child processes before it terminates, to ensure that no child
process is running after the parent has terminated. The children are terminated synchronously,
one by one, in the reverse start order.

The termination of the children is done after the `terminate/1` callback returns. Therefore in
`terminate/1` the child processes are still running, and you can interact with them, and even
start additional children.

## Caveats

Like any other `Parent`-based process, `Parent.GenServer` traps exits and uses the `:infinity`
shutdown strategy. As a result, a parent process which blocks for a long time (e.g. because its
communicating with a remote service) won't be able to handle child termination, and your
fault-tolerance might be badly affected. In addition, a blocking parent might completely paralyze
the system (or a subtree) shutdown. Setting a shutdown strategy to a finite time is a hacky
workaround that will lead to lingering orphan processes, and might cause some strange race
conditions which will be very hard to debug.

Therefore, be wary of having too much logic inside a parent process. Try to push as much
responsibilities as possible to other processes, such as children or siblings, and use parent
only for coordination and reporting tasks.

Finally, since parent trap exits, it's possible to receive an occasional stray `:EXIT` message
if the child crashes during its initialization.

By default `use Parent.GenServer` receives such messages and ignores them. If you're implementing
your own `handle_info`, make sure to include a clause for `:EXIT` messages:

      def handle_info({:EXIT, _pid, _reason}, state), do: {:noreply, state}

# `options`

```elixir
@type options() :: [Parent.option() | GenServer.option()]
```

# `state`

```elixir
@type state() :: term()
```

# `handle_stopped_children`

```elixir
@callback handle_stopped_children(info :: Parent.stopped_children(), state()) ::
  {:noreply, new_state}
  | {:noreply, new_state, timeout() | :hibernate}
  | {:stop, reason :: term(), new_state}
when new_state: state()
```

Invoked when some children have terminated.

The `info` map will contain all the children which have been stopped together. For example,
if child A is bound to child B, and child B terminates, parent will also terminate the child
A. In this case, `handle_stopped_children` is invoked only once, with the `info` map containing
entries for both children.

This callback will not be invoked in the following cases:

  - a child is terminated by invoking `Parent` functions such as `Parent.shutdown_child/1`
  - a child is restarted
  - a child is not ephemeral (see "Ephemeral children" in `Parent` for details)

# `start_link`

```elixir
@spec start_link(module(), arg :: term(), options()) :: GenServer.on_start()
```

Starts the parent process.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
