简体   繁体   中英

Can't send anything to spawned Erlang process

I have the following Erlang code:

#!/usr/bin/env escript
%%! -pz ../deps/amqp_client ../deps/rabbit_common ../deps/amqp_client/ebin ../deps/rabbit_common/ebin

% RMQ module
-module(rmq).
-export([main/1, send/1, validate/0, test/0]).
-include_lib("../deps/amqp_client/include/amqp_client.hrl").

main(_) ->
    %send(<<"test_esio">>),
    %validate(),
    Pid = spawn(rmq, test, []),
    % Pid = spawn(fun() -> test() end), <= I've tried this way too
    Pid ! s.

test() ->
    receive
        s ->
            io:format("BAR ~n"),
            send(<<"esio">>),
            test();
        get ->
            validate(),
            test();
        _ ->
            io:format("FOO"),
            test()
    end.

I run this with: excript rmq.erl

This code doesn't work. Looks like spawn doesn't work.

Rest of my code works, function send and validate works correctly if I run it from main (I've commented its). What I'm doing wrong?

Sorry, maybe it's a dumb question but I'm a beginner with erlang. I've tried search answer in internet and books and I failed...

The problem is not actually in spawn, but in module/escript confusion.

In few words, escript file are not really modules, not from point of Erlang VM, even if you use -module() directive. They are interpreted, and not compiled at all, and definitely they can not be called by module like "rmq:test()", or in you case trough dynamic module call by spawn .

Easiest solution is separate script from actual modules. In your rmq.es you would just start some proper module:

#!/usr/bin/env escript
%%! -pz ../deps/amqp_client ../deps/rabbit_common ../deps/amqp_client/ebin ../deps/rabbit_common/ebin

main(_) ->
  rmq:start().

And there in module rmq.erl :

-module(rmq).
-export([start/0, send/1, validate/0, test/0]).
-include_lib("../deps/amqp_client/include/amqp_client.hrl").

start() ->
    Pid = spawn(rmq, test, []),

    %% Pid = spawn(?MODULE, test, []),  %% works with macro too

    %% Pid = spawn(fun() -> test() end), <= I've tried this way too
    Pid ! s.

test() ->
    receive
        s ->
            io:format("BAR ~n"),
            send(<<"esio">>),
            test();
        get ->
            validate(),
            test();
        _ ->
            io:format("FOO"),
            test()
    end.

Or you could just start this module without escript, with -run flag like this

erl -pz deps/*/ebin -run rmq start 

EDIT regarding compilation problems

You compile your modules with erlc command . To just compile use erlc rmq.erl , which will produce rmq.beam file in current directory. But convention is to keep all your source files in src directory, all compiled files in ebin direcory, and things like run-scripts could be placed in the top directory. Something like that:

project
|-- ebin
|   |-- rmq.beam
|
|-- src
|   |-- rmq.erl
|
|-- rmq.es

Assuming that you run all your shell commands form project directory, to compile all file from src and place .beam binaries in ebin use erlc src/rmq.erl -o ebin , or erlc src/* -o ebin In documentation you can find you explanation of -o flag"

-o directory

The directory where the compiler should place the output files. If not specified, output files will be placed in the current working directory.

Then, after compilation you can run your code, either with erl Erlang VM or using escript (which kind-off uses erl .

erl to runs code from compiled modules, and to do that he needs to be able to locate those compiled *.ebin binaries. For this he uses code path, which is the list of directors in which he will search for those files. This list automatically consist standard library directories, and of course you can add to it directories with your own code with use of -pa flag.

-pa Dir1 Dir2 ...

Adds the specified directories to the beginning of the code path, similar to code:add_pathsa/1. See code(3). As an alternative to -pa, if several directories are to be prepended to the code and the directories have a common parent directory, that parent directory could be specified in the ERL_LIBS environment variable. See code(3).

In your case it would be erl -pa ebin , or to include binaries of all deps you can use erl -pa ebin -pa deps/*/ebin .

Exactly same options are used in second line of your escript. With exception of * character, which will not be expand like it would be in the shell. In escript you have to provide paths to each dependency separately. But the idea of including -pa ebin stays exactly the same.

To automate and standardize this process tools like rebar and erlang.mk where created (I would recommend the later). Using those should help you a little with your workflow.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM