You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by be...@apache.org on 2014/02/13 17:35:47 UTC
[14/23] goldrush commit: updated refs/heads/import-master to 71e6321
Protect statistics table and data from user errors
Project: http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/commit/81e3060c
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/tree/81e3060c
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/diff/81e3060c
Branch: refs/heads/import-master
Commit: 81e3060c53c07a2d9d78b1a7554be0c3d567a117
Parents: 879c698
Author: Pedram Nimreezi <de...@deadzen.com>
Authored: Thu Nov 7 02:13:23 2013 -0500
Committer: Pedram Nimreezi <de...@deadzen.com>
Committed: Thu Nov 7 02:21:18 2013 -0500
----------------------------------------------------------------------
src/glc.erl | 225 +++++++++++++++++++++++++-------------------
src/glc_code.erl | 11 +--
src/goldrush.app.src | 3 +-
src/gr_app.erl | 27 ++++++
src/gr_counter.erl | 161 +++++++++++++++++++++++++++++++
src/gr_counter_mgr.erl | 170 +++++++++++++++++++++++++++++++++
src/gr_sup.erl | 30 ++++++
src/gre.erl | 2 +-
8 files changed, 521 insertions(+), 108 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/glc.erl
----------------------------------------------------------------------
diff --git a/src/glc.erl b/src/glc.erl
index ecca4b7..1dc0928 100644
--- a/src/glc.erl
+++ b/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)])),
http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/glc_code.erl
----------------------------------------------------------------------
diff --git a/src/glc_code.erl b/src/glc_code.erl
index 810ca3c..1c20880 100644
--- a/src/glc_code.erl
+++ b/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
http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/goldrush.app.src
----------------------------------------------------------------------
diff --git a/src/goldrush.app.src b/src/goldrush.app.src
index 6feac3c..4f482b4 100644
--- a/src/goldrush.app.src
+++ b/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, []}
]}.
http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gr_app.erl
----------------------------------------------------------------------
diff --git a/src/gr_app.erl b/src/gr_app.erl
new file mode 100644
index 0000000..20b3f9b
--- /dev/null
+++ b/src/gr_app.erl
@@ -0,0 +1,27 @@
+%% Copyright (c) 2012, Magnus Klaar <kl...@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.
http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gr_counter.erl
----------------------------------------------------------------------
diff --git a/src/gr_counter.erl b/src/gr_counter.erl
new file mode 100644
index 0000000..f11550b
--- /dev/null
+++ b/src/gr_counter.erl
@@ -0,0 +1,161 @@
+%% Copyright (c) 2013, Pedram Nimreezi <de...@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
+%%%===================================================================
+
+
http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gr_counter_mgr.erl
----------------------------------------------------------------------
diff --git a/src/gr_counter_mgr.erl b/src/gr_counter_mgr.erl
new file mode 100644
index 0000000..5c9cabc
--- /dev/null
+++ b/src/gr_counter_mgr.erl
@@ -0,0 +1,170 @@
+%% Copyright (c) 2013, Pedram Nimreezi <de...@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
+%%%===================================================================
+
+
+
http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gr_sup.erl
----------------------------------------------------------------------
diff --git a/src/gr_sup.erl b/src/gr_sup.erl
new file mode 100644
index 0000000..b207d4c
--- /dev/null
+++ b/src/gr_sup.erl
@@ -0,0 +1,30 @@
+%% Copyright (c) 2012, Magnus Klaar <kl...@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]}}.
http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gre.erl
----------------------------------------------------------------------
diff --git a/src/gre.erl b/src/gre.erl
index eca4e2c..bea1981 100644
--- a/src/gre.erl
+++ b/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().