Agent's State and Garbage Collection

Am I correct to assume that an Agent’s state will not be garbage collected when the Agent’s process falls out of scope? I guess what I want to know is. What happens to a Agent when its process falls out of scope and do we have to manually stop all Agents when they are no longer needed?

Note I understand that a call to an Agent’s stop function will stop and release
the Agent’s process and state(stack).

All erlang process will continue to stop unless stopped. Whether or not a variable with that processes pid is in scope or not somewhere does not matter.

So if I have a function which returns a function that forms a closure around a Agent, then I should also return something which can stop that agent right? Something like…

{function with Agent, function with call to Agent’s stop function}

A way to make sure it is always killed is to link it to a process (Agent.start_link) and thus when that process dies, so will it. It could even be a janitorial process that just kills itself after a set amount of time or so. :slight_smile:

Don’t think of it as forming a closure over an Agent. Processes are not values. Pids are values, but pids just point to processes.

Good point but you still have to have a way to stop the process via its pid.

Sure, but that’s not the only way. When a process is killed all linked processes are also killed.

1 Like

It will die if the other process crashes, but not if it stops/ends normally - you’d have to use GenServer’s terminate callback or equivalent to kill it in that case.

One way that makes sense to me when thinking Erlang/Elixir processes, and hence Agents, is that they very much behave like OS processes. You start a process and it keeps going, whether you have a reference to it or not, until it decides to terminate or you kill it. If it is a GenServer then you can kill it nicely so it will clean up before it dies, or kill it nastily so it just dies immediately.

When a process dies there is no way of making it automatically save any of its state. That is something you must explicitly take care of yourself.

EDIT: just fixed the some bad typing.

3 Likes

I started this discussion because I wanted to know what would happen to the resources created by an Agent in a function which closed around that agent… Basically I was creating a simple memoization function which carried the previously calculated values in a map contained in an Agent.

def start_mem(f \\ &(&1 + 1)) do
    {_, agt} = Agent.start_link(fn -> %{} end)
    #return a tuple {function add/retrieve from agent, function stop agent}
    {
      fn v ->
        case Agent.get(agt, fn m -> Map.has_key?(m, v) end) do
          false ->
            ans = f.(v)
            Agent.update(agt, fn m -> Map.put(m, v, ans) end)
            ans
          true ->
            Agent.get(agt, fn m -> Map.get(m, v) end)
        end
      end,
      fn -> Agent.stop(agt) end
      }
  end