Current section
Files
Jump to
Current section
Files
src/booklet_ffi.erl
-module(booklet_ffi).
-behaviour(application).
-export([start/2, stop/1]).
-behaviour(supervisor).
-export([init/1, start_worker/0]).
-export([make/1, get/1, update/2]).
% TODO: Query the worker again for the table if the table ref is invalid
% TODO: store default_value inside the tuple so we can always recover
% -- Public API ----------------------------------------------------------------
make(DefaultValue) ->
Table = get_table(),
Key = make_ref(),
true = ets:insert_new(Table, {Key, 0, DefaultValue}),
{Table, Key}.
get({Table, Key}) ->
ets:lookup_element(Table, Key, 3).
update({Table, Key}, Updater) ->
[{Key, Rev, Value}] = ets:lookup(Table, Key),
NewValue = Updater(Value),
Pattern = {Key, Rev, '_'},
Replacement = {const, {Key, Rev + 1, NewValue}},
case ets:select_replace(Table, [{Pattern, [], [Replacement]}]) of
0 -> update({Table, Key}, Updater);
_ -> NewValue
end.
% -- APPLICATION ---------------------------------------------------------------
start(_StartType, _StartArgs) ->
supervisor:start_link(?MODULE, supervisor).
stop(_State) ->
ok.
% -- SUPERVISOR ----------------------------------------------------------------
init(supervisor) ->
SupFlags = #{
strategy => one_for_one,
intensity => 6,
period => 3600
},
ChildSpec = #{
id => booklet,
start => {?MODULE, start_worker, []},
shutdown => brutal_kill
},
{ok, {SupFlags, [ChildSpec]}}.
% -- CHILD ---------------------------------------------------------------------
start_worker() ->
Pid = spawn_link(fun worker/0),
true = register(booklet, Pid),
{ok, Pid}.
worker() ->
Table = ets:new(booklet, [set, public, {keypos, 1}, {write_concurrency, auto}, {read_concurrency, true}]),
worker_loop(Table).
worker_loop(Table) ->
receive
{get, From, Ref} -> From ! {Ref, Table}, worker_loop(Table);
_ -> worker_loop(Table)
end.
get_table() ->
Ref = make_ref(),
booklet ! {get, self(), Ref},
receive {Ref, Table} -> Table end.