Current section

Files

Jump to
membrane_core lib membrane core timer.ex
Raw

lib/membrane/core/timer.ex

defmodule Membrane.Core.Timer do
@moduledoc false
alias Membrane.Clock
alias Membrane.Core.Message
alias Membrane.Time
require Membrane.Core.Message
@type id :: any()
@type interval :: Ratio.t() | Time.non_neg() | :no_interval
@type t :: %__MODULE__{
id: id,
interval: interval,
init_time: Time.t(),
clock: Clock.t(),
next_tick_time: Time.t(),
ratio: Clock.ratio(),
timer_ref: reference() | nil,
awaiting_message?: boolean()
}
@enforce_keys [:interval, :clock, :init_time, :id]
defstruct @enforce_keys ++
[next_tick_time: 0, ratio: Ratio.new(1), timer_ref: nil, awaiting_message?: false]
@spec start(id, interval, Clock.t()) :: t
def start(id, interval, clock) do
%__MODULE__{id: id, interval: interval, init_time: Time.monotonic_time(), clock: clock}
|> tick
end
@spec stop(t) :: :ok
def stop(%__MODULE__{interval: :no_interval}) do
:ok
end
def stop(%__MODULE__{timer_ref: timer_ref}) do
_time_left = Process.cancel_timer(timer_ref)
:ok
end
@spec update_ratio(t, Clock.ratio()) :: t
def update_ratio(%__MODULE__{} = timer, ratio) do
%__MODULE__{timer | ratio: ratio}
end
@spec handle_message_arrived(t) :: t
def handle_message_arrived(%__MODULE__{awaiting_message?: true} = timer) do
%{timer | awaiting_message?: false}
end
@spec tick(t) :: t
def tick(%__MODULE__{} = timer)
when timer.awaiting_message? or timer.interval == :no_interval do
timer
end
def tick(%__MODULE__{} = timer) do
%__MODULE__{
id: id,
interval: interval,
init_time: init_time,
next_tick_time: next_tick_time,
ratio: ratio
} = timer
next_tick_time = Ratio.add(Ratio.new(next_tick_time), Ratio.new(interval))
# Next tick time converted to BEAM clock time
beam_next_tick_time =
Ratio.add(Ratio.new(init_time), Ratio.div(next_tick_time, ratio))
|> Ratio.floor()
|> Time.as_milliseconds(:round)
timer_ref =
Process.send_after(self(), Message.new(:timer_tick, id), beam_next_tick_time, abs: true)
%__MODULE__{
timer
| next_tick_time: next_tick_time |> Ratio.floor(),
timer_ref: timer_ref,
awaiting_message?: true
}
end
@spec set_interval(t, interval) :: t
def set_interval(%__MODULE__{interval: :no_interval} = timer, interval) do
%__MODULE__{timer | interval: interval}
|> tick()
end
def set_interval(%__MODULE__{} = timer, interval) do
%__MODULE__{timer | interval: interval}
end
end