Current section
Files
Jump to
Current section
Files
src/blockade_event_manager.erl
%%%-----------------------------------------------------------------------------
%% @doc The event queue manager.
%% @private
%% @end
%%%-----------------------------------------------------------------------------
-module(blockade_event_manager).
-include("blockade_header.hrl").
-behaviour(gen_server).
-export([start_link/1]).
-export([init/1, handle_cast/2, handle_call/3, handle_info/2]).
%%------------------------------------------------------------------------------
%% Record definitions
%%------------------------------------------------------------------------------
-record(state,
{manager :: blockade:event_manager(),
event_queue = [] :: [blockade:queued_event()],
priority = ?DEFAULT_PRIORITY :: integer(),
discard_events = ?DEFAULT_DISCARD_EVENTS,
schduler_ref = undefined :: reference() | undefined,
emitted_priorities = [] :: [blockade:priority()],
priority_confirmed = false :: true | false,
priority_sync = true :: true | false}).
%%------------------------------------------------------------------------------
%% Internal API
%%------------------------------------------------------------------------------
start_link(#{name := Name} = Args) ->
gen_server:start_link({local, Name}, ?MODULE, Args, []).
%%------------------------------------------------------------------------------
%% Callbacks
%%------------------------------------------------------------------------------
init(Opts) ->
PrioSync = maps:get(priority_sync, Opts, true),
erlang:send_after(?EVENT_QUEUE_PRUNE, self(), queue_prune),
if PrioSync =:= true ->
erlang:send_after(?PRIORITY_EMIT_SCHEDULE, self(), emit_priority),
erlang:send_after(?PRIORITY_SYNC_SCHEDULE, self(), sync_priority);
true ->
ok
end,
{ok,
#state{manager = maps:get(name, Opts),
discard_events = maps:get(discard_events, Opts, ?DEFAULT_DISCARD_EVENTS),
priority = maps:get(priority, Opts, ?DEFAULT_PRIORITY),
emitted_priorities = [],
priority_confirmed = blockade_service:startup_prio_confr(Opts),
priority_sync = PrioSync}}.
handle_cast({discard_events, true}, #state{priority = P, event_queue = Eq} = State) ->
NewEq = blockade_service:queue_prune(Eq, P),
{noreply, State#state{discard_events = true, event_queue = NewEq}};
handle_cast({discard_events, false}, State) ->
{noreply, State#state{discard_events = false}};
handle_cast({dispatch, Event, Payload, #{priority := Ep} = Opts},
#state{manager = M, priority = Lp} = State)
when Ep >= Lp ->
blockade_service:dispatch_event(Event, Payload, M, Opts),
NewPriority = blockade_service:atomic_priority_update(M, Lp, Opts),
{noreply, State#state{priority = NewPriority}};
handle_cast({dispatch, Event, Payload, Opts},
#state{event_queue = Eq, priority = P, discard_events = De} = State) ->
{_Resp, Neq} = blockade_service:queue_event(Eq, {Event, Payload, Opts}, P, De),
{noreply, State#state{event_queue = Neq}};
handle_cast(prune_event_queue, State) ->
{noreply, State#state{event_queue = []}};
handle_cast({priority_emit, EmittedPrio}, #state{emitted_priorities = Ep} = State) ->
{noreply, State#state{emitted_priorities = [EmittedPrio | Ep]}};
handle_cast({set_priority, Priority, Opts},
#state{event_queue = Eq, manager = Man, schduler_ref = Sr} = State) ->
blockade_service:cancel_ref(Sr),
Neq = blockade_service:dispatch_queued(
lists:reverse(Eq), Man, Priority, []),
Nde = blockade_service:get_discard_opt(Opts, State#state.discard_events),
Nsr = blockade_service:get_reset_opt(Opts, Sr),
{noreply,
State#state{priority = Priority, schduler_ref = Nsr, event_queue = Neq,
discard_events = Nde}};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_call({dispatch, Event, Payload, #{priority := P} = Opts},
_From,
#state{manager = M} = State)
when P >= State#state.priority ->
blockade_service:dispatch_event(Event, Payload, State#state.manager, Opts),
NewPriority = blockade_service:atomic_priority_update(M, State#state.priority, Opts),
{reply, {ok, event_dispatched}, State#state{priority = NewPriority}};
handle_call({dispatch, Event, Payload, Opts},
_From,
#state{priority = P, event_queue = Eq, discard_events = De} = State) ->
{Resp, NewEq} = blockade_service:queue_event(Eq, {Event, Payload, Opts}, P, De),
{reply, {ok, Resp}, State#state{event_queue = NewEq}};
handle_call(get_priority, _From, #state{priority = Priority} = State) ->
{reply, {ok, Priority}, State};
handle_call(get_event_queue, _From, #state{event_queue = Eq} = State) ->
{reply, {ok, lists:reverse(Eq)}, State};
handle_call(get_state, _From, State) ->
StateMap =
#{manager => State#state.manager,
priority => State#state.priority,
event_queue => lists:reverse(State#state.event_queue),
discard_events => State#state.discard_events,
schduler_ref => State#state.schduler_ref,
emitted_priorities => State#state.emitted_priorities,
priority_confirmed => State#state.priority_confirmed,
priority_sync => State#state.priority_sync},
{reply, StateMap, State};
handle_call(_Msg, _From, State) ->
{reply, {error, unknown_msg}, State}.
handle_info(sync_priority, #state{priority_sync = false} = State) ->
{noreply, State};
handle_info(sync_priority,
#state{emitted_priorities = Ep, priority = Priority} = State) ->
SyncPrio = blockade_service:sync_priority(Ep, Priority),
erlang:send_after(?PRIORITY_SYNC_SCHEDULE, self(), sync_priority),
{noreply,
State#state{emitted_priorities = [], priority = SyncPrio, priority_confirmed = true}};
handle_info(emit_priority, #state{priority_sync = false} = State) ->
{noreply, State};
handle_info(emit_priority, #state{priority_confirmed = false} = State) ->
erlang:send_after(?PRIORITY_EMIT_SCHEDULE, self(), emit_priority),
{noreply, State};
handle_info(emit_priority, #state{priority = Priority, manager = Man} = State) ->
blockade_service:emit_priority(Man, Priority),
erlang:send_after(?PRIORITY_EMIT_SCHEDULE, self(), emit_priority),
{noreply, State};
handle_info(queue_prune,
#state{priority = P, event_queue = Eq, discard_events = true} = State) ->
erlang:send_after(?EVENT_QUEUE_PRUNE, self(), queue_prune),
NewEq = blockade_service:queue_prune(Eq, P),
{noreply, State#state{event_queue = NewEq}};
handle_info(queue_prune, #state{discard_events = false} = State) ->
erlang:send_after(?EVENT_QUEUE_PRUNE, self(), queue_prune),
{noreply, State};
handle_info(reset_priority, #state{event_queue = Eq, manager = Man} = State) ->
Neq = blockade_service:dispatch_queued(
lists:reverse(Eq), Man, ?DEFAULT_PRIORITY, []),
{noreply,
State#state{priority = ?DEFAULT_PRIORITY, schduler_ref = undefined, event_queue = Neq}};
handle_info(_Msg, State) ->
{noreply, State}.