Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ ERLC_OPTS := +debug_info +"{cover_enabled, true}"
TEST_ERLC_OPTS := -I include -I test/epocxy $(ERLC_OPTS)

CT_OPTS := -cover test/epocxy.coverspec
CT_SUITES = batch_feeder ets_buffer cxy_ctl cxy_cache cxy_fount
CT_SUITES = ets_ring_buffer_ro
## epocxy_sup epocxy_ets_fsm ets_ring_buffer_ro ets_buffer batch_feeder cxy_ctl cxy_cache cxy_fount

DIALYZER_OPTS := -I include -Werror_handling -Wrace_conditions -Wunmatched_returns

Expand Down
160 changes: 160 additions & 0 deletions src/epocxy_ets_fsm.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2014-2014, DuoMark International, Inc.
%%% @author Jay Nelson <jay@duomark.com>
%%% @reference 2014-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
%%% The epocxy ETS FSM is the owner of all ETS tables needed when
%%% constructing ets_buffers and other patterns. It allows the
%%% generation of an unnamed ETS table so that atomic operations
%%% which swap one table for another to replace a set of data can
%%% be efficiently implemented without worrying about the ETS
%%% owning process dying and losing the table.
%%%
%%% Tables are created as sets with public access and a key that
%%% is the first field of a record. The caller may request read
%%% and/or write concurrency to be set. The purpose of generated
%%% tables is for maximum concurrency, so they must be accessible
%%% by all processes.
%%%
%%% @since v0.9.9a
%%% @end
%%%------------------------------------------------------------------------------
-module(epocxy_ets_fsm).
-author('Jay Nelson <jay@duomark.com>').

-behaviour(gen_fsm).

%% API
-export([
start_link/0,
create_ets_table/1, create_ets_table/2,
create_named_ets_table/2,
delete_ets_table/1,
change_owner/2
]).

%% gen_fsm state functions
-export(['READY'/2, 'READY'/3]).

%% gen_fsm callbacks
-export([init/1, terminate/3, code_change/4,
handle_event/3, handle_sync_event/4, handle_info/3]).

-define(SERVER, ?MODULE).

-record(eef_state, {}).


%%%===================================================================
%%% External API
%%%===================================================================

-type ets_concurrency() :: none | read_only | write_only | read_and_write.
-export_type([ets_concurrency/0]).

-spec start_link () -> {ok, pid()}.
-spec create_ets_table (ets_concurrency()) -> ets:tid().
-spec create_ets_table (atom(), ets_concurrency()) -> ets:tid().
-spec create_named_ets_table (atom(), ets_concurrency()) -> ets:tid().
-spec delete_ets_table (atom() | ets:tid()) -> ok.
-spec change_owner (atom() | ets:tid(), pid()) -> ok.

start_link() ->
gen_fsm:start_link({local, ?SERVER}, ?MODULE, {}, []).

create_ets_table(Concurrency_Type) ->
Options = make_options(Concurrency_Type),
gen_fsm:sync_send_event(?SERVER, {create_ets_table, Options}).

create_ets_table(Name, Concurrency_Type)
when is_atom(Name) ->
Options = make_options(Concurrency_Type),
gen_fsm:sync_send_event(?SERVER, {create_ets_table, Name, Options}).

create_named_ets_table(Name, Concurrency_Type)
when is_atom(Name) ->
Options = [named_table | make_options(Concurrency_Type)],
gen_fsm:sync_send_event(?SERVER, {create_ets_table, Name, Options}).

delete_ets_table(Table_Id_Or_Name)
when is_integer (Table_Id_Or_Name);
is_atom (Table_Id_Or_Name) ->
true = gen_fsm:sync_send_event(?SERVER, {delete_ets_table, Table_Id_Or_Name}),
ok.

change_owner(Table_Id_Or_Name, New_Owner)
when is_integer (Table_Id_Or_Name), is_pid (New_Owner);
is_atom (Table_Id_Or_Name), is_pid (New_Owner) ->
true = gen_fsm:sync_send_event(?SERVER, {change_owner, Table_Id_Or_Name, New_Owner}),
ok.

make_options(none) -> make_base_options([]);
make_options(read_only) -> make_base_options([{read_concurrency, true}]);
make_options(write_only) -> make_base_options([{write_concurrency, true}]);
make_options(read_and_write) -> make_base_options([{read_concurrency, true},
{write_concurrency, true}]).

make_base_options(Concurrency_Options) ->
[set, public, {keypos, 2}] ++ Concurrency_Options.


%%%===================================================================
%%% gen_fsm callbacks
%%%===================================================================

