[erlang-questions] gen_server and init
Jesper Louis Andersen
jesper.louis.andersen@REDACTED
Mon Jul 27 20:29:17 CEST 2015
On Mon, Jul 27, 2015 at 4:40 PM, Loïc Hoguin <essen@REDACTED> wrote:
> This and sending yourself a message is a bad idea. It will usually work,
> until it doesn't, and you will have a very hard time figuring out why.
I'm quite curious. What is the scenario where this fails? I have never
observed this in practice on very busy systems, and I think going through
proc_lib for this is a detour I'd rather not we'd have to take.
Here are two modules, z0 and z.erl. They are very boring gen servers:
-module(z0).
-behaviour(gen_server).
-ifdef(PULSE).
-include_lib("pulse_otp/include/pulse_otp.hrl").
-endif.
-export([start_link/0]).
%% Operational API
-export([read/0, read_p/1]).
%% gen_server API
-export([
init/1,
handle_cast/2,
handle_call/3,
terminate/2,
code_change/3,
handle_info/2
]).
%% API
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
read() ->
gen_server:call(?MODULE, read).
read_p(P) ->
gen_server:call(P, read).
%% Callbacks
init([]) ->
{ok, initializing, 0}.
handle_call(read, _From, State) ->
{reply, State, State}.
handle_cast(_M, State) ->
{noreply, State}.
handle_info(timeout, _State) ->
{noreply, ready}.
terminate(_How, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
---------------------
-module(z).
-behaviour(gen_server).
-ifdef(PULSE).
-include_lib("pulse_otp/include/pulse_otp.hrl").
-endif.
-export([start_link/0]).
%% Operational API
-export([read/0, read_p/1]).
%% gen_server API
-export([
init/1,
handle_cast/2,
handle_call/3,
terminate/2,
code_change/3,
handle_info/2
]).
%% API
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
read() ->
gen_server:call(?MODULE, read).
read_p(P) ->
gen_server:call(P, read).
%% Callbacks
init([]) ->
self() ! timeout,
{ok, initializing}.
handle_call(read, _From, State) ->
{reply, State, State}.
handle_cast(_M, State) ->
{noreply, State}.
handle_info(timeout, _State) ->
{noreply, ready}.
terminate(_How, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
---------------------
Ok, now with these down, we can use EQC and PULSE to generate the
counterexample if any should be in there:
-module(z_eqc).
-include_lib("eqc/include/eqc.hrl").
-include_lib("eqc/include/eqc_statem.hrl").
-include_lib("pulse/include/pulse.hrl").
-include_lib("pulse_otp/include/pulse_otp.hrl").
-compile(export_all).
-record(state, { init, ref }).
initial_state() ->
#state { init = false }.
server_start() ->
{ok, Pid} = z:start_link(),
unlink(Pid),
Pid.
%% SERVER START_LINK
server_start_pre(S) -> not initialized(S).
server_start_args(_S) -> [].
server_start_next(S, Ref, []) ->
S#state { ref = Ref, init = true }.
server_start_post(_S, [], Ref) -> is_pid(Ref).
%% READ BY PID
read_p(P) ->
z:read_p(P).
read_p_pre(S) -> initialized(S).
read_p_args(#state { ref = Ref }) -> [Ref].
read_p_return(_S, [_Ref]) -> ready.
%% READ
read() ->
try z:read() of
M -> M
catch
_Class:_Err ->
{error, undefined}
end.
read_args(_S) -> [].
read_return(#state { init = false}, []) -> {error, undefined};
read_return(#state { init = true}, []) -> ready.
%% Run a test under PULSE to randomize the process schedule as well.
prop_model_pulse() ->
?SETUP(fun() ->
setup(),
fun() -> ok end
end,
?LET(Shrinking, parameter(shrinking, false),
?FORALL(Cmds, parallel_commands(?MODULE),
?ALWAYS(if not Shrinking -> 1; Shrinking -> 20 end,
?PULSE(HSR={_, _, R},
begin
ok = cleanup(),
run_parallel_commands(?MODULE, Cmds)
end,
aggregate(command_names(Cmds),
pretty_commands(?MODULE, Cmds, HSR, R == ok))))))).
setup() ->
error_logger:tty(false),
ok.
cleanup() ->
case whereis(z) of
undefined -> ok;
Pid ->
exit(Pid, kill),
timer:sleep(3)
end,
ok.
initialized(#state { init = I }) -> I.
pulse_instrument() ->
[ pulse_instrument(File) || File <- filelib:wildcard("*.erl") ],
ok.
pulse_instrument(File) ->
io:format("Compiling: ~p~n", [File]),
{ok, Mod} = compile:file(File, [{d, 'PULSE', true}, {d, 'WITH_PULSE',
true},
{d, 'EQC_TESTING', true},
{parse_transform, pulse_instrument}]),
code:purge(Mod),
code:load_file(Mod),
Mod.
-----------------------------
However, when I run this, I get no errors, even though I'm trying to behave
non-nicely:
I won't call the Pid until I know about it.
I will blindly call the name, z, and if I get an error on this, I'll verify
that the possible linearization is that the process was not started yet. If
I get a result, I force it to be ready:
60> eqc:module({testing_time, 30}, z_eqc).
prop_model_pulse:
....................................................................................................(x10)....................................................................................................(x100)............................(x10).......
Time limit reached: 30.0 seconds.
OK, passed 3970 tests
49.857% {z_eqc,read,0}
44.492% {z_eqc,read_p,1}
5.651% {z_eqc,server_start,0}
[]
...
So: What is the scenario where this approach fails?
--
J.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20150727/0bbf7617/attachment.htm>
More information about the erlang-questions
mailing list