defmodule ThousandIsland.Acceptor do @moduledoc false use Task, restart: :transient @spec start_link( {server :: Supervisor.supervisor(), parent :: Supervisor.supervisor(), pos_integer(), ThousandIsland.ServerConfig.t()} ) :: {:ok, pid()} def start_link(arg), do: Task.start_link(__MODULE__, :run, [arg]) @spec run( {server :: Supervisor.supervisor(), parent :: Supervisor.supervisor(), pos_integer(), ThousandIsland.ServerConfig.t()} ) :: no_return def run({server_pid, parent_pid, acceptor_id, %ThousandIsland.ServerConfig{} = server_config}) do ThousandIsland.ProcessLabel.set(:acceptor, server_config, acceptor_id) listener_pid = ThousandIsland.Server.listener_pid(server_pid) {listener_socket, listener_span} = ThousandIsland.Listener.acceptor_info(listener_pid, acceptor_id) connection_sup_pid = ThousandIsland.AcceptorSupervisor.connection_sup_pid(parent_pid) acceptor_span = ThousandIsland.Telemetry.start_child_span(listener_span, :acceptor) # Pre-create the handler config once to avoid Map.take on every connection (hot path) handler_config = ThousandIsland.HandlerConfig.from_server_config(server_config) accept(listener_socket, connection_sup_pid, server_config, handler_config, acceptor_span, 0) end defp accept(listener_socket, connection_sup_pid, server_config, handler_config, span, count) do with {:ok, socket} <- server_config.transport_module.accept(listener_socket), :ok <- ThousandIsland.Connection.start( connection_sup_pid, socket, server_config, handler_config, span ) do accept(listener_socket, connection_sup_pid, server_config, handler_config, span, count + 1) else {:error, :too_many_connections} -> ThousandIsland.Telemetry.span_event(span, :spawn_error) accept( listener_socket, connection_sup_pid, server_config, handler_config, span, count + 1 ) {:error, reason} when reason in [:econnaborted, :einval] -> ThousandIsland.Telemetry.span_event(span, reason) accept( listener_socket, connection_sup_pid, server_config, handler_config, span, count + 1 ) {:error, :closed} -> ThousandIsland.Telemetry.stop_span(span, %{connections: count}) {:error, reason} -> ThousandIsland.Telemetry.stop_span(span, %{connections: count}, %{error: reason}) raise "Unexpected error in accept: #{inspect(reason)}" end end end