浏览代码

Protect statistics table and data from user errors

amiramix-master
Pedram Nimreezi 11 年前
父节点
当前提交
81e3060c53
共有 8 个文件被更改,包括 521 次插入108 次删除
  1. +126
    -99
      src/glc.erl
  2. +4
    -7
      src/glc_code.erl
  3. +2
    -1
      src/goldrush.app.src
  4. +27
    -0
      src/gr_app.erl
  5. +161
    -0
      src/gr_counter.erl
  6. +170
    -0
      src/gr_counter_mgr.erl
  7. +30
    -0
      src/gr_sup.erl
  8. +1
    -1
      src/gre.erl

+ 126
- 99
src/glc.erl 查看文件

@ -200,12 +200,10 @@ module_data(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.
Counters = ets:new(counters, [set,public]),
ets:insert(Counters, [{input,0}, {filter,0}, {output,0}]),
%% the abstract_tables/1 function expects a list of name-tid pairs.
%% tables are referred to by name in the generated code. the table/1
%% function maps names to tids.
Tables = [{params,Params}, {counters,Counters}],
Tables = [{params,Params}],
Query2 = glc_lib:reduce(Query),
{ok, #module{'query'=Query, tables=Tables, qtree=Query2}}.
@ -261,102 +259,131 @@ nullquery_exists_test() ->
?assertError(badarg, Mod:info(invalid)),
?assertEqual({null, false}, Mod:info('query')).
init_counters_test() ->
{compiled, Mod} = setup_query(testmod4, glc:null(false)),
?assertEqual(0, Mod:info(input)),
?assertEqual(0, Mod:info(filter)),
?assertEqual(0, Mod:info(output)).
filtered_event_test() ->
%% If no selection condition is specified no inputs can match.
{compiled, Mod} = setup_query(testmod5, glc:null(false)),
glc:handle(Mod, gre:make([], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(1, Mod:info(filter)),
?assertEqual(0, Mod:info(output)).
nomatch_event_test() ->
%% If a selection condition but no body is specified the event
%% is expected to count as filtered out if the condition does
%% not hold.
{compiled, Mod} = setup_query(testmod6, glc:eq('$n', 'noexists@nohost')),
glc:handle(Mod, gre:make([{'$n', 'noexists2@nohost'}], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(1, Mod:info(filter)),
?assertEqual(0, Mod:info(output)).
opfilter_eq_test() ->
%% If a selection condition but no body is specified the event
%% counts as input to the query, but not as filtered out.
{compiled, Mod} = setup_query(testmod7, glc:eq('$n', 'noexists@nohost')),
glc:handle(Mod, gre:make([{'$n', 'noexists@nohost'}], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(0, Mod:info(filter)),
?assertEqual(1, Mod:info(output)),
done.
opfilter_gt_test() ->
{compiled, Mod} = setup_query(testmod8, glc:gt(a, 1)),
glc:handle(Mod, gre:make([{'a', 2}], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(0, Mod:info(filter)),
glc:handle(Mod, gre:make([{'a', 0}], [list])),
?assertEqual(2, Mod:info(input)),
?assertEqual(1, Mod:info(filter)),
?assertEqual(1, Mod:info(output)),
done.
opfilter_lt_test() ->
{compiled, Mod} = setup_query(testmod9, glc:lt(a, 1)),
glc:handle(Mod, gre:make([{'a', 0}], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(0, Mod:info(filter)),
?assertEqual(1, Mod:info(output)),
glc:handle(Mod, gre:make([{'a', 2}], [list])),
?assertEqual(2, Mod:info(input)),
?assertEqual(1, Mod:info(filter)),
?assertEqual(1, Mod:info(output)),
done.
allholds_op_test() ->
{compiled, Mod} = setup_query(testmod10,
glc:all([glc:eq(a, 1), glc:eq(b, 2)])),
glc:handle(Mod, gre:make([{'a', 1}], [list])),
glc:handle(Mod, gre:make([{'a', 2}], [list])),
?assertEqual(2, Mod:info(input)),
?assertEqual(2, Mod:info(filter)),
glc:handle(Mod, gre:make([{'b', 1}], [list])),
glc:handle(Mod, gre:make([{'b', 2}], [list])),
?assertEqual(4, Mod:info(input)),
?assertEqual(4, Mod:info(filter)),
glc:handle(Mod, gre:make([{'a', 1},{'b', 2}], [list])),
?assertEqual(5, Mod:info(input)),
?assertEqual(4, Mod:info(filter)),
?assertEqual(1, Mod:info(output)),
done.
anyholds_op_test() ->
{compiled, Mod} = setup_query(testmod11,
glc:any([glc:eq(a, 1), glc:eq(b, 2)])),
glc:handle(Mod, gre:make([{'a', 2}], [list])),
glc:handle(Mod, gre:make([{'b', 1}], [list])),
?assertEqual(2, Mod:info(input)),
?assertEqual(2, Mod:info(filter)),
glc:handle(Mod, gre:make([{'a', 1}], [list])),
glc:handle(Mod, gre:make([{'b', 2}], [list])),
?assertEqual(4, Mod:info(input)),
?assertEqual(2, Mod:info(filter)),
done.
with_function_test() ->
Self = self(),
{compiled, Mod} = setup_query(testmod12,
glc:with(glc:eq(a, 1), fun(Event) -> Self ! gre:fetch(a, Event) end)),
glc:handle(Mod, gre:make([{a,1}], [list])),
?assertEqual(1, Mod:info(output)),
?assertEqual(1, receive Msg -> Msg after 0 -> notcalled end),
done.
events_test_() ->
{foreach,
fun() ->
error_logger:tty(false),
application:start(syntax_tools),
application:start(compiler),
application:start(goldrush)
end,
fun(_) ->
application:stop(goldrush),
application:stop(compiler),
application:stop(syntax_tools),
error_logger:tty(true)
end,
[
{"init counters test",
fun() ->
{compiled, Mod} = setup_query(testmod4, glc:null(false)),
?assertEqual(0, Mod:info(input)),
?assertEqual(0, Mod:info(filter)),
?assertEqual(0, Mod:info(output))
end
},
{"filtered events test",
fun() ->
%% If no selection condition is specified no inputs can match.
{compiled, Mod} = setup_query(testmod5, glc:null(false)),
glc:handle(Mod, gre:make([], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(1, Mod:info(filter)),
?assertEqual(0, Mod:info(output))
end
},
{"nomatch event test",
fun() ->
%% If a selection condition but no body is specified the event
%% is expected to count as filtered out if the condition does
%% not hold.
{compiled, Mod} = setup_query(testmod6, glc:eq('$n', 'noexists@nohost')),
glc:handle(Mod, gre:make([{'$n', 'noexists2@nohost'}], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(1, Mod:info(filter)),
?assertEqual(0, Mod:info(output))
end
},
{"opfilter equal test",
fun() ->
%% If a selection condition but no body is specified the event
%% counts as input to the query, but not as filtered out.
{compiled, Mod} = setup_query(testmod7, glc:eq('$n', 'noexists@nohost')),
glc:handle(Mod, gre:make([{'$n', 'noexists@nohost'}], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(0, Mod:info(filter)),
?assertEqual(1, Mod:info(output))
end
},
{"opfilter greater than test",
fun() ->
{compiled, Mod} = setup_query(testmod8, glc:gt(a, 1)),
glc:handle(Mod, gre:make([{'a', 2}], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(0, Mod:info(filter)),
glc:handle(Mod, gre:make([{'a', 0}], [list])),
?assertEqual(2, Mod:info(input)),
?assertEqual(1, Mod:info(filter)),
?assertEqual(1, Mod:info(output))
end
},
{"opfilter less than test",
fun() ->
{compiled, Mod} = setup_query(testmod9, glc:lt(a, 1)),
glc:handle(Mod, gre:make([{'a', 0}], [list])),
?assertEqual(1, Mod:info(input)),
?assertEqual(0, Mod:info(filter)),
?assertEqual(1, Mod:info(output)),
glc:handle(Mod, gre:make([{'a', 2}], [list])),
?assertEqual(2, Mod:info(input)),
?assertEqual(1, Mod:info(filter)),
?assertEqual(1, Mod:info(output))
end
},
{"allholds op test",
fun() ->
{compiled, Mod} = setup_query(testmod10,
glc:all([glc:eq(a, 1), glc:eq(b, 2)])),
glc:handle(Mod, gre:make([{'a', 1}], [list])),
glc:handle(Mod, gre:make([{'a', 2}], [list])),
?assertEqual(2, Mod:info(input)),
?assertEqual(2, Mod:info(filter)),
glc:handle(Mod, gre:make([{'b', 1}], [list])),
glc:handle(Mod, gre:make([{'b', 2}], [list])),
?assertEqual(4, Mod:info(input)),
?assertEqual(4, Mod:info(filter)),
glc:handle(Mod, gre:make([{'a', 1},{'b', 2}], [list])),
?assertEqual(5, Mod:info(input)),
?assertEqual(4, Mod:info(filter)),
?assertEqual(1, Mod:info(output))
end
},
{"anyholds op test",
fun() ->
{compiled, Mod} = setup_query(testmod11,
glc:any([glc:eq(a, 1), glc:eq(b, 2)])),
glc:handle(Mod, gre:make([{'a', 2}], [list])),
glc:handle(Mod, gre:make([{'b', 1}], [list])),
?assertEqual(2, Mod:info(input)),
?assertEqual(2, Mod:info(filter)),
glc:handle(Mod, gre:make([{'a', 1}], [list])),
glc:handle(Mod, gre:make([{'b', 2}], [list])),
?assertEqual(4, Mod:info(input)),
?assertEqual(2, Mod:info(filter))
end
},
{"with function test",
fun() ->
Self = self(),
{compiled, Mod} = setup_query(testmod12,
glc:with(glc:eq(a, 1), fun(Event) -> Self ! gre:fetch(a, Event) end)),
glc:handle(Mod, gre:make([{a,1}], [list])),
?assertEqual(1, Mod:info(output)),
?assertEqual(1, receive Msg -> Msg after 0 -> notcalled end)
end
}
]
}.
union_error_test() ->
?assertError(badarg, glc:union([glc:eq(a, 1)])),

+ 4
- 7
src/glc_code.erl 查看文件

@ -316,9 +316,8 @@ param_variable(Key) ->
%% @todo Pass state record. Only Generate code if `statistics' is enabled.
-spec abstract_count(atom()) -> syntaxTree().
abstract_count(Counter) ->
abstract_apply(ets, update_counter,
[abstract_apply(table, [?erl:atom(counters)]),
?erl:abstract(Counter),
abstract_apply(gr_counter, update,
[?erl:abstract(Counter),
?erl:abstract({2,1})]).
@ -326,10 +325,8 @@ abstract_count(Counter) ->
%% @todo Pass state record. Only Generate code if `statistics' is enabled.
-spec abstract_getcount(atom()) -> [syntaxTree()].
abstract_getcount(Counter) ->
[abstract_apply(ets, lookup_element,
[abstract_apply(table, [?erl:atom(counters)]),
?erl:abstract(Counter),
?erl:abstract(2)])].
[abstract_apply(gr_counter, check,
[?erl:abstract(Counter)])].
%% abstract code util functions

+ 2
- 1
src/goldrush.app.src 查看文件

@ -1,7 +1,8 @@
{application, goldrush, [
{description, "Erlang event stream processor"},
{vsn, "0.1.2"},
{vsn, "0.1.3"},
{registered, []},
{applications, [kernel, stdlib, syntax_tools, compiler]},
{mod, {gr_app, []}},
{env, []}
]}.

+ 27
- 0
src/gr_app.erl 查看文件

@ -0,0 +1,27 @@
%% Copyright (c) 2012, Magnus Klaar <klaar@ninenines.eu>
%%
%% 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_app).
-behaviour(application).
-export([
start/2,
stop/1
]).
start(_Type, _Args) ->
gr_sup:start_link().
stop(_State) ->
ok.

+ 161
- 0
src/gr_counter.erl 查看文件

@ -0,0 +1,161 @@
%% Copyright (c) 2013, Pedram Nimreezi <deadzen@deadzen.com>
%%
%% 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).
-behaviour(gen_server).
%% API
-export([start_link/0,
check/0, check/1,
update/2]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-define(SERVER, ?MODULE).
-record(state, {init=true, table_id}).
%%%===================================================================
%%% API
%%%===================================================================
check() ->
gen_server:call(?MODULE, check).
check(Counter) ->
gen_server:call(?MODULE, {check, Counter}).
update(Counter, Value) ->
gen_server:cast(?MODULE, {update, Counter, Value}).
%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?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(check, _From, State) ->
TableId = State#state.table_id,
{reply, {ok, ets:tab2list(TableId)}, State};
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, 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
%%%===================================================================

+ 170
- 0
src/gr_counter_mgr.erl 查看文件

@ -0,0 +1,170 @@
%% Copyright (c) 2013, Pedram Nimreezi <deadzen@deadzen.com>
%%
%% 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_mgr).
-behaviour(gen_server).
%% API
-export([start_link/0]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-define(SERVER, ?MODULE).
-record(state, {table_id}).
-define(CTR, gr_counter).
%%%===================================================================
%%% API
%%%===================================================================
setup(Data) ->
gen_server:cast(?MODULE, {setup, Data}).
%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
process_flag(trap_exit, true),
setup([{input,0}, {filter,0}, {output,0}]),
{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(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_cast({setup, Data}, State) ->
Ctr = whereis(?CTR),
link(Ctr),
TableId = ets:new(?MODULE, [set, private]),
ets:insert(TableId, Data),
ets:setopts(TableId, {heir, self(), Data}),
ets:give_away(TableId, Ctr, Data),
{noreply, State#state{table_id=TableId}};
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({'EXIT', _Pid, killed}, State) ->
{noreply, State};
handle_info({'ETS-TRANSFER', TableId, _Pid, Data}, State) ->
Ctr = wait_for_ctr(),
link(Ctr),
ets:give_away(TableId, Ctr, Data),
{noreply, State#state{table_id=TableId}}.
wait_for_ctr() ->
case whereis(?CTR) of
undefined ->
timer:sleep(1),
wait_for_ctr();
Pid -> Pid
end.
%%--------------------------------------------------------------------
%% @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
%%%===================================================================

+ 30
- 0
src/gr_sup.erl 查看文件

@ -0,0 +1,30 @@
%% Copyright (c) 2012, Magnus Klaar <klaar@ninenines.eu>
%%
%% 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.
%% @doc Top level supervisor for goldrush.
-module(gr_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
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]}}.

+ 1
- 1
src/gre.erl 查看文件

@ -25,7 +25,7 @@
]).
-type event() :: {list, [{atom(), term()}]}.
-export_types([event/0]).
-export_type([event/0]).
%% @doc Construct an event term.
-spec make(term(), [list]) -> event().

正在加载...
取消
保存