defmodule Chaperon.Action.SpreadAsync do @moduledoc """ Action that calls a function with a given `rate` over a given `interval` of time (ms). """ defstruct func: nil, rate: nil, interval: nil, task_name: nil @type rate :: non_neg_integer @type time :: non_neg_integer @type t :: %__MODULE__{ func: Chaperon.CallFunction.callback(), rate: rate, interval: time, task_name: atom } end defimpl Chaperon.Actionable, for: Chaperon.Action.SpreadAsync do alias Chaperon.Session use Chaperon.Session.Logging def run(action, session) do delay = round(action.interval / action.rate) session |> log_info("SpreadAsync[#{action.rate} / #{action.interval} @ #{delay}]") 1..action.rate |> Enum.map(fn _ -> action |> execute_task(session, delay) end) |> Enum.reduce(session, fn task, session -> session |> Session.add_async_task(action.func, task) end) |> Session.ok() end defp execute_task(action, session, delay) do session = session |> Session.delay(delay) |> Session.fork() Chaperon.Worker.Supervisor.schedule_async(fn -> session |> Session.time(action.func, fn session -> apply(session.scenario.module, action.func, [session]) end) end) end def abort(action, session) do # TODO {:ok, action, session} end end defimpl String.Chars, for: Chaperon.Action.SpreadAsync do def to_string(%{func: func, rate: r, interval: i}) when is_atom(func) do "SpreadAsync[#{func}, #{r}, #{i}]" end def to_string(%{func: func, rate: r, interval: i}) when is_function(func) do "SpreadAsync[#{inspect(func)}, #{r}, #{i}]" end end