Runs a connection check in an isolated process under a hard deadline.
Connection checks talk to the network, and the libraries behind them bound neither their runtime nor their crashes:
:gen_smtp_client.open/1runs in the calling process and, past the TCP connect, waits on a hard-coded?TIMEOUTof 1_200_000 ms — thetimeoutoption bounds onlyconnect. A tarpit relay parks the caller for twenty minutes.- ExAws retries transport errors with backoff, which adds up to minutes.
Every call site is a LiveView callback, so the check must be watched in both directions, and getting only one of them right is worse than getting neither:
- The check must not kill the caller.
Task.async/1links, and a LiveView does not trap exits, so a raise or an abnormal exit inside the check killed the operator's page outright — beforeTask.yield/2could hand back{:exit, reason}, which is why that clause never ran. - The caller must not lose the check. With a bare
spawn_monitor/1the deadline lives in the caller'sreceive/after, so when the LiveView goes away mid-check — the operator hit refresh — nothing is left to fire it. The check stays parked in gen_smtp for twenty minutes holding its socket, and, being unlinked, it is now unreachable rather than merely slow. That is the first hazard relocated, not removed.
So: link, monitor, and unlink before dying — which is exactly what
LiveView's own start_async does (phoenix_live_view/async.ex: Task.start_link/1,
then a monitor on top, then the work wrapped in try/after Process.unlink/1).
The link reaps the check when the
caller dies; the monitor delivers the result and the crash reason; unlinking
before dying keeps the check's own failure from travelling back up the link. At
the deadline the caller unlinks before killing, because :kill is untrappable
— the check cannot unlink itself, and the link would carry :killed straight
back.
The one signal a link necessarily carries is an untrappable :kill of the check
process by a third party. Nothing holds its pid, so nothing can; Task and
LiveView accept the same exposure.
Summary
Types
Talks to the network; answers :ok, {:ok, note} when it succeeded but has
something the operator needs to know, or {:error, message}.
Functions
Runs check in an isolated process, bounded by deadline milliseconds.
Types
Functions
Runs check in an isolated process, bounded by deadline milliseconds.
Returns what check returned. If it crashes, exits, or overruns the deadline,
returns {:error, message} — either way the caller is left standing, and the
check does not outlive it.