Current section

Files

Jump to
metrics src metrics_process_tracker.erl
Raw

src/metrics_process_tracker.erl

%% Copyright (c) 2016, Benoit Chesneau.
%%
%% This file is part of barrel_metrics released under the BSD license.
%% See the NOTICE for more information.
%% @doc module to track processes associated to a counter
-module(metrics_process_tracker).
-behaviour(gen_server).
%% public API
-export([track/1, track/2]).
%% internal API
-export([start_link/0]).
%% gen_server call backs
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3,
terminate/2]).
-include_lib("stdlib/include/ms_transform.hrl").
-define(TAB, metrics).
-record(state, {}).
%% @doc track the number of processes for a name
%% this add the current process
-spec track(any()) -> ok | {error, term()}.
track(Name) ->
track(Name, self()).
%% @doc track the number of processes for a name
-spec track(any(), pid()) -> ok | {error, term()}.
track(Name, Pid) ->
%% we only increment once the value
case ets:insert_new(?TAB, {{Pid, Name}, Name}) of
true -> metrics:update(Name);
false -> ok
end,
%% maybe monitor the pid
case ets:insert_new(?TAB, {Pid, m}) of
true -> gen_server:cast(?MODULE, {monitor, Pid});
false -> ok
end.
% ----------------------------------------------------------
% - internal api
% ----------------------------------------------------------
start_link() ->
_ = create_tabs(),
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
create_tabs() ->
case ets:info(?TAB, name) of
undefined ->
?TAB = ets:new(?TAB, [named_table, ordered_set, public,
{write_concurrency, true},
{read_concurrency, true}]);
_ ->
true
end.
% ----------------------------------------------------------
% - gen_server api
% ----------------------------------------------------------
init(_) ->
init_monitors(),
{ok, #state{}}.
handle_call(_Msg, _From, State) ->
{reply, bad_call, State}.
handle_cast({monitor, Pid}, State) ->
_ = erlang:monitor(process, Pid),
{noreply, State}.
handle_info({'DOWN', _, _, Pid, _}, State) ->
_ = process_is_down(Pid),
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
% ----------------------------------------------------------
% - private api
% ----------------------------------------------------------
%% @private
process_is_down(Pid) ->
case ets:take(?TAB, Pid) of
[] ->
ok;
[{Pid, m}] ->
Cond = ets:fun2ms(fun({{A, B}, _}) when A =:= Pid -> B end),
Names = ets:select(?TAB, Cond),
case Names of
[] -> ok;
_ ->
lists:foreach(fun(Name) ->
metrics:update(Name, {c, -1}),
ets:delete(?TAB, {Pid, Name})
end, Names)
end
end.
init_monitors() ->
Cond = ets:fun2ms(fun({A, B}) when B =:= m -> A end),
Pids = ets:select(?TAB, Cond),
[erlang:monitor(process, Pid) || Pid <- Pids].