Reputation: 1389
I tried to design one server to start temporary process when it receives request. However when I tried to terminate or kill the temporary process, I got noproc
error. Can anyone tell me how to solve this problem?
I got the following files:
temp.erl (main supervisor)
-module(temp).
-behaviour(supervisor).
-export([start_link/0, init/1]).
-export([list_children/0]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
list_children() ->
supervisor:which_children(?MODULE).
init([]) ->
process_flag(trap_exit, true),
io:format("****Main supervisor****~n"),
{ok, {{one_for_one, 5, 10},
[{temp_srv, {temp_srv, start_link, []},
permanent, infinity, worker, [temp_srv]},
{temp_sup, {temp_sup, start_link, []},
permanent, infinity, supervisor, [temp_sup]}]}}.
temp_srv.erl (server)
-module(temp_srv).
-behaviour(gen_server).
-export([init/1, handle_cast/2, handle_info/2, handle_call/3, terminate/2, code_change/3]).
-export([start_link/0, echo_call/1]).
-record(state, {}).
-define(SERVER, ?MODULE).
echo_call(Data) ->
gen_server:call(?SERVER, {echo, Data}).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
init([]) ->
process_flag(trap_exit, true),
{ok, #state{}}.
handle_call({echo, Data}, _From, State) ->
Result = temp_sup:assign_task({echo, Data}),
{reply, Result, State};
handle_call(_Request, _From, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
handle_cast(_Request, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_Old, State, _Extra) ->
{ok, State}.
temp_sup.erl (supervisor to start temporary process)
-module(temp_sup).
-behaviour(supervisor).
-export([start_link/0, init/1]).
-export([start_temp_worker/0, list_children/0, assign_task/1, select_available_children/1, select_finished_children/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
list_children() ->
supervisor:which_children(?MODULE).
init([]) ->
io:format("****Temp supervisor*****~n"),
spawn_link(fun start_pool/0),
{ok, {{simple_one_for_one, 0, 1}, [{temp_worker, {temp_worker, start_link, []}, temporary, 1000, worker, [temp_worker]}]}}.
start_pool() ->
[start_temp_worker() || _ <- lists:seq(1, 3)].
start_temp_worker() ->
supervisor:start_child(?MODULE, []).
assign_task(Data) ->
case list_children() of
[] ->
{ok, Pid} = start_temp_worker(),
gen_server:call(Pid, Data);
ChildL when is_list(ChildL) ->
case select_finished_children(ChildL) of
no_pid_to_kill -> ok;
{ok, KPid} -> gen_server:call(KPid, stop)
end,
{ok, Pid} = select_available_children(ChildL),
start_temp_worker(),
io:format("Assign task to ~p~n", [Pid]),
gen_server:call(Pid, Data)
end.
select_available_children([ChildH | ChildT]) ->
{undefined, Pid, worker, [temp_worker]} = ChildH,
case gen_server:call(Pid, check_status) of
active ->
io:format("Find available children ~p~n", [Pid]),
{ok, Pid};
running -> select_available_children(ChildT);
done -> select_available_children(ChildT)
end.
select_finished_children([ChildH | ChildT]) ->
{undefined, KPid, worker, [temp_worker]} = ChildH,
case gen_server:call(KPid, check_status) of
done ->
io:format("Find process ~p to kill~n", [KPid]),
{ok, KPid};
_ -> select_finished_children(ChildT)
end;
select_finished_children([]) -> no_pid_to_kill.
temp_worker.erl (temporary process)
-module(temp_worker).
-behaviour(gen_server).
-export([start_link/0, init/1]).
-export([handle_info/2, handle_call/3, handle_cast/2, code_change/3, terminate/2]).
-record(state, {status}).
start_link() ->
gen_server:start_link(?MODULE, [], []).
init([]) ->
process_flag(trap_exit, true),
io:format("Start temporary worker process ~p~n", [self()]),
{ok, #state{status = active}}.
handle_info(_Request, State) ->
{noreply, State}.
handle_call(check_status, _From, #state{status = Status} = State) ->
{reply, Status, State};
handle_call({echo, Msg}, _From, State) ->
io:format("~p get echo request for message ~p~n", [self(), Msg]),
{reply, Msg, State#state{status = done}};
handle_call(stop, _From, State) ->
{stop, normal, stopped, State};
handle_call(_Request, _From, State) ->
{noreply, State}.
handle_cast(_Request, State) ->
{noreply, State}.
code_change(_Old, State, _Extra) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
The design is supposed to start three temporary process once the temporary supervisor starts, once it receives request, it will terminate one temporary process which has finished work.
That's how I run these code:
9> temp:start_link().
****Main supervisor****
****Temp supervisor*****
Start temporary worker process <0.98.0>
{ok,<0.94.0>}
Start temporary worker process <0.99.0>
Start temporary worker process <0.101.0>
10> temp_srv:echo_call(asd).
Find available children <0.98.0>
Start temporary worker process <0.102.0>
Assign task to <0.98.0>
<0.98.0> get echo request for message asd
asd
11> temp_srv:echo_call(asd).
Find process <0.98.0> to kill
=ERROR REPORT==== 23-May-2017::23:16:34 ===
** Generic server temp_srv terminating
** Last message in was {echo,asd}
** When Server state == {state}
** Reason for termination ==
** {{noproc,{gen_server,call,[<0.98.0>,check_status]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]},
{temp_sup,select_available_children,1,[{file,"temp_sup.erl"},{line,41}]},
{temp_sup,assign_task,1,[{file,"temp_sup.erl"},{line,33}]},
{temp_srv,handle_call,3,[{file,"temp_srv.erl"},{line,21}]},
{gen_server,try_handle_call,4,[{file,"gen_server.erl"},{line,615}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,647}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}
=ERROR REPORT==== 23-May-2017::23:16:34 ===
** Generic server temp terminating
** Last message in was {'EXIT',<0.92.0>,
{{{noproc,
{gen_server,call,[<0.98.0>,check_status]}},
{gen_server,call,[temp_srv,{echo,asd}]}},
[{gen_server,call,2,
[{file,"gen_server.erl"},{line,204}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,674}]},
{shell,exprs,7,[{file,"shell.erl"},{line,686}]},
{shell,eval_exprs,7,
[{file,"shell.erl"},{line,641}]},
{shell,eval_loop,3,
[{file,"shell.erl"},{line,626}]}]}}
** When Server state == {state,
{local,temp},
one_for_one,
[{child,<0.96.0>,temp_sup,
{temp_sup,start_link,[]},
permanent,infinity,supervisor,
[temp_sup]},
{child,<0.104.0>,temp_srv,
{temp_srv,start_link,[]},
permanent,infinity,worker,
[temp_srv]}],
undefined,5,10,
[-576459529],
0,temp,[]}
** Reason for termination ==
** {{{noproc,{gen_server,call,[<0.98.0>,check_status]}},
{gen_server,call,[temp_srv,{echo,asd}]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,674}]},
{shell,exprs,7,[{file,"shell.erl"},{line,686}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,641}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,626}]}]}
** exception exit: {{noproc,{gen_server,call,[<0.98.0>,check_status]}},
{gen_server,call,[temp_srv,{echo,asd}]}}
in function gen_server:call/2 (gen_server.erl, line 204)
Upvotes: 0
Views: 1673
Reputation: 48649
From the error docs:
noproc -- Trying to link to a non-existing process.
The error message says the problematic line is:
case gen_server:call(Pid, check_status) of
That means Pid is is a non-existent process. In your output, you can see:
Assign task to <0.98.0>
<0.98.0> get echo request for message asd
asd
I think that means process <0.98.0> has finished executing and has terminated. You can verify that by outputting process_info(Pid)
, which will return undefined
for a non-existent process. That process is at the head of your ChildL
list. You can verify that with an io:format()
statement:
assign_task(Data) ->
...
case select_finished_children(ChildL) of
no_pid_to_kill -> ok;
{ok, KPid} -> gen_server:call(KPid, stop)
end,
io:format("assign_task(): ChildL: ~w~n", [ChildL]), %%<***HERE
io:format("****Info: ~w~n", [process_info(Pid)]), %%<***HERE
{ok, Pid} = select_available_children(ChildL),
Then your code does this:
select_available_children([ChildH | ChildT]) ->
{undefined, Pid, worker, [temp_worker]} = ChildH,
case gen_server:call(Pid, check_status) of
ChildH
contains the pid <0.98.0>
, which has terminated, and that pid gets bound to Pid
. Then gen_server:call(Pid)
executes, which results in a noproc
error.
Upvotes: 1