erlanggen-fsm

gen_fsm erlang timeout


I have a question on gen_fsm timeout. Let's say you have a set of gen_fsm with two states (in future can be more): idle which is the starting state and working.

Any 5 seconds the gen_fsm will check a particular parameter and according to it will remain in idle or will move to working. Whenever a gen_fsm will move to working, it will send a message (using gen_fsm:send_all_state_event/2) to all the other gen_fsm: the ones in idle should move to working while the one in working should not care about the incoming message.

Now, the point is that I don't want to have a skew in the states timeout (e.g. what happens if the machine is in working for 3 seconds and receives a message? A 5 seconds timeout is no more valid, since I want to preserve a fixed timeout no matter what (this means that a timeout message should be triggered at fixed times).

Here are the salient parts of my solution, which uses now() and time:now_diff/2. I have a small drift anyhow, but seems fair since talking about fractions of seconds.

Do you think it is valid?

{ok, idle, #state{time = now()}, 5000}.

idle(timeout, State) ->
    %% do some operations
    {next_state, idle, State#state{time = now()}, 5000}.

working(timeout, State) ->
    %% do some other actions
    {next_state, working, State#state{time = now()}, 5000}.

handle_event(work, working, #state{time = Time} = State) ->
    Timeout = round(timer:now_diff(now(), Time) / 1000),
    {next_state, working, State, Timeout}.

handle_event(work, StateName, state{time = Time} = State) ->
    Timeout = round(timer:now_diff(now(), Time) / 1000),
    {next_state, working, State, Timeout}.

Solution

  • You can use either timer:apply_interval to call a api function that sends the event to your fsm.

    Or you use timer:send_interval to send a custom message that you can handle in the handle_info callback in your gen_fsm.