dictのサーバ化 其の弐
とりあえず、例外は全部捕まえることにした。
%% File: dict_server.erl %% dictをgen_serverを使ってサーバー化する(ただのwrapper) %% Tsungのソースを参考にした。 %% %% ToDo: %% * from_list, mergeの扱い %% %% -module(dict_server). -behabiour(gen_server). %% External exports -export([start/0, stop/0]). -export([append/2, append_list/2, erase/1, fetch/1, fetch_keys/0, filter/1, find/1, fold/2, is_key/1, map/1, store/2, to_list/0, update/2, update/3, update_counter/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- %% server function start() -> gen_server:start({global, ?MODULE}, ?MODULE, [],[]). stop() -> gen_server:cast({global, ?MODULE}, stop). %% wrappers append(Key, Value) -> gen_server:call({global, ?MODULE}, {append, Key, Value}). append_list(Key, ValList) -> gen_server:call({global, ?MODULE}, {append_list, Key, ValList}). erase(Key) -> gen_server:call({global, ?MODULE}, {erase, Key}). fetch(Key) -> gen_server:call({global, ?MODULE}, {fetch, Key}). fetch_keys() -> gen_server:call({global, ?MODULE}, {fetch_keys}). filter(Pred) -> gen_server:call({global, ?MODULE}, {filter, Pred}). find(Key) -> gen_server:call({global, ?MODULE}, {find, Key}). fold(Fun, Acc0) -> gen_server:call({global, ?MODULE}, {fold, Fun, Acc0}). %from_list(List) is_key(Key) -> gen_server:call({global, ?MODULE}, {is_key, Key}). map(Fun) -> gen_server:call({global, ?MODULE}, {map, Fun}). %merge(Fun) store(Key, Value) -> gen_server:call({global, ?MODULE}, {store, Key, Value}). to_list() -> gen_server:call({global, ?MODULE}, {to_list}). update(Key, Fun) -> gen_server:call({global, ?MODULE}, {update, Key, Fun}). update(Key, Fun, Initial) -> gen_server:call({global, ?MODULE}, {update, Key, Fun, Initial}). update_counter(Key, Increment) -> gen_server:call({global, ?MODULE}, {update_counter, Key, Increment}). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init([]) -> {ok, dict:new()}. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call({append, Key, Value}, _From, State) -> do_call(result_is_new_state, fun() -> dict:append(Key, Value, State) end, State); handle_call({append_list, Key,ValList}, _From, State) -> do_call(result_is_new_state, fun() -> dict:append_list(Key, ValList, State) end, State); handle_call({erase, Key}, _From, State) -> do_call(result_is_new_state, fun() -> dict:erase(Key, State) end, State); handle_call({fetch, Key}, _From, State) -> do_call(result_is_reply, fun() -> dict:fetch(Key, State) end, State); handle_call({fetch_keys}, _From, State) -> do_call(result_is_reply, fun() -> dict:fetch_keys(State) end, State); handle_call({filter, Pred}, _From, State) -> do_call(result_is_new_state, fun() -> dict:filter(Pred, State) end, State); handle_call({find, Key}, _From, State) -> do_call(result_is_reply, fun() -> dict:find(Key, State) end, State); handle_call({fold, Fun, Acc0}, _From, State) -> do_call(result_is_reply, fun() -> dict:fold(Fun, Acc0, State) end, State); handle_call({is_key, Key}, _From, State) -> do_call(result_is_reply, fun() -> dict:is_key(Key, State) end, State); handle_call({map, Fun}, _From, State) -> do_call(result_is_new_state, fun() -> dict:map(Fun, State) end, State); handle_call({store, Key, Value}, _From, State) -> do_call(result_is_new_state, fun() -> dict:store(Key, Value, State) end, State); handle_call({to_list}, _From, State) -> do_call(result_is_reply, fun() -> dict:to_list(State) end, State); handle_call({update, Key, Fun}, _From, State) -> do_call(result_is_new_state, fun() -> dict:update(Key, Fun, State) end, State); handle_call({update, Key, Fun, Initial}, _From, State) -> do_call(result_is_new_state, fun() -> dict:update(Key, Fun, Initial, State) end, State); handle_call({update_counter, Key, Increment}, _From, State) -> do_call(result_is_new_state, fun() -> dict:update_counter(Key, Increment, State) end, State). %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast(stop, State) -> {stop, normal, State}; handle_cast(_Request, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_info(_Info, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignore by gen_server) %%---------------------------------------------------------------------- terminate(normal, _State) -> ok; terminate(_Reason, _State) -> ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState, NewStateData} %%---------------------------------------------------------------------- code_change(_OldVsn, StateData, _Extra) -> {ok, StateData}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- do_call(result_is_new_state, Fun, OldState) -> case error_handle(Fun) of {throw, Term} -> {reply, Term, OldState}; {'EXIT', Reason} -> {reply, {'EXIT', Reason}, OldState}; NewState -> {reply, ok, NewState} end; do_call(result_is_reply, Fun, State) -> case error_handle(Fun) of {throw, Term} -> {reply, Term, State}; {'EXIT', Reason} -> {reply, {'EXIT', Reason}, State}; Result -> {reply, Result, State} end. error_handle(F) -> try F() of Result -> Result catch throw:Term -> {throw, Term}; exit:Reason -> {'EXIT', Reason}; error:Reason -> {'EXIT', {Reason, erlang:get_stacktrace()}} end.
stdlibからthrowやexitが直接呼ばれることないよなぁ。ランタイムエラー(erlang:error)だけしか出てこないんじゃないかという疑問もある。