|
| 1 | +%%%------------------------------------------------------------------------------ |
| 2 | +%%% @copyright (c) 2014-2014, DuoMark International, Inc. |
| 3 | +%%% @author Jay Nelson <jay@duomark.com> |
| 4 | +%%% @reference 2014-2015 Development sponsored by TigerText, Inc. [http://tigertext.com/] |
| 5 | +%%% @reference The license is based on the template for Modified BSD from |
| 6 | +%%% <a href="http://opensource.org/licenses/BSD-3-Clause">OSI</a> |
| 7 | +%%% @doc |
| 8 | +%%% The epocxy ETS FSM is the owner of all ETS tables needed when |
| 9 | +%%% constructing ets_buffers and other patterns. It allows the |
| 10 | +%%% generation of an unnamed ETS table so that atomic operations |
| 11 | +%%% which swap one table for another to replace a set of data can |
| 12 | +%%% be efficiently implemented without worrying about the ETS |
| 13 | +%%% owning process dying and losing the table. |
| 14 | +%%% |
| 15 | +%%% Tables are created as sets with public access and a key that |
| 16 | +%%% is the first field of a record. The caller may request read |
| 17 | +%%% and/or write concurrency to be set. The purpose of generated |
| 18 | +%%% tables is for maximum concurrency, so they must be accessible |
| 19 | +%%% by all processes. |
| 20 | +%%% |
| 21 | +%%% @since v0.9.8d |
| 22 | +%%% @end |
| 23 | +%%%------------------------------------------------------------------------------ |
| 24 | +-module(epocxy_ets_fsm). |
| 25 | +-author('Jay Nelson <jay@duomark.com>'). |
| 26 | + |
| 27 | +-behaviour(gen_fsm). |
| 28 | + |
| 29 | +%% API |
| 30 | +-export([ |
| 31 | + start_link/0, |
| 32 | + create_ets_table/1, create_ets_table/2, |
| 33 | + delete_ets_table/1 |
| 34 | + ]). |
| 35 | + |
| 36 | +%% gen_fsm state functions |
| 37 | +-export(['READY'/2, 'READY'/3]). |
| 38 | + |
| 39 | +%% gen_fsm callbacks |
| 40 | +-export([init/1, terminate/3, code_change/4, |
| 41 | + handle_event/3, handle_sync_event/4, handle_info/3]). |
| 42 | + |
| 43 | +-define(SERVER, ?MODULE). |
| 44 | + |
| 45 | +-record(eef_state, {}). |
| 46 | + |
| 47 | + |
| 48 | +%%%=================================================================== |
| 49 | +%%% External API |
| 50 | +%%%=================================================================== |
| 51 | + |
| 52 | +-type ets_concurrency() :: none | read_only | write_only | read_and_write. |
| 53 | +-export_type([ets_concurrency/0]). |
| 54 | + |
| 55 | +-spec start_link() -> {ok, pid()}. |
| 56 | +-spec create_ets_table(ets_concurrency()) -> ets:tid(). |
| 57 | +-spec create_ets_table(atom(), ets_concurrency()) -> ets:tid(). |
| 58 | +-spec delete_ets_table(atom() | ets:tid()) -> ok. |
| 59 | + |
| 60 | +start_link() -> |
| 61 | + gen_fsm:start_link({local, ?SERVER}, ?MODULE, {}, []). |
| 62 | + |
| 63 | +create_ets_table(Concurrency_Type) -> |
| 64 | + Options = make_options(Concurrency_Type), |
| 65 | + gen_fsm:sync_send_event(?SERVER, {create_ets_table, Options}). |
| 66 | + |
| 67 | +create_ets_table(Name, Concurrency_Type) |
| 68 | + when is_atom(Name) -> |
| 69 | + Options = [named_table | make_options(Concurrency_Type)], |
| 70 | + gen_fsm:sync_send_event(?SERVER, {create_ets_table, Name, Options}). |
| 71 | + |
| 72 | +delete_ets_table(Table_Id_Or_Name) -> |
| 73 | + gen_fsm:sync_send_event(?SERVER, {delete_ets_table, Table_Id_Or_Name}). |
| 74 | + |
| 75 | +make_options(none) -> make_base_options([]); |
| 76 | +make_options(read_only) -> make_base_options([{read_concurrency, true}]); |
| 77 | +make_options(write_only) -> make_base_options([{write_concurrency, true}]); |
| 78 | +make_options(read_and_write) -> make_base_options([{read_concurrency, true}, |
| 79 | + {write_concurrency, true}]). |
| 80 | + |
| 81 | +make_base_options(Concurrency_Options) -> |
| 82 | + [set, public, {keypos, 2}] ++ Concurrency_Options. |
| 83 | + |
| 84 | + |
| 85 | +%%%=================================================================== |
| 86 | +%%% gen_fsm callbacks |
| 87 | +%%%=================================================================== |
| 88 | + |
| 89 | +-type internal_state() :: #eef_state{}. |
| 90 | +-type state_name() :: 'READY'. |
| 91 | +-type create_ets_cmd() :: {create_ets_table, proplists:proplist()} |
| 92 | + | {create_ets_table, Name::atom(), proplists:proplist()}. |
| 93 | +-type delete_ets_cmd() :: {delete_ets_table, ets:tid() | atom()}. |
| 94 | +-type stop_cmd() :: stop. |
| 95 | +%% -type ready_cmds() :: create_ets_cmd() | delete_ets_cmd() | stop_cmd(). |
| 96 | + |
| 97 | +-spec init({}) -> {ok, 'READY', internal_state()}. |
| 98 | +-spec terminate (any(), state_name(), internal_state()) -> ok. |
| 99 | +-spec code_change (any(), state_name(), internal_state(), any()) -> {ok, state_name(), #eef_state{}}. |
| 100 | + |
| 101 | +init({}) -> |
| 102 | + {ok, 'READY', #eef_state{}}. |
| 103 | + |
| 104 | +terminate (_Reason, _State_Name, #eef_state{}) -> ok. |
| 105 | +code_change (_OldVsn, State_Name, #eef_state{} = State, _Extra) -> {ok, State_Name, State}. |
| 106 | + |
| 107 | +%% The FSM has only the 'READY' state. |
| 108 | +-type from() :: {pid(), reference()}. |
| 109 | +-spec 'READY'(create_ets_cmd(), from(), internal_state()) -> ets:tid(); |
| 110 | + (delete_ets_cmd(), from(), internal_state()) -> ok; |
| 111 | + (stop_cmd(), from(), internal_state()) -> {stop, normal}. |
| 112 | + |
| 113 | +'READY'({create_ets_table, Name, Options}, _From, #eef_state{} = State) when is_atom(Name), is_list(Options) -> {reply, ets:new(Name, Options), 'READY', State}; |
| 114 | +'READY'({create_ets_table, Options}, _From, #eef_state{} = State) when is_list(Options) -> {reply, ets:new(noname, Options), 'READY', State}; |
| 115 | +'READY'({delete_ets_table, Table}, _From, #eef_state{} = State) when is_atom(Table); is_integer(Table) -> {reply, ets:delete(Table), 'READY', State}; |
| 116 | + |
| 117 | +%% Stop the ets owner FSM process. |
| 118 | +'READY'(stop, _From, #eef_state{}) -> {stop, normal}. |
| 119 | + |
| 120 | +%% No asynch events are expected. |
| 121 | +'READY'(_Any, #eef_state{} = State) -> {next_state, 'READY', State}. |
| 122 | + |
| 123 | +-spec handle_info (any(), state_name(), internal_state()) |
| 124 | + -> {next_state, state_name(), internal_state()}. |
| 125 | + |
| 126 | +handle_info (_Info, State_Name, #eef_state{} = State) -> |
| 127 | + {next_state, State_Name, State}. |
| 128 | + |
| 129 | +%% Handle event and sync_event aren't used |
| 130 | +-spec handle_event(any(), state_name(), internal_state()) |
| 131 | + -> {next_state, state_name(), internal_state()}. |
| 132 | +-spec handle_sync_event(any(), {pid(), reference()}, state_name(), internal_state()) |
| 133 | + -> {reply, any(), state_name(), internal_state()}. |
| 134 | + |
| 135 | +handle_event (_Event, State_Name, #eef_state{} = State) -> {next_state, State_Name, State}. |
| 136 | +handle_sync_event (_Event, _From, State_Name, #eef_state{} = State) -> {reply, ok, State_Name, State}. |
0 commit comments