-module(livery_client_concurrency).
-moduledoc """
Client layer: cap in-flight requests.
A lock-free `atomics` counter admits up to `limit` concurrent requests
through this layer; past the cap a request returns `{error, overloaded}`
without calling downstream. Add it with `livery_client:concurrency/1`.
""".
-export([limiter/1, call/3]).
-export_type([state/0]).
-opaque state() :: #{ref := atomics:atomics_ref(), limit := non_neg_integer()}.
-spec limiter(non_neg_integer()) -> state().
limiter(Limit) when is_integer(Limit), Limit >= 0 ->
#{ref => atomics:new(1, [{signed, false}]), limit => Limit}.
-spec call(livery_client:request(), livery_client:next(), state()) ->
{ok, livery_client:response()} | {error, term()}.
call(Req, Next, #{ref := Ref, limit := Limit}) ->
case atomics:add_get(Ref, 1, 1) of
Count when Count > Limit ->
atomics:sub(Ref, 1, 1),
{error, overloaded};
_Count ->
try
Next(Req)
after
atomics:sub(Ref, 1, 1)
end
end.