fxn
Running code when a script exits
I have a script executed with mix run that has an infinite loop that collects some data in an agent. I’d like to print that data when I interrupt the script.
Tried System.at_exit/1 without success, maybe because the only way I know to interrupt the execution is C-c C-c.
Is there a way to accomplish that?
Most Liked
josevalim
There are two questions here:
-
How to shut the system down cleanly
-
How to handle certain signals (interrupt)
To answer 1, System.stop/1 is the answer. If terminate/2 callbacks are not invoked, make sure those processes are trapping exits.
To answer 2, since Erlang/OTP 20, there is OS signal handling: http://erlang.org/doc/man/kernel_app.html#os-signal-event-handler
It is a bit of a long answer. In a nutshell, you need to implement a gen_event handler and react to the callbacks you are interested. There is a more detailed guide here: https://medium.com/@ellispritchard/graceful-shutdown-on-kubernetes-with-signals-erlang-otp-20-a22325e8ae98
josevalim
Another trick you could do is System.at_exit(fn int -> your_code(); System.stop(int) end). ![]()
fxn
I have investigated a bit the solution with the signal handler.
Erlang executes :init.stop() on SIGTERM by default. That means that a simple kill PID shuts the thing down orderly.
However, you need to figure out the PID of the process, and open a different terminal to execute the command. Besides, custom code passed to System.at_exit/1 is not invoked by the event handler.
Ctrl-C sends SIGINT. The only shortcut that I’ve seen sends a different signal is Ctrl-\, which sends SIGQUIT. So, I am using this event handler:
defmodule QuitScript do
@behaviour :gen_event
def init(_), do: {:ok, nil}
def handle_event(:sigquit, _state), do: :remove_handler
def handle_event(signal, state), do: :erl_signal_handler.handle_event(signal, state)
def handle_call(_, state), do: {:ok, :ok, state}
def terminate(_reason, _state) do
IO.puts("My exit code")
System.stop()
end
end
:gen_event.swap_handler(:erl_signal_server, {:erl_signal_handler, []}, {QuitScript, []})
Notice that the terminate/2 callback allows you to run custom code on exit.
If you throw that to a script and execute it for example like this
mix run --no-halt foo.exs
Ctrl-C still behaves as always, and additionally Ctrl-\ shuts the thing down orderly (you only see ^\ echoed in the terminal, not too bad).
Last Post!
fxn
Alternatively, we could move System.stop() to the callback:
defmodule QuitScript do
@behaviour :gen_event
def init(_), do: {:ok, nil}
def handle_event(:sigquit, _state), do: System.stop()
def handle_event(signal, state), do: :erl_signal_handler.handle_event(signal, state)
def handle_call(_, state), do: {:ok, :ok, state}
def terminate(_reason, _state) do
IO.puts("My exit code")
end
end
:gen_event.swap_handler(:erl_signal_server, {:erl_signal_handler, []}, {QuitScript, []})
Looks more natural to me this way, though in the previous example the order in which the custom code runs is more obvious.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #hex
- #security









