2
votes

I'm not quite sure why I can't get this but I'm sure the answer is extraordinarily simple. I'm just testing out a few things and in my tests discovered that I would like to spawn a process within handle_info of my gen_server.

However despite me trying different combinations the best result I'm getting from my child is dying with an error and returning {undef, [{bob, hello, [], []}]}.

Code:

-module(test).
-behaviour(gen_server).
-export([start_link/1, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link(Args) ->
    gen_server:start_link({local, Args}, ?MODULE, Args, []).

init(Args) ->
    io:format("Init ~p ~p~n",[self(), Args]),
    {ok, Args}.

handle_call(_, _, State) ->
    io:format("Call ~p~n",[self()]),
    {reply, ok, State}.

handle_cast(_, State) ->
    io:format("Cast ~p~n",[self()]),
    {noreply, State}.

handle_info(_, State) ->
    io:format("Info ~p~n",[self()]),
    spawn(?MODULE,fun hello/1,[]),
    {noreply, State}.

terminate(_, _) ->
    ok.

code_change(_, State, _) ->
    {ok, State}.

hello([]) ->
    io:format("WOOT ~p~n",[self()]).

My first goal was to determine if multiple servers could be started with one module. The second was if handle_info was executed in a separate process... for some reason when I read it was asynchronous I thought it was in another process. Now the third is to spawn a process within that call.

My typical shell goes something like (with comments):

> c(test), {ok, P} = gen_server:start_link(bob)
> %% Warns me the function hello in any incarnation is not used
> P ! woot.
> %% An error of some kind depending on what I've done
> f(P), gen_server:stop(bob).

I've used hello/1 with [] and _, hello/0. As well as spawn/1, spawn/3, spawn_link/1 and spawn_link/3... I've used ?MODULE, test, State and for chuckles {local, State} as the module parameter. I've parted with what I've seen on multiple websites and put in fun hello/0 and fun hello/1 when passing the function. This produces crashes but got rid of the compiler warning.

Where did I go wrong?

1

1 Answers

4
votes

The third argument to spawn/3 is a list containing the number of arguments which the spawned function requires. For instance, if the spawned function takes 1 argument, the list will contain 1 argument; and if the spawned function takes 3 arguments, the list will contain 3 arguments. Finally, if the spawned function takes 0 arguments, the list will contain 0 arguments, i.e. the empty list.

You defined hello like this:

hello([]) ->
    io:format("WOOT ~p~n",[self()]).

That's a function with arity 1, and not only that, the only argument that matches the function is the empty list. In order to pass one argument to a function that you spawn, you would have to write:

spawn(?MODULE, hello ,[SomeArg])

And, because you want to match a function that takes the empty list as its only argument, SomeArg has to be the empty list:

spawn(?MODULE, hello, [[]])

Note that the syntax for spawn/3 is MFA, or module name, function name, arguments, which is a list containing the arguments.

Next, you have two more problems:

  1. You shouldn't call gen_server:start_link(bob) as you did here:

    {ok, P} = gen_server:start_link(bob)
    

    There is no such function as gen_server:start_link/1. Instead, you need to call your user interface function start_link/1, which then calls gen_server:start_link/3. You might want to rename your user interface function my_gen_server_starter(), so that you are clear about what it does.

  2. %% Warns me the function hello in any incarnation is not used

    You have to export a function to be able to spawn it. Because you didn't export the function and because no functions inside the module call the function, that means the function can never execute.