summaryrefslogtreecommitdiff
path: root/lib/limiter.ex
blob: 84c601cd54aa4b6db997dd33e7dc581b67a42f31 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
defmodule Limiter do
  @ets __MODULE__.ETS

  def new(name, max_running, max_waiting) do
    name = atom_name(name)
    :persistent_term.put(name, {max_running, max_waiting})
    :ets.new(name, [:public, :named_table])
    :ok
  end

  def limit(name, fun) do
    {max_running, max_waiting} = :persistent_term.get(atom_name(name))
    max = max_running + max_waiting
    counter = inc(name)

    cond do
      counter <= max_running ->
        fun.()

      counter > max ->
        {:error, :overload}

      counter > max_running ->
        wait(name, fun)
    end
  after
    dec(name)
  end

  defp wait(name, fun) do
    Process.sleep(150)
    dec(name)
    limit(name, fun)
  end

  defp inc(name) do
    name = atom_name(name)
    :ets.update_counter(name, name, {2, 1}, {name, 0})
  end

  def dec(name) do
    name = atom_name(name)
    :ets.update_counter(name, name, {2, -1}, {name, 0})
  end

  defp atom_name(suffix), do: Module.concat(@ets, suffix)
end