1 -module(beam_stats_producer).
3 -include("beam_stats.hrl").
5 -behaviour(gen_server).
13 % Force production and distribution. Blocks. Mainly for testing.
17 %% gen_server callbacks
27 %% ============================================================================
29 %% ============================================================================
31 -define(SIGNAL_PRODUCTION , beam_stats_production_signal).
32 -define(FORCE_PRODUCTION , beam_stats_force_production).
35 { consumers = ordsets:new() :: ordsets:ordset(pid())
36 , stats_state :: beam_stats_state:t()
42 %% ============================================================================
44 %% ============================================================================
47 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
49 -spec subscribe(pid()) ->
52 gen_server:cast(?MODULE, {subscribe, PID}).
54 -spec unsubscribe(pid()) ->
57 gen_server:cast(?MODULE, {unsubscribe, PID}).
59 -spec force_production() ->
62 gen_server:call(?MODULE, ?FORCE_PRODUCTION).
64 %% ============================================================================
65 %% gen_server callbacks (unused)
66 %% ============================================================================
68 code_change(_OldVsn, State, _Extra) ->
71 terminate(_Reason, _State) ->
74 %% ============================================================================
75 %% gen_server callbacks
76 %% ============================================================================
79 ok = schedule_first_production(),
80 Consumers = ordsets:new(),
81 StatsState = beam_stats_state:new(),
82 {ok, #state{consumers=Consumers, stats_state=StatsState}}.
84 handle_cast({subscribe, PID}, #state{consumers=Consumers1}=State) ->
85 Consumers2 = ordsets:add_element(PID, Consumers1),
86 {noreply, State#state{consumers=Consumers2}};
88 handle_cast({unsubscribe, PID}, #state{consumers=Consumers1}=State) ->
89 Consumers2 = ordsets:del_element(PID, Consumers1),
90 {noreply, State#state{consumers=Consumers2}}.
92 handle_call(?FORCE_PRODUCTION, _From, State1) ->
93 State2 = produce(State1),
96 handle_info(?SIGNAL_PRODUCTION, #state{}=State1) ->
97 State2 = produce(State1),
98 ok = schedule_next_production(),
101 %% ============================================================================
103 %% ============================================================================
105 -spec produce(state()) ->
107 produce(#state{consumers=ConsumersSet, stats_state=StatsState1}=State) ->
108 StatsState2 = beam_stats_state:update(StatsState1),
109 Stats = beam_stats_state:export(StatsState2),
110 ConsumersList = ordsets:to_list(ConsumersSet),
111 ok = push_to_consumers(Stats, ConsumersList),
112 State#state{stats_state = StatsState2}.
114 -spec schedule_first_production() ->
116 schedule_first_production() ->
117 _ = self() ! ?SIGNAL_PRODUCTION,
120 -spec schedule_next_production() ->
122 schedule_next_production() ->
123 ProductionInterval = beam_stats_config:production_interval(),
124 _ = erlang:send_after(ProductionInterval, self(), ?SIGNAL_PRODUCTION),
127 -spec push_to_consumers(beam_stats:t(), [pid()]) ->
129 push_to_consumers(Stats, Consumers) ->
130 Push = fun (Consumer) -> gen_server:cast(Consumer, Stats) end,
131 lists:foreach(Push, Consumers).