aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src
diff options
context:
space:
mode:
authorAnders Svensson <[email protected]>2015-06-19 00:56:55 +0200
committerAnders Svensson <[email protected]>2015-06-19 03:03:14 +0200
commit083ae6d719a8ab5d626ad4b28e836475b6c1a92b (patch)
treed9ecb612a697bd19ac67883e5219e1a9e1c73596 /lib/diameter/src
parentda3e5d67b3600f1844b6d4f734eff05ba577ad95 (diff)
downloadotp-083ae6d719a8ab5d626ad4b28e836475b6c1a92b.tar.gz
otp-083ae6d719a8ab5d626ad4b28e836475b6c1a92b.tar.bz2
otp-083ae6d719a8ab5d626ad4b28e836475b6c1a92b.zip
Don't receive initial messages out of order
Forwarding an sctp message from the listener process at the same time that the controlling process is changed means there's no guarantee that the message order will be preserved. Selectively receive the peeloff message before entering the gen_server loop to ensure the order is preserved.
Diffstat (limited to 'lib/diameter/src')
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl62
1 files changed, 36 insertions, 26 deletions
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 43039ebd6e..29ac73a6a6 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -239,35 +239,49 @@ i({connect, Pid, Opts, Addrs, Ref}) ->
mode = {connect, connect(Sock, RAs, RP, [])},
socket = Sock};
-%% An accepting transport spawned by diameter.
-i({accept, Pid, LPid, Sock, Ref})
+%% An accepting transport spawned by diameter, not yet owning an
+%% association.
+i({accept, Pid, LPid, LSock, Ref})
when is_pid(Pid) ->
putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self()}),
monitor(process, Pid),
monitor(process, LPid),
- #transport{parent = Pid,
- mode = {accept, LPid},
- socket = Sock};
+ wait([peeloff], #transport{parent = Pid,
+ mode = {accept, LPid},
+ socket = LSock});
%% An accepting transport spawned at association establishment.
-i({accept, Ref, LPid, Sock, Id}) ->
+i({accept, Ref, LPid, LSock, _Id}) ->
putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self()}),
- MRef = monitor(process, LPid),
- %% Wait for a signal that the transport has been started before
- %% processing other messages.
+ erlang:send_after(?ACCEPT_TIMEOUT, self(), accept_timeout),
+ monitor(process, LPid),
+ wait([Ref, peeloff], #transport{mode = {accept, LPid},
+ socket = LSock}).
+
+%% wait/2
+%%
+%% Wait for diameter to start the transport process and for the
+%% association to be peeled off before processing other messages.
+
+wait(Keys, S) ->
+ lists:foldl(fun i/2, S, Keys).
+
+i(K, #transport{mode = {accept, _},
+ socket = LSock}
+ = S) ->
receive
- {Ref, Pid} -> %% transport started
- #transport{parent = Pid,
- mode = {accept, LPid},
- socket = Sock};
- {'DOWN', MRef, process, _, _} = T -> %% listener down
- close(Sock, Id),
+ {K, Pid} when is_reference(K) -> %% transport process started
+ S#transport{parent = Pid};
+ {K, Sock, T, Matches} when K == peeloff -> %% association
+ {sctp, LSock, _RA, _RP, _Data} = T, %% assert
+ ok = accept_peer(Sock, Matches),
+ t(setelement(2, T, Sock), S#transport{socket = Sock});
+ accept_timeout = T ->
+ x(T);
+ {'DOWN', _, process, _, _} = T ->
x(T)
- after ?ACCEPT_TIMEOUT ->
- close(Sock, Id),
- x(timeout)
end.
%% close/2
@@ -536,14 +550,6 @@ t(T,S) ->
%% transition/2
-%% Listening process is transfering ownership of an association.
-transition({peeloff, Sock, {sctp, LSock, _RA, _RP, _Data} = Msg, Matches},
- #transport{mode = {accept, _},
- socket = LSock}
- = S) ->
- ok = accept_peer(Sock, Matches),
- transition(setelement(2, Msg, Sock), S#transport{socket = Sock});
-
%% Incoming message.
transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) ->
setopts(Sock),
@@ -578,6 +584,10 @@ transition({'DOWN', _, process, Pid, _}, #transport{mode = {accept, Pid}}) ->
transition({'DOWN', _, process, _Pid, _}, #transport{mode = accept}) ->
ok;
+%% Timeout after transport process has been started.
+transition(accept_timeout, _) ->
+ ok;
+
%% Request for the local port number.
transition({resolve_port, Pid}, #transport{socket = Sock})
when is_pid(Pid) ->