I have a process running on node2. Can I register this process using register/2 on node1? Basically I am trying to do this:
register(process_name, spawn_link(node2, module, function, [Arg1, Arg2]))
I get this error:
** exception error: bad argument
in function register/2
called as register(process_name, <5902.92.0>)
When I register a process local to node1, this works perfectly fine. I could not find any documentation which prevents registration of processes of other nodes.
Thanks.
Actually it is well documented, and the expected behaviour as register() is for local process registration.
http://www.erlang.org/doc/man/erlang.html#register-2
Failure: badarg if PidOrPort is not an existing, local process or port, [...]
If you want global registration across your cluster, read http://erlang.org/doc/man/global.html
Note that if you use standard OTP behaviours, (gen_server, etc) most of the time you don't need to use the global module directly.
Related
I was trying to execute a function a few seconds in the future by using timer:apply_after() but when I tried to compile the module it threw an error.
For example, say I have a module with two functions - one that prints an age, and another that uses a timer to execute it at a future time.
-module(test).
-export([main/0]).
print_age(Age) ->
io:format("Your age: ~p~n", [Age]).
main() ->
timer:apply_after(2000, test, print_age, [20]).
When I compile the module, I get:
> c(test).
test.erl:4: Warning: function print_age/1 is unused
{ok,test}
Although there's nothing in the timer docs to indicate it, the function must be exported in order for the timer to execute it:
-module(test).
-export([main/0, print_age/1]).
print_age(Age) ->
io:format("Your age: ~p~n", [Age]).
main() ->
timer:apply_after(2000, test, print_age, [20]).
One could get around the compiler error by adding -compile({nowarn_unused_function, {print_age,1}}). to the top of the module, but then it'll turn into a runtime error when the timer attempts to execute the function:
=ERROR REPORT==== 6-Jun-2018::13:37:14 ===
Error in process <0.89.0> with exit value:
{undef,[{test,print_age,[20],[]}]}
I don't know all the inner details of how timer works, but this answer indicates that it's running in a gen_server in a separate process.
The timer module is a standard gen_server running in a separate process. All the function in the timer module are public interfaces that execute a hidden gen_server:call or gen_server:cast to the timer server. This is a common usage to hide the internal of a server and allow further evolutions without impact on existing applications.
It makes sense I guess, since the current process must be able to continue executing code, and which function to execute must be stored safely somewhere until the delay is passed. And since one module can only access a function in another module if it's been exported, the function to execute must be exported too.
When I try to create a new process on separate node using
Pid = spawn(mynode, mymodule, myfunction, [self()])
(myfunction/1 is exported), I get this error:
Error in process <0.10.0> on node 'no#de1' with exit value:
{undef,[{mymodule, myfunction, [<33.64.0>], []}]}
I tried to set -compile(export_all) flag, but assuming the additional braces in error log, this is not the case.
I don't know what causes the error and I have no clue what to do.
The error you get is saying “There is no module ‘mymodule’ and/or no function ‘mymodule:myfunction/1’”.
This means mymodule is not loaded in the code server of your separate node.
To load mymodule's code there you need something like this snippet or this function
Did you check that the module mymodule is in the path of no#de1?
When you spwan a process using spawn(mynode, mymodule, myfunction, [self()]), the VM needs to load the code before executing it.
If you use a high order function (a fun) in this way spawn(Node, Fun), then in is not more necessary to have the code in the path (but beware that any call to a function in the function definition need to be solved on the remote node)
go to no#de1 and run m(mymodule). It should clarify if the module is loadable and which functions does export.
also: check if the other node is reachable. Do a net_adm:ping on it.
Is there a way to instruct the Erlang VM to apply a set of process flags to every new process that is spawned in the system?
For example in testing environment I would like every process to have save_calls flag set.
One way for doing this is to combine the Erlang tracing functionalities with a .erlang file.
Specifically, you could either use the low-level tracing capabilities provided by erlang:trace/3 or you could simply exploit the dbg:tracer/2 function to create a new tracing process which executes your custom handler function every time a tracing message is received.
To automate things a bit, you could then create an Erlang Start Up File in the directory where you're running your code or in your home directory. The Erlang Start Up File is a special file, called .erlang, which gets executed every time you start the run-time system.
Something like the following should do the job:
% -*- Erlang -*-
erlang:display("This is automatically executed.").
dbg:tracer(process, {fun ({trace, Pid, spawn, Pid2, {M, F, Args}}, Data) ->
process_flag(Pid2, save_calls, Data),
Data;
(_Trace, Data) ->
Data
end, 100}).
dbg:p(new, [procs, sos]).
Basically, I'm creating a new tracing process, which will trace processes (first argument). I'm specifying an handler function to get executed and some initial data. In the handler function, I'm setting the save_calls flag for newly spawned processes, whilst I'm ignoring all other tracing messages. I set the save_calls' option to 100, using the Initial Data parameter. In the last call, I'm telling dbg that I'm interested only in newly created processes. I'm also setting the sos (set_on_spawn) option to ensure inheritance of the tracing flags.
Finally, note how you need to use a variant of the process_flag function, which takes an extra argument (the Pid of the process you want to set the flag for).
Three erlang processes have been created in erlang shell and registered locally, named Pid, Pid2, Pid3. Then I want to terminate "Pid" firstly by function "exit/2".
(emacs#yus-iMac.local)62> exit(lsaaa_dispatch,test). %lsaaa_dispatch is the above Pid's local registered name
** exception error: bad argument
in function exit/2
called as exit(lsaaa_dispatch,test)
Why exception happens?
And why all the three process are terminate? Because I can't find them in Pman.
exit/2 accepts a pid, not a term. And it's defined by the kernel 'erlang' module.
exit(whereis(lsaaa_dispatch), test).
Of your second question, think of the relationship these processes have with your shell.
In project gproc's file gen_leader.erl,a customized behavior is created. But In the following statement, what is module "gen"? I can't find this module in the "erlang document tools http://www.erlang.org/erldoc"? Could you give me some explanation?
behaviour_info(callbacks) ->
[{init,1},
{elected,2},
{surrendered,3},
{handle_leader_call,4},
{handle_leader_cast,3},
{handle_local_only, 4},
{from_leader,3},
{handle_call,3},
{handle_cast,2},
{handle_DOWN,3},
{handle_info,2},
{terminate,2},
{code_change,4}];
behaviour_info(_Other) ->
undefined.
start_link(Name, [_|_] = CandidateNodes, Workers,
Mod, Arg, Options) when is_atom(Name) ->
gen:start(?MODULE, link, {local,Name}, Mod, %<<++++++ What's the meaning?
{CandidateNodes, Workers, Arg}, Options).
It looks like gen:start() is referring to gen.erl. According to the documentation in the file, gen.erl implements the generic parts of gen_server, gen_fsm and other OTP behaviors. In this case, it looks like gen_start handles spawning new processes. It checks to see if a process has already been spawned with the given name. If it has, an error is returned. If it has not, a new process is spawned by calling the module's start or start_link function.
In other words, when you call gen_server:start or gen_fsm:start, it calls gen:start (which does basic sanity checking) and gen:start, in turn, calls the module's start or start_link. When you're creating custom OTP behaviors, you'll have to call gen:start directly so that you don't need to replicate the error checking code in gen.erl.