Terminate GenServer after a certain period

Sqllitex.Server has stop/1

Consider this standin:

# file: my_app/lib/server.ex
#
defmodule Server do
  use GenServer

  def start_link(args),
    do: GenServer.start_link(__MODULE__, args)

  def init(args),
    do: {:ok, args}

  def handle_cast(:stop, state) do
    {:stop, :normal, state}
  end

  def terminate(reason, state) do
    IO.puts("#{__MODULE__} terminate - reason: #{inspect(reason)} state: #{inspect(state, pretty: true)}")
    :ok
  end

  # ---

  def stop(pid),
    do: GenServer.cast(pid, :stop)

end

and this application:

# file: my_app/lib/my_app/application.ex
#
# created with "mix new my_app --sup"
#
defmodule MyApp.Application do
  use Application

  # @server_timeout 30 * 60 * 1000
  @server_timeout 30_000
  @supervisor_name MyApp.Supervisor

  def start(_type, _args) do
    children = [
      Supervisor.child_spec({Server, [:config, :server]}, restart: :transient)
    ]

    # restart: transient
    # otherwise supervisor will restart Server process

    opts = [strategy: :one_for_one, name: @supervisor_name]

    case Supervisor.start_link(children, opts) do
      {:ok, _} = on_start ->
        Task.start(__MODULE__, :timeout_server, [])
        on_start

      on_start ->
        on_start
    end
  end

  def timeout_server() do
    with {:ok, server_pid} <- find_server() do
      # Terminate this task if server terminates (and vice versa)
      Process.link(server_pid)
      # Now wait
      Process.sleep(@server_timeout)
      # Let server terminate normally
      Process.unlink(server_pid)
      # Initiate server termination
      Server.stop(server_pid)
    end
  end

  def find_server() do
    case Enum.find(Supervisor.which_children(@supervisor_name), &server_module?/1) do
      {_, server_pid, _, _} when is_pid(server_pid) ->
        {:ok, server_pid}

      _ ->
        {:error, :not_found}
    end
  end

  defp server_module?({_, _, _, [Server | _]}),
    do: true

  defp server_module?(_),
    do: false
end

then

$ iex -S mix
Erlang/OTP 22 [erts-10.5.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Compiling 2 files (.ex)
Interactive Elixir (1.9.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> MyApp.Application.find_server()
{:ok, #PID<0.149.0>}
iex(2)> Elixir.Server terminate - reason: :normal state: [:config, :server]
iex(2)> MyApp.Application.find_server()
{:error, :not_found}
iex(3)> 
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution
a
$ 
1 Like