how to handle termination of gen_fsm - erlang

I have a MAIN process that spawn an implementation of a gen_fsm behavior, but this MAIN process is not an implementation of supervisor behavior, its just another module.
Let say the implementation of gen_fsm is called GAME_ROOM.
My case is like this:
When ever there are 3 peoples ready, the MAIN process will spawn a new GAME_ROOM.
I use gen_fsm:start_link function to initiate a new GAME_ROOM, so if the GAME_ROOM exit by error, my MAIN process could spawn a new one, to replace the downed process.
I managed to make my MAIN process detect the EXIT event of all downed GAME_ROOM
The problem is: I need to restore all downed GAME_ROOM states at the new one.
My question is: How can I use gen_fsm's terminate function to pass the latest states of the gen_fsm to my MAIN process, so when I respawn a new GAME_ROOM, I can pass that states?

One simple way would be for GAME_ROOM terminate/3 to send a message with the necessary state information to MAIN. For this to work the GAME_ROOM must know the pid of MAIN (easy) and you have to be certain that terminate/3 is really called.

Read about process_flag ({trap_exit, true}) and handle info 'EXIT'.

First of all, I would really suggest you to look at using supervisors in your implementation, to avoid re-inventing the wheel.
A possibility could be to create an ETS table in your MAIN, so you can store data from within your gen_fsms which can survive process crashes.

My belief is that if GAME_ROOM exits because of an error, there is nothing to save (how do you know your state is valid, otherwise you would trap the error inside GAME_ROOM).

Related

Registering a child in the process that initiated the start_child call

