Search code examples
erlangets

Different behaviour of ETS with and without a shell


First a disclaimer I am learning erlang. Not an expert here at all. While making some examples using ETS I came across something I am not understanding (even after searching).

I have a process where I create a public ETS with

TableID = ets:new(tablename, [public])}

I then pass TableID to other processes. When I do this running the modules form the shell, all is ok. When I run exactly the same modules with erl -noshell -s ... or even without the -noshell option, it fails. I keep getting error:badarg as if the tabled does not exist. The ID is properly passes, but the table is actually behaving as private!

Is there a difference between running modules interactively from the shell or without?

Thanks


I am adding an example of the code I am using to try and debug the issue. As it is a piece of a larger software (and it is basically stripped to the bone to find the issue), it might be difficult to understand.

-record(loop_state, {
        commands
        }).

start() ->
    LoopState = #loop_state{commands = ets:new(commands, [public])},
    tcpserver_otp_backend:start(?MODULE, 7000, {?MODULE, loop}, LoopState).

loop(Socket, LoopState = #loop_state{commands = Commands}) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Data} ->
            % the call below fails, no error generated, AND only in non interactive shell
            A = newCommand(Commands, Data), 
            gen_tcp:send(Socket, A),
            loop(Socket, LoopState);
        {error, closed} ->
            ok
    end.


 newCommand(CommandTableId, Command) ->
    case ets:lookup(CommandTableId,Command) of 
        [] ->
            _A = ets:insert(CommandTableId, {Command, 1}),
            <<1, "new", "1">>; % used for testing
        _ ->
            <<1, "old", "1">> % used for testing
    end.

When I remove the "offending command" ets:lookup, all works again as int he interactive shell.


Solution

  • The problem seems to be that you create the ets table in your start() function. An ets table has an owner (by default the creating process), and when the owner dies, the table gets deleted. When you run the start/0 function from the command line by passing -s to erl, that owner process will be some internal process in the Erlang kernel that is part of handling the startup sequence. Regardless of whether you pass -noshell or not, that process is probably transient and will die before the lookup function gets time to execute, so the table no longer exists when the lookup finally happens.

    The proper place to create the ets table would be in the init() callback function of the gen_server that you start up. If it's supposed to be a public est table accessed by several processes, then you might want to create a separate server process whose task it is to own the table.