From 1804245358b731f7f9419e4bcc3b0a63278a98aa Mon Sep 17 00:00:00 2001 From: Pedram Nimreezi Date: Thu, 7 Nov 2013 16:02:21 -0500 Subject: [PATCH] Protect params table and extend per module supervision --- src/glc.erl | 122 ++++++++++--- src/glc_code.erl | 32 ++-- src/gr_counter.erl | 26 ++- src/gr_counter_sup.erl | 42 +++++ src/{gr_counter_mgr.erl => gr_manager.erl} | 48 +++-- src/gr_manager_sup.erl | 42 +++++ src/gr_param.erl | 194 +++++++++++++++++++++ src/gr_param_sup.erl | 42 +++++ src/gr_sup.erl | 8 +- 9 files changed, 474 insertions(+), 82 deletions(-) create mode 100644 src/gr_counter_sup.erl rename src/{gr_counter_mgr.erl => gr_manager.erl} (84%) create mode 100644 src/gr_manager_sup.erl create mode 100644 src/gr_param.erl create mode 100644 src/gr_param_sup.erl diff --git a/src/glc.erl b/src/glc.erl index 1dc0928..ff0a6e6 100644 --- a/src/glc.erl +++ b/src/glc.erl @@ -87,7 +87,7 @@ -record(module, { 'query' :: term(), - tables :: [{atom(), ets:tid()}], + tables :: [{atom(), atom()}], qtree :: term() }). @@ -167,7 +167,7 @@ union(Queries) -> %% function. The name of the query module is expected to be unique. -spec compile(atom(), list()) -> {ok, atom()}. compile(Module, Query) -> - {ok, ModuleData} = module_data(Query), + {ok, ModuleData} = module_data(Module, Query), case glc_code:compile(Module, ModuleData) of {ok, Module} -> {ok, Module} @@ -186,27 +186,73 @@ handle(Module, Event) -> %% is expected to be associated with an existing query module. Calling this %% function will result in a runtime error. -spec delete(atom()) -> ok. -delete(_Module) -> +delete(Module) -> + Params = params_name(Module), + Counts = counts_name(Module), + ManageParams = manage_params_name(Module), + ManageCounts = manage_counts_name(Module), + + [ begin + supervisor:terminate_child(Sup, Name), + supervisor:delete_child(Sup, Name) + end || {Sup, Name} <- + [{gr_manager_sup, ManageParams}, {gr_manager_sup, ManageCounts}, + {gr_param_sup, Params}, {gr_counter_sup, Counts}] + ], + + code:soft_purge(Module), + code:delete(Module), ok. %% @private Map a query to a module data term. --spec module_data(term()) -> {ok, #module{}}. -module_data(Query) -> +-spec module_data(atom(), term()) -> {ok, #module{}}. +module_data(Module, Query) -> %% terms in the query which are not valid arguments to the %% erl_syntax:abstract/1 functions are stored in ETS. %% the terms are only looked up once they are necessary to %% continue evaluation of the query. - Params = ets:new(params, [set,protected]), + %% query counters are stored in a shared ETS table. this should - %% be an optional feature. enable by defaults to simplify tests. - %% the abstract_tables/1 function expects a list of name-tid pairs. + %% be an optional feature. enabled by defaults to simplify tests. + %% the abstract_tables/1 function expects a list of name-atom pairs. %% tables are referred to by name in the generated code. the table/1 - %% function maps names to tids. - Tables = [{params,Params}], + %% function maps names to registered processes response for those tables. + Tables = module_tables(Module), Query2 = glc_lib:reduce(Query), {ok, #module{'query'=Query, tables=Tables, qtree=Query2}}. +%% @private Create a data managed supervised process for params, counter tables +module_tables(Module) -> + Params = params_name(Module), + Counts = counts_name(Module), + ManageParams = manage_params_name(Module), + ManageCounts = manage_counts_name(Module), + Counters = [{input,0}, {filter,0}, {output,0}], + + supervisor:start_child(gr_param_sup, + {Params, {gr_param, start_link, [Params]}, + transient, brutal_kill, worker, [Params]}), + supervisor:start_child(gr_counter_sup, + {Counts, {gr_counter, start_link, [Counts]}, + transient, brutal_kill, worker, [Counts]}), + supervisor:start_child(gr_manager_sup, + {ManageParams, {gr_manager, start_link, [ManageParams, Params, []]}, + transient, brutal_kill, worker, [ManageParams]}), + supervisor:start_child(gr_manager_sup, {ManageCounts, + {gr_manager, start_link, [ManageCounts, Counts, Counters]}, + transient, brutal_kill, worker, [ManageCounts]}), + [{params,Params}, {counters, Counts}]. + +reg_name(Module, Name) -> + list_to_atom("gr_" ++ atom_to_list(Module) ++ Name). + +params_name(Module) -> reg_name(Module, "_params"). +counts_name(Module) -> reg_name(Module, "_counters"). +manage_params_name(Module) -> reg_name(Module, "_params_mgr"). +manage_counts_name(Module) -> reg_name(Module, "_counters_mgr"). + + %% @todo Move comment. %% @private Map a query to a simplified query tree term. @@ -244,21 +290,6 @@ setup_query(Module, Query) -> ?assert(erlang:function_exported(Module, handle, 1)), {compiled, Module}. -nullquery_compiles_test() -> - {compiled, Mod} = setup_query(testmod1, glc:null(false)), - ?assertError(badarg, Mod:table(noexists)). - -params_table_exists_test() -> - {compiled, Mod} = setup_query(testmod2, glc:null(false)), - ?assert(is_integer(Mod:table(params))), - ?assertMatch([_|_], ets:info(Mod:table(params))). - -nullquery_exists_test() -> - {compiled, Mod} = setup_query(testmod3, glc:null(false)), - ?assert(erlang:function_exported(Mod, info, 1)), - ?assertError(badarg, Mod:info(invalid)), - ?assertEqual({null, false}, Mod:info('query')). - events_test_() -> {foreach, fun() -> @@ -274,6 +305,27 @@ events_test_() -> error_logger:tty(true) end, [ + {"null query compiles", + fun() -> + {compiled, Mod} = setup_query(testmod1, glc:null(false)), + ?assertError(badarg, Mod:table(noexists)) + end + }, + {"params table exists", + fun() -> + {compiled, Mod} = setup_query(testmod2, glc:null(false)), + ?assert(is_atom(Mod:table(params))), + ?assertMatch([_|_], gr_param:info(Mod:table(params))) + end + }, + {"null query exists", + fun() -> + {compiled, Mod} = setup_query(testmod3, glc:null(false)), + ?assert(erlang:function_exported(Mod, info, 1)), + ?assertError(badarg, Mod:info(invalid)), + ?assertEqual({null, false}, Mod:info('query')) + end + }, {"init counters test", fun() -> {compiled, Mod} = setup_query(testmod4, glc:null(false)), @@ -381,6 +433,26 @@ events_test_() -> ?assertEqual(1, Mod:info(output)), ?assertEqual(1, receive Msg -> Msg after 0 -> notcalled end) end + }, + {"delete test", + fun() -> + {compiled, Mod} = setup_query(testmod13, glc:null(false)), + ?assert(is_atom(Mod:table(params))), + ?assertMatch([_|_], gr_param:info(Mod:table(params))), + ?assert(is_list(code:which(Mod))), + ?assert(is_pid(whereis(params_name(Mod)))), + ?assert(is_pid(whereis(counts_name(Mod)))), + ?assert(is_pid(whereis(manage_params_name(Mod)))), + ?assert(is_pid(whereis(manage_counts_name(Mod)))), + + glc:delete(Mod), + + ?assertEqual(non_existing, code:which(Mod)), + ?assertEqual(undefined, whereis(params_name(Mod))), + ?assertEqual(undefined, whereis(counts_name(Mod))), + ?assertEqual(undefined, whereis(manage_params_name(Mod))), + ?assertEqual(undefined, whereis(manage_counts_name(Mod))) + end } ] }. diff --git a/src/glc_code.erl b/src/glc_code.erl index 2e2c7c9..1111bd9 100644 --- a/src/glc_code.erl +++ b/src/glc_code.erl @@ -9,7 +9,7 @@ -record(module, { 'query' :: term(), - tables :: [{atom(), ets:tid()}], + tables :: [{atom(), atom()}], qtree :: term() }). @@ -20,7 +20,8 @@ fields = [] :: [{atom(), syntaxTree()}], fieldc = 0 :: non_neg_integer(), paramvars = [] :: [{term(), syntaxTree()}], - paramstab = undefined :: ets:tid() + paramstab = undefined :: atom(), + countstab = undefined :: atom() }). -type nextFun() :: fun((#state{}) -> [syntaxTree()]). @@ -47,6 +48,7 @@ abstract_module(Module, Data) -> -spec abstract_module_(atom(), #module{}) -> [?erl:syntaxTree()]. abstract_module_(Module, #module{tables=Tables, qtree=Tree}=Data) -> {_, ParamsTable} = lists:keyfind(params, 1, Tables), + {_, CountsTable} = lists:keyfind(counters, 1, Tables), AbstractMod = [ %% -module(Module) ?erl:attribute(?erl:atom(module), [?erl:atom(Module)]), @@ -94,12 +96,11 @@ abstract_module_(Module, #module{tables=Tables, qtree=Tree}=Data) -> [?erl:clause([?erl:variable("Event")], none, abstract_filter(Tree, #state{ event=?erl:variable("Event"), - paramstab=ParamsTable}))]) + paramstab=ParamsTable, + countstab=CountsTable}))]) ], %% Transform Term -> Key to Key -> Term - ParamsList = [{K, V} || {V, K} <- ets:tab2list(ParamsTable)], - ets:delete_all_objects(ParamsTable), - ets:insert(ParamsTable, ParamsList), + gr_param:transform(ParamsTable), AbstractMod. %% @private Return the clauses of the table/1 function. @@ -258,22 +259,21 @@ abstract_getparam(Term, OnBound, #state{paramvars=Params}=State) -> -spec abstract_getparam_(term(), nextFun(), #state{}) -> [syntaxTree()]. -abstract_getparam_(Term, OnBound, #state{paramstab=Table, +abstract_getparam_(Term, OnBound, #state{paramstab=ParamsTable, paramvars=Params}=State) -> - Key = case ets:lookup(Table, Term) of + Key = case gr_param:lookup(ParamsTable, Term) of [{_, Key2}] -> Key2; [] -> - Key2 = ets:info(Table, size), - ets:insert(Table, {Term, Key2}), + Key2 = gr_param:size(ParamsTable), + gr_param:insert(ParamsTable, {Term, Key2}), Key2 end, [?erl:match_expr( param_variable(Key), - abstract_apply(ets, lookup_element, + abstract_apply(gr_param, lookup_element, [abstract_apply(table, [?erl:atom(params)]), - ?erl:abstract(Key), - ?erl:abstract(2)])) + ?erl:abstract(Key)])) ] ++ OnBound(State#state{paramvars=[{Term, param_variable(Key)}|Params]}). %% @private Generate a variable name for the value of a field. @@ -317,7 +317,8 @@ param_variable(Key) -> -spec abstract_count(atom()) -> syntaxTree(). abstract_count(Counter) -> abstract_apply(gr_counter, update, - [?erl:abstract(Counter), + [abstract_apply(table, [?erl:atom(counters)]), + ?erl:abstract(Counter), ?erl:abstract({2,1})]). @@ -326,7 +327,8 @@ abstract_count(Counter) -> -spec abstract_getcount(atom()) -> [syntaxTree()]. abstract_getcount(Counter) -> [abstract_apply(gr_counter, check, - [?erl:abstract(Counter)])]. + [abstract_apply(table, [?erl:atom(counters)]), + ?erl:abstract(Counter)])]. %% abstract code util functions diff --git a/src/gr_counter.erl b/src/gr_counter.erl index f11550b..76f7dd5 100644 --- a/src/gr_counter.erl +++ b/src/gr_counter.erl @@ -17,9 +17,9 @@ -behaviour(gen_server). %% API --export([start_link/0, - check/0, check/1, - update/2]). +-export([start_link/1, + check/1, check/2, + update/3]). %% gen_server callbacks -export([init/1, @@ -29,21 +29,19 @@ terminate/2, code_change/3]). --define(SERVER, ?MODULE). - -record(state, {init=true, table_id}). %%%=================================================================== %%% API %%%=================================================================== -check() -> - gen_server:call(?MODULE, check). +check(Server) -> + gen_server:call(Server, check). -check(Counter) -> - gen_server:call(?MODULE, {check, Counter}). +check(Server, Counter) -> + gen_server:call(Server, {check, Counter}). -update(Counter, Value) -> - gen_server:cast(?MODULE, {update, Counter, Value}). +update(Server, Counter, Value) -> + gen_server:cast(Server, {update, Counter, Value}). %%-------------------------------------------------------------------- %% @doc @@ -52,8 +50,8 @@ update(Counter, Value) -> %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} %% @end %%-------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). +start_link(Name) -> + gen_server:start_link({local, Name}, ?MODULE, [], []). %%%=================================================================== %%% gen_server callbacks @@ -94,7 +92,7 @@ handle_call({check, Counter}, _From, State) -> TableId = State#state.table_id, {reply, ets:lookup_element(TableId, Counter, 2), State}; handle_call(_Request, _From, State) -> - Reply = ok, + Reply = {error, unhandled_message}, {reply, Reply, State}. %%-------------------------------------------------------------------- diff --git a/src/gr_counter_sup.erl b/src/gr_counter_sup.erl new file mode 100644 index 0000000..234919b --- /dev/null +++ b/src/gr_counter_sup.erl @@ -0,0 +1,42 @@ +%% Copyright (c) 2013, Pedram Nimreezi +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(gr_counter_sup). + +-behaviour(supervisor). + +-type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). +-type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%% =================================================================== +%% API functions +%% =================================================================== +%% @hidden +-spec start_link() -> startlink_ret(). +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% =================================================================== +%% Supervisor callbacks +%% =================================================================== +%% @hidden +-spec init([]) -> {ok, { {one_for_one, 50, 10}, [supervisor:child_spec()]} }. +init(_Args) -> + {ok, { {one_for_one, 50, 10}, []} }. diff --git a/src/gr_counter_mgr.erl b/src/gr_manager.erl similarity index 84% rename from src/gr_counter_mgr.erl rename to src/gr_manager.erl index 5c9cabc..c64f74e 100644 --- a/src/gr_counter_mgr.erl +++ b/src/gr_manager.erl @@ -12,12 +12,12 @@ %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. --module(gr_counter_mgr). +-module(gr_manager). -behaviour(gen_server). %% API --export([start_link/0]). +-export([start_link/3]). %% gen_server callbacks -export([init/1, @@ -29,16 +29,14 @@ -define(SERVER, ?MODULE). --record(state, {table_id}). - --define(CTR, gr_counter). +-record(state, {table_id :: ets:tid(), managee :: atom()}). %%%=================================================================== %%% API %%%=================================================================== -setup(Data) -> - gen_server:cast(?MODULE, {setup, Data}). +setup(Name, Data) -> + gen_server:cast(Name, {setup, Data}). %%-------------------------------------------------------------------- %% @doc @@ -47,8 +45,8 @@ setup(Data) -> %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} %% @end %%-------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). +start_link(Name, Managee, Data) -> + gen_server:start_link({local, Name}, ?MODULE, [Managee, Data], []). %%%=================================================================== %%% gen_server callbacks @@ -65,10 +63,10 @@ start_link() -> %% {stop, Reason} %% @end %%-------------------------------------------------------------------- -init([]) -> +init([Managee, Data]) -> process_flag(trap_exit, true), - setup([{input,0}, {filter,0}, {output,0}]), - {ok, #state{}}. + setup(self(), Data), + {ok, #state{managee=Managee}}. %%-------------------------------------------------------------------- %% @private @@ -85,7 +83,7 @@ init([]) -> %% @end %%-------------------------------------------------------------------- handle_call(_Request, _From, State) -> - Reply = ok, + Reply = {error, unhandled_message}, {reply, Reply, State}. %%-------------------------------------------------------------------- @@ -98,13 +96,13 @@ handle_call(_Request, _From, State) -> %% {stop, Reason, State} %% @end %%-------------------------------------------------------------------- -handle_cast({setup, Data}, State) -> - Ctr = whereis(?CTR), - link(Ctr), +handle_cast({setup, Data}, State = #state{managee=Managee}) -> + ManageePid = whereis(Managee), + link(ManageePid), TableId = ets:new(?MODULE, [set, private]), ets:insert(TableId, Data), ets:setopts(TableId, {heir, self(), Data}), - ets:give_away(TableId, Ctr, Data), + ets:give_away(TableId, ManageePid, Data), {noreply, State#state{table_id=TableId}}; handle_cast(_Msg, State) -> {noreply, State}. @@ -119,19 +117,19 @@ handle_cast(_Msg, State) -> %% {stop, Reason, State} %% @end %%-------------------------------------------------------------------- -handle_info({'EXIT', _Pid, killed}, State) -> +handle_info({'EXIT', _Pid, _Reason}, State) -> {noreply, State}; -handle_info({'ETS-TRANSFER', TableId, _Pid, Data}, State) -> - Ctr = wait_for_ctr(), - link(Ctr), - ets:give_away(TableId, Ctr, Data), +handle_info({'ETS-TRANSFER', TableId, _Pid, Data}, State = #state{managee=Managee}) -> + ManageePid = wait_for_pid(Managee), + link(ManageePid), + ets:give_away(TableId, ManageePid, Data), {noreply, State#state{table_id=TableId}}. -wait_for_ctr() -> - case whereis(?CTR) of +wait_for_pid(Managee) -> + case whereis(Managee) of undefined -> timer:sleep(1), - wait_for_ctr(); + wait_for_pid(Managee); Pid -> Pid end. diff --git a/src/gr_manager_sup.erl b/src/gr_manager_sup.erl new file mode 100644 index 0000000..49c21cd --- /dev/null +++ b/src/gr_manager_sup.erl @@ -0,0 +1,42 @@ +%% Copyright (c) 2013, Pedram Nimreezi +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(gr_manager_sup). + +-behaviour(supervisor). + +-type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). +-type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%% =================================================================== +%% API functions +%% =================================================================== +%% @hidden +-spec start_link() -> startlink_ret(). +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% =================================================================== +%% Supervisor callbacks +%% =================================================================== +%% @hidden +-spec init([]) -> {ok, { {one_for_one, 50, 10}, [supervisor:child_spec()]} }. +init(_Args) -> + {ok, { {one_for_one, 50, 10}, []} }. diff --git a/src/gr_param.erl b/src/gr_param.erl new file mode 100644 index 0000000..700d6c3 --- /dev/null +++ b/src/gr_param.erl @@ -0,0 +1,194 @@ +%% Copyright (c) 2013, Pedram Nimreezi +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(gr_param). + +-behaviour(gen_server). + +%% API +-export([start_link/1, + list/1, size/1, insert/2, + lookup/2, lookup_element/2, + info/1, update/2, transform/1]). + +%% gen_server callbacks +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). + +-record(state, {init=true, table_id}). + +%%%=================================================================== +%%% API +%%%=================================================================== +list(Server) -> + gen_server:call(Server, list). + +size(Server) -> + gen_server:call(Server, size). + +insert(Server, Data) -> + gen_server:call(Server, {insert, Data}). + +lookup(Server, Term) -> + gen_server:call(Server, {lookup, Term}). + +lookup_element(Server, Term) -> + gen_server:call(Server, {lookup_element, Term}). + +info(Server) -> + gen_server:call(Server, info). + +update(Counter, Value) -> + gen_server:cast(?MODULE, {update, Counter, Value}). + +%% @doc Transform Term -> Key to Key -> Term +transform(Server) -> + gen_server:call(Server, transform). + +%%-------------------------------------------------------------------- +%% @doc +%% Starts the server +%% +%% @spec start_link() -> {ok, Pid} | ignore | {error, Error} +%% @end +%%-------------------------------------------------------------------- +start_link(Name) -> + gen_server:start_link({local, Name}, ?MODULE, [], []). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Initializes the server +%% +%% @spec init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% @end +%%-------------------------------------------------------------------- +init([]) -> + {ok, #state{}}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Handling call messages +%% +%% @spec handle_call(Request, From, State) -> +%% {reply, Reply, State} | +%% {reply, Reply, State, Timeout} | +%% {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, Reply, State} | +%% {stop, Reason, State} +%% @end +%%-------------------------------------------------------------------- +handle_call(list, _From, State) -> + TableId = State#state.table_id, + {reply, ets:tab2list(TableId), State}; +handle_call(size, _From, State) -> + TableId = State#state.table_id, + {reply, ets:info(TableId, size), State}; +handle_call({insert, Data}, _From, State) -> + TableId = State#state.table_id, + {reply, ets:insert(TableId, Data), State}; +handle_call({lookup, Term}, _From, State) -> + TableId = State#state.table_id, + {reply, ets:lookup(TableId, Term), State}; +handle_call({lookup_element, Term}, _From, State) -> + TableId = State#state.table_id, + {reply, ets:lookup_element(TableId, Term, 2), State}; +handle_call(info, _From, State) -> + TableId = State#state.table_id, + {reply, ets:info(TableId), State}; +handle_call(transform, _From, State) -> + TableId = State#state.table_id, + ParamsList = [{K, V} || {V, K} <- ets:tab2list(TableId)], + ets:delete_all_objects(TableId), + ets:insert(TableId, ParamsList), + {reply, ok, State}; +handle_call(_Request, _From, State) -> + Reply = {error, unhandled_message}, + {reply, Reply, State}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Handling cast messages +%% +%% @spec handle_cast(Msg, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% @end +%%-------------------------------------------------------------------- +handle_cast({update, Counter, Value}, State) -> + TableId = State#state.table_id, + ets:update_counter(TableId, Counter, Value), + {noreply, State}; +handle_cast(_Msg, State) -> + {noreply, State}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Handling all non call/cast messages +%% +%% @spec handle_info(Info, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% @end +%%-------------------------------------------------------------------- +handle_info({'ETS-TRANSFER', TableId, _Pid, _Data}, State) -> + {noreply, State#state{table_id=TableId}}; +handle_info(_Info, State) -> + {noreply, State}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_server terminates +%% with Reason. The return value is ignored. +%% +%% @spec terminate(Reason, State) -> void() +%% @end +%%-------------------------------------------------------------------- +terminate(_Reason, _State) -> + ok. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Convert process state when code is changed +%% +%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} +%% @end +%%-------------------------------------------------------------------- +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + + diff --git a/src/gr_param_sup.erl b/src/gr_param_sup.erl new file mode 100644 index 0000000..25d240f --- /dev/null +++ b/src/gr_param_sup.erl @@ -0,0 +1,42 @@ +%% Copyright (c) 2013, Pedram Nimreezi +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(gr_param_sup). + +-behaviour(supervisor). + +-type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). +-type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%% =================================================================== +%% API functions +%% =================================================================== +%% @hidden +-spec start_link() -> startlink_ret(). +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% =================================================================== +%% Supervisor callbacks +%% =================================================================== +%% @hidden +-spec init([]) -> {ok, { {one_for_one, 50, 10}, [supervisor:child_spec()]} }. +init(_Args) -> + {ok, { {one_for_one, 50, 10}, []} }. diff --git a/src/gr_sup.erl b/src/gr_sup.erl index b207d4c..dab4d7c 100644 --- a/src/gr_sup.erl +++ b/src/gr_sup.erl @@ -1,4 +1,5 @@ %% Copyright (c) 2012, Magnus Klaar +%% Copyright (c) 2013, Pedram Nimreezi %% %% Permission to use, copy, modify, and/or distribute this software for any %% purpose with or without fee is hereby granted, provided that the above @@ -25,6 +26,7 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> - Counter = ?CHILD(gr_counter, worker), - CounterMgr = ?CHILD(gr_counter_mgr, worker), - {ok, {{one_for_one, 5, 10}, [Counter, CounterMgr]}}. + CounterSup = ?CHILD(gr_counter_sup, supervisor), + ParamSup = ?CHILD(gr_param_sup, supervisor), + MgrSup = ?CHILD(gr_manager_sup, supervisor), + {ok, {{one_for_one, 5, 10}, [CounterSup, ParamSup, MgrSup]}}.