Skip to content

Instantly share code, notes, and snippets.

@cr0t
Created November 14, 2024 22:47
Show Gist options
  • Save cr0t/685184a621c18ba3617abdea7d07eb14 to your computer and use it in GitHub Desktop.
Save cr0t/685184a621c18ba3617abdea7d07eb14 to your computer and use it in GitHub Desktop.
defmodule PortsEx.Basic do
use GenServer
require Logger
@command "./bin/long.rb"
def start_link(opts \\ []),
do: GenServer.start_link(__MODULE__, nil, opts)
def init(_) do
Port.open({:spawn, @command}, [:binary, :exit_status])
{:ok, %{latest_output: nil, exit_status: nil}}
end
def handle_info({_port, {:data, line}}, state) do
latest_output = String.trim(line)
Logger.info("Latest output: #{latest_output}")
{:noreply, %{state | latest_output: latest_output}}
end
def handle_info({_port, {:exit_status, status}}, state) do
Logger.info("External exit: #{status}")
{:noreply, %{state | exit_status: status}}
end
def handle_info(msg, state) do
IO.inspect(msg, label: "Unexpected message")
{:noreply, state}
end
end
#!/usr/bin/env ruby
STDOUT.sync = true
puts "Starting up long process..."
TOTAL = 10
TOTAL.times do |n|
sleep 1
puts "Progress: step #{n + 1} of #{TOTAL}"
end
puts "Done"
defmodule PortsEx.Monitored do
use GenServer
require Logger
@command "./bin/long.rb"
def start_link(opts \\ []),
do: GenServer.start_link(__MODULE__, nil, opts)
def init(_) do
port = Port.open({:spawn, @command}, [:binary, :exit_status])
Port.monitor(port)
{:ok, %{port: port, latest_output: nil, exit_status: nil}}
end
def handle_info({_port, {:data, line}}, state) do
latest_output = String.trim(line)
Logger.info("Latest output: #{latest_output}")
{:noreply, %{state | latest_output: latest_output}}
end
def handle_info({_port, {:exit_status, status}}, state) do
Logger.info("External exit: #{status}")
{:noreply, %{state | exit_status: status}}
end
def handle_info({:DOWN, _ref, :port, port, :normal}, state) do
Logger.info("DOWN message from: #{inspect port}")
{:noreply, state}
end
def handle_info(msg, state) do
IO.inspect(msg, label: "Unexpected message")
{:noreply, state}
end
end
defmodule PortsEx.Trapped do
use GenServer
require Logger
@command "./bin/long.rb"
def start_link(opts \\ []),
do: GenServer.start_link(__MODULE__, nil, opts)
def init(_) do
Process.flag(:trap_exit, true)
port = Port.open({:spawn, @command}, [:binary, :exit_status])
Port.monitor(port)
{:ok, %{port: port, latest_output: nil, exit_status: nil}}
end
def terminate(reason, state) do
Logger.info("TERMINATE: #{inspect reason}. Cleaning up...")
Logger.info("Final state: #{inspect state}")
port_info = Port.info(state.port)
os_pid = port_info[:os_pid]
Logger.warning("Orphaned OS process: #{os_pid}")
:normal
end
def handle_info({_port, {:data, line}}, state) do
latest_output = String.trim(line)
Logger.info("Latest output: #{latest_output}")
{:noreply, %{state | latest_output: latest_output}}
end
def handle_info({_port, {:exit_status, status}}, state) do
Logger.info("External exit: #{status}")
{:noreply, %{state | exit_status: status}}
end
def handle_info({:DOWN, _ref, :port, port, :normal}, state) do
Logger.info("DOWN message from: #{inspect port}")
{:noreply, state}
end
def handle_info({:EXIT, port, :normal}, state) do
Logger.info("EXIT message from: #{inspect port}")
{:noreply, state}
end
def handle_info(msg, state) do
IO.inspect(msg, label: "Unexpected message")
{:noreply, state}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment