-module(livery_client_timeout). -moduledoc """ Client layer: bound a request to a deadline. Runs the downstream call in a monitored child process and returns `{error, timeout}` if it does not finish within `Ms`, killing the child (which tears down the in-flight connection). Add it with `livery_client:timeout/1`. """. -export([call/3]). -spec call(livery_client:request(), livery_client:next(), pos_integer()) -> {ok, livery_client:response()} | {error, term()}. call(Req, Next, Ms) -> Self = self(), Ref = make_ref(), {Pid, MRef} = spawn_monitor(fun() -> Result = try Next(Req) catch Class:Reason:Stack -> {'$crash', Class, Reason, Stack} end, Self ! {Ref, Result} end), receive {Ref, {'$crash', Class, Reason, Stack}} -> erlang:demonitor(MRef, [flush]), erlang:raise(Class, Reason, Stack); {Ref, Result} -> erlang:demonitor(MRef, [flush]), Result; {'DOWN', MRef, process, Pid, Reason} -> {error, {worker_down, Reason}} after Ms -> exit(Pid, kill), erlang:demonitor(MRef, [flush]), {error, timeout} end.