-type internal_state() :: #eef_state{}.
-type state_name() :: 'READY'.
-type create_ets_cmd() :: {create_ets_table, proplists:proplist()}
| {create_ets_table, Name::atom(), proplists:proplist()}.
-type delete_ets_cmd() :: {delete_ets_table, ets:tid() | atom()}.
-type change_owner_cmd() :: {change_owner, ets:tid() | atom(), pid()}.
-type stop_cmd() :: stop.
%% -type ready_cmds() :: create_ets_cmd() | delete_ets_cmd() | change_owner_cmd() | stop_cmd().

-spec init({}) -> {ok, 'READY', internal_state()}.
-spec terminate (any(), state_name(), internal_state()) -> ok.
-spec code_change (any(), state_name(), internal_state(), any()) -> {ok, state_name(), #eef_state{}}.

init({}) ->
{ok, 'READY', #eef_state{}}.

terminate (_Reason, _State_Name, #eef_state{}) -> ok.
code_change (_OldVsn, State_Name, #eef_state{} = State, _Extra) -> {ok, State_Name, State}.

%% The FSM has only the 'READY' state.
-type from() :: {pid(), reference()}.
-spec 'READY'(create_ets_cmd(), from(), internal_state()) -> ets:tid();
(delete_ets_cmd(), from(), internal_state()) -> true;
(change_owner_cmd(), from(), internal_state()) -> true;
(stop_cmd(), from(), internal_state()) -> {stop, normal}.

-define(READY(__Cmd), 'READY'(__Cmd, _From, #eef_state{} = State)).
-define(REPLY(__Reply), {reply, __Reply, 'READY', State}).

?READY({create_ets_table, Options}) when is_list(Options) -> ?REPLY(ets:new(no_name, Options));
?READY({create_ets_table, Name, Options}) when is_list(Options), is_atom(Name) -> ?REPLY(ets:new(Name, Options));
?READY({delete_ets_table, Table}) when is_atom(Table); is_integer(Table) -> ?REPLY(ets:delete(Table));
?READY({change_owner, Table, Pid}) when is_atom(Table); is_integer(Table) -> ?REPLY(ets:give_away(Table, Pid, {}));

%% Stop the ets owner FSM process.
'READY'(stop, _From, #eef_state{}) -> {stop, normal}.

%% No asynch events are expected.
'READY'(_Any, #eef_state{} = State) -> {next_state, 'READY', State}.

-spec handle_info (any(), state_name(), internal_state())
-> {next_state, state_name(), internal_state()}.

handle_info (_Info, State_Name, #eef_state{} = State) ->
{next_state, State_Name, State}.

%% Handle event and sync_event aren't used
-spec handle_event(any(), state_name(), internal_state())
-> {next_state, state_name(), internal_state()}.
-spec handle_sync_event(any(), {pid(), reference()}, state_name(), internal_state())
-> {reply, any(), state_name(), internal_state()}.

handle_event (_Event, State_Name, #eef_state{} = State) -> {next_state, State_Name, State}.
handle_sync_event (_Event, _From, State_Name, #eef_state{} = State) -> {reply, ok, State_Name, State}.
52 changes: 52 additions & 0 deletions src/epocxy_sup.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
%%%------------------------------------------------------------------------------
%%% @copyright (c) 2014-2015, DuoMark International, Inc.
%%% @author Jay Nelson <jay@duomark.com>
%%% @reference 2014-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/]
%%% @reference The license is based on the template for Modified BSD from
%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a>
%%% @doc
%%% The epocxy supervisor provides a supervised FSM to manage any ets
%%% tables that are needed by the concurrency library. This FSM is
%%% intended to outlive any patterns which are built on ets tables.
%%%
%%% @since v0.9.9a
%%% @end
%%%------------------------------------------------------------------------------
-module(epocxy_sup).
-author('Jay Nelson <jay@duomark.com>').

-behaviour(supervisor).

%% API
-export([start_link/0]).

%% Supervisor callbacks
-export([init/1]).

-define(SERVER, ?MODULE).


%% ===================================================================
%% API functions
%% ===================================================================

-spec start_link() -> {ok, pid()}.

start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, {}).


%% ===================================================================
%% Supervisor callbacks
%% ===================================================================

-type restart() :: {supervisor:strategy(), non_neg_integer(), non_neg_integer()}.
-type sup_init_return() :: {ok, {restart(), [supervisor:child_spec()]}}.

-spec init({}) -> sup_init_return().

-define(CHILD(__Mod, __Args), {__Mod, {__Mod, start_link, __Args}, temporary, 2000, worker, [__Mod]}).

init({}) ->
Epocxy_Ets_Fsm = ?CHILD(epocxy_ets_fsm, []),
{ok, { {one_for_one, 5, 60}, [Epocxy_Ets_Fsm]} }.
Loading