Current section
Files
Jump to
Current section
Files
src/ezmq_socket_fsm.erl
%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at http://mozilla.org/MPL/2.0/.
-module(ezmq_socket_fsm).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
-include("ezmq_internal.hrl").
-export([init/3, check/2, do/2, close/2, encap_msg/2, decap_msg/3]).
-record(fsm_state, {
module :: atom(),
state_name :: atom(),
state :: term()
}).
-type transport() :: pid().
-type check_type() :: 'send' | 'dequeue_send' | 'deliver' | 'deliver_recv' | 'recv'.
-type do_type() :: 'queue_send' | {'deliver_send', list(transport())} | {'deliver', transport()} | {'queue', transport()} | {'dequeue', transport()}.
%% FSM CHECKS
%% ==========
%%
%% {'send', Msg}:
%% is it permited the execute a send call in the current state,
%% return: {ok, Transport} | {error, Reason} | {queue, block | return}
%%
%% 'dequeue_send':
%% return the transport to send on or 'keep' when none is available
%% return: {ok, Transport} | 'keep'
%%
%% 'deliver':
%% should the recived message delivered to the socket owner or queue for later
%% return: ok | queue
%%
%% {'deliver_recv', Transport, IdMsg}
%% is IdMsg recived on Transport permited in the current state and/or
%% do we need to treat is as a socket fsm control message
%% return: ok | control | {error, Reason}
%%
%% 'recv':
%% is it permited the execute a recv call in the current state,
%% return: ok | {error, Reason}
%%
%% FSM do operations
%% =================
%%
%% do operations change the current state of FSM,
%% their return is:
%% {next_state, NextStateName, MqSState1, State} | {error, Reason}
%%
%% 'queue_send':
%% called the a new message has been queued for sending
%%
%% {'deliver_send', Transports}:
%% called the a new message has been send on Transports,
%% Transports == 'abort' signals that the last message was not sent
%%
%% {'deliver', Transport}:
%% called the a received message has been delivered to the socket owner
%%
%% {'queue', Transport}:
%% called the a received message has been queued for delivery to the socket owner
%%
%% {'dequeue', Transport}:
%% called the a queued received message has been delivered to the socket owner
init(Module, Opts, MqSState) ->
case Module:init(Opts) of
{ok, StateName, State} ->
{ok, MqSState#ezmq_socket{fsm = #fsm_state{module = Module, state_name = StateName, state = State}}};
Reply ->
{Reply, MqSState}
end.
%% check actions do not alter the state of the FSM
check(Action, MqSState = #ezmq_socket{fsm = Fsm}) ->
#fsm_state{module = Module, state_name = StateName, state = State} = Fsm,
R = Module:StateName(check, Action, MqSState, State),
lager:debug("ezmq_socket_fsm state: ~w, check: ~w, Result: ~w", [StateName, Action, R]),
R.
do(Action, MqSState = #ezmq_socket{fsm = Fsm}) ->
#fsm_state{module = Module, state_name = StateName, state = State} = Fsm,
case Module:StateName(do, Action, MqSState, State) of
{error, Reason} ->
lager:error("socket fsm for ~w exited with ~p, (~p,~p)~n", [Action, Reason, MqSState, State]),
error(Reason);
{next_state, NextStateName, NextMqSState, NextState} ->
lager:debug("ezmq_socket_fsm: state: ~w, Action: ~w, next_state: ~w", [StateName, Action, NextStateName]),
NewFsm = Fsm#fsm_state{state_name = NextStateName, state = NextState},
NextMqSState#ezmq_socket{fsm = NewFsm}
end.
close(Transport, MqSState = #ezmq_socket{fsm = Fsm}) ->
#fsm_state{module = Module, state_name = StateName, state = State} = Fsm,
case Module:close(StateName, Transport, MqSState, State) of
{error, Reason} ->
lager:error("socket fsm for ~w exited with ~p, (~p,~p)~n", [Transport, Reason, MqSState, State]),
error(Reason);
{next_state, NextStateName, NextMqSState, NextState} ->
lager:debug("ezmq_socket_fsm: state: ~w, Transport: ~w, next_state: ~w", [StateName, Transport, NextStateName]),
NewFsm = Fsm#fsm_state{state_name = NextStateName, state = NextState},
NextMqSState#ezmq_socket{fsm = NewFsm}
end.
encap_msg({Transport, Msg}, MqSState = #ezmq_socket{fsm = Fsm})
when is_pid(Transport), is_list(Msg) ->
#fsm_state{module = Module, state_name = StateName, state = State} = Fsm,
Module:encap_msg({Transport, Msg}, StateName, MqSState, State);
encap_msg({Transport, Msg = {_Identity, Parts}}, MqSState = #ezmq_socket{fsm = Fsm})
when is_pid(Transport), is_list(Parts) ->
#fsm_state{module = Module, state_name = StateName, state = State} = Fsm,
Module:encap_msg({Transport, Msg}, StateName, MqSState, State).
decap_msg(Transport, IdMsg = {_, Msg}, MqSState = #ezmq_socket{fsm = Fsm})
when is_pid(Transport), is_list(Msg) ->
#fsm_state{module = Module, state_name = StateName, state = State} = Fsm,
Module:decap_msg(Transport, IdMsg, StateName, MqSState, State).