%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2014-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
%% The reason for this (test) counter server is that the
%% agent test suite is implemented in such a way that the
%% agent is started once and then used for several test cases.
%% Each request is given a request id which *was* generated using
%% random! It is therefor possible, although unlikely, that a
%% request may get a request id that has recently been used,
%% which will cause the agent to silently reject the request.
%% For this reason, we start this server at the start of the
%% agent suite and stop it at the end and all request ids are
%% generated by this server.
%%
-module(snmp_test_mgr_counter_server).
-export([start/0, stop/0, increment/4]).
-define(SERVER, ?MODULE).
-define(TAB, snmp_test_mgr_counter_tab).
%%%-------------------------------------------------------------------
%%% API
%%%-------------------------------------------------------------------
-spec start() -> ok.
start() ->
Parent = self(),
ReqIdServer = spawn(fun() -> init(Parent) end),
receive
{ReqIdServer, ok} ->
ok;
{ReqIdServer, {error, Reason}} ->
exit({failed_starting_counter_server, Reason})
after 5000 ->
exit(ReqIdServer, kill), % Cleanup, just in case
exit({failed_starting_counter_server, timeout})
end.
-spec stop() -> {ok, Counters :: list()} | {error, Reason :: term()}.
stop() ->
request(stop).
-spec increment(Counter :: atom(),
Initial :: non_neg_integer(),
Increment :: pos_integer(),
Max :: pos_integer()) ->
Next :: pos_integer().
increment(Counter, Initial, Increment, Max) ->
Request = {increment, Counter, Initial, Increment, Max},
case request(Request) of
{ok, ReqId} ->
ReqId;
{error, Reason} ->
exit(Reason)
end.
request(Request) ->
Id = make_ref(),
Msg = {self(), Id, Request},
try
begin
global:send(?SERVER, Msg),
receive
{reply, Id, Reply} ->
{ok, Reply}
end
end
catch
T:E ->
{error, {T, E}}
end.
%%%-------------------------------------------------------------------
%%% Internal functions
%%%-------------------------------------------------------------------
init(Parent) ->
p("starting"),
case global:register_name(?SERVER, self()) of
yes ->
p("name registration ok"),
Parent ! {self(), ok};
no ->
p("name registration failed"),
Parent ! {self(), registration_failed},
exit(registration_failed)
end,
ets:new(?TAB, [set, named_table, {keypos, 1}]),
loop().
loop() ->
receive
{From, Id, {increment, Counter, Initial, Increment, Max}} ->
Position = 2,
Threshold = Max,
SetValue = Initial,
UpdateOp = {Position, Increment, Threshold, SetValue},
NextVal =
try ets:update_counter(?TAB, Counter, UpdateOp) of
Next when is_integer(Next) ->
p("increment ~w: (next) ~w", [Counter, Next]),
Next
catch
error:badarg ->
%% Oups, first time
p("increment ~w: (initial) ~w", [Counter, Initial]),
ets:insert(?TAB, {Counter, Initial}),
Initial
end,
From ! {reply, Id, NextVal},
loop();
{From, Id, stop} ->
p("stop"),
Counters = ets:tab2list(?TAB),
From ! {reply, Id, Counters},
exit(normal)
end.
p(F) ->
p(F, []).
p(F, A) ->
io:format("*** [~s] COUNTER-SERVER [~w] " ++ F ++ "~n",
[snmp_test_lib:formated_timestamp(), self() | A]).