I have a logic module that tells a supervisor to start child processes. I need to store those childrens pid in the logic modules state. But I also need to update a childs pid if the supervisor restarts it.
So I can't use the return value pid from the start_child call, since that will only give me the pid on the first start, not the restarts. Right now I make the child process call a register function (updates state with new pid) in the logic module from the childs init function. That way the logic module can update the pid in its state whenever a process is restarted. The logic module is a gen_server and I'm doing a cast when i register the child process.
Can anyone see a problem with this and are there any other more "proper" way of doing it?
One problem is that you have the ChildPid and the child might be dead by now. So sending it a message through a cast will mean the message is lost. And through a call you will crash yourself with an {'EXIT', noproc} unless you catch it out of the call. Your solution must take into account that a Pid might be long gone the instant you send a message. Usually by ignoring that the message is lost, by crashing yourself, or by remedying the problem and then go on.
There are a couple of options. This is a loose list:
Do as you do. Let the childs register themselves.
Let the logic module have a monitor on the child. That way you know if it dies.
Use Erlang Solutions gproc module: https://github.com/esl/gproc which gives you a neat interface to an ETS table keeping track of the information. Note that you can look up a pid in gproc and await its arrival if the process is just restarting.
Use supervisor:which_children to find the relevant child.
Roll your own ETS table as a variant of gproc
local names have to be atoms, but globally registered names can be any term (they are stored internally in a ETS table looking somewhat like gproc's, see the global_name_server in kernel/stdlib). Use the global structure to track the pids in question.

Data persistence when worker process dies, how?

I have worker processes that needs gathered/calculated data as arguments on start up. This is then needed on re-starts as well. Where should I put the initialization code? Inside the supervisors init? Or inside the modules start_link, or init? Are there any best practices in Erlang when it comes to this?
If the gen_server component has critical state, or state which cannot be re-calculated/re-gathered, I generally avoid keeping the state in gen_server itself. I instead choose to maintain state in an external process/ets table. If you are going by this approach, make sure the ets table is either created by an externel process (which you are sure will not die), for eg., the application process -or- create the ets table in the init method of the gen_server and use the "ets:give_away/3" method to hand it off to an external process (of course, you would need to check if the table is already created in the gen_server's init method).. Else the ets table will be destroyed when the process dies..

Can I handle any received message in gen_fsm state callbacks?

I noticed that messages sent to the pid of a gen_fsm process are matched in the state callbacks as events. Is this just accidental or can I rely on this feature?
Normally I would expect general messages sent to a gen_fsm to show up in the handle_info/3 callback and thought I would have to re-send it using gen_fsm:send_event.
Does gen_fsm try to match the message first to the state callback and then allways with the handle_info/3 callback? Or only if it doesn't match a state callback clause?
However when I try it my message seems to be handled twice according to debug output.
So basically the question can also be stated like: how to correctly handle received messages as events in gen_fsm state functions?
Clarification: that some of the events are occurring by getting messages passed should be considered given for this question.
I'm aware that in many cases its cleaner to make the protocol visible by using function calls into the fsm only.
I'm not so sure if this would improve the current framework where the mentioned gen_fsm has to fit in: Diverse protocol stacks where each layer calls a connect() function to attach (and sometimes start) the lower layer. Packets are sent to lower layers ba calling a function (send) and received by receiveing a message. Much like gen_tcp.
By looking at the code for gen_fsm I already figured out that general messages are only passed to handle_info, so only the question remains wether to call the state function directly from the handle_info/3 callback or resent using gen_fsm:send_event.
General messages are handled by handle_info callback, unless you have something like this in your code:
handle_info(Info, StateName, StateData) ->
?MODULE:StateName(Info, StateData).
Which avoids resending, but I do not recommend neither that, nor resending.
Delivering events exclusively by means of API calls encapsulating send_event/sync_send_event/send_all_state_event/sync_send_all_state_event makes protocol explicit. Which is a right thing, as it is easier to understand, maintain and document with edoc.

State in OTP event manager process (not handler!)

Can an OTP event manager process (e.g. a logger) have some state of its own (e.g. logging level) and filter/transform events based on it?
I also have a need to put some state into the gen_event itself, and my best idea at the moment is to use the process dictionary (get/put). Handlers are invoked in the context of the gen_event process, so the same process dictionary will be there for all handler calls.
Yes, process dictionaries are evil, but in this case they seem less evil than alternatives (ets table, state server).
The gen_event implementation as contained in the OTP does no provide means for adding state.
You could extend the implementation to achieve this and use your implementation instead of gen_event. However I would advise against it.
The kind of state you want to add to the event manager belongs really in the event handler for several reasons:
You might want to use different levels in different handlers, e.g. only show errors on the console but write everything to the disk.
If the event level would be changed in the manager event handlers depending on getting all unfiltered events might cease to function (events have more uses than just logging). This might lead to hard to debug problems.
If you want a event manager for multiple handlers that all only get filtered events you can easily achieve this by having two managers: one for unfiltered messages and one for e.g. level filtered messages. Then install a handler to the unfiltered one, filter in the handler by level (easy) and pass on the filtered events to the other manager. All handlers that only want to get filtered messages can be registered to the second manager.
The handlers can have their own state that gets passed on every callback like:
Module:handle_event(Event, State) -> Result
Filtering might look like this (assuming e.g. {level N, Content} events):
handle_event({level, Lvl, Content}, State#state{max_level=Max}) when Lvl >= Max ->
gen_event:notify(filtered_man, Content);
The State can be changed either by special events, by gen_event:call\3,4 (preferably) or by messages handled by handle_info.
For details see Gen_Event Behaviour and gen_event(3)
When you start_link a gen_event process - thing that you should always do via a supervisor -, you can merely specify a name for the new process, if you need/want it to be registered.
As far as I can see, there's no way to initiate a state of some sort using that behaviour.
Of course, you can write your own behaviour, on the top of a gen_event or of a simple gen_server.
As an alternative, you might use a separate gen_event process for each debugging level.
Or you can just filter the messages in the handlers.

How Do You Determine The PID of the Parent of a Process

I have a process in erlang that is supposed to do something immediately after spawn, then send the result back to the parent when it is finished. How do I figure out the PID of the process that spawned it?
You should pass self() to the child as one of the arguments to the entry function.
spawn_link(?MODULE, child, [self()]).
#Eridius' answer is the preferred way to do it. Requiring a process to register a name may have unintended side-effects such as increasing the visibility of the process not to mention the hassle of coming up with unique names when you have lots of processes.
The best way is definitely to pass it as an argument to the function called to start the child process. If you are spawning funs, which generally is a Good Thing to do, be careful of doing:
spawn_link(fun () -> child(self()) end)
which will NOT do as you intended. (Hint: when is self() called)
Generally you should avoid registering a process, i.e. giving it a global name, unless you really want it to be globally known. Spawning a fun means that you don't have to export the spawned function as you should generally avoid exporting functions that aren't meant to be called from other modules.
You can use the BIF register to give the spawning / parent process a name (an atom) then refer back to the registered name from other processes.
FUNC() ->
%% Do something
%% Then send message to parent
parent ! MESSAGE.
...
register(parent, self()),
spawn(MODULE, FUNC, [ARGS]).
See Getting Started With Erlang §3.3 and The Erlang Reference Manual §10.3.

Resources