Hello i am trying to figure out when using the gen_statem why do the calls block,since my fsm is running in a separate process.
-module(fsm).
-record(data,{
current="None",
intvCount=0,
jobCount=0
}).
-export([init/1,terminate/3,callback_mode/0,code_change/4]).
-export([state/1,start/0,interview/2,reject/2,wait/1]).
-export([sitting_home/3,interviewing/3]).
-export([handle_event/3]).
-behaviour(gen_statem).
handle_event({call,From},get_state,Data)->
io:format("why you need state>"),
{keep_state,Data};
handle_event({call,From},Event,Data)->
{keep,state,Data}.
%API
start()->
gen_statem:start_link(?MODULE,[],[]).
state(PID)->
gen_statem:call(PID,get_state).
interview(PID,Company)->
gen_statem:call(PID,{intv,Company}).
reject(PID,Company)->
gen_statem:call(PID,{reject,Company}).
wait(PID)->
gen_statem:call(PID,{wait}).
%mandatory
code_change(V,State,Data,Extra)->{ok,State,Data}.
callback_mode() ->
state_functions.
init([])->
{ok,sitting_home,#data{current="None",jobCount=0,intvCount=0}}.
terminate(Reasom,State,Data)->
void.
% State implementations
sitting_home({call,From},{intv,Company},Data=#data{intvCount=C})->
io:format("called for interview"),
{next_state,interviewing,Data#data{intvCount=C+1},{reply,From,something}};
sitting_home({call,From},Event,Data)->
{keep_state,Data}.
interviewing({call,From},{rejected,Company},Data)->
{next_state,sitting_home,Data,{reply,From,somethingelse}};
interviewing({call,From},wait,Data)->
{keep_state,Data}.
Usage:
>{ok,Pid}=fsm:start().
>fsm:state(). //blocks !
>{ok,Pid}=fsm:start().
>fsm:interview(Pid).
called for interview %and blocks
Why do both calls block? I am spawning a different process besides the shell in which the fsm runs using the gen_statem:start_link.
Why in both cases it blocks?
Update
I have updated my post after i was pointed out that i forgot to use reply in order to send something back to the caller.However the handle_event/3 still blocks even in this form:
handle_event({call,From},get_state,Data)->
{keep_state,Data,[{reply,From,Data}]}.
Because that's what gen_statem:call does:
Makes a synchronous call to the gen_statem ServerRef by sending a request and waiting until its reply arrives
and your state functions don't send any replies. They should look like
sitting_home({call,From},{intv,Company},Data=#data{intvCount=C})->
io:format("called for interview"),
{next_state,interviewing,Data#data{intvCount=C+1},{reply,From,WhateverReplyYouWant}};
or
sitting_home({call,From},{intv,Company},Data=#data{intvCount=C})->
io:format("called for interview"),
gen_statem:reply(From, WhateverReplyYouWant),
{next_state,interviewing,Data#data{intvCount=C+1}};
If there's no useful reply, consider
using cast instead of call (and handling cast as the EventType in your state functions), or
ok as the reply.
Related
Suppose I need to send a large amount of data from the client to the server using python gRPC. And I want to continue the rest computation when sending the message out instead of blocking the code. Is there any way can implement this?
I will illustrate the question by an example using the modified code from the greeter_client.py
for i in range(5):
res=computation()
response = stub.SayHello(helloworld_pb2.HelloRequest(data=res))
I want the computation of the next iteration continue while sending the "res" of last iteration. To this end, I have tried the "async/await", which looks like this
async with aio.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
for j in range(5):
res=computation()
response = await stub.SayHello(helloworld_pb2.HelloRequest(data=res))
But the running time is actually the same with the version without async/await. The async/await does not work. I am wondering is there anything wrong in my codes or there are other ways?
Concurrency is different than parallelism. AsyncIO allows multiple coroutines to run on the same thread, but they are not actually computed at the same time. If the thread is given a CPU-heavy work like "computation()" in your snippet, it doesn't yield control back to the event loop, hence there won't be any progress on other coroutines.
Besides, in the snippet, the RPC depends on the result of "computation()", this meant the work will be serialized for each RPC. But we can still gain some concurrency from AsyncIO, by handing them over to the event loop with asyncio.gather():
async with aio.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
async def one_hello():
res=computation()
response = await stub.SayHello(helloworld_pb2.HelloRequest(data=res))
await asyncio.gather(*(one_hello() for _ in range(5)))
I am implementing a gen_fsm using the gen_statem module and when i am trying to check its state as a handle_event_function i get the following error:
> ** exception error: {function_clause,
> {gen_statem,call,[{ok,<0.139.0>},state,0]}}
> in function gen:do_for_proc/2
> called as gen:do_for_proc({ok,<0.139.0>},#Fun<gen.0.9801092>)
> in call from gen_statem:'-call_clean/4-fun-0-'/5 (gen_statem.erl, line 637) 25> c("C:/Erlang/Genserv/fsm.erl").
Below is my code which is separated into :
-mandatory methods for the fsm to work
-api that the client can use (state change,get the state,start)
- generic handlers for when client demands something related to state
-state implementations
Module
-module(fsm).
-record(state,{
current="None",
intvCount=0,
jobCount=0
}).
-export([init/1,terminate/3,callback_mode/0,code_change/4]).
-export([state/1,start/0,hire/2,fire/2,interview/2]).
-export([sitting_home/3,interviewing/3,working/3]).
-behaviour(gen_statem).
%API
start()->
gen_statem:start_link(?MODULE,[],[]).
state(PID)->
gen_statem:call(PID,state,0).
hire(PID,Company)->
gen_statem:call(PID,{hire,Company},0).
fire(PID,Company)->
gen_statem:call(PID,{fire,Company},0).
interview(PID,Company)->
gen_state:call(PID,{intv,Company},0).
%mandatory
code_change(V,State,Data,Extra)->{ok,State,Data}.
callback_mode() ->
[state_functions,handle_event_function].
init([])->
{ok,sitting_home,#state{current="None",jobCount=0,intvCount=0}}.
terminate(Reasom,State,Data)->
void.
% Generic handlers
handle_event({call,From},state,State)->
{keep_state,State,[{reply,From,State}]};
handle_event(_,_,State)->
{keep_state,State}.
% State implementations
sitting_home({call,From},{intv,Company},State=#state{intvCount=C})->
{next_state,interviewing,State#state{intvCount=C+1},[{reply,From,"Interviewing by:"++Company}]};
sitting_home(EventType,Event,State)->
handle_event(EventType,Event,State).
interviewing({call,From},{rejected,Company},State)->
{next_state,sitting_home,State,[{reply,From,"rejected by:"++Company}]};
interviewing({call,From},{accepted,Company},State=#state{jobCount=J})->
{next_state,
working,
State#state{jobCount=J+1,current=Company},
[{reply,From,"accepted offer from:"++Company}]
};
interviewing(EventType,Event,State)->
handle_event(EventType,Event,State).
working({call,From},{fire,Company},State=#state{current=C})->
{next_state,working,State#state{current="None"},[{reply,From,"Got fired from"++Company}]};
working(EventType,Event,State)->
handle_event(EventType,Event,State).
Usage
FSM_PID=fsm:start().
fsm:state(PID). //crashes when demanding state
fsm:hire(PID,"Some Company").
fsm:start() returns the tuple {ok,Pid}. You cannot use directly the return of the function in your next call. Instead you must do something like:
{ok,P} = fsm:start().
fsm:state(P).
Then you will experience other troubles, the first one is that you specify a timeout of 0, so you will get a timeout error. You will have to change this, and continue debugging :o)
[edit]
You don't need to specify the Pid, it is done in background by the gen_statem code, the gen_statem:call function is executed in the caller context so it still has access to the caller Pid.
In fact it is a little more complex, depending if you specify a timeout or not, the gen_statem:call/3 will spawn a process to call the gen:call/4 function or will directly call it, thus the Pid sent to the state machine will be either the spawned function's one or the caller's one.
The gen:call/4 also add a Reference in the message to the state machine to "sign" the answer and guaranty that an incoming message from any other part of the application will not be interpreted as the answer.
This is a general pattern in erlang (and programming) to hide as much as possible the system mechanism in interface function. Just as you do in the function state/1
In this program, I cannot for the life of me figure out how to access the value of the counter in a process.
-module(counter).
-export([start/0,loop/1,increment/1,value/1,stop/1]).
%% First the interface functions.
start() ->
spawn(counter, loop, [0]).
increment(Counter) ->
Counter ! increment.
value(Counter) ->
Counter ! {self(),value},
receive
{Counter,Value} ->
Value
end.
stop(Counter) ->
Counter ! stop.
%% The counter loop.
loop(Val) ->
receive
increment ->
loop(Val + 1);
{From,value} ->
From ! {self(),Val},
loop(Val);
stop -> % No recursive call here
true;
Other -> % All other messages
loop(Val)
end.
I assume it's:
{From,value} ->
From ! {self(),Val},
loop(Val);
which returns the value of the counter, but every time I use PID ! {PID,value}, or something similar to that it returns the thing after !, e.g. {<0.57.0>, value}.
TL;DR
You shouldn't use ! operator explicitly, it is considered an anti-pattern. You could run into some problems with typos in atoms or some bad data, just like you did this time.
To ensure correct communication with you one usually create some wrapper functions witch handle correct data format and communication with process. Function just like increment/1 value/1 and stop/1. In fact if you would use those, you would get expected results; in your case, assuming that PID is your counter, just call counter:value(PID).
Let me explain
There are few thing you seem to getting little bit wrong.
First of all ! will send message to another process. And that's all it does. Since everything in Erlang is expression (needs to return something, have a value) each call to ! will return right hand side of !. PID ! ok. will return ok, no matter what (there is slight chance that it will fail, but lets no go there). You send your message, and go on with your life, or execution.
Than, some process after receiving your message might decide to send you some message back. In case of {From, value} it will, in case of increment it wont. If you are expecting to get message back you need to wait for it and retrieve it from your mailbox. receive clause will do both waiting and retrieving. So if you decise to use ! on your own you should fallow it with receive with correct pattern match. You can see that value/1 function does just that.
Third thing is correct use of process ID's. I guess you started your counter correctly and you have it's Pid, and you can send messages to it with !. But if you expect it to send something back it needs to know your process ID, your address if you will. So you should have called PID ! {MyPid, values}. How to get MyPid? With self() function. Again, just like in value/1 function.
And last thing many people get wrong at the begging. counter module is just a file with some functions, it's not whole actor/process, and it's not an object. Fact that some value/1 and stop/1 are implemented in it, it doesn't mean that they will be run on counter actor/process. They are functions like any other, and if you call them they will be evaluated in your actor/process, on your stack (same goes for calling them from shell, shell is just another actor). You can spawn new process and tell it to run loop/1 function, but that's all it does. All increment/1 value/1 and stop/1 calls will be executed "on your side".
If this is somewhat confusing try to imagine some simpler function inside counter module, like
add(A, B) ->
A + B.
You can execute it from shell even without any counter process started. It will be created in your process, on your stack, it will add two numbers and return result.
This is important because when you call counter:value(Counter). it will execute Counter ! {self(),value}, "on your side", on your process, so self() will return Pid of your process, not the Pid of counter.
In theory you don't need to know this if you are just using those wrapper function (API or interface if you will), but since you are learning Erlang I would guess you will soon have to write such wrapper. Understanding what happens where is crucial then. Just remember that there is no magic in modules, no secret binding or special execution. Those are just plain old functions and they will be behaving just like in any other language. Only spawn, receive and maybe ! are little different.
I want to extend gen_server (create a gen_server_extra) with some additional functionality. The requirements are:
The gen_server_extra processes should behave like a regular gen_server's. E.g, they should accept calls via gen_server:call, integrate with SASL, fit OTC supervision tree, etc.
gen_server_extra processes should have an additional functionality, provided by gen_server_extra. That basically means some of the messages will be handled by gen_server_extra code, without passing them to the callback module. The rest of the messages are passed to callback module as is.
gen_server_extra functionality requires its own state which should be hidden from the callback module.
What is the simplest approach to do that?
The best, most modular approach would be to implement a new behavior in a module (e.g. gen_ext_server) and wrap the gen_server behavior from there.
First, make sure your behavior is identical to gen_server:
-module(gen_ext_server).
-behavior(gen_server).
% Exports...
behaviour_info(Type) -> gen_server:behaviour_info(Type).
Implement all callbacks needed for gen_server, keep the name of the callback module that implements your behavior in your state:
init([Mod|ExtraArgs]) ->
% ...
ModState = Mod:init(ExtraArgs),
#state{mod = Mod, mod_state = ModState, internal = [...]}
Then, in each gen_server callback, implement your behavior and then call the callback module if needed:
handle_call(internal, _From, State) ->
% Do internal stuff...
{reply, ok, State};
handle_call(Normal, From, State = #state{mod = Mod, mod_state = ModState}) ->
case Mod:handle_call(Normal, From, ModState) of
{reply, Reply, NewState} ->
{reply, Reply, #state{mod_state = NewState};
... ->
...
end.
Implement similar functionality for handle_cast/2, handle_info/2, terminate/1 etc.
well, i would not call it customization, but rather a new behavior. You need to define your own behavior. A tutorial which will take you through this is found at trapexit.org.
However, the requirements are not very proper.
The main essence of giving the call back functions access to the server state is to write normal sequential code manipulating the state of your application when and where you want without interrupting the concurrency details.
Otherwise if that is the way to go, implement your own behavior.
If I want to always send an event to the initial state of a gen_fsm when I have spawned it, where should I put that function call? Right after start_link or from the process that invoked start_link in the first place. Are there any best practices here?
If you just want to alter the state of the FSM after you start it, you might simply implement the init function for your state machine:
Reading from: http://www.erlang.org/doc/man/gen_fsm.html#Module:init-1
Whenever a gen_fsm is started using
gen_fsm:start/3,4 or
gen_fsm:start_link/3,4, this function
is called by the new process to
initialize.
Args is the Args argument provided to
the start function.
If initialization is successful, the
function should return
{ok,StateName,StateData},
{ok,StateName,StateData,Timeout} or
{ok,StateName,StateData,hibernate},
where StateName is the initial state
name and StateData the initial state
data of the gen_fsm.
Also, using the init function, you're sure about the atomicity of the two functions (start_link and init). They will both succeed or fail.
I thik it is right to send first event from the process invoking FSM start function. Or return timeout = 0 from init/1 and handle 'timeout' event in the initial state.
On the other hand, it makes races possible if your gen_fsm is a rgistered process. If that is the case I would send message to the gen_fsm process PID from init/1 befor registering.