A module calls a gen_server to handle a stream, which uses record as State.
handle_call handles stream using a function from State, which separates completed piece of data and tail,
Now next time, the tail should be fed first but with an updated State, before the module sends in more data.
handle_call({stream, Data}, _From, State = #mystate{myfun=Fun}) ->
case Fun(Data) of
{completed piece,tail} ->
dosomethingwithpieace,
NewState = State##mystate{myfun=resetfun()};
% How do i call this again to feed Tail first with new state?
{stillstreaming, Currentstate} ->
NewState = State##mystate{myfun=CurrentState};
I cannot call gen_server:call(self(),{stream, Tail}) because State needs to be updated first.
And I cannot reply with a new State, because module will send in more data and tail will disappear.
Is there a way to call it again with updated State without replying with tail and feeding tail back from module??
Update,Code:
% caller module
case gen_tcp:recv(Socket, 0) of % cannot set Length as it will block untill it is done reading Length number of bytes
{ok, Data} ->
Response = gen_server:call(Pid, {handle_init,Socket,Data}),
case Response of
{ok, continue} ->
pre_loop(Socket, Pid);
{ok, logged_in} ->
{UserId, UserName} = get_user_data(), % ignore it for now.
receiver_loop(UserId, UserName, Socket, Pid);
{stop, Reason} ->
io:format("Error at pre_loop: ~p~n", [Reason]);
_ ->
io:format("Unknown response from call in pre-loop: ~p~n", [Response])
end;
{error, closed} -> % done here as no data was stored in mnesia yet.
gen_server:stop(Pid),
io:format("Client died in pre_loop~n")
end.
and gen_server module:
% gen_server module
handle_call({handle_init, _Socket, Data}, _From, State = #server_state{data_fun = {incomplete, Fun}}) ->
case catch Fun(Data) of
{incomplete, F} ->
NewState = State#server_state{data_fun = {incomplete, F}},
{reply, {ok, continue}, NewState};
{with_tail, Term, Tail} ->
% handle Term login/register only
case handle_input_init(Term, Tail) of
{incomplete, Fn, logged_in} ->
NewState = State#server_state{data_fun = {incomplete, Fn}},
{reply, {ok, logged_in}, NewState};
{incomplete, Fn} ->
NewState = State#server_state{data_fun = {incomplete, Fn}},
{reply, {ok, continue}, NewState};
{stop, malformed_data} ->
{reply, {stop, malformed_data}, State}
end;
_ ->
{reply, {stop, malformed_data}, State}
end;
handle_call(_Message, _From, State = #server_state{}) ->
{reply, {stop , unknown_call}, State}.
handle_input_init(Term, Tail) ->
case handle_term_init(Term) of
{ok, login_passed} ->
io:format("send user a login pass msg"),
handle_tail_init(Tail, logged_in);
{error, login_failed} ->
io:format("send user a login failed error~n"),
handle_tail_init(Tail);
{ok, registration_passed} ->
io:format("send user a registeration passed msg"),
handle_tail_init(Tail);
{error, registration_failed} ->
io:format("send user a registeration failed error"),
handle_tail_init(Tail);
{error, invalidreq} ->
io:format("send user an invalid requst error~n"),
handle_tail_init(Tail)
end.
handle_tail_init(Tail) ->
case catch jsx:decode(Tail, [stream, return_tail, return_maps]) of
{incomplete, F} ->
{incomplete, F};
{with_tail, Term, Tail2} ->
handle_input_init(Term, Tail2);
_ ->
{stop, malformed_data}
end.
handle_tail_init(Tail, logged_in) -> % because it was logged in already, any further requests should be ignored
case catch jsx:decode(Tail, [stream, return_tail, return_maps]) of
{incomplete, F} ->
{incomplete, F, logged_in};
{with_tail, _Term, Tail2} ->
io:format("send user an invalid requst error~n"),
handle_tail_init(Tail2, logged_in);
_ ->
{stop, malformed_data}
end.
handle_term_init(Term) ->
case Term of
#{<<"Login">> := [UserName,Password]} ->
login_user(UserName,Password);
#{<<"Register">> := [UserName,Password]} ->
register_user(UserName,Password);
_ ->
{error, invalidreq}
end.
It is working as expected but this is my very first Erlang code ever and i am positive that it can be simplified to a single recursive handle_call, maintaining OTP style, the reason I chose Erlang.
I cannot call gen_server:call(self(),{stream, Tail}) because State
needs to be updated first.
I can't really understand what you are trying to say, but if you mean:
I cannot recursively call gen_server:call(self(),{stream, Tail}), i.e. I cannot write code in handle:call() that recursively calls handle_call().
then you can certainly send all the data inside handle:call() to another function that recursively calls itself:
handle_call(...) ->
...
NewState = ....
Result = ...
{FinalResult, FinalState} = helper_func(Result, NewState, Tail)
{reply, FinalResult, FinalState}
helper_func(Result, State, []) ->
{Result, State};
helper_func(Result, State, [Piece|Tail]) ->
...
NewState = ...
NewResult = ...
helper_func(NewResult, NewState, Tail). %% Recursive call here
Related
I was following this example of executing periodic tasks given by Hynek -Pichi- Vychodil here.
And i ran into a problem as I wanted to pass some Arguments also to the start_link function which would be used inside my do_task() function. But as given here the start_link/4 needs to return {ok,Pid} and in my case it is returning {ok,{Ref,Arguments}} and thus is failing.
How I can I fix this up.Here's my code:
start_link(Period,SERVER,Args) when Period > 0, is_integer(Period) ->
gen_server:start_link({local, SERVER}, ?MODULE, [Period,Args], []).
init([Period,Args]) ->
StartT = erlang:monotonic_time(millisecond),
self() ! tick,
{ok, {StartT, Period,Args}}.
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(tick, {StartT, Period,Args} = S) ->
Next = Period - (erlang:monotonic_time(millisecond)-StartT) rem Period,
_Timer = erlang:send_after(Next, self(), tick),
do_task(Args),
{ok, S};
handle_info(_Info, State) ->
{noreply, State}.
Here
Period is->30000
and Arguments is -> {A,[a,b],'something'}
And here is the crash log
[error] gen_server '95ef60ae-b2fa-491a-821d-ffae85cc57f6' terminated with reason: bad return value: {ok,{-576460723187,30000,{A,[a,b],'something'}}
handle_info cannot return an ok tuple.
(Answer given as a comment.)
Now Im playing with the gen_server
I have two modules - one is Gen Server mod, second - logic module
and would like to send the message to the PID through the gen_server:call
here is the snip of code:
lookup_by_date(FromTime, ToTime) ->
gen_server:call({global, ?MODULE}, {lookup_by_date,FromTime,ToTime}).
here is the handle_call func:
handle_call({lookup_by_date, FromTime, ToTime}, _From, _State) ->
FromSec = calendar:datetime_to_gregorian_seconds(FromTime),
ToSec = calendar:datetime_to_gregorian_seconds(ToTime),
Pid = spawn(fun()-> logic:handler() end),
{reply, Pid !{lookup_by_date, FromSec, ToSec}, _State};
aand the logic mod code:
lookup_by_date(FromTime, ToTime) -> lookup_by_date(FromTime, ToTime, ets:first(auth), []).
lookup_by_date(_FromTime, _ToTime, '$end_of_table', Acc) -> {reply, Acc, ok};
lookup_by_date(FromTime, ToTime, Key, Acc) ->
case ets:lookup(auth, Key) of
[{Login, Pass, TTL, Unix, Unix2}] ->
F = calendar:datetime_to_gregorian_seconds(Unix2),
T = calendar:datetime_to_gregorian_seconds(Unix2),
if
F >= FromTime, T =< ToTime -> NewAcc = [{Login, Pass, TTL, Unix, Unix2}|Acc],
N = ets:next(auth, Key),
lookup_by_date(FromTime, ToTime, N, NewAcc);
true -> N = ets:next(auth, Key),
lookup_by_date(FromTime, ToTime, N, Acc)
end
end.
handler() ->
receive
{lookup_by_date, FromTime, ToTime}->
lookup_by_date(FromTime, ToTime),
handler();
Other->
io:format("Error message for ~p~n" ,[Other]),
handler()
end.
but i am getting the error (actually not an error)
2> c(cache_server).
{ok,cache_server}
3> c(logic).
{ok,logic}
4> cache_server:start([{ttl, 15000}]).
{ok,<0.73.0>}
5> cache_server:insert(test, root, 15000).
{auth,test,root,15000,1484309726435,
{{2017,1,13},{14,15,11}}}
6> cache_server:lookup_by_date({{2017,1,13},{14,15,11}},{{2017,1,13},{14,15,11}}).
{lookup_by_date,63651536111,63651536111}
I am receiving data from - {reply, Pid !{lookup_by_date, FromSec, ToSec}, _State};
but dont receive data from the "logic:lookup_by_date" function
Is there anyway you show me the right direction because Im stuck a little bit.
Thx...
In your code, the reply to the gen_server call is:
Pid !{lookup_by_date, FromSec, ToSec}
In Erlang messages are asynchronous, they are just sent to the process, so this code doesn't wait for a response, and it simply returns, immediatly, the message you are sending. It is why you get the reply {lookup_by_date, FromSec, ToSec}.
In your case you don't have to spawn a process, but simply call the lookup_by_date function:
handle_call({lookup_by_date, FromTime, ToTime}, _From, _State) ->
FromSec = calendar:datetime_to_gregorian_seconds(FromTime),
ToSec = calendar:datetime_to_gregorian_seconds(ToTime),
{reply, logic:lookup_by_date(FromSec, ToSec), _State};
Note: Your gen_server doesn't use the result, its state is not modified by the request, so you could directly call the function lookup_by_date and include the time conversion in it.
I am following http://learnyousomeerlang.com/static/erlang/kitty_gen_server.erl .
I have my application logic inside of temple.erl. All of this code is tested & runs as I expect it to. My land.erl is intended to be the server that contains the Temple.
My understanding (please correct any ignorance) is that I can use gen_server to abstract message passing and keep track of state with minimal overhead.
I understand that the second tuplevalue in my init function
init([]) -> {ok, temple:new()}. is my server's state - in this case, the return value of temple:new().
So I c(land). (code below), and try this:
19> {ok, Pid2} = land:start_link().
{ok,<0.108.0>}
20> land:join(Pid2, a).
ok
and I just get the atom ok back when I send the join message From reading the code and comparing my experiences running the kitty_gen_server, I think the state is updated correctly with the value temple:join(Temple, Name), but the ok atom is the response value from
handle_call({join, Name}, _From, Temple) ->
{reply, ok, temple:join(Temple, Name)};
how can I update my state with temple:join(Temple, Name), and then return this value to the client? I don't want to call the same function twice eg.
handle_call({join, Name}, _From, Temple) ->
{reply, temple:join(Temple, Name), temple:join(Temple, Name)};
So looking at the kitty_gen_server I tried
handle_call({join, Name}, _From, Temple) ->
[{reply, JoinedTemple, JoinedTemple} || JoinedTemple <- temple:join(Temple, Name)];
and I get a function clause crash when I try this with a message about syntax error ||, and then I see this is only for list comprehensions..
how can I compute the value of temple:join(Temple, Name)] and return to the caller of land:join and update the Land's state?
-module(land).
-behaviour(gen_server).
-export([start_link/0, join/2, input/3, fight/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start_link() ->
gen_server:start_link(?MODULE, [], []).
join(Pid, Name) ->
gen_server:call(Pid, {join, Name}).
input(Pid, Action, Target) ->
gen_server:call(Pid, {input, Action, Target}).
fight(Pid) ->
gen_server:call(Pid, fight).
init([]) -> {ok, temple:new()}.
handle_call({join, Name}, _From) ->
{reply, ok, temple:join(Name)}.
handle_call({join, Name}, _From, Temple) ->
{reply, temple:join(Temple, Name), temple:join(Temple, Name)};
handle_call(terminate, _From, Temple) ->
{stop, normal, ok, Temple}.
handle_info(Msg, Temple) ->
io:format("Unexpected message: ~p~n",[Msg]),
{noreply, Temple }.
terminate(normal, Temple) ->
io:format("Temple bathed in blood.~p~n", [Temple]),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
handle_cast(_, Temple) ->
{noreply, Temple}.
You can store the new state in a variable and then return a tuple containing that variable twice:
handle_call({join, Name}, _From, Temple) ->
NewTemple = temple:join(Temple, Name),
{reply, NewTemple, NewTemple};
I'm writing something to process Erlang source code. Pretty much the first line of the program is:
{ok, Forms} = epp_dodger:parse_file(Filename)
However, I want to do some simple unit testing. So: how do I persuade epp_dodger to take its input from a string instead of a file?
Alternatively, it has epp_dodger:parse_form/2,3, which takes an IODevice, so how do I provide an IODevice over a string?
The code below (which is admittedly a bit hackish) starts a gen_server that takes a string as an argument, and then fulfills the Erlang I/O Protocol enough to satisfy epp_dodger:parse/2 to be able to read and parse the string from that process.
Here's an example of using it:
1> {ok,P} = iostr:start("-module(x).\n-export([f/0]).\nf() -> ok.\n").
2> epp_dodger:parse(P,1).
{ok,[{tree,attribute,
{attr,1,[],none},
{attribute,
{tree,atom,{attr,1,[],none},module},
[{tree,atom,{attr,1,[],none},x}]}},
{tree,attribute,
{attr,2,[],none},
{attribute,
{tree,atom,{attr,2,[],none},export},
[{tree,list,
{attr,2,[],none},
{list,
[{tree,arity_qualifier,
{attr,2,[],none},
{arity_qualifier,
{tree,atom,{attr,...},f},
{tree,integer,{...},...}}}],
none}}]}},
{tree,function,
{attr,3,[],none},
{func,
{tree,atom,{attr,3,[],none},f},
[{tree,clause,
{attr,3,[],none},
{clause,[],none,[{atom,3,ok}]}}]}}]}
The process dies once the string is exhausted.
Note that the code probably misses a few things — handling unicode properly, for example — but it should give you a good idea of how to make a more robust I/O server for this purpose if needed.
-module(iostr).
-behaviour(gen_server).
-export([start_link/1, start/1, stop/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {
data,
line = 1,
lines
}).
start_link(Data) ->
gen_server:start_link(?MODULE, [Data], []).
start(Data) ->
gen_server:start(?MODULE, [Data], []).
stop(Pid) ->
gen_server:cast(Pid, stop).
init([Data0]) ->
Data = [Line++"\n" || Line <- string:tokens(Data0, "\n")],
{ok, #state{data=Data,lines=length(Data)}}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({io_request,From,ReplyAs,{get_until,_,_,_,_,_}},
#state{data=[],lines=L}=State) ->
From ! {io_reply, ReplyAs, {eof,L}},
{stop, normal, State};
handle_info({io_request,From,ReplyAs,{get_until,_,_,M,F,Args}},
#state{data=Data,line=L}=State) ->
case handler(Data,L,[],M,F,Args) of
eof ->
Lines = State#state.lines,
From ! {io_reply, ReplyAs, {eof,Lines}},
{stop, normal, State#state{data=[]}};
{ok,Result,Rest,NData,NL} ->
From ! {io_reply, ReplyAs, Result},
case Rest of
[] ->
{noreply, State#state{data=NData,line=NL}};
_ ->
{noreply, State#state{data=[Rest|NData],line=NL}}
end
end;
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
handler([Input|Data],L,Cont,M,F,Extra) ->
case catch apply(M,F,[Cont,Input|Extra]) of
{done,eof,_} ->
eof;
{done,Result,Rest} ->
{ok,Result,Rest,Data,L+1};
{more,NCont} ->
case Data of
[] -> eof;
_ -> handler(Data,L+1,NCont,M,F,Extra)
end
end.
I got one error when starting my gen server, i want to know how to debug it, thanks!
I run "example:add_listener(self(), "127.0.0.1", 10999)." after start_link.
The error is :
=ERROR REPORT==== 11-May-2011::13:41:57 ===
** Generic server <0.37.0> terminating
** Last message in was {'EXIT',<0.35.0>,
{{timeout,
{gen_server,call,
[<0.35.0>,
{add_listener,"127.0.0.1",10999}]}},
[{gen_server,call,2},
{erl_eval,do_apply,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
** When Server state == {state,example,
{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]}}},
{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]}}},
[]}
** Reason for termination ==
** {{timeout,{gen_server,call,[<0.35.0>,{add_listener,"127.0.0.1",10999}]}},
[{gen_server,call,2},
{erl_eval,do_apply,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}
** exception exit: {timeout,{gen_server,call,
[<0.35.0>,{add_listener,"127.0.0.1",10999}]}}
in function gen_server:call/2
My code is :
-module(test_ess_tcp).
-export([start_link/0,
add_listener/3,
remove_listener/3]).
-export([init/2, handle_call/3, handle_cast/2, handle_info/2]).
-export([terminate/2, sock_opts/0, new_connection/4]).
-behavior(ess_tcp).
start_link() ->
ess_tcp:start_link(?MODULE, []).
add_listener(Pid, IpAddr, Port) ->
gen_server:call(Pid, {add_listener, IpAddr, Port}).
remove_listener(Pid, IpAddr, Port) ->
gen_server:call(Pid, {remove_listener, IpAddr, Port}).
init([], State) ->
%% Example storing callback module specific state
%% This modifies the server state
{ok, ess_tcp:store_cb_state([], State)}.
handle_call({add_listener, IpAddr, Port}, _From, State) ->
%% Example of getting callback module state
io:format("Not used here, but just an example"),
[] = ess_tcp:get_cb_state(State),
case ess_tcp:add_listen_socket({IpAddr, Port}, State) of
{ok, State1} ->
{reply, ok, State1};
Error ->
{reply, Error, State}
end;
handle_call({remove_listener, IpAddr, Port}, _From, State) ->
case ess_tcp:remove_listen_socket({IpAddr, Port}, State) of
{ok, State1} ->
{reply, ok, State1};
Error ->
{reply, Error, State}
end;
handle_call(_Msg, _From, State) ->
{reply, ignored, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({tcp, Sock, Data}, State) ->
Me = self(),
P = spawn(fun() -> worker(Me, Sock, Data) end),
gen_tcp:controlling_process(Sock, P),
{noreply, State};
handle_info(_Msg, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
sock_opts() ->
[binary, {active, once}, {packet, 0}].
new_connection(_IpAddr, _Port, Sock, State) ->
Me = self(),
P = spawn(fun() -> worker(Me, Sock) end),
gen_tcp:controlling_process(Sock, P),
{ok, State}.
worker(Owner, Sock) ->
gen_tcp:send(Sock, "Hello\n"),
inet:setopts(Sock, [{active, once}]),
gen_tcp:controlling_process(Sock, Owner).
worker(Owner, Sock, Data) ->
gen_tcp:send(Sock, Data),
inet:setopts(Sock, [{active, once}]),
gen_tcp:controlling_process(Sock, Owner).
Well, your gen_server:call is getting a timeout when called. That means that the gen_server is either taking longer than the default 3 second timeout for a call or it is blocked somewhere.
using tracing to debug kind of behaviour is ideal. For instance if you type this in the shell before running the test:
dbg:tracer(),dbg:p(all,c),dbg:tpl(ess_tcp, x).
you will trace on all functions within ess_tcp to see what is going on in there. For more info about dbg see http://www.erlang.org/doc/man/dbg.html
actually best tool to debug in erlang is io:format statements. Put io:formats where you have doubts and see if you get the expected values.
Regarding above question it is mainly stuck somewhere !!