0

I am trying to make sense of the following error:

4> c(sv).
{ok,sv}
5> B=sv:start().
<0.93.0>
6> =ERROR REPORT==== 14-Jul-2020::21:44:13.256000 ===
Error in process <0.93.0> with exit value:
{undef,[{common,createProcess,
                [{monitor,monitor,
                          {monstate,undefined,undefined,undefined,true,
                                    undefined,false}}],
                []},
        {sv,server,1,[{file,"sv.erl"},{line,14}]}]}

I have a sv module that instantiates a process from monitor module which in turn instantiates a process from worker module. Instantiantions take place via a call to the common module. Both sv and monitor use records defined in a .hrl module.

sv.erl -> monitor.erl -> worker.erl
 |            |
   common.erl
   records.hrl

common.erl

%%% common functionality module
-module(common).
-export([createProcess/1]).
-import(worker,[worker/1]).
-import(monitor,[monitor/1]).
-include("records.hrl").

createProcess({M,F,A})->
    Pid=spawn(M,F,[A]),
    Ref=erlang:monitor(process,Pid),
    {Pid,Ref}.

records.hrl

-record(monstate,{
    queue,
    qc,
    wpid,
    free=true,
    wref,
    init=false
}).

-record(sstate,{
    init=false,
    mpid=null,
    mref=null
}).

sv.erl

-module(sv).
-import(common,[createProcess/1]).
-include("records.hrl").
-export([start/0,server/1]).



start()->
    spawn(?MODULE,server,[#sstate{init=false}]).

server(State=#sstate{init=I})when I=:=false ->
    {MPid,MRef}=createProcess({monitor,monitor,#monstate{init=false}}),
    server(State#sstate{init=true,mpid=MPid,mref=MRef});

server(State=#sstate{mpid=MPid,mref=MRef})->
    receive
           {From,state}->From ! State,
                            server(State);
           {From,Message}-> MPid ! {request,{From,Message}},
                            server(State);
                
            {'DOWN',MRef,process,MPid,_}-> {NewMPid,NewMRef}=createProcess({?MODULE,monitor,#monstate{init=false}}),
                                            server(State#sstate{mpid=NewMPid,mref=NewMRef});
            _ ->exit(invalid_message)
                                    
    end.

monitor.erl

%%% servers as both a gen_server that can be queried  and a supervisor for its worker process
-module(monitor).
-export([monitor/1]).
-import(common,[createProcess/1]).

-include("records.hrl").

-define(QUEUE_SIZE,5).

tryEnqueue(Message,MState=#monstate{queue=Q,qc=C}) when C<?QUEUE_SIZE->
    NewQueue=queue:in(Message,Q),
    {queued,MState#monstate{qc=C+1,queue=NewQueue}};
tryEnqueue(_,MState)->{queue_full,MState}.

monitor(MState=#monstate{wpid=_,wref=_,init=I}) when I=:= false ->
    {WorkerPid,WorkerRef}=createProcess({worker,worker,self()}),
    monitor(MState#monstate{wpid=WorkerPid,wref=WorkerRef,init=true,qc=0,queue=queue:new()});

monitor(MState=#monstate{wpid=W,free=Free,wref=Ref,queue=Q,qc=C})->
    receive
        
        {request,{From ,Message}} -> case Free of 
                                            true -> W ! {From,Message},
                                                    monitor(MState#monstate{free=false});
                                                                             
                                            false -> 
                                                     St=case tryEnqueue({From,Message},MState) of 
                                                            {queue_full,S} -> From ! {queue_full,Message},S;
                                                            {queued,S} -> S
                                                        end,
                                                      monitor(St)
                                      end;
                                         
                                        
                                      
                                  
        {worker,{finished,_}}-> case queue:out(Q) of
                                    {{_,Element},Rest} -> W ! Element,
                                                          monitor(MState#monstate{free=false,queue=Rest,qc=C-1});
                                    {empty,Rest}       -> monitor(MState#monstate{free=true,queue=Rest})
                                end;

        {'DOWN',Ref,process,_,_}->
             {NewWorkerPid,NewWorkerRef}=createProcess({?MODULE,worker,self()}),
             monitor(MState#monstate{wpid=NewWorkerPid,wref=NewWorkerRef,free=true});

        _->exit(invalid_message)

    end.

So i do not know how to interpret the error:

  • there was a call to the common module , that had a MFA of : [{monitor,monitor,{monstate,undefined,undefined,.....}] . The content inside the { } looks okay but i do not get the encompassing [ ].

P.S My common module that spawns processes from both the sv and the monitor module has those modules imported and their associated records included too.I do not understand the problem.

Bercovici Adrian
  • 8,794
  • 17
  • 73
  • 152

1 Answers1

1

The error states that the common:createProcess/1 is undefined. Most likely this is happening because the module common was not loaded previously in the code server.
Run an c(common). too.

Keep in mind that the -import compile directive only means Calls in this module to the funcion X/Y are to be performed by the module Module as Module:X/Y , but does not create a module dependency

José M
  • 3,294
  • 1
  • 14
  • 16
  • I still do not get if module `A` imports module `B` , why do i need to compile module `B` separately? Anyway ,after i compiled the `common` module i get an infinite loop , still with `undef`.Where should my modules be imported ? Shouldn't they be in the module that calls the `spawn` ? (in my case the `common` ) . – Bercovici Adrian Jul 14 '20 at 19:54
  • 2
    @BercoviciAdrian Erlang `import` is not at all like, say, Python `import`; it isn't needed to be able to call another module's functions. In fact, it is never needed -- it's available only for convenience, and frankly, you should never use it. In Erlang, all `import` does is make another module's functions callable without a `module:` prefix. See [this answer](https://stackoverflow.com/a/21638327) for more explanation. – Steve Vinoski Jul 14 '20 at 20:28
  • 1
    @BercoviciAdrian if the monitor module was not loaded either, the spawned process will terminate, prompting the spawn of another process... and so on. (Also, +1 to avoid using `import,` it hurts legibility a lot because you have to check it to know if a call is local or remote, and some modules are quite big) – José M Jul 14 '20 at 20:47
  • Well then when having alot of modules , do you load them one by one ? It seems impractical. – Bercovici Adrian Jul 15 '20 at 05:36
  • 1
    @BercoviciAdrian You don't believe that multinode, >80 modules, erlang services are started in such a way, do you?. Modules are grouped in applications, which in turn form a release. The rrelease is he one that has the compiled code and the boot script. Check [learn you some erlang chapter on applications](https://learnyousomeerlang.com/building-otp-applications) and the ones that follow – José M Jul 15 '20 at 06:41