You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by kx...@apache.org on 2015/09/23 14:29:34 UTC

[40/50] rebar commit: updated refs/heads/import to 5dea85d

Add memoization server

Copy memo.erl from https://github.com/tuncer/memo and rename to
rmemo.erl for use in rebar. We rename it to avoid collisions.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-rebar/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-rebar/commit/02c43007
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-rebar/tree/02c43007
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-rebar/diff/02c43007

Branch: refs/heads/import
Commit: 02c43007b4500018b4bc028d4ddff6f11001b4a8
Parents: a3ab9cd
Author: Tuncer Ayaz <tu...@gmail.com>
Authored: Thu Jun 11 20:03:14 2015 +0200
Committer: Tuncer Ayaz <tu...@gmail.com>
Committed: Thu Jun 11 20:22:22 2015 +0200

----------------------------------------------------------------------
 ebin/rebar.app |   3 +-
 src/rmemo.erl  | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 279 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-rebar/blob/02c43007/ebin/rebar.app
----------------------------------------------------------------------
diff --git a/ebin/rebar.app b/ebin/rebar.app
index 9ebc83c..975fabc 100644
--- a/ebin/rebar.app
+++ b/ebin/rebar.app
@@ -45,7 +45,8 @@
               rebar_xref,
               rebar_metacmds,
               rebar_getopt,
-              rebar_mustache ]},
+              rebar_mustache,
+              rmemo ]},
   {registered, []},
   {applications,
    [

http://git-wip-us.apache.org/repos/asf/couchdb-rebar/blob/02c43007/src/rmemo.erl
----------------------------------------------------------------------
diff --git a/src/rmemo.erl b/src/rmemo.erl
new file mode 100644
index 0000000..752c811
--- /dev/null
+++ b/src/rmemo.erl
@@ -0,0 +1,277 @@
+%%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%%% ex: ft=erlang ts=4 sw=4 et
+%%%
+%%%-------------------------------------------------------------------
+%%% @author Tuncer Ayaz
+%%% @copyright 2015, Tuncer Ayaz
+%%% @doc
+%%% memoization server
+%%% @end
+%%%-------------------------------------------------------------------
+%%%
+%%% Copyright (c) 2015 Tuncer Ayaz
+%%%
+%%% 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(rmemo).
+
+-behaviour(gen_server).
+
+%% API
+-export(
+   [
+    start/0,
+    start_link/0,
+    stop/0,
+    call/2,
+    call/3
+   ]).
+
+%% gen_server callbacks
+-export(
+   [
+    init/1,
+    handle_call/3,
+    handle_cast/2,
+    handle_info/2,
+    terminate/2,
+    code_change/3
+   ]).
+
+-define(SERVER, ?MODULE).
+-define(TABLE, ?MODULE).
+
+-record(state,
+        {
+          ets_tab :: ets:tab()
+        }).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Start the server
+%% @end
+%%--------------------------------------------------------------------
+-type reason() :: term().
+-type error() :: {error, reason()}.
+-type start_res() :: {ok, pid()} | 'ignore' | error().
+-spec start() -> start_res().
+start() ->
+    gen_server:start({local, ?SERVER}, ?MODULE, [], []).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Start the server
+%% @end
+%%--------------------------------------------------------------------
+-type start_link_res() :: start_res().
+-spec start_link() -> start_link_res().
+start_link() ->
+    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Stop the server
+%% @end
+%%--------------------------------------------------------------------
+stop() ->
+    gen_server:cast(?SERVER, stop).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Call function and memoize result
+%%
+%% Instead of
+%%
+%% <code>Res = Fun(A1, A2, [List1])</code>
+%%
+%% you call
+%%
+%% <code>Res = memo:call(Fun, [A1, A2, [List1]])</code>
+%%
+%% or instead of
+%%
+%% <code>
+%% Res = mod:expensive_function(A1, A2, [List1])
+%% </code>
+%%
+%% you call
+%%
+%% <code>
+%% Res = memo:call(fun mod:expensive_function/3, [A1, A2, [List1]])
+%% </code>
+%%
+%% and any subsequent call will fetch the cached result and avoid the
+%% computation.
+%%
+%% This is of course only useful for expensive computations that are
+%% known to produce the same result given same arguments. It's worth
+%% mentioning that your call should be side-effect free, as naturally
+%% those won't be replayed.
+%%
+%% @end
+%%--------------------------------------------------------------------
+-type fun_args() :: list().
+-spec call(fun(), fun_args()) -> term().
+call(F, A) ->
+    call_1({F, A}).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Call function and memoize result
+%%
+%% Instead of
+%%
+%% <code>Res = mod:expensive_function(A1, A2, [List1])</code>
+%%
+%% you call
+%%
+%% <code>Res = memo:call(mod, expensive_function, [A1, A2, [List1]])</code>
+%%
+%% and any subsequent call will fetch the cached result and avoid the
+%% computation.
+%%
+%% This is of course only useful for expensive computations that are
+%% known to produce the same result given same arguments. It's worth
+%% mentioning that your call should be side-effect free, as naturally
+%% those won't be replayed.%%
+%%
+%% @end
+%%--------------------------------------------------------------------
+%% fun() is not just the name of a fun, so we define an alias for
+%% atom() for call(M, F, A).
+-type fun_name() :: atom().
+-spec call(module(), fun_name(), fun_args()) -> term().
+call(M, F, A) when is_list(A) ->
+    call_1({M, F, A}).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initialize the server
+%% @end
+%%--------------------------------------------------------------------
+init(_) ->
+    {ok,
+     #state{
+        ets_tab = ets_tab()
+       }
+    }.
+
+-spec ets_tab() -> ets:tab().
+ets_tab() ->
+    ets:new(
+      ?TABLE,
+      [
+       named_table,
+       protected,
+       set,
+       {read_concurrency, true}
+      ]
+     ).
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handle call messages
+%% @end
+%%--------------------------------------------------------------------
+handle_call({save, Key, Res}, _From, State) ->
+    {reply, save(Key, Res), State};
+handle_call(_Request, _From, State) ->
+    {reply, {error, undefined_call}, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handle cast messages
+%% @end
+%%--------------------------------------------------------------------
+handle_cast(stop, State) ->
+    {stop, normal, State};
+handle_cast(_Msg, State) ->
+    {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handle all non call/cast messages
+%% @end
+%%--------------------------------------------------------------------
+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.
+%% @end
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+    ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%% @end
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+-type call() :: {module(), fun_name(), fun_args()} | {fun(), fun_args()}.
+-spec call_1(call()) -> term().
+call_1(Call) ->
+    Key = key(Call),
+    case ets:lookup(?TABLE, Key) of
+        [] ->
+            Res = apply(Call),
+            true = gen_server:call(?SERVER, {save, Key, Res}, infinity),
+            Res;
+        [{Key, Mem}] ->
+            Mem
+    end.
+
+-type key_args() :: call().
+-type key() :: non_neg_integer().
+-spec key(key_args()) -> key().
+key(Call) ->
+    erlang:phash2(Call).
+
+-spec apply(call()) -> term().
+apply({F, A}) ->
+    erlang:apply(F, A);
+apply({M, F, A}) ->
+    erlang:apply(M, F, A).
+
+-type val() :: term().
+-spec save(key(), val()) -> true.
+save(K, V) ->
+    ets:insert(?TABLE, {K, V}).