Skip to content

Commit

Permalink
Implement a non-blocking gen_sctp:connect
Browse files Browse the repository at this point in the history
This patch extends the functionality of gen_sctp:connect by providing
a non-blocking connect call when Timeout = 0.

Especially when using multi-homed connections, the current implementation
has a race-condition that can cause the wrong sctp_association_change
record to be returned to the application.

With this patch, when an application calls connect with
Timeout = 0 then the application is responsible for receiving
the #sctp_assoc_change{} message and correctly determining the
connect it originated from (for example, by examining the
remote host and/or port). The application should have at least
{active, once} or be prepared to gen_sctp:recv the connect
result.

The behaviour is unchanged if Timeout /= 0.

Example application code:

try_connect(A) ->
    cancel_timer(A),
    case gen_sctp:connect(A#assoc.sock, A#assoc.remote_addr_order,
       A#assoc.remote_port, [{active, once}], 0) of
	ok ->
	    Timer = erlang:send_after(?ASSOC_RETRY_TIME,self(),
                        ?TimerMsg(A, assoc_retry_expiry)),
	    %% these results indicate connection in progress
	    {connecting, A#assoc{timer = Timer}};
	Err ->
	    {Err, A}
    end.
  • Loading branch information
dotsimon authored and bjorng committed Dec 3, 2009
1 parent 2666b68 commit 92f3cee
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 7 deletions.
7 changes: 6 additions & 1 deletion lib/kernel/doc/src/gen_sctp.xml
Expand Up @@ -155,7 +155,7 @@
</desc>
</func>
<func>
<name>connect(Socket, Addr, Port, [Opt], Timeout) -&gt; {ok, Assoc} | {error, posix()}</name>
<name>connect(Socket, Addr, Port, [Opt], Timeout) -&gt; ok | {ok, Assoc} | {error, posix()}</name>
<fsummary>Establish a new association for the socket <c>Socket</c>, with a peer (SCTP server socket)</fsummary>
<type>
<v>Socket = sctp_socket()</v>
Expand Down Expand Up @@ -185,6 +185,11 @@
inbound_streams = int(),
assoc_id = assoc_id()
} </pre>

If <c>Timeout</c> is 0, then the result of <c>connect/*</c> is
ok. The caller must receive or recv/* the sctp_assoc_change
message to determine the result of the connect call.

<p>The number of outbound and inbound streams can be set by
giving an <c>sctp_initmsg</c> option to <c>connect</c>
as in:</p>
Expand Down
20 changes: 14 additions & 6 deletions lib/kernel/src/inet_sctp.erl
Expand Up @@ -64,16 +64,24 @@ close(S) ->
listen(S, Flag) ->
prim_inet:listen(S, Flag).

%% A non-blocking connect is implemented when the initial call to
%% gen_sctp:connect has Timeout = 0. Then the application is
%% responsible for receiving the #sctp_assoc_change{} message and
%% correctly determining the connect it originated from (for
%% example, by examining the remote host and/or port).
%% The application should have at least {active, once} or be prepared
%% to gen_sctp:recv the connect result.

connect(S, Addr, Port, Opts, Timer) ->
case prim_inet:chgopts(S, Opts) of
ok ->
case prim_inet:getopt(S, active) of
{ok,Active} ->
Timeout = inet:timeout(Timer),
case prim_inet:connect(S, Addr, Port, Timeout) of
ok ->
ok when Timeout /= 0 ->
connect_get_assoc(S, Addr, Port, Active, Timer);
Err1 -> Err1
OkOrErr1 -> OkOrErr1
end;
Err2 -> Err2
end;
Expand All @@ -89,10 +97,10 @@ connect(S, Addr, Port, Opts, Timer) ->
%% connect_get_assoc/5 below mistakes it for an invalid response
%% for a socket in {active,false} or {active,once} modes.
%%
%% In {active,true} mode it probably gets right, but it is
%% a blocking connect that is implemented even for {active,true},
%% and that may be a shortcoming. A non-blocking connect
%% would be nice to have.
%% In {active,true} mode the window of time for the race is smaller,
%% but it is possible and also it is a blocking connect that is
%% implemented even for {active,true}, and that may be a
%% shortcoming.

connect_get_assoc(S, Addr, Port, false, Timer) ->
case recv(S, inet:timeout(Timer)) of
Expand Down

0 comments on commit 92f3cee

Please sign in to comment.