[erlang-questions] Re: Shell is terminated (and then restarts) mysteriously
Robert Virding
robert.virding@REDACTED
Sat Jan 22 05:55:17 CET 2011
It can be simplified even more. We use this test.erl:
-module(test).
-export([test/0]).
test() ->
fun (_, []) -> ok;
(F, [_|Xs]) ->
F(F, Xs)
%% F(F, Xs, xxx)
end.
And use it like:
rv ~/erlang$ erl
Erlang R14B (erts-5.8.1) [source] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.8.1 (abort with ^G)
1> c(test).
{ok,test}
2> test:test().
#Fun<test.0.93149374>>
Here we move the comment to use the incorrect recursive call and continue:
3> c(test).
{ok,test}
4> c(test).
*** ERROR: Shell process terminated! ***
Eshell V5.8.1 (abort with ^G)
1>
The cause of the behaviour is that the first call to test:test/0 returns a fun which is saved in the history list. Changing that fun and recompiling/reloading the file loads in a new version. The old fun now points to the old version of the module. Recompiling/reloading the file again means that the fun on the history list would now point to a non-existent module so the shell process is killed by the code server.
In the case of an error the history list contains the actual exit reason. You can see this with h(). In your first case the exit reason contained the fun so the shell process was killed.
Funs don't fit really well into code handling so it can sometimes be hard to detect to which version of the code it points, especially if the fun itself hasn't been modified or the module has been significantly modified. In your second case the fun in the exit reason wasn't the one you changed.
Robert
----- "Taylor Venable" <taylor@REDACTED> wrote:
> On Thu, Jan 20, 2011 at 23:02, Taylor Venable <taylor@REDACTED>
> wrote:
> > Hi there. I'm trying to learn Erlang by working on some simple
> > programs, and as I'm working with the shell I'm noticing some (what
> to
> > me at least is) odd behaviour. Every once in a while, after
> compiling
> > some code, it will state that the shell process was terminated.
>
> OK, after much playing I finally have a working minimal example of
> how
> to reproduce this every single time, at least for my installation.
>
> But first, with R14B I hacked lib/kernel/src/user_drv.erl on line 202
> to print out R, which I assume is the reason why the EXIT message was
> received. The value of R was the atom 'killed'. Don't know if that
> has
> any significance. Then I upgraded to an unmodified R14B01, but there
> was no difference in behaviour.
>
> After a few hours of coding, and about 10 shell restarts, I happened
> to notice a pattern. From that I worked my way to this example. Start
> with this in test.erl:
>
> -module(test).
> -export([test/0]).
>
> test() ->
> F = fun (_, []) -> ok;
> (F, [_|Xs]) ->
> F(F, Xs, xxx)
> end,
> F(F, [foo]).
>
> Note: the recursive invocation of F is an intentional bug. Fire up
> erl:
>
> $ erl
> Erlang R14B01 (erts-5.8.2) [source] [64-bit] [smp:2:2] [rq:2]
> [async-threads:0] [hipe] [kernel-poll:false]
>
> Eshell V5.8.2 (abort with ^G)
> 1> c(test).
> {ok,test}
> 2> test:test().
> ** exception error: test:'-test/0-fun-0-'/2 called with 3 arguments
> in function test:'-test/0-fun-0-'/2
>
> OK, good, that's what I expect. Now change "F(F, Xs, xxx)" to "F(F,
> Xs)" and then compile and run again:
>
> 3> c(test).
> {ok,test}
> 4> test:test().
> ok
>
> Again, good. Now put the buggy recursive invocation of F back (i.e.
> undo the fix we just made so we have the original file again).
> Compile
> and run again:
>
> 5> c(test).
> *** ERROR: Shell process terminated! ***
> Eshell V5.8.2 (abort with ^G)
> 1>
>
> There is the shell termination. Using this method, I get the
> termination every single time.
>
> And, if I use either result(1), or history(1), at the beginning, I do
> not get the termination. This sounds to me similar to what Dan
> Gudmundsson said about the shell (probably through the history or
> result list) keeping some kind of reference into the module's code.
>
> However, constructing a similar dynamic function call does NOT cause
> the problem:
>
> -module(test).
> -export([test/0]).
>
> test() ->
> F = fun (F) -> F(xxx) end,
> G = fun () -> ok end,
> F(G).
>
> 1> c(test).
> {ok,test}
> 2> test:test().
> ** exception error: test:'-test/0-fun-1-'/0 called with one argument
> in function test:'-test/0-fun-0-'/1
>
> This is what we expect, now remove the xxx and recompile.
>
> 3> c(test).
> {ok,test}
> 4> test:test().
> ok
>
> OK, let's put the xxx back and try it again.
>
> 5> c(test).
> {ok,test}
> 6> test:test().
> ** exception error: test:'-test/0-fun-1-'/0 called with one argument
> in function test:'-test/0-fun-0-'/1
>
> To my admittedly ignorant eyes, it seems like exactly the same kind
> of
> error that I've created, but unlike before, this example does NOT
> break the shell. It seems to me that the only difference of much
> consequence is whether the buggy call is recursive or not. The
> recursive call breaks the shell, the non-recursive call does not. So
> I
> guess now the question for me is, is this an expected result? And if
> so, why?
>
> Thanks for reading.
>
> --
> Taylor C. Venable
> http://metasyntax.net/
>
> ________________________________________________________________
> erlang-questions (at) erlang.org mailing list.
> See http://www.erlang.org/faq.html
> To unsubscribe; mailto:erlang-questions-unsubscribe@REDACTED
More information about the erlang-questions
mailing list