%%%---------------------------------------------------------------------- %%% File : base_server.erl %%% Author : Ulf Wiger %%% Purpose : %%% Created : 2 Dec 2002 by Ulf Wiger %%%---------------------------------------------------------------------- -module(base_server). -author('ulf.wiger@ericsson.com'). -behaviour(gen_server). % see erl -man gen_server %% External exports -export([start_link/0]). %% API functions -export([get_value/0, set_value/1]). %% Init function -export([install/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). %% The server's state -record(state, {}). -record(base, {key, value}). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- install() -> {ok, Nodes} = application:get_env(kernel, sync_nodes_optional), mnesia:delete_schema(Nodes), mnesia:create_schema(Nodes), mnesia:start(), mnesia:create_table(base, [{ram_copies, Nodes}, {attributes, record_info(fields, base)}]). %% start_link(ServerName, CallbackModule, Arg, Options) %% I think it's good practice to use the module name as registered name. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, _Arg = [], _Options = []). get_value() -> gen_server:call(?MODULE, get_value). set_value(S) -> gen_server:call(?MODULE, {set_value, S}). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init([] = _Arg) -> io:format("~p starting.~n", [?MODULE]), ok = mnesia:wait_for_tables([base], infinity), transaction(fun() -> case mnesia:read({base,var}) of [] -> mnesia:write(#base{key = var, value = undefined}); _ -> ok end end), {ok, #state{}}. %%---------------------------------------------------------------------- %% 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(get_value, _From, State) -> Value = transaction(fun() -> [#base{value = V}] = mnesia:read({base,var}), V end), {reply, Value, State}; handle_call({set_value, V}, _From, State) -> OldValue = transaction(fun() -> [#base{value = OldV} = Obj] = mnesia:read({base, var}), mnesia:write(Obj#base{value = V}), OldV end), {reply, {ok, OldValue}, State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- %% My recommendation: do not consume unknown casts. There should be no such %% thing. handle_cast(Msg, State) -> {stop, {unknown_cast, Msg}, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- %% My recommendation: You should probably consume unknown messages, but %% print them, or log them, or something; chances are it's an error. handle_info(Info, State) -> io:format("unknown msg (~p)~n", [Info]), {noreply, State}. %%---------------------------------------------------------------------- %% Func: code_change/3 %% Purpose: Transform state during code upgrade %% Returns: {ok, NewState}. %% %% Good rule of thumb: always include a code_change function that, at a %% minimum, does nothing (i.e. returns the old state). If you let the %% release_handler run through a full upgrade, a server that doesn't %% implement a working code_change() function will crash the upgrade %% (unless, of course, it's excluded from the upgrade or handled like an %% exception. This is harder to do than to implement the standard code_change %% callback below.) Note that the code_change function differs slightly for %% gen_server, gen_fsm, etc. You have to read the manual... %%---------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(_Reason, _State) -> ok. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- transaction(F) -> mnesia:activity(transaction, F).