From 9b3796b912fb1410a983e9ace5d892cdeda72926 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Wed, 10 Apr 2019 18:33:11 +0200
Subject: [socket] Update recv to handle Timeout = nowait
Update function recv and its spec(s) to handle
the Timeout value of nowait.
---
erts/preloaded/ebin/socket.beam | Bin 70456 -> 71412 bytes
erts/preloaded/src/socket.erl | 69 +++++++++++++++++++++++++++++++++++++---
2 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index e37aa81b7c..71a41bd599 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 0f0d8f7a02..1d651f99ed 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1664,13 +1664,25 @@ recv(Socket, Length) ->
?SOCKET_RECV_FLAGS_DEFAULT,
?SOCKET_RECV_TIMEOUT_DEFAULT).
--spec recv(Socket, Length, Flags) -> {ok, Data} | {error, Reason} when
+-spec recv(Socket, Length, Flags) -> {ok, Data} |
+ {error, Reason} when
Socket :: socket(),
Length :: non_neg_integer(),
Flags :: recv_flags(),
Data :: binary(),
Reason :: term()
- ; (Socket, Length, Timeout) -> {ok, Data} | {error, Reason} when
+ ; (Socket, Length, nowait) -> {ok, Data} |
+ {ok, {Data, SelInfo}} |
+ {ok, SelInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Data :: binary(),
+ SelInfo :: {select, RecvRef},
+ RecvRef :: reference(),
+ Reason :: term()
+ ; (Socket, Length, Timeout) -> {ok, Data} |
+ {error, Reason} when
Socket :: socket(),
Length :: non_neg_integer(),
Timeout :: timeout(),
@@ -1682,7 +1694,19 @@ recv(Socket, Length, Flags) when is_list(Flags) ->
recv(Socket, Length, Timeout) ->
recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
--spec recv(Socket, Length, Flags, Timeout) -> {ok, Data} | {error, Reason} when
+-spec recv(Socket, Length, Flags, nowait) -> {ok, Data} |
+ {ok, {Data, SelInfo}} |
+ {ok, SelInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Data :: binary(),
+ SelInfo :: {select, RecvRef},
+ RecvRef :: reference(),
+ Reason :: term()
+ ; (Socket, Length, Flags, Timeout) -> {ok, Data} |
+ {error, Reason} when
Socket :: socket(),
Length :: non_neg_integer(),
Flags :: recv_flags(),
@@ -1693,7 +1717,9 @@ recv(Socket, Length, Timeout) ->
recv(#socket{ref = SockRef}, Length, Flags, Timeout)
when (is_integer(Length) andalso (Length >= 0)) andalso
is_list(Flags) andalso
- (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ (is_integer(Timeout) orelse
+ (Timeout =:= infinity) orelse
+ (Timeout =:= nowait)) ->
EFlags = enc_recv_flags(Flags),
do_recv(SockRef, undefined, Length, EFlags, <<>>, Timeout).
@@ -1701,8 +1727,12 @@ recv(#socket{ref = SockRef}, Length, Flags, Timeout)
%% with Length = 0. This case makes it neccessary to have a timeout function
%% clause since we may never wait for anything (no receive select), and so the
%% the only timeout check will be the function clause.
+%% Note that the Timeout value of 'nowait' has a special meaning. It means
+%% that we will either return with data or with the with {error, NNNN}. In
+%% wich case the caller will receive a select message at some later time.
do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
- when (Timeout =:= infinity) orelse
+ when (Timeout =:= nowait) orelse
+ (Timeout =:= infinity) orelse
(is_integer(Timeout) andalso (Timeout > 0)) ->
TS = timestamp(Timeout),
RecvRef = make_ref(),
@@ -1723,6 +1753,16 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
<>,
next_timeout(TS, Timeout));
+
+ %% Did not get all the user asked for, but the user also
+ %% specified 'nowait', so deliver what we got and the
+ %% select info.
+ {ok, false = _Completed, Bin} when (Timeout =:= nowait) andalso
+ (size(Acc) =:= 0) ->
+ SelInfo = {select, RecvRef},
+ {ok, {Bin, SelInfo}};
+
+
{ok, false = _Completed, Bin} when (size(Acc) =:= 0) ->
%% We got the first chunk of it.
%% We will be notified (select message) when there
@@ -1761,6 +1801,21 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
{error, {timeout, Acc}}
end;
+
+ %% The user does not want to wait!
+ %% The user will be informed that there is something to read
+ %% via the select socket message (see below).
+
+ {error, eagain} when (Timeout =:= nowait) ->
+ SelInfo = {select, RecvRef},
+ if
+ (size(Acc) =:= 0) ->
+ {ok, SelInfo};
+ true ->
+ {ok, {Acc, SelInfo}}
+ end;
+
+
%% We return with the accumulated binary (if its non-empty)
{error, eagain} when (Length =:= 0) andalso (size(Acc) > 0) ->
%% CAN WE REALLY DO THIS? THE NIF HAS SELECTED!! OR?
@@ -3462,6 +3517,8 @@ flush_select_msgs(SockRef, Ref) ->
%% A timestamp in ms
+timestamp(nowait = T) ->
+ T;
timestamp(infinity) ->
undefined;
timestamp(_) ->
@@ -3471,6 +3528,8 @@ timestamp() ->
{A,B,C} = os:timestamp(),
A*1000000000+B*1000+(C div 1000).
+next_timeout(_, nowait = Timeout) ->
+ Timeout;
next_timeout(_, infinity = Timeout) ->
Timeout;
next_timeout(TS, Timeout) ->
--
cgit v1.2.3
From 6ef2e9e290ffb378ff5a18b9c278d1a2ce15cf55 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Fri, 12 Apr 2019 16:02:36 +0200
Subject: [socket] Update recvfrom to handle Timeout = nowait
Update function recvfrom and its spec(s) to handle
the Timeout value of nowait.
Also replaced timestamp method with erlang:monotonic_time(milli_seconds).
---
erts/preloaded/ebin/socket.beam | Bin 71412 -> 72020 bytes
erts/preloaded/src/socket.erl | 69 ++++++++++++++++++++++++++++++++--------
2 files changed, 55 insertions(+), 14 deletions(-)
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 71a41bd599..d0e9817b8c 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 1d651f99ed..3bd268403a 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1906,8 +1906,20 @@ recvfrom(Socket, BufSz) ->
?SOCKET_RECV_FLAGS_DEFAULT,
?SOCKET_RECV_TIMEOUT_DEFAULT).
--spec recvfrom(Socket, Flags, Timeout) ->
- {ok, {Source, Data}} | {error, Reason} when
+-spec recvfrom(Socket, Flags, nowait) ->
+ {ok, {Source, Data}} |
+ {ok, SelInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ SelInfo :: {select, RecvRef},
+ RecvRef :: reference(),
+ Reason :: term()
+ ; (Socket, Flags, Timeout) ->
+ {ok, {Source, Data}} |
+ {error, Reason} when
Socket :: socket(),
Flags :: recv_flags(),
Timeout :: timeout(),
@@ -1922,6 +1934,17 @@ recvfrom(Socket, BufSz) ->
Source :: sockaddr() | undefined,
Data :: binary(),
Reason :: term()
+ ; (Socket, BufSz, nowait) ->
+ {ok, {Source, Data}} |
+ {ok, SelInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ SelInfo :: {select, RecvRef},
+ RecvRef :: reference(),
+ Reason :: term()
; (Socket, BufSz, Timeout) ->
{ok, {Source, Data}} | {error, Reason} when
Socket :: socket(),
@@ -1938,20 +1961,35 @@ recvfrom(Socket, BufSz, Flags) when is_list(Flags) ->
recvfrom(Socket, BufSz, Timeout) ->
recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
--spec recvfrom(Socket, BufSz, Flags, Timeout) ->
- {ok, {Source, Data}} | {error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- Flags :: recv_flags(),
- Timeout :: timeout(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- Reason :: term().
+-spec recvfrom(Socket, BufSz, Flags, nowait) ->
+ {ok, {Source, Data}} |
+ {ok, SelInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ SelInfo :: {select, RecvRef},
+ RecvRef :: reference(),
+ Reason :: term()
+ ; (Socket, BufSz, Flags, Timeout) ->
+ {ok, {Source, Data}} |
+ {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term().
recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout)
when (is_integer(BufSz) andalso (BufSz >= 0)) andalso
is_list(Flags) andalso
- (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ (is_integer(Timeout) orelse
+ (Timeout =:= infinity) orelse
+ (Timeout =:= nowait)) ->
EFlags = enc_recv_flags(Flags),
do_recvfrom(SockRef, BufSz, EFlags, Timeout).
@@ -1962,6 +2000,10 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) ->
{ok, {_Source, _NewData}} = OK ->
OK;
+ {error, eagain} when (Timeout =:= nowait) ->
+ SelInfo = {select, RecvRef},
+ {ok, SelInfo};
+
{error, eagain} ->
%% There is nothing just now, but we will be notified when there
%% is something to read (a select message).
@@ -3525,8 +3567,7 @@ timestamp(_) ->
timestamp().
timestamp() ->
- {A,B,C} = os:timestamp(),
- A*1000000000+B*1000+(C div 1000).
+ erlang:monotonic_time(milli_seconds).
next_timeout(_, nowait = Timeout) ->
Timeout;
--
cgit v1.2.3
From 3419aee8487840941124dbcfc24ddde12df49c63 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Fri, 12 Apr 2019 18:44:10 +0200
Subject: [socket] Update recvmsg to handle Timeout = nowait
Update function recvmsg and its spec(s) to handle
the Timeout value of nowait.
---
erts/preloaded/ebin/socket.beam | Bin 72020 -> 72648 bytes
erts/preloaded/src/socket.erl | 42 +++++++++++++++++++++++++++++++++++++---
2 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index d0e9817b8c..326fe49a69 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 3bd268403a..5b460ae81b 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -2043,6 +2043,14 @@ recvmsg(Socket) ->
Socket :: socket(),
Flags :: recv_flags(),
MsgHdr :: msghdr(),
+ Reason :: term()
+ ; (Socket, nowait) -> {ok, MsgHdr} |
+ {ok, SelInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ SelInfo :: {select, RecvRef},
+ RecvRef :: reference(),
Reason :: term()
; (Socket, Timeout) -> {ok, MsgHdr} | {error, Reason} when
Socket :: socket(),
@@ -2055,7 +2063,16 @@ recvmsg(Socket, Flags) when is_list(Flags) ->
recvmsg(Socket, Timeout) ->
recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
--spec recvmsg(Socket, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
+-spec recvmsg(Socket, Flags, nowait) -> {ok, MsgHdr} |
+ {ok, SelInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ MsgHdr :: msghdr(),
+ SelInfo :: {select, RecvRef},
+ RecvRef :: reference(),
+ Reason :: term()
+ ; (Socket, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
Socket :: socket(),
Flags :: recv_flags(),
Timeout :: timeout(),
@@ -2077,7 +2094,20 @@ recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz) andalso is_integer(CtrlSz)
-spec recvmsg(Socket,
BufSz, CtrlSz,
- Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
+ Flags, nowait) -> {ok, MsgHdr} |
+ {ok, SelInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ CtrlSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ MsgHdr :: msghdr(),
+ SelInfo :: {select, RecvRef},
+ RecvRef :: reference(),
+ Reason :: term()
+ ; (Socket,
+ BufSz, CtrlSz,
+ Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
Socket :: socket(),
BufSz :: non_neg_integer(),
CtrlSz :: non_neg_integer(),
@@ -2090,7 +2120,9 @@ recvmsg(#socket{ref = SockRef}, BufSz, CtrlSz, Flags, Timeout)
when (is_integer(BufSz) andalso (BufSz >= 0)) andalso
(is_integer(CtrlSz) andalso (CtrlSz >= 0)) andalso
is_list(Flags) andalso
- (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ (is_integer(Timeout) orelse
+ (Timeout =:= infinity) orelse
+ (Timeout =:= nowait)) ->
EFlags = enc_recv_flags(Flags),
do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout).
@@ -2101,6 +2133,10 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) ->
{ok, _MsgHdr} = OK ->
OK;
+ {error, eagain} when (Timeout =:= nowait) ->
+ SelInfo = {select, RecvRef},
+ {ok, SelInfo};
+
{error, eagain} ->
%% There is nothing just now, but we will be notified when there
%% is something to read (a select message).
--
cgit v1.2.3
From b030b056c66d2afc8dfd0c03187f0ffafc0612e6 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 15 Apr 2019 11:35:05 +0200
Subject: [socket] Update send to handle Timeout = nowait
Update function send and its spec(s) to handle
the Timeout value of nowait.
---
erts/preloaded/ebin/socket.beam | Bin 72648 -> 73296 bytes
erts/preloaded/src/socket.erl | 76 +++++++++++++++++++++++++++++++++-------
2 files changed, 64 insertions(+), 12 deletions(-)
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 326fe49a69..b7bf0b7377 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 5b460ae81b..fb08314d59 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1278,7 +1278,8 @@ accept(Socket) ->
accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) ->
{error, timeout};
accept(#socket{ref = LSockRef}, Timeout)
- when is_integer(Timeout) orelse (Timeout =:= infinity) ->
+ when is_integer(Timeout) orelse
+ (Timeout =:= infinity) ->
do_accept(LSockRef, Timeout).
do_accept(LSockRef, Timeout) ->
@@ -1331,29 +1332,65 @@ send(Socket, Data) ->
Data :: iodata(),
Flags :: send_flags(),
Reason :: term()
+ ; (Socket, Data, nowait) -> ok |
+ {ok, SelInfo} |
+ {ok, {RestData, SelInfo}} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ RestData :: binary(),
+ SelInfo :: {select, SendRef},
+ SendRef :: reference(),
+ Reason :: term()
; (Socket, Data, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Timeout :: timeout(),
- Reason :: term().
+ Socket :: socket(),
+ Data :: iodata(),
+ Timeout :: timeout(),
+ Reason :: term().
send(Socket, Data, Flags) when is_list(Flags) ->
send(Socket, Data, Flags, ?SOCKET_SEND_TIMEOUT_DEFAULT);
send(Socket, Data, Timeout) ->
send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, Timeout).
--spec send(Socket, Data, Flags, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Flags :: send_flags(),
- Timeout :: timeout(),
- Reason :: term().
+-spec send(Socket, Data, Flags, nowait) -> ok |
+ {ok, SelInfo} |
+ {ok, {RestData, SelInfo}} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ RestData :: binary(),
+ SelInfo :: {select, SendRef},
+ SendRef :: reference(),
+ Reason :: term()
+ ; (Socket, Data, Flags, nowait) -> ok |
+ {ok, SelInfo} |
+ {ok, {RestData, SelInfo}} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ RestData :: binary(),
+ SelInfo :: {select, SendRef},
+ SendRef :: reference(),
+ Reason :: term()
+ ; (Socket, Data, Flags, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Reason :: term().
send(Socket, Data, Flags, Timeout) when is_list(Data) ->
Bin = erlang:list_to_binary(Data),
send(Socket, Bin, Flags, Timeout);
send(#socket{ref = SockRef}, Data, Flags, Timeout)
- when is_binary(Data) andalso is_list(Flags) ->
+ when is_binary(Data) andalso
+ is_list(Flags) andalso
+ ((Timeout =:= nowait) orelse
+ (Timeout =:= infinity) orelse
+ (is_integer(Timeout) andalso (Timeout > 0))) ->
EFlags = enc_send_flags(Flags),
do_send(SockRef, Data, EFlags, Timeout).
@@ -1363,6 +1400,14 @@ do_send(SockRef, Data, EFlags, Timeout) ->
case nif_send(SockRef, SendRef, Data, EFlags) of
ok ->
ok;
+
+
+ {ok, Written} when (Timeout =:= nowait) ->
+ <<_:Written/binary, Rest/binary>> = Data,
+ SelInfo = {select, SendRef},
+ {ok, {Rest, SelInfo}};
+
+
{ok, Written} ->
NewTimeout = next_timeout(TS, Timeout),
%% We are partially done, wait for continuation
@@ -1384,6 +1429,13 @@ do_send(SockRef, Data, EFlags, Timeout) ->
cancel(SockRef, send, SendRef),
{error, {timeout, size(Data)}}
end;
+
+
+ {error, eagain} ->
+ SelInfo = {select, SendRef},
+ {ok, SelInfo};
+
+
{error, eagain} ->
receive
{?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} ->
--
cgit v1.2.3
From 3f0358735f2ceabe77118b9a547a7bdccfc653b3 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 15 Apr 2019 11:51:19 +0200
Subject: [socket] Add type and macro for select info
Add proper type(s) for the select info. Also add
a utility macro to build the term.
---
erts/preloaded/ebin/socket.beam | Bin 73296 -> 73388 bytes
erts/preloaded/src/socket.erl | 242 ++++++++++++++++++++--------------------
2 files changed, 123 insertions(+), 119 deletions(-)
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index b7bf0b7377..13c8b601c7 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index fb08314d59..c8168a6ee7 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -57,6 +57,10 @@
]).
-export_type([
+ select_tag/0,
+ select_ref/0,
+ select_info/0,
+
domain/0,
type/0,
protocol/0,
@@ -585,6 +589,13 @@
#{level := integer(), type := integer(), data := binary()}.
+-type select_tag() :: atom().
+-type select_ref() :: reference().
+-type select_info() :: {select, select_tag(), select_ref()}.
+
+-define(SELECT_INFO(T, R), {select, T, R}).
+
+
%% This is used in messages sent from the nif-code to erlang processes:
%%
%% {?SOCKET_TAG, Socket :: socket(), Tag :: atom(), Info :: term()}
@@ -1431,7 +1442,7 @@ do_send(SockRef, Data, EFlags, Timeout) ->
end;
- {error, eagain} ->
+ {error, eagain} when (Timeout =:= nowait) ->
SelInfo = {select, SendRef},
{ok, SelInfo};
@@ -1724,15 +1735,14 @@ recv(Socket, Length) ->
Data :: binary(),
Reason :: term()
; (Socket, Length, nowait) -> {ok, Data} |
- {ok, {Data, SelInfo}} |
- {ok, SelInfo} |
+ {ok, SelectInfo} |
+ {ok, {Data, SelectInfo}} |
{error, Reason} when
- Socket :: socket(),
- Length :: non_neg_integer(),
- Data :: binary(),
- SelInfo :: {select, RecvRef},
- RecvRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, Length, Timeout) -> {ok, Data} |
{error, Reason} when
Socket :: socket(),
@@ -1747,24 +1757,23 @@ recv(Socket, Length, Timeout) ->
recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
-spec recv(Socket, Length, Flags, nowait) -> {ok, Data} |
- {ok, {Data, SelInfo}} |
- {ok, SelInfo} |
+ {ok, SelectInfo} |
+ {ok, {Data, SelectInfo}} |
{error, Reason} when
- Socket :: socket(),
- Length :: non_neg_integer(),
- Flags :: recv_flags(),
- Data :: binary(),
- SelInfo :: {select, RecvRef},
- RecvRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, Length, Flags, Timeout) -> {ok, Data} |
{error, Reason} when
- Socket :: socket(),
- Length :: non_neg_integer(),
- Flags :: recv_flags(),
- Timeout :: timeout(),
- Data :: binary(),
- Reason :: term().
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ Data :: binary(),
+ Reason :: term().
recv(#socket{ref = SockRef}, Length, Flags, Timeout)
when (is_integer(Length) andalso (Length >= 0)) andalso
@@ -1811,8 +1820,7 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
%% select info.
{ok, false = _Completed, Bin} when (Timeout =:= nowait) andalso
(size(Acc) =:= 0) ->
- SelInfo = {select, RecvRef},
- {ok, {Bin, SelInfo}};
+ {ok, {Bin, ?SELECT_INFO(recv, RecvRef)}};
{ok, false = _Completed, Bin} when (size(Acc) =:= 0) ->
@@ -1859,12 +1867,12 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
%% via the select socket message (see below).
{error, eagain} when (Timeout =:= nowait) ->
- SelInfo = {select, RecvRef},
+ SelectInfo = ?SELECT_INFO(recv, RecvRef),
if
(size(Acc) =:= 0) ->
- {ok, SelInfo};
+ {ok, SelectInfo};
true ->
- {ok, {Acc, SelInfo}}
+ {ok, {Acc, SelectInfo}}
end;
@@ -1960,51 +1968,49 @@ recvfrom(Socket, BufSz) ->
-spec recvfrom(Socket, Flags, nowait) ->
{ok, {Source, Data}} |
- {ok, SelInfo} |
+ {ok, SelectInfo} |
{error, Reason} when
- Socket :: socket(),
- Flags :: recv_flags(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- SelInfo :: {select, RecvRef},
- RecvRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, Flags, Timeout) ->
{ok, {Source, Data}} |
{error, Reason} when
- Socket :: socket(),
- Flags :: recv_flags(),
- Timeout :: timeout(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- Reason :: term()
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term()
; (Socket, BufSz, Flags) ->
{ok, {Source, Data}} | {error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- Flags :: recv_flags(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- Reason :: term()
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term()
; (Socket, BufSz, nowait) ->
{ok, {Source, Data}} |
- {ok, SelInfo} |
+ {ok, SelectInfo} |
{error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- SelInfo :: {select, RecvRef},
- RecvRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, BufSz, Timeout) ->
{ok, {Source, Data}} | {error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- Timeout :: timeout(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- Reason :: term().
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Timeout :: timeout(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term().
recvfrom(Socket, Flags, Timeout) when is_list(Flags) ->
recvfrom(Socket, 0, Flags, Timeout);
@@ -2015,16 +2021,15 @@ recvfrom(Socket, BufSz, Timeout) ->
-spec recvfrom(Socket, BufSz, Flags, nowait) ->
{ok, {Source, Data}} |
- {ok, SelInfo} |
+ {ok, SelectInfo} |
{error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- Flags :: recv_flags(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- SelInfo :: {select, RecvRef},
- RecvRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, BufSz, Flags, Timeout) ->
{ok, {Source, Data}} |
{error, Reason} when
@@ -2052,9 +2057,10 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) ->
{ok, {_Source, _NewData}} = OK ->
OK;
+
{error, eagain} when (Timeout =:= nowait) ->
- SelInfo = {select, RecvRef},
- {ok, SelInfo};
+ {ok, ?SELECT_INFO(recvfrom, RecvRef)};
+
{error, eagain} ->
%% There is nothing just now, but we will be notified when there
@@ -2097,18 +2103,17 @@ recvmsg(Socket) ->
MsgHdr :: msghdr(),
Reason :: term()
; (Socket, nowait) -> {ok, MsgHdr} |
- {ok, SelInfo} |
+ {ok, SelectInfo} |
{error, Reason} when
- Socket :: socket(),
- MsgHdr :: msghdr(),
- SelInfo :: {select, RecvRef},
- RecvRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, Timeout) -> {ok, MsgHdr} | {error, Reason} when
- Socket :: socket(),
- Timeout :: timeout(),
- MsgHdr :: msghdr(),
- Reason :: term().
+ Socket :: socket(),
+ Timeout :: timeout(),
+ MsgHdr :: msghdr(),
+ Reason :: term().
recvmsg(Socket, Flags) when is_list(Flags) ->
recvmsg(Socket, 0, 0, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT);
@@ -2116,26 +2121,25 @@ recvmsg(Socket, Timeout) ->
recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
-spec recvmsg(Socket, Flags, nowait) -> {ok, MsgHdr} |
- {ok, SelInfo} |
+ {ok, SelectInfo} |
{error, Reason} when
- Socket :: socket(),
- Flags :: recv_flags(),
- MsgHdr :: msghdr(),
- SelInfo :: {select, RecvRef},
- RecvRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ MsgHdr :: msghdr(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
- Socket :: socket(),
- Flags :: recv_flags(),
- Timeout :: timeout(),
- MsgHdr :: msghdr(),
- Reason :: term()
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ MsgHdr :: msghdr(),
+ Reason :: term()
; (Socket, BufSz, CtrlSz) -> {ok, MsgHdr} | {error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- CtrlSz :: non_neg_integer(),
- MsgHdr :: msghdr(),
- Reason :: term().
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ CtrlSz :: non_neg_integer(),
+ MsgHdr :: msghdr(),
+ Reason :: term().
recvmsg(Socket, Flags, Timeout) when is_list(Flags) ->
recvmsg(Socket, 0, 0, Flags, Timeout);
@@ -2147,26 +2151,25 @@ recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz) andalso is_integer(CtrlSz)
-spec recvmsg(Socket,
BufSz, CtrlSz,
Flags, nowait) -> {ok, MsgHdr} |
- {ok, SelInfo} |
+ {ok, SelectInfo} |
{error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- CtrlSz :: non_neg_integer(),
- Flags :: recv_flags(),
- MsgHdr :: msghdr(),
- SelInfo :: {select, RecvRef},
- RecvRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ CtrlSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ MsgHdr :: msghdr(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket,
BufSz, CtrlSz,
Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- CtrlSz :: non_neg_integer(),
- Flags :: recv_flags(),
- Timeout :: timeout(),
- MsgHdr :: msghdr(),
- Reason :: term().
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ CtrlSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ MsgHdr :: msghdr(),
+ Reason :: term().
recvmsg(#socket{ref = SockRef}, BufSz, CtrlSz, Flags, Timeout)
when (is_integer(BufSz) andalso (BufSz >= 0)) andalso
@@ -2185,9 +2188,10 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) ->
{ok, _MsgHdr} = OK ->
OK;
+
{error, eagain} when (Timeout =:= nowait) ->
- SelInfo = {select, RecvRef},
- {ok, SelInfo};
+ {ok, ?SELECT_INFO(recvmsg, RecvRef)};
+
{error, eagain} ->
%% There is nothing just now, but we will be notified when there
--
cgit v1.2.3
From 4159f06540cf41e04b60d98f7106774774ce87b7 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 15 Apr 2019 12:01:37 +0200
Subject: [socket] Update sendto to handle Timeout = nowait
Update function sendto and its spec(s) to handle
the Timeout value of nowait.
---
erts/preloaded/ebin/socket.beam | Bin 73388 -> 73912 bytes
erts/preloaded/src/socket.erl | 150 +++++++++++++++++++++++-----------------
2 files changed, 88 insertions(+), 62 deletions(-)
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 13c8b601c7..40af29476d 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index c8168a6ee7..c0b1e4d8dc 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1339,25 +1339,24 @@ send(Socket, Data) ->
send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT).
-spec send(Socket, Data, Flags) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Flags :: send_flags(),
- Reason :: term()
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ Reason :: term()
; (Socket, Data, nowait) -> ok |
- {ok, SelInfo} |
- {ok, {RestData, SelInfo}} |
+ {ok, SelectInfo} |
+ {ok, {RestData, SelectInfo}} |
{error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- RestData :: binary(),
- SelInfo :: {select, SendRef},
- SendRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ Data :: iodata(),
+ RestData :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, Data, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Timeout :: timeout(),
- Reason :: term().
+ Socket :: socket(),
+ Data :: iodata(),
+ Timeout :: timeout(),
+ Reason :: term().
send(Socket, Data, Flags) when is_list(Flags) ->
send(Socket, Data, Flags, ?SOCKET_SEND_TIMEOUT_DEFAULT);
@@ -1365,33 +1364,31 @@ send(Socket, Data, Timeout) ->
send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, Timeout).
-spec send(Socket, Data, Flags, nowait) -> ok |
- {ok, SelInfo} |
- {ok, {RestData, SelInfo}} |
+ {ok, SelectInfo} |
+ {ok, {RestData, SelectInfo}} |
{error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Flags :: send_flags(),
- RestData :: binary(),
- SelInfo :: {select, SendRef},
- SendRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ RestData :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, Data, Flags, nowait) -> ok |
- {ok, SelInfo} |
- {ok, {RestData, SelInfo}} |
+ {ok, SelectInfo} |
+ {ok, {RestData, SelectInfo}} |
{error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Flags :: send_flags(),
- RestData :: binary(),
- SelInfo :: {select, SendRef},
- SendRef :: reference(),
- Reason :: term()
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ RestData :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, Data, Flags, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Flags :: send_flags(),
- Timeout :: timeout(),
- Reason :: term().
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Reason :: term().
send(Socket, Data, Flags, Timeout) when is_list(Data) ->
Bin = erlang:list_to_binary(Data),
@@ -1415,8 +1412,7 @@ do_send(SockRef, Data, EFlags, Timeout) ->
{ok, Written} when (Timeout =:= nowait) ->
<<_:Written/binary, Rest/binary>> = Data,
- SelInfo = {select, SendRef},
- {ok, {Rest, SelInfo}};
+ {ok, {Rest, ?SELECT_INFO(send, SendRef)}};
{ok, Written} ->
@@ -1443,8 +1439,7 @@ do_send(SockRef, Data, EFlags, Timeout) ->
{error, eagain} when (Timeout =:= nowait) ->
- SelInfo = {select, SendRef},
- {ok, SelInfo};
+ {ok, ?SELECT_INFO(send, SendRef)};
{error, eagain} ->
@@ -1482,17 +1477,25 @@ sendto(Socket, Data, Dest) ->
sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT).
-spec sendto(Socket, Data, Dest, Flags) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: binary(),
- Dest :: null | sockaddr(),
- Flags :: send_flags(),
- Reason :: term()
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: null | sockaddr(),
+ Flags :: send_flags(),
+ Reason :: term()
+ ; (Socket, Data, Dest, nowait) -> ok |
+ {ok, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Dest :: null | sockaddr(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, Data, Dest, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Dest :: null | sockaddr(),
- Timeout :: timeout(),
- Reason :: term().
+ Socket :: socket(),
+ Data :: iodata(),
+ Dest :: null | sockaddr(),
+ Timeout :: timeout(),
+ Reason :: term().
sendto(Socket, Data, Dest, Flags) when is_list(Flags) ->
sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT);
@@ -1500,13 +1503,22 @@ sendto(Socket, Data, Dest, Timeout) ->
sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT, Timeout).
--spec sendto(Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: binary(),
- Dest :: null | sockaddr(),
- Flags :: send_flags(),
- Timeout :: timeout(),
- Reason :: term().
+-spec sendto(Socket, Data, Dest, Flags, nowait) -> ok |
+ {ok, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: null | sockaddr(),
+ Flags :: send_flags(),
+ SelectInfo :: select_info(),
+ Reason :: term()
+ ; (Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: null | sockaddr(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Reason :: term().
sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) ->
Bin = erlang:list_to_binary(Data),
@@ -1515,14 +1527,18 @@ sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout)
when is_binary(Data) andalso
(Dest =:= null) andalso
is_list(Flags) andalso
- (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ ((Timeout =:= nowait) orelse
+ (Timeout =:= infinity) orelse
+ (is_integer(Timeout) andalso (Timeout > 0))) ->
EFlags = enc_send_flags(Flags),
do_sendto(SockRef, Data, Dest, EFlags, Timeout);
sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout)
when is_binary(Data) andalso
((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso
is_list(Flags) andalso
- (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ ((Timeout =:= nowait) orelse
+ (Timeout =:= infinity) orelse
+ (is_integer(Timeout) andalso (Timeout > 0))) ->
EFlags = enc_send_flags(Flags),
do_sendto(SockRef, Data, Dest, EFlags, Timeout).
@@ -1534,6 +1550,11 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) ->
%% We are done
ok;
+ {ok, Written} when (Timeout =:= nowait) ->
+ <<_:Written/binary, Rest/binary>> = Data,
+ {ok, {Rest, ?SELECT_INFO(sendto, SendRef)}};
+
+
{ok, Written} ->
%% We are partially done, wait for continuation
receive
@@ -1555,6 +1576,11 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) ->
{error, timeout}
end;
+
+ {error, eagain} when (Timeout =:= nowait) ->
+ {ok, ?SELECT_INFO(sendto, SendRef)};
+
+
{error, eagain} ->
receive
{?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} ->
--
cgit v1.2.3
From 7246c98eac93687343e96913acd07e6261c0933c Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 15 Apr 2019 12:34:57 +0200
Subject: [socket] Update sendmsg to handle Timeout = nowait
Update function sendmsg and its spec(s) to handle
the Timeout value of nowait.
---
erts/preloaded/ebin/socket.beam | Bin 73912 -> 74296 bytes
erts/preloaded/src/socket.erl | 54 ++++++++++++++++++++++++++++++----------
2 files changed, 41 insertions(+), 13 deletions(-)
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 40af29476d..c353f62b18 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index c0b1e4d8dc..b2085b4b59 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1624,11 +1624,18 @@ sendmsg(Socket, MsgHdr) ->
MsgHdr :: msghdr(),
Flags :: send_flags(),
Reason :: term()
+ ; (Socket, MsgHdr, nowait) -> ok |
+ {ok, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ SelectInfo :: select_info(),
+ Reason :: term()
; (Socket, MsgHdr, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- MsgHdr :: msghdr(),
- Timeout :: timeout(),
- Reason :: term().
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Timeout :: timeout(),
+ Reason :: term().
sendmsg(Socket, MsgHdr, Flags) when is_list(Flags) ->
sendmsg(Socket, MsgHdr, Flags, ?SOCKET_SENDMSG_TIMEOUT_DEFAULT);
@@ -1637,19 +1644,34 @@ sendmsg(Socket, MsgHdr, Timeout)
sendmsg(Socket, MsgHdr, ?SOCKET_SENDMSG_FLAGS_DEFAULT, Timeout).
--spec sendmsg(Socket, MsgHdr, Flags, Timeout) ->
- ok | {ok, Remaining} | {error, Reason} when
- Socket :: socket(),
- MsgHdr :: msghdr(),
- Flags :: send_flags(),
- Timeout :: timeout(),
- Remaining :: erlang:iovec(),
- Reason :: term().
+-spec sendmsg(Socket, MsgHdr, Flags, nowait) ->
+ ok |
+ {ok, Remaining} |
+ {ok, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Flags :: send_flags(),
+ Remaining :: erlang:iovec(),
+ SelectInfo :: select_info(),
+ Reason :: term()
+ ; (Socket, MsgHdr, Flags, Timeout) ->
+ ok |
+ {ok, Remaining} |
+ {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Remaining :: erlang:iovec(),
+ Reason :: term().
sendmsg(#socket{ref = SockRef}, #{iov := IOV} = MsgHdr, Flags, Timeout)
when is_list(IOV) andalso
is_list(Flags) andalso
- (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ ((Timeout =:= nowait) orelse
+ (Timeout =:= infinity) orelse
+ (is_integer(Timeout) andalso (Timeout > 0))) ->
try ensure_msghdr(MsgHdr) of
M ->
EFlags = enc_send_flags(Flags),
@@ -1669,6 +1691,7 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) ->
%% We are done
ok;
+
{ok, Written} when is_integer(Written) andalso (Written > 0) ->
%% We should not retry here since the protocol may not
%% be able to handle a message being split. Leave it to
@@ -1680,6 +1703,11 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) ->
cancel(SockRef, sendmsg, SendRef),
{ok, do_sendmsg_rest(maps:get(iov, MsgHdr), Written)};
+
+ {error, eagain} when (Timeout =:= nowait) ->
+ {ok, ?SELECT_INFO(sendmsg, SendRef)};
+
+
{error, eagain} ->
receive
{?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} ->
--
cgit v1.2.3
From 0fbe9b5bddf3d6f0869068666256274660a98d53 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 15 Apr 2019 14:00:31 +0200
Subject: [socket] Update accept to handle Timeout = nowait
Update function accept and its spec(s) to handle
the Timeout value of nowait.
---
erts/preloaded/ebin/socket.beam | Bin 74296 -> 74516 bytes
erts/preloaded/src/socket.erl | 26 ++++++++++++++++++++------
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index c353f62b18..76ea46ea73 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index b2085b4b59..073a699b1a 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1279,18 +1279,27 @@ listen(#socket{ref = SockRef}, Backlog)
accept(Socket) ->
accept(Socket, ?SOCKET_ACCEPT_TIMEOUT_DEFAULT).
--spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when
- LSocket :: socket(),
- Timeout :: timeout(),
- Socket :: socket(),
- Reason :: term().
+-spec accept(LSocket, nowait) ->
+ {ok, Socket} |
+ {ok, SelectInfo} |
+ {error, Reason} when
+ LSocket :: socket(),
+ Socket :: socket(),
+ SelectInfo :: select_info(),
+ Reason :: term()
+ ; (LSocket, Timeout) -> {ok, Socket} | {error, Reason} when
+ LSocket :: socket(),
+ Timeout :: timeout(),
+ Socket :: socket(),
+ Reason :: term().
%% Do we really need this optimization?
accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) ->
{error, timeout};
accept(#socket{ref = LSockRef}, Timeout)
when is_integer(Timeout) orelse
- (Timeout =:= infinity) ->
+ (Timeout =:= infinity) orelse
+ (Timeout =:= nowait) ->
do_accept(LSockRef, Timeout).
do_accept(LSockRef, Timeout) ->
@@ -1301,6 +1310,11 @@ do_accept(LSockRef, Timeout) ->
Socket = #socket{ref = SockRef},
{ok, Socket};
+
+ {error, eagain} when (Timeout =:= nowait) ->
+ {ok, ?SELECT_INFO(accept, AccRef)};
+
+
{error, eagain} ->
%% Each call is non-blocking, but even then it takes
%% *some* time, so just to be sure, recalculate before
--
cgit v1.2.3
From 575282a3a9790b4866ae8976a5af339b41ad2cf6 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 16 Apr 2019 10:39:50 +0200
Subject: [socket] First nowait test case (sendto and recvfrom udp4)
Added test case api_a_sendto_and_recvfrom_udp4.
Also, fixed duplicate spec.
---
erts/emulator/test/socket_SUITE.erl | 456 ++++++++++++++++++++++++++++++++++++
erts/preloaded/ebin/socket.beam | Bin 74516 -> 74380 bytes
erts/preloaded/src/socket.erl | 10 -
3 files changed, 456 insertions(+), 10 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 49b0fcccc2..a38862dbed 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -81,6 +81,12 @@
api_b_sendmsg_and_recvmsg_tcp4/1,
api_b_sendmsg_and_recvmsg_tcpL/1,
+ %% *** API async ***
+ api_a_sendto_and_recvfrom_udp4/1,
+ %% api_a_sendmsg_and_recvmsg_udp4/1,
+ %% api_a_send_and_recv_tcp4/1,
+ %% api_a_sendmsg_and_recvmsg_tcp4/1,
+
%% *** API Options ***
api_opt_simple_otp_options/1,
api_opt_simple_otp_rcvbuf_option/1,
@@ -565,6 +571,7 @@ use_group(Group, Env, Default) ->
groups() ->
[{api, [], api_cases()},
{api_basic, [], api_basic_cases()},
+ {api_async, [], api_async_cases()},
{api_options, [], api_options_cases()},
{api_op_with_timeout, [], api_op_with_timeout_cases()},
{socket_closure, [], socket_closure_cases()},
@@ -639,6 +646,7 @@ groups() ->
api_cases() ->
[
{group, api_basic},
+ {group, api_async},
{group, api_options},
{group, api_op_with_timeout}
].
@@ -659,6 +667,14 @@ api_basic_cases() ->
api_b_sendmsg_and_recvmsg_tcpL
].
+api_async_cases() ->
+ [
+ api_a_sendto_and_recvfrom_udp4%% ,
+ %% api_a_sendmsg_and_recvmsg_udp4,
+ %% api_a_send_and_recv_tcp4,
+ %% api_a_sendmsg_and_recvmsg_tcp4
+ ].
+
api_options_cases() ->
[
api_opt_simple_otp_options,
@@ -2616,6 +2632,446 @@ api_b_send_and_recv_tcp(InitState) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically send and receive on an IPv4 UDP (dgram) socket using
+%% sendto and recvfrom. But we try to be async. That is, we use
+%% the 'nowait' value for the Timeout argument (and await the eventual
+%% select message). Note that we only do this for the recvfrom,
+%% since its much more difficult to "arrange" for sendto.
+%%
+api_a_sendto_and_recvfrom_udp4(suite) ->
+ [];
+api_a_sendto_and_recvfrom_udp4(doc) ->
+ [];
+api_a_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_a_sendto_and_recvfrom_udp4,
+ fun() ->
+ Send = fun(Sock, Data, Dest) ->
+ socket:sendto(Sock, Data, Dest)
+ end,
+ Recv = fun(Sock) ->
+ socket:recvfrom(Sock, 0, nowait)
+ end,
+ InitState = #{domain => inet,
+ send => Send,
+ recv => Recv},
+ ok = api_a_send_and_recv_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_a_send_and_recv_udp(InitState) ->
+ ServerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind socket (to local address)",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, Port} ->
+ {ok, State#{port => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, local_sa := LSA, port := Port}) ->
+ ServerSA = LSA#{port => Port},
+ ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "try recv request (with nowait)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, Tag, RecvRef}} ->
+ {ok, State#{recv_stag => Tag,
+ recv_sref => RecvRef}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await select message",
+ cmd => fun(#{sock := Sock, recv_sref := RecvRef}) ->
+ receive
+ {'$socket', Sock, select, RecvRef} ->
+ ok
+ end
+ end},
+ #{desc => "announce ready (select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, select),
+ ok
+ end},
+ #{desc => "now read the data",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {Src, ?BASIC_REQ}} ->
+ {ok, State#{req_src => Src}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_data)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_data),
+ ok
+ end},
+ #{desc => "await continue (send reply)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply",
+ cmd => fun(#{sock := Sock, req_src := Src, send := Send}) ->
+ Send(Sock, ?BASIC_REP, Src)
+ end},
+ #{desc => "announce ready (send)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ State2 = maps:remove(tester, State),
+ State3 = maps:remove(recv_stag, State2),
+ State4 = maps:remove(recv_sref, State3),
+ State5 = maps:remove(req_src, State4),
+ {ok, State5};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ ClientSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ {Tester, ServerSA} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester,
+ server_sa => ServerSA}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "open socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ Sock = sock_open(Domain, dgram, udp),
+ SA = sock_sockname(Sock),
+ {ok, State#{sock => Sock, sa => SA}}
+ end},
+ #{desc => "bind socket (to local address)",
+ cmd => fun(#{sock := Sock, lsa := LSA}) ->
+ sock_bind(Sock, LSA),
+ ok
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (send request)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request",
+ cmd => fun(#{sock := Sock, server_sa := Server, send := Send}) ->
+ Send(Sock, ?BASIC_REQ, Server)
+ end},
+ #{desc => "announce ready (send request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await continue (recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "try recv reply (with nowait)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, Tag, RecvRef}} ->
+ {ok, State#{recv_stag => Tag,
+ recv_sref => RecvRef}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await select message",
+ cmd => fun(#{sock := Sock, recv_sref := RecvRef}) ->
+ receive
+ {'$socket', Sock, select, RecvRef} ->
+ ok
+ end
+ end},
+ #{desc => "announce ready (select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, select),
+ ok
+ end},
+ #{desc => "now read the data",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ case Recv(Sock) of
+ {ok, {_Src, ?BASIC_REP}} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_data)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_data),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ State2 = maps:remove(tester, State),
+ State3 = maps:remove(recv_stag, State2),
+ State4 = maps:remove(recv_sref, State3),
+ {ok, State4};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client",
+ cmd => fun(#{client := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, ServerSA} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_sa => ServerSA}}
+ end},
+
+ %% Start the client
+ #{desc => "order client start",
+ cmd => fun(#{client := Pid,
+ server_sa := ServerSA} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, ServerSA),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, init)
+ end},
+
+ %% The actual test
+ #{desc => "order server continue (recv)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await server ready (recv_select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, recv_select)
+ end},
+
+ #{desc => "order client continue (send request)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, send_req),
+ ok
+ end},
+ #{desc => "await client ready (send request)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, send_req)
+ end},
+ #{desc => "await server ready (select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, select)
+ end},
+ #{desc => "await server ready (recv_data)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, recv_data)
+ end},
+
+ #{desc => "order client continue (recv)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await client ready (recv_select)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, recv_select)
+ end},
+ #{desc => "order server continue (send reply)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (send)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, send)
+ end},
+ #{desc => "await client ready (select)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, select)
+ end},
+ #{desc => "await client ready (recv_data)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, recv_data)
+ end},
+
+ %% Terminations
+ #{desc => "order client to terminate",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await client termination",
+ cmd => fun(#{client := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(client, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(server, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ ServerInitState = InitState,
+ Server = ?SEV_START("server", ServerSeq, ServerInitState),
+
+ i("start client evaluator(s)"),
+ ClientInitState = InitState,
+ Client = ?SEV_START("client", ClientSeq, ClientInitState),
+
+ i("start 'tester' evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client => Client#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 76ea46ea73..98adde8ae5 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 073a699b1a..c805c5586d 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1386,16 +1386,6 @@ send(Socket, Data, Timeout) ->
Flags :: send_flags(),
RestData :: binary(),
SelectInfo :: select_info(),
- Reason :: term()
- ; (Socket, Data, Flags, nowait) -> ok |
- {ok, SelectInfo} |
- {ok, {RestData, SelectInfo}} |
- {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Flags :: send_flags(),
- RestData :: binary(),
- SelectInfo :: select_info(),
Reason :: term()
; (Socket, Data, Flags, Timeout) -> ok | {error, Reason} when
Socket :: socket(),
--
cgit v1.2.3
From c1b8ff3ac77c5a4476ae980b4702dcf706903549 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 16 Apr 2019 10:51:31 +0200
Subject: [socket] Add nowait test case (sendmsg and recvmsg udp4)
Added test case api_a_sendmsg_and_recvmsg_udp4.
---
erts/emulator/test/socket_SUITE.erl | 47 ++++++++++++++++++++++++++++++++++---
1 file changed, 44 insertions(+), 3 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index a38862dbed..4d1dca9ecc 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -83,7 +83,7 @@
%% *** API async ***
api_a_sendto_and_recvfrom_udp4/1,
- %% api_a_sendmsg_and_recvmsg_udp4/1,
+ api_a_sendmsg_and_recvmsg_udp4/1,
%% api_a_send_and_recv_tcp4/1,
%% api_a_sendmsg_and_recvmsg_tcp4/1,
@@ -669,8 +669,8 @@ api_basic_cases() ->
api_async_cases() ->
[
- api_a_sendto_and_recvfrom_udp4%% ,
- %% api_a_sendmsg_and_recvmsg_udp4,
+ api_a_sendto_and_recvfrom_udp4,
+ api_a_sendmsg_and_recvmsg_udp4%%,
%% api_a_send_and_recv_tcp4,
%% api_a_sendmsg_and_recvmsg_tcp4
].
@@ -2662,6 +2662,47 @@ api_a_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically send and receive on an IPv4 UDP (dgram) socket using
+%% sendto and recvfrom. But we try to be async. That is, we use
+%% the 'nowait' value for the Timeout argument (and await the eventual
+%% select message). Note that we only do this for the recvfrom,
+%% since its much more difficult to "arrange" for sendto.
+%%
+api_a_sendmsg_and_recvmsg_udp4(suite) ->
+ [];
+api_a_sendmsg_and_recvmsg_udp4(doc) ->
+ [];
+api_a_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_a_sendmsg_and_recvmsg_udp4,
+ fun() ->
+ Send = fun(Sock, Data, Dest) ->
+ MsgHdr = #{addr => Dest,
+ %% ctrl => CMsgHdrs,
+ iov => [Data]},
+ socket:sendmsg(Sock, MsgHdr)
+ end,
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock, nowait) of
+ {ok, #{addr := Source,
+ iov := [Data]}} ->
+ {ok, {Source, Data}};
+ {ok, _} = OK ->
+ OK;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ send => Send,
+ recv => Recv},
+ ok = api_a_send_and_recv_udp(InitState)
+ end).
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
api_a_send_and_recv_udp(InitState) ->
--
cgit v1.2.3
From 2934b6538a86d71c6a185ec5bc6fa23e1b7b9b5a Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 16 Apr 2019 12:19:52 +0200
Subject: [socket] Add nowait test case (send and recv tcp4)
Added test case api_a_send_and_recv_tcp4.
---
erts/emulator/test/socket_SUITE.erl | 563 +++++++++++++++++++++++++++++++++++-
1 file changed, 547 insertions(+), 16 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 4d1dca9ecc..1026b83b57 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -84,7 +84,7 @@
%% *** API async ***
api_a_sendto_and_recvfrom_udp4/1,
api_a_sendmsg_and_recvmsg_udp4/1,
- %% api_a_send_and_recv_tcp4/1,
+ api_a_send_and_recv_tcp4/1,
%% api_a_sendmsg_and_recvmsg_tcp4/1,
%% *** API Options ***
@@ -670,8 +670,8 @@ api_basic_cases() ->
api_async_cases() ->
[
api_a_sendto_and_recvfrom_udp4,
- api_a_sendmsg_and_recvmsg_udp4%%,
- %% api_a_send_and_recv_tcp4,
+ api_a_sendmsg_and_recvmsg_udp4,
+ api_a_send_and_recv_tcp4%%,
%% api_a_sendmsg_and_recvmsg_tcp4
].
@@ -2667,8 +2667,8 @@ api_a_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) ->
%% Basically send and receive on an IPv4 UDP (dgram) socket using
%% sendto and recvfrom. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
-%% select message). Note that we only do this for the recvfrom,
-%% since its much more difficult to "arrange" for sendto.
+%% select message). Note that we only do this for the recvmsg,
+%% since its much more difficult to "arrange" for sendmsg.
%%
api_a_sendmsg_and_recvmsg_udp4(suite) ->
[];
@@ -2757,7 +2757,7 @@ api_a_send_and_recv_udp(InitState) ->
cmd => fun(#{tester := Tester} = _State) ->
?SEV_AWAIT_CONTINUE(Tester, tester, recv)
end},
- #{desc => "try recv request (with nowait)",
+ #{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
{ok, {select, Tag, RecvRef}} ->
@@ -2786,7 +2786,7 @@ api_a_send_and_recv_udp(InitState) ->
?SEV_ANNOUNCE_READY(Tester, select),
ok
end},
- #{desc => "now read the data",
+ #{desc => "now read the data (request)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
{ok, {Src, ?BASIC_REQ}} ->
@@ -2795,11 +2795,12 @@ api_a_send_and_recv_udp(InitState) ->
ERROR
end
end},
- #{desc => "announce ready (recv_data)",
+ #{desc => "announce ready (recv request)",
cmd => fun(#{tester := Tester}) ->
- ?SEV_ANNOUNCE_READY(Tester, recv_data),
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
ok
end},
+
#{desc => "await continue (send reply)",
cmd => fun(#{tester := Tester} = _State) ->
?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
@@ -2925,7 +2926,7 @@ api_a_send_and_recv_udp(InitState) ->
?SEV_ANNOUNCE_READY(Tester, select),
ok
end},
- #{desc => "now read the data",
+ #{desc => "now read the data (reply)",
cmd => fun(#{sock := Sock, recv := Recv}) ->
case Recv(Sock) of
{ok, {_Src, ?BASIC_REP}} ->
@@ -2934,9 +2935,9 @@ api_a_send_and_recv_udp(InitState) ->
ERROR
end
end},
- #{desc => "announce ready (recv_data)",
+ #{desc => "announce ready (recv reply)",
cmd => fun(#{tester := Tester}) ->
- ?SEV_ANNOUNCE_READY(Tester, recv_data),
+ ?SEV_ANNOUNCE_READY(Tester, recv_rep),
ok
end},
@@ -3026,9 +3027,9 @@ api_a_send_and_recv_udp(InitState) ->
cmd => fun(#{server := Pid} = _State) ->
ok = ?SEV_AWAIT_READY(Pid, server, select)
end},
- #{desc => "await server ready (recv_data)",
+ #{desc => "await server ready (recv request)",
cmd => fun(#{server := Pid} = _State) ->
- ok = ?SEV_AWAIT_READY(Pid, server, recv_data)
+ ok = ?SEV_AWAIT_READY(Pid, server, recv_req)
end},
#{desc => "order client continue (recv)",
@@ -3053,9 +3054,9 @@ api_a_send_and_recv_udp(InitState) ->
cmd => fun(#{client := Pid} = _State) ->
ok = ?SEV_AWAIT_READY(Pid, client, select)
end},
- #{desc => "await client ready (recv_data)",
+ #{desc => "await client ready (recv reply)",
cmd => fun(#{client := Pid} = _State) ->
- ok = ?SEV_AWAIT_READY(Pid, client, recv_data)
+ ok = ?SEV_AWAIT_READY(Pid, client, recv_rep)
end},
%% Terminations
@@ -3113,6 +3114,536 @@ api_a_send_and_recv_udp(InitState) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically send and receive using the "common" functions (send and recv)
+%% on an IPv4 TCP (stream) socket. But we try to be async. That is, we use
+%% the 'nowait' value for the Timeout argument (and await the eventual
+%% select message). Note that we only do this for the recv,
+%% since its much more difficult to "arrange" for send.
+%% We *also* test async for accept
+api_a_send_and_recv_tcp4(suite) ->
+ [];
+api_a_send_and_recv_tcp4(doc) ->
+ [];
+api_a_send_and_recv_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_b_send_and_recv_tcp4,
+ fun() ->
+ Send = fun(Sock, Data) ->
+ socket:send(Sock, Data)
+ end,
+ Recv = fun(Sock) ->
+ socket:recv(Sock, 0, nowait)
+ end,
+ InitState = #{domain => inet,
+ send => Send,
+ recv => Recv},
+ ok = api_a_send_and_recv_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_a_send_and_recv_tcp(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create listen socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{lsock := LSock, lsa := LSA} = State) ->
+ case socket:bind(LSock, LSA) of
+ {ok, Port} ->
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, lport := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "await connection (nowait)",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock, nowait) of
+ {ok, {select, Tag, Ref}} ->
+ ?SEV_IPRINT("accept select: "
+ "~n Tag: ~p"
+ "~n Ref: ~p", [Tag, Ref]),
+ {ok, State#{accept_stag => Tag,
+ accept_sref => Ref}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept_select),
+ ok
+ end},
+ #{desc => "await select message",
+ cmd => fun(#{lsock := Sock, accept_sref := Ref}) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ ok
+ end
+ end},
+ #{desc => "announce ready (select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, select),
+ ok
+ end},
+ #{desc => "await connection (again)",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock, nowait) of
+ {ok, Sock} ->
+ ?SEV_IPRINT("accepted: "
+ "~n Sock: ~p", [Sock]),
+ {ok, State#{csock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ #{desc => "await continue (recv request)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv_req)
+ end},
+ #{desc => "try recv request (with nowait, expect select)",
+ cmd => fun(#{csock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, Tag, Ref}} ->
+ ?SEV_IPRINT("recv select: "
+ "~n Tag: ~p"
+ "~n Ref: ~p", [Tag, Ref]),
+ {ok, State#{recv_stag => Tag,
+ recv_sref => Ref}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await select message",
+ cmd => fun(#{csock := Sock, recv_sref := RecvRef}) ->
+ receive
+ {'$socket', Sock, select, RecvRef} ->
+ ok
+ end
+ end},
+ #{desc => "announce ready (select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, select),
+ ok
+ end},
+ #{desc => "now read the data (request)",
+ cmd => fun(#{csock := Sock, recv := Recv} = _State) ->
+ case Recv(Sock) of
+ {ok, ?BASIC_REQ} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+
+ #{desc => "await continue (send reply)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_rep)
+ end},
+ #{desc => "send reply",
+ cmd => fun(#{csock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REP)
+ end},
+ #{desc => "announce ready (send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_rep),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close connection socket",
+ cmd => fun(#{csock := Sock}) ->
+ socket:close(Sock)
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := Sock}) ->
+ socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ ClientSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Port} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, server_port => Port}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ SSA = LSA#{port => Port},
+ {ok, State#{local_sa => LSA, server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (connect)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
+ end},
+ #{desc => "connect to server",
+ cmd => fun(#{sock := Sock, server_sa := SSA}) ->
+ socket:connect(Sock, SSA)
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+
+ #{desc => "await continue (send request)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request (to server)",
+ cmd => fun(#{sock := Sock, send := Send}) ->
+ ok = Send(Sock, ?BASIC_REQ)
+ end},
+ #{desc => "announce ready (send request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+
+ #{desc => "try recv reply (with nowait, expect select)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, Tag, Ref}} ->
+ ?SEV_IPRINT("recv select: "
+ "~n Tag: ~p"
+ "~n Ref: ~p", [Tag, Ref]),
+ {ok, State#{recv_stag => Tag,
+ recv_sref => Ref}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await select message",
+ cmd => fun(#{sock := Sock, recv_sref := RecvRef}) ->
+ receive
+ {'$socket', Sock, select, RecvRef} ->
+ ok
+ end
+ end},
+ #{desc => "announce ready (select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, select),
+ ok
+ end},
+ #{desc => "now read the data (reply)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, ?BASIC_REP} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_rep),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock}) ->
+ socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client",
+ cmd => fun(#{client := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+
+ %% Start the client
+ #{desc => "order client start",
+ cmd => fun(#{client := Pid, server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Port),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, init)
+ end},
+
+ %% *** The actual test ***
+ #{desc => "order server to continue (with accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept),
+ ok
+ end},
+ #{desc => "await server ready (accept select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, accept_select)
+ end},
+ #{desc => "order client to continue (with connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
+ ok
+ end},
+ #{desc => "await server ready (select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, select)
+ end},
+ #{desc => "await server ready (accept)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, accept)
+ end},
+ #{desc => "await client ready (connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, client, connect)
+ end},
+
+ #{desc => "order server to continue (recv request)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv_req),
+ ok
+ end},
+ #{desc => "await server ready (recv select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, recv_select)
+ end},
+ #{desc => "order client to continue (send request)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, send_req),
+ ok
+ end},
+ #{desc => "await client ready (send request)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, send_req)
+ end},
+ #{desc => "await server ready (select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, select)
+ end},
+ #{desc => "await server ready (recv request)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, recv_req)
+ end},
+
+ #{desc => "order client to continue (recv reply)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv_rep),
+ ok
+ end},
+ #{desc => "await client ready (recv select)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, client, recv_select)
+ end},
+ #{desc => "order server to continue (send reply)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, send_rep),
+ ok
+ end},
+ #{desc => "await server ready (send reply)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, send_rep)
+ end},
+ #{desc => "await client ready (select)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, client, select)
+ end},
+ #{desc => "await client ready (reply recv)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, recv_rep)
+ end},
+
+
+ %% *** Termination ***
+ #{desc => "order client to terminate",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client termination",
+ cmd => fun(#{client := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client, State),
+ {ok, State1}
+ end},
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, InitState),
+
+ i("start client evaluator"),
+ Client = ?SEV_START("client", ClientSeq, InitState),
+ i("await evaluator(s)"),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client => Client#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
+
+
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
--
cgit v1.2.3
From e95430644d7f9144a632b89370222c3b7119dc33 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 16 Apr 2019 12:27:51 +0200
Subject: [socket] Add nowait test case (sendmsg and recvmsg tcp4)
Added test case api_a_sendmsg_and_recvmsg_tcp4.
---
erts/emulator/test/socket_SUITE.erl | 48 +++++++++++++++++++++++++++++++++----
1 file changed, 43 insertions(+), 5 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 1026b83b57..b63a6fc526 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -85,7 +85,7 @@
api_a_sendto_and_recvfrom_udp4/1,
api_a_sendmsg_and_recvmsg_udp4/1,
api_a_send_and_recv_tcp4/1,
- %% api_a_sendmsg_and_recvmsg_tcp4/1,
+ api_a_sendmsg_and_recvmsg_tcp4/1,
%% *** API Options ***
api_opt_simple_otp_options/1,
@@ -671,8 +671,8 @@ api_async_cases() ->
[
api_a_sendto_and_recvfrom_udp4,
api_a_sendmsg_and_recvmsg_udp4,
- api_a_send_and_recv_tcp4%%,
- %% api_a_sendmsg_and_recvmsg_tcp4
+ api_a_send_and_recv_tcp4,
+ api_a_sendmsg_and_recvmsg_tcp4
].
api_options_cases() ->
@@ -3121,14 +3121,14 @@ api_a_send_and_recv_udp(InitState) ->
%% the 'nowait' value for the Timeout argument (and await the eventual
%% select message). Note that we only do this for the recv,
%% since its much more difficult to "arrange" for send.
-%% We *also* test async for accept
+%% We *also* test async for accept.
api_a_send_and_recv_tcp4(suite) ->
[];
api_a_send_and_recv_tcp4(doc) ->
[];
api_a_send_and_recv_tcp4(_Config) when is_list(_Config) ->
?TT(?SECS(10)),
- tc_try(api_b_send_and_recv_tcp4,
+ tc_try(api_a_send_and_recv_tcp4,
fun() ->
Send = fun(Sock, Data) ->
socket:send(Sock, Data)
@@ -3144,6 +3144,44 @@ api_a_send_and_recv_tcp4(_Config) when is_list(_Config) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically send and receive using the msg functions (sendmsg and recvmsg)
+%% on an IPv4 TCP (stream) socket. But we try to be async. That is, we use
+%% the 'nowait' value for the Timeout argument (and await the eventual
+%% select message). Note that we only do this for the recvmsg,
+%% since its much more difficult to "arrange" for sendmsg.
+%% We *also* test async for accept.
+api_a_sendmsg_and_recvmsg_tcp4(suite) ->
+ [];
+api_a_sendmsg_and_recvmsg_tcp4(doc) ->
+ [];
+api_a_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_sendmsg_and_recvmsg_tcp4,
+ fun() ->
+ Send = fun(Sock, Data) ->
+ MsgHdr = #{iov => [Data]},
+ socket:sendmsg(Sock, MsgHdr)
+ end,
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock, nowait) of
+ {ok, #{addr := undefined,
+ iov := [Data]}} ->
+ {ok, Data};
+ {ok, _} = OK ->
+ OK;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ send => Send,
+ recv => Recv},
+ ok = api_a_send_and_recv_tcp(InitState)
+ end).
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
api_a_send_and_recv_tcp(InitState) ->
--
cgit v1.2.3
From 1039d05f393d470a1edba93a0026f4fba7d2ce5b Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 16 Apr 2019 13:43:45 +0200
Subject: [socket] Add cancel and the first recvfrom(nowait) cancel test case
Add the (public) cancel function and the first of
the cancel test cases (recvfrom(nowait)).
---
erts/emulator/test/socket_SUITE.erl | 245 +++++++++++++++++++++++++++++++++++-
erts/preloaded/ebin/socket.beam | Bin 74380 -> 74584 bytes
erts/preloaded/src/socket.erl | 23 +++-
3 files changed, 266 insertions(+), 2 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index b63a6fc526..aecd65096e 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -86,6 +86,11 @@
api_a_sendmsg_and_recvmsg_udp4/1,
api_a_send_and_recv_tcp4/1,
api_a_sendmsg_and_recvmsg_tcp4/1,
+ api_a_recvfrom_cancel_udp4/1,
+ %% api_a_recvmsg_cancel_udp4/1,
+ %% api_a_accept_cancel_tcp4/1,
+ %% api_a_recv_cancel_tcp4/1,
+ %% api_a_recvmsg_cancel_tcp4/1,
%% *** API Options ***
api_opt_simple_otp_options/1,
@@ -672,7 +677,12 @@ api_async_cases() ->
api_a_sendto_and_recvfrom_udp4,
api_a_sendmsg_and_recvmsg_udp4,
api_a_send_and_recv_tcp4,
- api_a_sendmsg_and_recvmsg_tcp4
+ api_a_sendmsg_and_recvmsg_tcp4,
+ api_a_recvfrom_cancel_udp4%% ,
+ %% api_a_recvmsg_cancel_udp4,
+ %% api_a_accept_cancel_tcp4,
+ %% api_a_recv_cancel_tcp4,
+ %% api_a_recvmsg_cancel_tcp4
].
api_options_cases() ->
@@ -3682,6 +3692,239 @@ api_a_send_and_recv_tcp(InitState) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make an async (Timeout = nowait) call to recvfrom,
+%% wait some time and then cancel.
+%%
+api_a_recvfrom_cancel_udp4(suite) ->
+ [];
+api_a_recvfrom_cancel_udp4(doc) ->
+ [];
+api_a_recvfrom_cancel_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_a_recvfrom_cancel_udp4,
+ fun() ->
+ Recv = fun(Sock) ->
+ case socket:recvfrom(Sock, 0, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ recv => Recv},
+ ok = api_a_recv_cancel_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_a_recv_cancel_udp(InitState) ->
+ ServerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind socket (to local address)",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, Port} ->
+ {ok, State#{port => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, local_sa := LSA, port := Port}) ->
+ ServerSA = LSA#{port => Port},
+ ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "try recv request (with nowait, expect select)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, _, RecvRef} = SelectInfo} ->
+ {ok, State#{recv_select_info => SelectInfo}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await select message (without success)",
+ cmd => fun(#{sock := Sock}) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ {error, {unexpected_select, Ref}}
+ after 5000 ->
+ ok
+ end
+ end},
+ #{desc => "announce ready (no select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, no_select),
+ ok
+ end},
+ #{desc => "await continue (cancel)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, cancel)
+ end},
+ #{desc => "cancel",
+ cmd => fun(#{sock := Sock, recv_select_info := SelectInfo}) ->
+ ok = socket:cancel(Sock, SelectInfo)
+ end},
+ #{desc => "announce ready (cancel)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, cancel),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ State2 = maps:remove(tester, State),
+ State3 = maps:remove(recv_stag, State2),
+ State4 = maps:remove(recv_sref, State3),
+ State5 = maps:remove(req_src, State4),
+ {ok, State5};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, ServerSA} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_sa => ServerSA}}
+ end},
+
+ %% The actual test
+ #{desc => "order server continue (recv)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await server ready (recv select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, recv_select)
+ end},
+ #{desc => "await server ready (no select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, no_select)
+ end},
+ #{desc => "order server continue (cancel)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, cancel),
+ ok
+ end},
+ #{desc => "await server ready (cancel)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, cancel)
+ end},
+
+ %% Terminations
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(server, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ ServerInitState = InitState,
+ Server = ?SEV_START("server", ServerSeq, ServerInitState),
+
+ i("start 'tester' evaluator"),
+ TesterInitState = #{server => Server#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Server, Tester]).
+
+
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 98adde8ae5..b8983eb8e8 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index c805c5586d..f4d8f175f8 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -53,7 +53,9 @@
getopt/3,
sockname/1,
- peername/1
+ peername/1,
+
+ cancel/2
]).
-export_type([
@@ -2586,6 +2588,25 @@ peername(#socket{ref = SockRef}) ->
nif_peername(SockRef).
+%% ===========================================================================
+%%
+%% cancel - cancel an operation resulting in a select
+%%
+%% A call to accept, recv/recvfrom/recvmsg and send/sendto/sendmsg
+%% can result in a select if they are called with the Timeout argument
+%% set to nowait. This is indicated by the return of the select-info.
+%% Such a operation can be cancelled by calling this function.
+%%
+
+-spec cancel(Socket, SelectInfo) -> ok | {error, Reason} when
+ Socket :: socket(),
+ SelectInfo :: select_info(),
+ Reason :: term().
+
+cancel(#socket{ref = SockRef}, {select, Tag, Ref}) ->
+ cancel(SockRef, Tag, Ref).
+
+
%% ===========================================================================
%%
--
cgit v1.2.3
From 906a4e1161f1903caa4b7bafd5ca74f2f6789217 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 16 Apr 2019 13:49:56 +0200
Subject: [socket] Add recvmsg(nowait) cancel test case
Add the recvmsg(nowait) cancel test cases.
---
erts/emulator/test/socket_SUITE.erl | 34 +++++++++++++++++++++++++++++++---
1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index aecd65096e..5a02f7904e 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -87,7 +87,7 @@
api_a_send_and_recv_tcp4/1,
api_a_sendmsg_and_recvmsg_tcp4/1,
api_a_recvfrom_cancel_udp4/1,
- %% api_a_recvmsg_cancel_udp4/1,
+ api_a_recvmsg_cancel_udp4/1,
%% api_a_accept_cancel_tcp4/1,
%% api_a_recv_cancel_tcp4/1,
%% api_a_recvmsg_cancel_tcp4/1,
@@ -678,8 +678,8 @@ api_async_cases() ->
api_a_sendmsg_and_recvmsg_udp4,
api_a_send_and_recv_tcp4,
api_a_sendmsg_and_recvmsg_tcp4,
- api_a_recvfrom_cancel_udp4%% ,
- %% api_a_recvmsg_cancel_udp4,
+ api_a_recvfrom_cancel_udp4,
+ api_a_recvmsg_cancel_udp4%%,
%% api_a_accept_cancel_tcp4,
%% api_a_recv_cancel_tcp4,
%% api_a_recvmsg_cancel_tcp4
@@ -3720,6 +3720,34 @@ api_a_recvfrom_cancel_udp4(_Config) when is_list(_Config) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make an async (Timeout = nowait) call to recvmsg,
+%% wait some time and then cancel.
+%%
+api_a_recvmsg_cancel_udp4(suite) ->
+ [];
+api_a_recvmsg_cancel_udp4(doc) ->
+ [];
+api_a_recvmsg_cancel_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_a_recvmsg_cancel_udp4,
+ fun() ->
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ recv => Recv},
+ ok = api_a_recv_cancel_udp(InitState)
+ end).
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
api_a_recv_cancel_udp(InitState) ->
--
cgit v1.2.3
From bf9bde53d1ba34a23547290df78928e65a916f5e Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 16 Apr 2019 14:25:37 +0200
Subject: [socket] Add accept(nowait) cancel test case
Add the accept(nowait) cancel test cases.
Also fixed some timetrap times.
---
erts/emulator/test/socket_SUITE.erl | 244 ++++++++++++++++++++++++++++++++++--
1 file changed, 237 insertions(+), 7 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 5a02f7904e..6d32dcff14 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -88,7 +88,7 @@
api_a_sendmsg_and_recvmsg_tcp4/1,
api_a_recvfrom_cancel_udp4/1,
api_a_recvmsg_cancel_udp4/1,
- %% api_a_accept_cancel_tcp4/1,
+ api_a_accept_cancel_tcp4/1,
%% api_a_recv_cancel_tcp4/1,
%% api_a_recvmsg_cancel_tcp4/1,
@@ -679,8 +679,8 @@ api_async_cases() ->
api_a_send_and_recv_tcp4,
api_a_sendmsg_and_recvmsg_tcp4,
api_a_recvfrom_cancel_udp4,
- api_a_recvmsg_cancel_udp4%%,
- %% api_a_accept_cancel_tcp4,
+ api_a_recvmsg_cancel_udp4,
+ api_a_accept_cancel_tcp4%%,
%% api_a_recv_cancel_tcp4,
%% api_a_recvmsg_cancel_tcp4
].
@@ -3679,13 +3679,13 @@ api_a_send_and_recv_tcp(InitState) ->
i("start client evaluator"),
Client = ?SEV_START("client", ClientSeq, InitState),
- i("await evaluator(s)"),
i("start tester evaluator"),
TesterInitState = #{server => Server#ev.pid,
client => Client#ev.pid},
Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+ i("await evaluator(s)"),
ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
@@ -3702,7 +3702,7 @@ api_a_recvfrom_cancel_udp4(suite) ->
api_a_recvfrom_cancel_udp4(doc) ->
[];
api_a_recvfrom_cancel_udp4(_Config) when is_list(_Config) ->
- ?TT(?SECS(5)),
+ ?TT(?SECS(10)),
tc_try(api_a_recvfrom_cancel_udp4,
fun() ->
Recv = fun(Sock) ->
@@ -3730,7 +3730,7 @@ api_a_recvmsg_cancel_udp4(suite) ->
api_a_recvmsg_cancel_udp4(doc) ->
[];
api_a_recvmsg_cancel_udp4(_Config) when is_list(_Config) ->
- ?TT(?SECS(5)),
+ ?TT(?SECS(10)),
tc_try(api_a_recvmsg_cancel_udp4,
fun() ->
Recv = fun(Sock) ->
@@ -3805,7 +3805,7 @@ api_a_recv_cancel_udp(InitState) ->
#{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, _, RecvRef} = SelectInfo} ->
+ {ok, {select, _, _} = SelectInfo} ->
{ok, State#{recv_select_info => SelectInfo}};
{ok, X} ->
{error, {unexpected_select_info, X}};
@@ -3953,6 +3953,236 @@ api_a_recv_cancel_udp(InitState) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make an async (Timeout = nowait) call to recvmsg,
+%% wait some time and then cancel.
+%%
+api_a_accept_cancel_tcp4(suite) ->
+ [];
+api_a_accept_cancel_tcp4(doc) ->
+ [];
+api_a_accept_cancel_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_accept_cancel_tcp4,
+ fun() ->
+ Accept = fun(Sock) ->
+ case socket:accept(Sock, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ accept => Accept},
+ ok = api_a_accept_cancel_tcp(InitState)
+ end).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_a_accept_cancel_tcp(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create listen socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{lsock := LSock, lsa := LSA} = State) ->
+ case socket:bind(LSock, LSA) of
+ {ok, Port} ->
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, lport := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "await connection (nowait)",
+ cmd => fun(#{lsock := LSock, accept := Accept} = State) ->
+ case Accept(LSock) of
+ {ok, {select, T, R} = SelectInfo} ->
+ ?SEV_IPRINT("accept select: "
+ "~n T: ~p"
+ "~n R: ~p", [T, R]),
+ {ok, State#{accept_select_info => SelectInfo}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept_select),
+ ok
+ end},
+ #{desc => "await select message (without success)",
+ cmd => fun(#{lsock := Sock}) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ {error, {unexpected_select, Ref}}
+ after 5000 ->
+ ok
+ end
+ end},
+ #{desc => "announce ready (no select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, no_select),
+ ok
+ end},
+ #{desc => "await continue (cancel)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, cancel)
+ end},
+ #{desc => "cancel",
+ cmd => fun(#{lsock := Sock, accept_select_info := SelectInfo}) ->
+ ok = socket:cancel(Sock, SelectInfo)
+ end},
+ #{desc => "announce ready (cancel)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, cancel),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := Sock}) ->
+ socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+
+ %% *** The actual test ***
+ #{desc => "order server to continue (with accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept),
+ ok
+ end},
+ #{desc => "await server ready (accept select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, accept_select)
+ end},
+ #{desc => "await server ready (no select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, no_select)
+ end},
+ #{desc => "order server to continue (cancel)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, cancel),
+ ok
+ end},
+ #{desc => "await server ready (cancel)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, cancel)
+ end},
+
+ %% *** Termination ***
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, InitState),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Server, Tester]).
+
+
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
--
cgit v1.2.3
From faf4dc5c4e1724f1cd18c7242ceb4c8949f10260 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 16 Apr 2019 14:52:15 +0200
Subject: [socket] Add recv(nowait) cancel on tcp test case
Add the recv(nowait) cancel for tcp IPv4 test cases.
---
erts/emulator/test/socket_SUITE.erl | 391 +++++++++++++++++++++++++++++++++++-
1 file changed, 382 insertions(+), 9 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 6d32dcff14..7f329ce9d2 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -89,7 +89,7 @@
api_a_recvfrom_cancel_udp4/1,
api_a_recvmsg_cancel_udp4/1,
api_a_accept_cancel_tcp4/1,
- %% api_a_recv_cancel_tcp4/1,
+ api_a_recv_cancel_tcp4/1,
%% api_a_recvmsg_cancel_tcp4/1,
%% *** API Options ***
@@ -680,8 +680,8 @@ api_async_cases() ->
api_a_sendmsg_and_recvmsg_tcp4,
api_a_recvfrom_cancel_udp4,
api_a_recvmsg_cancel_udp4,
- api_a_accept_cancel_tcp4%%,
- %% api_a_recv_cancel_tcp4,
+ api_a_accept_cancel_tcp4,
+ api_a_recv_cancel_tcp4%%,
%% api_a_recvmsg_cancel_tcp4
].
@@ -2552,11 +2552,7 @@ api_b_send_and_recv_tcp(InitState) ->
?SEV_ANNOUNCE_CONTINUE(Server, accept),
ok
end},
- #{desc => "sleep",
- cmd => fun(_) ->
- ?SLEEP(?SECS(1)),
- ok
- end},
+ ?SEV_SLEEP(?SECS(1)),
#{desc => "order client to continue (with connect)",
cmd => fun(#{client := Client} = _State) ->
?SEV_ANNOUNCE_CONTINUE(Client, connect),
@@ -3955,7 +3951,7 @@ api_a_recv_cancel_udp(InitState) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Basically we make an async (Timeout = nowait) call to recvmsg,
+%% Basically we make an async (Timeout = nowait) call to accept,
%% wait some time and then cancel.
%%
api_a_accept_cancel_tcp4(suite) ->
@@ -4182,6 +4178,383 @@ api_a_accept_cancel_tcp(InitState) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make an async (Timeout = nowait) call to recv,
+%% wait some time and then cancel.
+%%
+api_a_recv_cancel_tcp4(suite) ->
+ [];
+api_a_recv_cancel_tcp4(doc) ->
+ [];
+api_a_recv_cancel_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_recv_cancel_tcp4,
+ fun() ->
+ Recv = fun(Sock) ->
+ socket:recv(Sock, 0, nowait)
+ end,
+ InitState = #{domain => inet,
+ recv => Recv},
+ ok = api_a_recv_cancel_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_a_recv_cancel_tcp(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create listen socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{lsock := LSock, lsa := LSA} = State) ->
+ case socket:bind(LSock, LSA) of
+ {ok, Port} ->
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, lport := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "await connection (nowait)",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock) of
+ {ok, CSock} ->
+ {ok, State#{csock => CSock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ #{desc => "await continue (nowait recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "try recv request (with nowait, expect select)",
+ cmd => fun(#{csock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, T, R} = SelectInfo} ->
+ ?SEV_IPRINT("recv select: "
+ "~n Tag: ~p"
+ "~n Ref: ~p", [T, R]),
+ {ok, State#{recv_select_info => SelectInfo}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await select message",
+ cmd => fun(#{csock := Sock}) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ {error, {unexpected_select, Ref}}
+ after 5000 ->
+ ok
+ end
+ end},
+ #{desc => "announce ready (no select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, no_select),
+ ok
+ end},
+ #{desc => "await continue (cancel)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, cancel)
+ end},
+ #{desc => "cancel",
+ cmd => fun(#{csock := Sock, recv_select_info := SelectInfo}) ->
+ ok = socket:cancel(Sock, SelectInfo)
+ end},
+ #{desc => "announce ready (cancel)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, cancel),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close connection socket",
+ cmd => fun(#{csock := Sock}) ->
+ socket:close(Sock)
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := Sock}) ->
+ socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ ClientSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Port} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, server_port => Port}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ SSA = LSA#{port => Port},
+ {ok, State#{local_sa => LSA, server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (connect)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
+ end},
+ #{desc => "connect to server",
+ cmd => fun(#{sock := Sock, server_sa := SSA}) ->
+ socket:connect(Sock, SSA)
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock}) ->
+ socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client",
+ cmd => fun(#{client := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+
+ %% Start the client
+ #{desc => "order client start",
+ cmd => fun(#{client := Pid, server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Port),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, init)
+ end},
+
+ %% *** The actual test ***
+ #{desc => "order server to continue (with accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept),
+ ok
+ end},
+ #{desc => "order client to continue (connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
+ ok
+ end},
+ #{desc => "await client ready (connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, client, connect)
+ end},
+ #{desc => "await server ready (accept)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, accept)
+ end},
+
+ #{desc => "order server to continue (recv)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await server ready (recv select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, recv_select)
+ end},
+ #{desc => "await server ready (no select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, no_select)
+ end},
+ #{desc => "order server to continue (send request)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, cancel),
+ ok
+ end},
+ #{desc => "await server ready (cancel)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, server, cancel)
+ end},
+
+ %% *** Termination ***
+ #{desc => "order client to terminate",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client termination",
+ cmd => fun(#{client := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client, State),
+ {ok, State1}
+ end},
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, InitState),
+
+ i("start client evaluator"),
+ Client = ?SEV_START("client", ClientSeq, InitState),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client => Client#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
+
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--
cgit v1.2.3
From 22258359ff6633300ea6f28638eaae66deb0649a Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 16 Apr 2019 14:58:40 +0200
Subject: [socket] Add recvmsg(nowait) cancel on tcp test case
Add the recvmsg(nowait) cancel for tcp IPv4 test cases.
---
erts/emulator/test/socket_SUITE.erl | 29 ++++++++++++++++++++++++++---
1 file changed, 26 insertions(+), 3 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 7f329ce9d2..f5757d5ce6 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -90,7 +90,7 @@
api_a_recvmsg_cancel_udp4/1,
api_a_accept_cancel_tcp4/1,
api_a_recv_cancel_tcp4/1,
- %% api_a_recvmsg_cancel_tcp4/1,
+ api_a_recvmsg_cancel_tcp4/1,
%% *** API Options ***
api_opt_simple_otp_options/1,
@@ -681,8 +681,8 @@ api_async_cases() ->
api_a_recvfrom_cancel_udp4,
api_a_recvmsg_cancel_udp4,
api_a_accept_cancel_tcp4,
- api_a_recv_cancel_tcp4%%,
- %% api_a_recvmsg_cancel_tcp4
+ api_a_recv_cancel_tcp4,
+ api_a_recvmsg_cancel_tcp4
].
api_options_cases() ->
@@ -4201,6 +4201,29 @@ api_a_recv_cancel_tcp4(_Config) when is_list(_Config) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make an async (Timeout = nowait) call to recvmsg,
+%% wait some time and then cancel.
+%%
+api_a_recvmsg_cancel_tcp4(suite) ->
+ [];
+api_a_recvmsg_cancel_tcp4(doc) ->
+ [];
+api_a_recvmsg_cancel_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_recvmsg_cancel_tcp4,
+ fun() ->
+ Recv = fun(Sock) ->
+ socket:recvmsg(Sock, nowait)
+ end,
+ InitState = #{domain => inet,
+ recv => Recv},
+ ok = api_a_recv_cancel_tcp(InitState)
+ end).
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
api_a_recv_cancel_tcp(InitState) ->
--
cgit v1.2.3
From 08f4df99bbaafbba86a216734fa603bd2996f2a3 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Wed, 17 Apr 2019 16:53:08 +0200
Subject: [socket|test] Add async to ttest
Make it possible for the tttest server to run with async.
---
erts/emulator/test/esock_ttest/esock-ttest | 22 +-
.../test/esock_ttest/esock-ttest-server-sock | 24 +-
erts/emulator/test/socket_SUITE.erl | 46 ++-
.../test/socket_test_ttest_tcp_client_socket.erl | 82 ++---
.../emulator/test/socket_test_ttest_tcp_server.erl | 103 ++++---
.../test/socket_test_ttest_tcp_server_socket.erl | 18 +-
.../emulator/test/socket_test_ttest_tcp_socket.erl | 331 +++++++++++++++++----
7 files changed, 458 insertions(+), 168 deletions(-)
diff --git a/erts/emulator/test/esock_ttest/esock-ttest b/erts/emulator/test/esock_ttest/esock-ttest
index cf1d9cd9ab..9adc51fc8b 100755
--- a/erts/emulator/test/esock_ttest/esock-ttest
+++ b/erts/emulator/test/esock_ttest/esock-ttest
@@ -60,6 +60,9 @@ usage() ->
"~n Which domain to use."
"~n Only valid for server."
"~n Defaults to: inet"
+ "~n --async Asynchronous mode (Timeout = nowait)"
+ "~n This option is only valid for transport = sock."
+ "~n Also, its only used when active =/= false."
"~n --active boolean() | once."
"~n Valid for both client and server."
"~n Defaults to: false"
@@ -111,6 +114,7 @@ process_args(Args) ->
process_server_args(Args) ->
Defaults = #{role => server,
domain => inet,
+ async => false,
active => false,
transport => {sock, plain}},
process_server_args(Args, Defaults).
@@ -124,6 +128,9 @@ process_server_args(["--domain", Domain|Args], State)
(Domain =:= "inet6")) ->
process_server_args(Args, State#{domain => list_to_atom(Domain)});
+process_server_args(["--async"|Args], State) ->
+ process_server_args(Args, State#{async => true});
+
process_server_args(["--active", Active|Args], State)
when ((Active =:= "false") orelse
(Active =:= "once") orelse
@@ -145,6 +152,7 @@ process_server_args([Arg|_], _State) ->
process_client_args(Args) ->
Defaults = #{role => client,
+ async => false,
active => false,
transport => {sock, plain},
%% Will cause error if not provided
@@ -159,10 +167,13 @@ process_client_args(Args) ->
process_client_args([], State) ->
process_client_args_ensure_max_outstanding(State);
+process_client_args(["--async"|Args], State) ->
+ process_client_args(Args, State#{async => true});
+
process_client_args(["--active", Active|Args], State)
- when ((Active =:= "false") orelse
- (Active =:= "once") orelse
- (Active =:= "true")) ->
+ when (Active =:= "false") orelse
+ (Active =:= "once") orelse
+ (Active =:= "true") ->
process_client_args(Args, State#{active => list_to_atom(Active)});
process_client_args(["--transport", "gen" | Args], State) ->
@@ -280,9 +291,10 @@ exec(#{role := server,
end;
exec(#{role := server,
domain := Domain,
+ async := Async,
active := Active,
transport := {sock, Method}}) ->
- case socket_test_ttest_tcp_server_socket:start(Method, Domain, Active) of
+ case socket_test_ttest_tcp_server_socket:start(Method, Domain, Async, Active) of
{ok, {Pid, _}} ->
MRef = erlang:monitor(process, Pid),
receive
@@ -323,12 +335,14 @@ exec(#{role := client,
end;
exec(#{role := client,
server := ServerInfo,
+ async := Async,
active := Active,
transport := {sock, Method},
msg_id := MsgID,
max_outstanding := MaxOutstanding,
runtime := RunTime}) ->
case socket_test_ttest_tcp_client_socket:start(true,
+ Async,
Method,
ServerInfo,
Active,
diff --git a/erts/emulator/test/esock_ttest/esock-ttest-server-sock b/erts/emulator/test/esock_ttest/esock-ttest-server-sock
index 4ec0d335d9..4ea8ce0185 100755
--- a/erts/emulator/test/esock_ttest/esock-ttest-server-sock
+++ b/erts/emulator/test/esock_ttest/esock-ttest-server-sock
@@ -24,9 +24,27 @@ EMU=$ERL_TOP/erts/emulator
EMU_TEST=$EMU/test
ESOCK_TTEST=$EMU_TEST/esock_ttest
-if [ $# = 1 ]; then
- ACTIVE="--active $1"
+# $1 - async - boolean()
+# $2 - active - once | boolean()
+if [ $# = 2 ]; then
+
+ async=$1
+ active=$2
+
+ if [ $async = true ]; then
+ ASYNC="--async"
+ else
+ ASYNC=
+ fi
+
+ ACTIVE="--active $async"
+
+else
+ echo " Missing args: async and active"
+ echo ""
+ exit 1
fi
-$ESOCK_TTEST/esock-ttest --server --transport sock $ACTIVE
+
+$ESOCK_TTEST/esock-ttest --server $ASYNC --transport sock $ACTIVE
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index f5757d5ce6..d4478bd9e3 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -2729,8 +2729,7 @@ api_a_send_and_recv_udp(InitState) ->
%% *** Init part ***
#{desc => "which local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{local_sa => LSA}}
end},
#{desc => "create socket",
@@ -2864,8 +2863,7 @@ api_a_send_and_recv_udp(InitState) ->
%% *** Init part ***
#{desc => "local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{lsa => LSA}}
end},
#{desc => "open socket",
@@ -2876,8 +2874,12 @@ api_a_send_and_recv_udp(InitState) ->
end},
#{desc => "bind socket (to local address)",
cmd => fun(#{sock := Sock, lsa := LSA}) ->
- sock_bind(Sock, LSA),
- ok
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
end},
#{desc => "announce ready (init)",
cmd => fun(#{tester := Tester}) ->
@@ -3209,8 +3211,7 @@ api_a_send_and_recv_tcp(InitState) ->
%% *** Init part ***
#{desc => "which local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{lsa => LSA}}
end},
#{desc => "create listen socket",
@@ -3401,10 +3402,8 @@ api_a_send_and_recv_tcp(InitState) ->
%% *** The init part ***
#{desc => "which server (local) address",
cmd => fun(#{domain := Domain, server_port := Port} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain,
- addr => LAddr},
- SSA = LSA#{port => Port},
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
{ok, State#{local_sa => LSA, server_sa => SSA}}
end},
#{desc => "create socket",
@@ -3764,8 +3763,7 @@ api_a_recv_cancel_udp(InitState) ->
%% *** Init part ***
#{desc => "which local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{local_sa => LSA}}
end},
#{desc => "create socket",
@@ -3999,8 +3997,7 @@ api_a_accept_cancel_tcp(InitState) ->
%% *** Init part ***
#{desc => "which local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{lsa => LSA}}
end},
#{desc => "create listen socket",
@@ -4245,8 +4242,7 @@ api_a_recv_cancel_tcp(InitState) ->
%% *** Init part ***
#{desc => "which local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{lsa => LSA}}
end},
#{desc => "create listen socket",
@@ -4389,10 +4385,8 @@ api_a_recv_cancel_tcp(InitState) ->
%% *** The init part ***
#{desc => "which server (local) address",
cmd => fun(#{domain := Domain, server_port := Port} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain,
- addr => LAddr},
- SSA = LSA#{port => Port},
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
{ok, State#{local_sa => LSA, server_sa => SSA}}
end},
#{desc => "create socket",
@@ -21692,7 +21686,9 @@ ttest_tcp_server_start(Node, _Domain, gen, Active) ->
socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active);
ttest_tcp_server_start(Node, Domain, sock, Active) ->
TransportMod = socket_test_ttest_tcp_socket,
- Transport = {TransportMod, #{domain => Domain, method => plain}},
+ Transport = {TransportMod, #{domain => Domain,
+ async => true,
+ method => plain}},
socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active).
ttest_tcp_server_stop(Pid) ->
@@ -21714,7 +21710,9 @@ ttest_tcp_client_start(Node,
Domain, sock,
ServerInfo, Active, MsgID, MaxOutstanding, RunTime) ->
TransportMod = socket_test_ttest_tcp_socket,
- Transport = {TransportMod, #{domain => Domain, method => plain}},
+ Transport = {TransportMod, #{domain => Domain,
+ async => true,
+ method => plain}},
socket_test_ttest_tcp_client:start_monitor(Node,
Notify,
Transport,
diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl
index ccace2a560..ca7eff4437 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl
@@ -21,89 +21,95 @@
-module(socket_test_ttest_tcp_client_socket).
-export([
- start/3, start/4, start/6, start/7,
+ start/4, start/5, start/7, start/8,
stop/1
]).
-define(TRANSPORT_MOD, socket_test_ttest_tcp_socket).
--define(MOD(D, M), {?TRANSPORT_MOD, #{domain => D, method => M}}).
+-define(MOD(D, A, M), {?TRANSPORT_MOD, #{domain => D,
+ async => A,
+ method => M}}).
-start(Method, ServerInfo, Active)
+start(Method, Async, Active, ServerInfo)
when is_list(ServerInfo) ->
Domain = local,
- socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Method),
- ServerInfo, Active);
-start(Method, ServerInfo = {Addr, _}, Active)
+ socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method),
+ Active, ServerInfo);
+start(Method, Async, Active, ServerInfo = {Addr, _})
when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
Domain = inet,
- socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Method),
- ServerInfo, Active);
-start(Method, ServerInfo = {Addr, _}, Active)
+ socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method),
+ Active, ServerInfo);
+start(Method, Async, Active, ServerInfo = {Addr, _})
when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
Domain = inet6,
- socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Method),
- ServerInfo, Active).
+ socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method),
+ Active, ServerInfo).
-start(Method, ServerInfo, Active, MsgID)
+start(Method, Async, Active, ServerInfo, MsgID)
when is_list(ServerInfo) ->
%% This is just a simplification
Domain = local,
- socket_test_ttest_tcp_client:start(?MOD(Domain, Method),
- ServerInfo, Active, MsgID);
-start(Method, ServerInfo = {Addr, _}, Active, MsgID)
+ socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method),
+ Active, ServerInfo, MsgID);
+start(Method, Async, Active, ServerInfo = {Addr, _}, MsgID)
when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
%% This is just a simplification
Domain = inet,
- socket_test_ttest_tcp_client:start(?MOD(Domain, Method),
- ServerInfo, Active, MsgID);
-start(Method, ServerInfo = {Addr, _}, Active, MsgID)
+ socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method),
+ Active, ServerInfo, MsgID);
+start(Method, Async, Active, ServerInfo = {Addr, _}, MsgID)
when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
Domain = inet6,
- socket_test_ttest_tcp_client:start(?MOD(Domain, Method),
- ServerInfo, Active, MsgID).
+ socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method),
+ Active, ServerInfo, MsgID).
-start(Method, ServerInfo, Active, MsgID, MaxOutstanding, RunTime)
+start(Method, Async, Active, ServerInfo, MsgID, MaxOutstanding, RunTime)
when is_list(ServerInfo) ->
Domain = local,
socket_test_ttest_tcp_client:start(false,
- ?MOD(Domain, Method),
- ServerInfo, Active,
+ ?MOD(Domain, Async, Method),
+ Active, ServerInfo,
MsgID, MaxOutstanding, RunTime);
-start(Method, ServerInfo = {Addr, _}, Active, MsgID, MaxOutstanding, RunTime)
+start(Method, Async, Active, ServerInfo = {Addr, _},
+ MsgID, MaxOutstanding, RunTime)
when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
Domain = inet,
socket_test_ttest_tcp_client:start(false,
- ?MOD(Domain, Method),
- ServerInfo, Active,
+ ?MOD(Domain, Async, Method),
+ Active, ServerInfo,
MsgID, MaxOutstanding, RunTime);
-start(Method, ServerInfo = {Addr, _}, Active, MsgID, MaxOutstanding, RunTime)
+start(Method, Async, Active, ServerInfo = {Addr, _},
+ MsgID, MaxOutstanding, RunTime)
when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
Domain = inet6,
socket_test_ttest_tcp_client:start(false,
- ?MOD(Domain, Method),
- ServerInfo, Active,
+ ?MOD(Domain, Async, Method),
+ Active, ServerInfo,
MsgID, MaxOutstanding, RunTime).
-start(Quiet, Method, ServerInfo, Active, MsgID, MaxOutstanding, RunTime)
+start(Quiet, Async, Active, Method, ServerInfo, MsgID, MaxOutstanding, RunTime)
when is_list(ServerInfo) ->
Domain = local,
socket_test_ttest_tcp_client:start(Quiet,
- ?MOD(Domain, Method),
- ServerInfo, Active,
+ ?MOD(Domain, Async, Method),
+ Active, ServerInfo,
MsgID, MaxOutstanding, RunTime);
-start(Quiet, Method, ServerInfo = {Addr, _}, Active, MsgID, MaxOutstanding, RunTime)
+start(Quiet, Async, Active, Method, ServerInfo = {Addr, _},
+ MsgID, MaxOutstanding, RunTime)
when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
Domain = inet,
socket_test_ttest_tcp_client:start(Quiet,
- ?MOD(Domain, Method),
- ServerInfo, Active,
+ ?MOD(Domain, Async, Method),
+ Active, ServerInfo,
MsgID, MaxOutstanding, RunTime);
-start(Quiet, Method, ServerInfo = {Addr, _}, Active, MsgID, MaxOutstanding, RunTime)
+start(Quiet, Async, Active, Method, ServerInfo = {Addr, _},
+ MsgID, MaxOutstanding, RunTime)
when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
Domain = inet6,
socket_test_ttest_tcp_client:start(Quiet,
- ?MOD(Domain, Method),
- ServerInfo, Active,
+ ?MOD(Domain, Async, Method),
+ Active, ServerInfo,
MsgID, MaxOutstanding, RunTime).
stop(Pid) ->
diff --git a/erts/emulator/test/socket_test_ttest_tcp_server.erl b/erts/emulator/test/socket_test_ttest_tcp_server.erl
index e916fcb93e..f0a2ae73ec 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_server.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_server.erl
@@ -90,7 +90,9 @@ start_monitor(_, Transport, Active) ->
start(Transport, Active) ->
do_start(self(), Transport, Active).
-
+%% Note that the Async option is actually only "used" for the
+%% socket transport module (it details how to implement the
+%% active feature).
do_start(Parent, Transport, Active)
when is_pid(Parent) andalso
(is_atom(Transport) orelse is_tuple(Transport)) andalso
@@ -167,7 +169,7 @@ server_init(Starter, Parent, Transport, Active) ->
end.
process_transport(Mod, _) when is_atom(Mod) ->
- {Mod, fun(Port) -> Mod:listen(Port) end, infinity};
+ {Mod, false, fun(Port) -> Mod:listen(Port) end, infinity};
process_transport({Mod, #{stats_interval := T} = Opts}, Active)
when (Active =/= false) ->
{Mod, fun(Port) -> Mod:listen(Port, Opts#{stats_to => self()}) end, T};
@@ -179,42 +181,59 @@ process_transport({Mod, Opts}, _Active) ->
server_loop(State) ->
- server_loop( server_handle_message( server_accept(State) ) ).
+ server_loop( server_handle_message( server_accept(State, ?ACC_TIMEOUT), 0) ).
-server_accept(#{mod := Mod,
- active := Active,
- lsock := LSock,
- handlers := Handlers} = State) ->
- case Mod:accept(LSock, ?ACC_TIMEOUT) of
+server_accept(#{mod := Mod, lsock := LSock} = State, Timeout) ->
+ case Mod:accept(LSock, Timeout) of
{ok, Sock} ->
- ?I("accepted connection from ~s",
- [case Mod:peername(Sock) of
- {ok, Peer} ->
- format_peername(Peer);
- {error, _} ->
- "-"
- end]),
- {Pid, _} = handler_start(),
- ?I("handler ~p started -> try transfer socket control", [Pid]),
- case Mod:controlling_process(Sock, Pid) of
- ok ->
- maybe_start_stats_timer(State, Pid),
- ?I("server-accept: handler ~p started", [Pid]),
- handler_continue(Pid, Mod, Sock, Active),
- Handlers2 = [Pid | Handlers],
- State#{handlers => Handlers2};
- {error, CPReason} ->
- (catch Mod:close(Sock)),
- (catch Mod:close(LSock)),
- exit({controlling_process, CPReason})
- end;
- {error, timeout} ->
+ server_handle_accepted(State, Sock);
+ {error, timeout} when (Timeout =/= nowait) ->
State;
{error, AReason} ->
(catch Mod:close(LSock)),
exit({accept, AReason})
end.
+%% server_accept(#{mod := Mod,
+%% lsock := LSock} = State) ->
+%% case Mod:accept(LSock, ?ACC_TIMEOUT) of
+%% {ok, Sock} ->
+%% server_handle_accepted(State, Sock);
+%% {error, timeout} ->
+%% State;
+%% {error, AReason} ->
+%% (catch Mod:close(LSock)),
+%% exit({accept, AReason})
+%% end.
+
+server_handle_accepted(#{mod := Mod,
+ lsock := LSock,
+ active := Active,
+ handlers := Handlers} = State,
+ Sock) ->
+ ?I("accepted connection from ~s",
+ [case Mod:peername(Sock) of
+ {ok, Peer} ->
+ format_peername(Peer);
+ {error, _} ->
+ "-"
+ end]),
+ {Pid, _} = handler_start(),
+ ?I("handler ~p started -> try transfer socket control", [Pid]),
+ case Mod:controlling_process(Sock, Pid) of
+ ok ->
+ maybe_start_stats_timer(State, Pid),
+ ?I("server-accept: handler ~p started", [Pid]),
+ handler_continue(Pid, Mod, Sock, Active),
+ Handlers2 = [Pid | Handlers],
+ State#{handlers => Handlers2};
+ {error, CPReason} ->
+ (catch Mod:close(Sock)),
+ (catch Mod:close(LSock)),
+ exit({controlling_process, CPReason})
+ end.
+
+
format_peername({Addr, Port}) ->
case inet:gethostbyaddr(Addr) of
{ok, #hostent{h_name = N}} ->
@@ -237,7 +256,7 @@ start_stats_timer(Time, ProcStr, Pid) ->
server_handle_message(#{mod := Mod,
lsock := LSock,
parent := Parent,
- handlers := H} = State) ->
+ handlers := H} = State, Timeout) ->
receive
{timeout, _TRef, {stats, Interval, ProcStr, Pid}} ->
case server_handle_stats(ProcStr, Pid) of
@@ -247,7 +266,7 @@ server_handle_message(#{mod := Mod,
ok
end,
State;
-
+
{?MODULE, Ref, Parent, stop} ->
reply(Parent, Ref, ok),
lists:foreach(fun(P) -> handler_stop(P) end, H),
@@ -257,7 +276,7 @@ server_handle_message(#{mod := Mod,
{'DOWN', _MRef, process, Pid, Reason} ->
server_handle_down(Pid, Reason, State)
- after 0 ->
+ after Timeout ->
State
end.
@@ -342,15 +361,15 @@ handler_init(Parent) ->
?I("received continue"),
reply(Parent, Ref, ok),
handler_initial_activation(Mod, Sock, Active),
- handler_loop(#{parent => Parent,
- mod => Mod,
- sock => Sock,
- active => Active,
- start => ?T(),
- mcnt => 0,
- bcnt => 0,
- last_reply => none,
- acc => <<>>})
+ handler_loop(#{parent => Parent,
+ mod => Mod,
+ sock => Sock,
+ active => Active,
+ start => ?T(),
+ mcnt => 0,
+ bcnt => 0,
+ last_reply => none,
+ acc => <<>>})
after 5000 ->
?I("timeout when message queue: "
diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl
index d1de230637..4045bf4e4e 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl
@@ -21,18 +21,26 @@
-module(socket_test_ttest_tcp_server_socket).
-export([
- start/3,
+ start/4,
stop/1
]).
-define(TRANSPORT_MOD, socket_test_ttest_tcp_socket).
-%% -define(MOD(D, M), {?TRANSPORT_MOD, #{domain => D,
+%% -define(MOD(M), {?TRANSPORT_MOD, #{async => false,
%% method => M,
%% stats_interval => 10000}}).
--define(MOD(D, M), {?TRANSPORT_MOD, #{domain => D, method => M}}).
+-define(MOD(D,M,A), {?TRANSPORT_MOD, #{domain => D,
+ async => A,
+ method => M}}).
-start(Method, Domain, Active) ->
- socket_test_ttest_tcp_server:start(?MOD(Domain, Method), Active).
+start(Method, Domain, Async, Active) ->
+ socket_test_ttest_tcp_server:start(?MOD(Domain, Method, Async), Active).
+ %% {ok, {Pid, AddrPort}} ->
+ %% MRef = erlang:monitor(process, Pid),
+ %% {ok, {Pid, MRef, AddrPort}};
+ %% {error, _} = ERROR ->
+ %% ERROR
+ %% end.
stop(Pid) ->
socket_test_ttest_tcp_server:stop(Pid).
diff --git a/erts/emulator/test/socket_test_ttest_tcp_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_socket.erl
index cf68bfe591..65fbba44c6 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_socket.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_socket.erl
@@ -57,7 +57,7 @@
%% ==========================================================================
-%% This does not really work. Its just a placeholder for the time beeing...
+%% This does not really work. Its just a placeholder for the time being...
%% getopt(Sock, Opt) when is_atom(Opt) ->
%% socket:getopt(Sock, socket, Opt).
@@ -68,22 +68,32 @@
%% ==========================================================================
-accept(#{sock := LSock, opts := #{method := Method} = Opts}) ->
+%% The way we use server async its no point in doing a async accept call
+%% (we do never actually run the test with more than one client).
+accept(#{sock := LSock, opts := #{async := Async,
+ method := Method} = Opts}) ->
case socket:accept(LSock) of
- {ok, Sock} ->
+ {ok, Sock} ->
Self = self(),
- Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end),
+ Reader = spawn(fun() ->
+ reader_init(Self, Sock, Async, false, Method)
+ end),
maybe_start_stats_timer(Opts, Reader),
{ok, #{sock => Sock, reader => Reader, method => Method}};
{error, _} = ERROR ->
ERROR
end.
-accept(#{sock := LSock, opts := #{method := Method} = Opts}, Timeout) ->
+%% If a timeout has been explictly specified, then we do not use
+%% async here. We will pass it on to the reader process.
+accept(#{sock := LSock, opts := #{async := Async,
+ method := Method} = Opts}, Timeout) ->
case socket:accept(LSock, Timeout) of
{ok, Sock} ->
Self = self(),
- Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end),
+ Reader = spawn(fun() ->
+ reader_init(Self, Sock, Async, false, Method)
+ end),
maybe_start_stats_timer(Opts, Reader),
{ok, #{sock => Sock, reader => Reader, method => Method}};
{error, _} = ERROR ->
@@ -153,7 +163,8 @@ connect(Addr, Port, #{domain := Domain} = Opts) ->
do_connect(LocalSA, ServerSA, Cleanup, Opts#{proto => tcp}).
do_connect(LocalSA, ServerSA, Cleanup, #{domain := Domain,
- proto := Proto,
+ proto := Proto,
+ async := Async,
method := Method} = Opts) ->
try
begin
@@ -181,7 +192,9 @@ do_connect(LocalSA, ServerSA, Cleanup, #{domain := Domain,
throw({error, {connect, CReason}})
end,
Self = self(),
- Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end),
+ Reader = spawn(fun() ->
+ reader_init(Self, Sock, Async, false, Method)
+ end),
maybe_start_stats_timer(Opts, Reader),
{ok, #{sock => Sock, reader => Reader, method => Method}}
end
@@ -219,9 +232,9 @@ listen() ->
listen(0).
listen(Port) when is_integer(Port) ->
- listen(Port, #{domain => inet, method => plain});
+ listen(Port, #{domain => inet, async => false, method => plain});
listen(Path) when is_list(Path) ->
- listen(Path, #{domain => local, method => plain}).
+ listen(Path, #{domain => local, async => false, method => plain}).
listen(0, #{domain := local} = Opts) ->
listen(mk_unique_path(), Opts);
@@ -241,8 +254,10 @@ listen(Port, #{domain := Domain} = Opts)
do_listen(SA,
Cleanup,
- #{domain := Domain, proto := Proto, method := Method} = Opts)
- when (Method =:= plain) orelse (Method =:= msg) ->
+ #{domain := Domain, proto := Proto,
+ async := Async, method := Method} = Opts)
+ when (Method =:= plain) orelse (Method =:= msg) andalso
+ is_boolean(Async) ->
try
begin
Sock = case socket:open(Domain, stream, Proto) of
@@ -339,13 +354,18 @@ sockname(#{sock := Sock}) ->
%% ==========================================================================
-reader_init(ControllingProcess, Sock, Active, Method)
+reader_init(ControllingProcess, Sock, Async, Active, Method)
when is_pid(ControllingProcess) andalso
+ is_boolean(Async) andalso
(is_boolean(Active) orelse (Active =:= once)) andalso
((Method =:= plain) orelse (Method =:= msg)) ->
+ put(verbose, false),
MRef = erlang:monitor(process, ControllingProcess),
reader_loop(#{ctrl_proc => ControllingProcess,
ctrl_proc_mref => MRef,
+ async => Async,
+ select_info => undefined,
+ select_num => 0, % Count the number of select messages
active => Active,
sock => Sock,
method => Method}).
@@ -356,11 +376,11 @@ reader_loop(#{active := false,
ctrl_proc := Pid} = State) ->
receive
{?MODULE, stop} ->
- exit(normal);
+ reader_exit(State, stop);
{?MODULE, Pid, controlling_process, NewPid} ->
- MRef = maps:get(ctrl_proc_mref, State),
- erlang:demonitor(MRef, [flush]),
+ OldMRef = maps:get(ctrl_proc_mref, State),
+ erlang:demonitor(OldMRef, [flush]),
NewMRef = erlang:monitor(process, NewPid),
Pid ! {?MODULE, self(), controlling_process},
reader_loop(State#{ctrl_proc => NewPid,
@@ -371,17 +391,16 @@ reader_loop(#{active := false,
{'DOWN', MRef, process, Pid, Reason} ->
case maps:get(ctrl_proc_mref, State) of
- MRef when (Reason =:= normal) ->
- exit(normal);
MRef ->
- exit({controlling_process, Reason});
+ reader_exit(State, {ctrl_exit, Reason});
_ ->
reader_loop(State)
end
end;
%% Read *once* and then change to false
-reader_loop(#{active := once,
+reader_loop(#{async := false,
+ active := once,
sock := Sock,
method := Method,
ctrl_proc := Pid} = State) ->
@@ -392,25 +411,23 @@ reader_loop(#{active := once,
{error, timeout} ->
receive
{?MODULE, stop} ->
- exit(normal);
+ reader_exit(State, stop);
{?MODULE, Pid, controlling_process, NewPid} ->
- MRef = maps:get(ctrl_proc_mref, State),
- erlang:demonitor(MRef, [flush]),
- MRef = erlang:monitor(process, NewPid),
+ OldMRef = maps:get(ctrl_proc_mref, State),
+ erlang:demonitor(OldMRef, [flush]),
+ NewMRef = erlang:monitor(process, NewPid),
Pid ! {?MODULE, self(), controlling_process},
reader_loop(State#{ctrl_proc => NewPid,
- ctrl_proc_mref => MRef});
+ ctrl_proc_mref => NewMRef});
{?MODULE, active, NewActive} ->
reader_loop(State#{active => NewActive});
{'DOWN', MRef, process, Pid, Reason} ->
case maps:get(ctrl_proc_mref, State) of
- MRef when (Reason =:= normal) ->
- exit(normal);
- MRef ->
- exit({controlling_process, Reason});
+ MRef ->
+ reader_exit(State, {ctrl_exit, Reason});
_ ->
reader_loop(State)
end
@@ -418,17 +435,86 @@ reader_loop(#{active := once,
reader_loop(State)
end;
- {error, closed} ->
+ {error, closed} = E1 ->
+ Pid ! ?CLOSED_MSG(Sock, Method),
+ reader_exit(State, E1);
+
+ {error, Reason} = E2 ->
+ Pid ! ?ERROR_MSG(Sock, Method, Reason),
+ reader_exit(State, E2)
+ end;
+reader_loop(#{async := true,
+ select_info := undefined,
+ active := once,
+ sock := Sock,
+ method := Method,
+ ctrl_proc := Pid} = State) ->
+ case do_recv(Method, Sock, nowait) of
+ {ok, {select, _, _} = SelectInfo} ->
+ reader_loop(State#{select_info => SelectInfo});
+ {ok, Data} ->
+ Pid ! ?DATA_MSG(Sock, Method, Data),
+ reader_loop(State#{active => false});
+
+ {error, closed} = E1 ->
Pid ! ?CLOSED_MSG(Sock, Method),
- exit(normal);
+ reader_exit(State, E1);
- {error, Reason} ->
+ {error, Reason} = E2 ->
Pid ! ?ERROR_MSG(Sock, Method, Reason),
- exit(Reason)
+ reader_exit(State, E2)
+ end;
+reader_loop(#{async := true,
+ select_info := {select, _, Ref},
+ select_num := N,
+ active := once,
+ sock := Sock,
+ method := Method,
+ ctrl_proc := Pid} = State) ->
+ receive
+ {?MODULE, stop} ->
+ reader_exit(State, stop);
+
+ {?MODULE, Pid, controlling_process, NewPid} ->
+ OldMRef = maps:get(ctrl_proc_mref, State),
+ erlang:demonitor(OldMRef, [flush]),
+ NewMRef = erlang:monitor(process, NewPid),
+ Pid ! {?MODULE, self(), controlling_process},
+ reader_loop(State#{ctrl_proc => NewPid,
+ ctrl_proc_mref => NewMRef});
+
+ {?MODULE, active, NewActive} ->
+ reader_loop(State#{active => NewActive});
+
+ {'DOWN', MRef, process, Pid, Reason} ->
+ case maps:get(ctrl_proc_mref, State) of
+ MRef ->
+ reader_exit(State, {ctrl_exit, Reason});
+ _ ->
+ reader_loop(State)
+ end;
+
+ {'$socket', Sock, select, Ref} ->
+ case do_recv(Method, Sock, nowait) of
+ {ok, Data} when is_binary(Data) ->
+ Pid ! ?DATA_MSG(Sock, Method, Data),
+ reader_loop(State#{active => false,
+ select_info => undefined,
+ select_num => N+1});
+
+ {error, closed} = E1 ->
+ Pid ! ?CLOSED_MSG(Sock, Method),
+ reader_exit(State, E1);
+
+ {error, Reason} = E2 ->
+ Pid ! ?ERROR_MSG(Sock, Method, Reason),
+ reader_exit(State, E2)
+ end
end;
%% Read and forward data continuously
-reader_loop(#{active := true,
+reader_loop(#{async := false,
+ active := true,
sock := Sock,
method := Method,
ctrl_proc := Pid} = State) ->
@@ -439,25 +525,23 @@ reader_loop(#{active := true,
{error, timeout} ->
receive
{?MODULE, stop} ->
- exit(normal);
+ reader_exit(State, stop);
{?MODULE, Pid, controlling_process, NewPid} ->
- MRef = maps:get(ctrl_proc_mref, State),
- erlang:demonitor(MRef, [flush]),
- MRef = erlang:monitor(process, NewPid),
+ OldMRef = maps:get(ctrl_proc_mref, State),
+ erlang:demonitor(OldMRef, [flush]),
+ NewMRef = erlang:monitor(process, NewPid),
Pid ! {?MODULE, self(), controlling_process},
reader_loop(State#{ctrl_proc => NewPid,
- ctrl_proc_mref => MRef});
+ ctrl_proc_mref => NewMRef});
{?MODULE, active, NewActive} ->
reader_loop(State#{active => NewActive});
{'DOWN', MRef, process, Pid, Reason} ->
case maps:get(ctrl_proc_mref, State) of
- MRef when (Reason =:= normal) ->
- exit(normal);
MRef ->
- exit({controlling_process, Reason});
+ reader_exit(State, {ctrl_exit, Reason});
_ ->
reader_loop(State)
end
@@ -465,27 +549,170 @@ reader_loop(#{active := true,
reader_loop(State)
end;
- {error, closed} ->
+ {error, closed} = E1 ->
+ Pid ! ?CLOSED_MSG(Sock, Method),
+ reader_exit(State, E1);
+
+ {error, Reason} = E2 ->
+ Pid ! ?ERROR_MSG(Sock, Method, Reason),
+ reader_exit(State, E2)
+ end;
+reader_loop(#{async := true,
+ select_info := undefined,
+ active := true,
+ sock := Sock,
+ method := Method,
+ ctrl_proc := Pid} = State) ->
+ case do_recv(Method, Sock) of
+ {ok, {select, _, _} = SelectInfo} ->
+ reader_loop(State#{select_info => SelectInfo});
+ {ok, Data} ->
+ Pid ! ?DATA_MSG(Sock, Method, Data),
+ reader_loop(State);
+
+ {error, closed} = E1 ->
Pid ! ?CLOSED_MSG(Sock, Method),
- exit(normal);
+ reader_exit(State, E1);
- {error, Reason} ->
+ {error, Reason} = E2 ->
Pid ! ?ERROR_MSG(Sock, Method, Reason),
- exit(Reason)
+ reader_exit(State, E2)
+ end;
+reader_loop(#{async := true,
+ select_info := {select, _, Ref},
+ select_num := N,
+ active := true,
+ sock := Sock,
+ method := Method,
+ ctrl_proc := Pid} = State) ->
+ receive
+ {?MODULE, stop} ->
+ reader_exit(State, stop);
+
+ {?MODULE, Pid, controlling_process, NewPid} ->
+ OldMRef = maps:get(ctrl_proc_mref, State),
+ erlang:demonitor(OldMRef, [flush]),
+ NewMRef = erlang:monitor(process, NewPid),
+ Pid ! {?MODULE, self(), controlling_process},
+ reader_loop(State#{ctrl_proc => NewPid,
+ ctrl_proc_mref => NewMRef});
+
+ {?MODULE, active, NewActive} ->
+ reader_loop(State#{active => NewActive});
+
+ {'DOWN', MRef, process, Pid, Reason} ->
+ case maps:get(ctrl_proc_mref, State) of
+ MRef ->
+ reader_exit(State, {ctrl_exit, Reason});
+ _ ->
+ reader_loop(State)
+ end;
+
+ {'$socket', Sock, select, Ref} ->
+ case do_recv(Method, Sock, nowait) of
+ {ok, Data} when is_binary(Data) ->
+ Pid ! ?DATA_MSG(Sock, Method, Data),
+ reader_loop(State#{select_info => undefined,
+ select_num => N+1});
+
+ {error, closed} = E1 ->
+ Pid ! ?CLOSED_MSG(Sock, Method),
+ reader_exit(State, E1);
+
+ {error, Reason} = E2 ->
+ Pid ! ?ERROR_MSG(Sock, Method, Reason),
+ reader_exit(State, E2)
+ end
end.
-do_recv(plain, Sock) ->
- socket:recv(Sock, 0, ?READER_RECV_TIMEOUT);
-do_recv(msg, Sock) ->
- case socket:recvmsg(Sock, 0, 0, [], ?READER_RECV_TIMEOUT) of
+do_recv(Method, Sock) ->
+ do_recv(Method, Sock, ?READER_RECV_TIMEOUT).
+
+do_recv(plain, Sock, Timeout) ->
+ socket:recv(Sock, 0, Timeout);
+do_recv(msg, Sock, Timeout) ->
+ case socket:recvmsg(Sock, 0, 0, [], Timeout) of
{ok, #{iov := [Bin]}} ->
{ok, Bin};
+ {ok, {select, _, _}} = OK ->
+ OK;
{error, _} = ERROR ->
ERROR
end.
-
-
+
+
+reader_exit(#{async := false, active := Active}, stop) ->
+ vp("reader stopped when active: ~w", [Active]),
+ exit(normal);
+reader_exit(#{async := true,
+ active := Active,
+ select_info := SelectInfo,
+ select_num := N}, stop) ->
+ vp("reader stopped when active: ~w"
+ "~n Current select info: ~p"
+ "~n Number of select messages: ~p", [Active, SelectInfo, N]),
+ exit(normal);
+reader_exit(#{async := false, active := Active}, {ctrl_exit, normal}) ->
+ vp("reader ctrl exit when active: ~w", [Active]),
+ exit(normal);
+reader_exit(#{async := true,
+ active := Active,
+ select_info := SelectInfo,
+ select_num := N}, {ctrl_exit, normal}) ->
+ vp("reader ctrl exit when active: ~w"
+ "~n Current select info: ~p"
+ "~n Number of select messages: ~p", [Active, SelectInfo, N]),
+ exit(normal);
+reader_exit(#{async := false, active := Active}, {ctrl_exit, Reason}) ->
+ vp("reader exit when ctrl crash when active: ~w", [Active]),
+ exit({controlling_process, Reason});
+reader_exit(#{async := true,
+ active := Active,
+ select_info := SelectInfo,
+ select_num := N}, {ctrl_exit, Reason}) ->
+ vp("reader exit when ctrl crash when active: ~w"
+ "~n Current select info: ~p"
+ "~n Number of select messages: ~p", [Active, SelectInfo, N]),
+ exit({controlling_process, Reason});
+reader_exit(#{async := false, active := Active}, {error, closed}) ->
+ vp("reader exit when socket closed when active: ~w", [Active]),
+ exit(normal);
+reader_exit(#{async := true,
+ active := Active,
+ select_info := SelectInfo,
+ select_num := N}, {error, closed}) ->
+ vp("reader exit when socket closed when active: ~w "
+ "~n Current select info: ~p"
+ "~n Number of select messages: ~p", [Active, SelectInfo, N]),
+ exit(normal);
+reader_exit(#{async := false, active := Active}, {error, Reason}) ->
+ vp("reader exit when socket error when active: ~w", [Active]),
+ exit(Reason);
+reader_exit(#{async := true,
+ active := Active,
+ select_info := SelectInfo,
+ select_num := N}, {error, Reason}) ->
+ vp("reader exit when socket error when active: ~w: "
+ "~n Current select info: ~p"
+ "~n Number of select messages: ~p", [Active, SelectInfo, N]),
+ exit(Reason).
+
+
+
+
+
%% ==========================================================================
+vp(F, A) ->
+ vp(get(verbose), F, A).
+
+vp(true, F, A) ->
+ p(F, A);
+vp(_, _, _) ->
+ ok.
+
+p(F, A) ->
+ io:format(F ++ "~n", A).
+
--
cgit v1.2.3
From 2a040cf7c0cc6415b4bb5152b58d8a98a59d6a81 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Thu, 25 Apr 2019 11:34:50 +0200
Subject: [esock|doc] Document the new asynchronous feature
Document the new way to make asynchronous calls to some
of the functions: accept, all recv and all send.
OTP-15731
---
erts/doc/src/socket.xml | 253 +++++++++++++++++++++++++++++++++++++---
erts/doc/src/socket_usage.xml | 26 ++++-
erts/preloaded/ebin/socket.beam | Bin 74584 -> 74628 bytes
erts/preloaded/src/socket.erl | 8 +-
4 files changed, 263 insertions(+), 24 deletions(-)
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index 9b34bf1df1..3ff0eb4e1b 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -40,6 +40,20 @@
the functions,
e.g. recv/3,
has a timeout argument.
+
+ Some functions allow for an asynchronous call.
+ This is achieved by setting the Timeout argument to
+ nowait. For instance, if calling the
+ recv/3
+ function with Timeout set to nowait (recv(Sock, 0, nowait))
+ when there is actually nothing to read, it will return with
+ {ok, SelectInfo}. When data eventually arrives a 'select' message
+ will be sent to the caller:
+ {'$socket', Sock, select, Info}. The caller can now make another
+ call to the recv function and now expect data.
+ Note that all other users are locked out until the
+ 'current user' has called the function (recv in this case).
+
There is currently no support for Windows.
Support for IPv6 has been implemented but not tested.
@@ -64,6 +78,15 @@
accept/1,2.
+
+
+
+
+
+
+
+
+
@@ -257,7 +280,7 @@
-
+
Accept a connection on a socket.
Accept a connection on a socket.
@@ -268,6 +291,26 @@
+
+
+ Accept a connection on a socket.
+
+ Accept a connection on a socket.
+
+ This call is used with connection-based socket types
+ (stream or seqpacket). It extracs the first pending
+ connection request for the listen socket and returns the (newly)
+ connected socket.
+
+ In the case when there is no connections waiting, the function
+ will return with the SelectInfo. The caller can then await a
+ select message, {'$socket', Sock, select, Info} (where
+ Info is the select_ref() from the SelectInfo),
+ when a client connects (a subsequent call to accept will then return
+ the socket).
+
+
+
Bind a name to a socket.
@@ -281,6 +324,18 @@
+
+
+ Cancel an asynchronous request.
+
+ Cancel an asynchronous request.
+
+ Call this function in order to cancel a previous
+ asynchronous call to, e.g.
+ recv/3.
+
+
+
Close a socket.
@@ -406,48 +461,136 @@
-
-
+
+
+ Receive a message from a socket.
+
+ Receive a message from a socket.
+ There is a special case for the argument Length.
+ If it is set to zero (0), it means "give me everything you
+ currently have".
+
+
+
+
+
+
Receive a message from a socket.
Receive a message from a socket.
+
There is a special case for the argument Length.
If it is set to zero (0), it means "give me everything you
currently have".
+
+ In the case when there is no data waiting, the function
+ will return with the SelectInfo. The caller can then await a
+ select message, {'$socket', Sock, select, Info} (where
+ Info is the select_ref() from the SelectInfo),
+ when data has arrived (a subsequent call to recv will then return
+ the data).
+ Note that if a length (> 0) is specified, and only part
+ of that amount of data is available, the function will return with
+ that data and the SelectInfo (if the caller don't
+ want to wait for the remaining data, it must immediately call
+ the cancel/2 function.)
-
-
+
+
Receive a message from a socket.
Receive a message from a socket.
This function reads "messages", which means that regardless of
- how much we want to read, it returns when we get a message.
+ how much we want to read, it returns when we get a message
+ (if the buffer size is to small, the message will be truncated).
+ The BufSz argument basically defines the size of the
+ receive buffer. By setting the value to zero (0), the configured
+ size (setopt with Level = otp and
+ Key = rcvbuf) is used.
+ It may be impossible to know what (buffer) size is appropriate
+ "in advance", and in those cases it may be convenient to use the
+ (recv) 'peek' flag. When this flag is provided, the message is *not*
+ "consumed" from the underlying buffers, so another recvfrom call
+ is needed, possibly with a then adjusted buffer size.
+
+
+
+
+
+
+
+ Receive a message from a socket.
+
+ Receive a message from a socket.
+ This function reads "messages", which means that regardless of
+ how much we want to read, it returns when we get a message
+ (if the buffer size is to small, the message will be truncated).
The BufSz argument basically defines the size of the
receive buffer. By setting the value to zero (0), the configured
- size (setopt with Level = otp and Key = rcvbuf)
- is used.
+ size (setopt with Level = otp and
+ Key = rcvbuf) is used.
It may be impossible to know what (buffer) size is appropriate
"in advance", and in those cases it may be convenient to use the
(recv) 'peek' flag. When this flag is provided, the message is *not*
"consumed" from the underlying buffers, so another recvfrom call
is needed, possibly with a then adjusted buffer size.
+
+ In the case when there is no data waiting, the function
+ will return with the SelectInfo. The caller can then await a
+ select message, {'$socket', Sock, select, Info} (where
+ Info is the select_ref() from the SelectInfo),
+ when data has arrived (a subsequent call to recvfrom will then return
+ the data).
-
-
+
-
+
+
+ Receive a message from a socket.
+
+ Receive a message from a socket.
+ This function reads "messages", which means that regardless of
+ how much we want to read, it returns when we get a message.
+ The message will be delivered in the form of a msghdr(),
+ which may contain the source address (if socket not connected),
+ a list of cmsghdr_recv() (depends on what socket options have
+ been set and what the protocol and platform supports) and
+ also a set of flags, providing further info about the read.
+
+ The BufSz argument basically defines the size of the
+ receive buffer. By setting the value to zero (0), the configured
+ size (setopt with Level = otp and
+ Key = rcvbuf) is used.
+
+ The CtrlSz argument basically defines the size of the
+ receive buffer for the control messages.
+ By setting the value to zero (0), the configured size (setopt
+ with Level = otp) is used.
+
+ It may be impossible to know what (buffer) size is appropriate
+ "in advance", and in those cases it may be convenient to use the
+ (recv) 'peek' flag. When this flag is provided, the message is *not*
+ "consumed" from the underlying buffers, so another recvmsg call
+ is needed, possibly with a then adjusted buffer size.
+
+
+
+
+
+
+
Receive a message from a socket.
Receive a message from a socket.
@@ -461,8 +604,8 @@
The BufSz argument basically defines the size of the
receive buffer. By setting the value to zero (0), the configured
- size (setopt with Level = otp and Key = rcvbuf)
- is used.
+ size (setopt with Level = otp and
+ Key = rcvbuf) is used.
The CtrlSz argument basically defines the size of the
receive buffer for the control messages.
@@ -474,25 +617,75 @@
(recv) 'peek' flag. When this flag is provided, the message is *not*
"consumed" from the underlying buffers, so another recvmsg call
is needed, possibly with a then adjusted buffer size.
+
+ In the case when there is no data waiting, the function
+ will return with the SelectInfo. The caller can then await a
+ select message, {'$socket', Sock, select, Info} (where
+ Info is the select_ref() from the SelectInfo),
+ when data has arrived (a subsequent call to recvmsg will then return
+ the data).
-
-
+
+
+ Send a message on a socket.
+
+ Send a message on a connected socket.
+
+
+
+
+
+
Send a message on a socket.
Send a message on a connected socket.
+
+ In the case when there is no room in the (system-) buffers,
+ the function will return with the SelectInfo. The caller
+ can then await a select message, {'$socket', Sock, select, Info}
+ (where Info is the select_ref() from the
+ SelectInfo), when there is room for more data (a subsequent
+ call to send will then send the data).
+ Note that if not all the data was sent, the function will return
+ with the remaining data and the SelectInfo
+ (if the caller don't
+ want to wait to be able to send the rest, it should immediately call
+ the cancel/2 function.)
-
-
+
+
+ Send a message on a socket.
+
+ Send a message on a socket. The destination, if needed
+ (socket not connected) is provided in the MsgHdr,
+ which also contains the message to send,
+ The MsgHdr may also contain an list of optional cmsghdr_send()
+ (depends on what the protocol and platform supports).
+
+ Unlike the send function,
+ this one sends one message.
+ This means that if, for whatever reason, its not possible to send the
+ message in one go, the function will instead return with the
+ remaining data ({ok, Remaining}). Thereby leaving it
+ up to the caller to decide what to do (retry with the remaining data
+ of give up).
+
+
+
+
+
+
+
Send a message on a socket.
Send a message on a socket. The destination, if needed
@@ -509,20 +702,42 @@
up to the caller to decide what to do (retry with the remaining data
of give up).
+ In the case when there is no room in the (system-) buffers,
+ the function will return with the SelectInfo. The caller
+ can then await a select message, {'$socket', Sock, select, Info}
+ (where Info is the select_ref() from the
+ SelectInfo), when there is room for more data (a subsequent
+ call to sendmsg will then send the data).
-
-
+
+
Send a message on a socket.
Send a message on a socket, to the specified destination.
+
+
+
+ Send a message on a socket.
+
+ Send a message on a socket, to the specified destination.
+
+ In the case when there is no room in the (system-) buffers,
+ the function will return with the SelectInfo. The caller
+ can then await a select message, {'$socket', Sock, select, Info}
+ (where Info is the select_ref() from the
+ SelectInfo), when there is room for more data (a subsequent
+ call to sendto will then send the data).
+
+
+
diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml
index 4b3872d7e3..3d18bd1af7 100644
--- a/erts/doc/src/socket_usage.xml
+++ b/erts/doc/src/socket_usage.xml
@@ -34,11 +34,35 @@
Introduction
The socket interface (module) is basically an "thin" layer on top of
the OS socket interface. It is assumed that, unless you have special needs,
- gen_[tcp|udp|sctp] should be sufficent.
+ gen_[tcp|udp|sctp] should be sufficent (when they become available).
Note that just because we have a documented and described option,
it does not mean that the OS supports it. So its recommended
that the user reads the platform specific documentation for the
option used.
+
+ Asynchronous calls
+ Some functions allow for an asynchronous call
+ (accept/2,
+ recv/3,4,
+ recvfrom/3,4,
+ recvmsg/2,3,5,
+ send/3,4,
+ sendmsg/3,4 and
+ sendto/4,5).
+ This is achieved by setting the Timeout argument to
+ nowait. For instance, if calling the
+ recv/3
+ function with Timeout set to nowait (i.e. recv(Sock, 0, nowait))
+ when there is actually nothing to read, it will return with
+ {ok, SelectInfo}. When data eventually arrives a 'select message'
+ will be sent to the caller:
+ {'$socket', Sock, select, Info}. The caller can make another
+ call to the recv function and now expect data.
+ Note that all other users are locked out until the
+ 'current user' has called the function (recv in this case). So either
+ immediately call the function or
+ cancel.
+
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index b8983eb8e8..892aab1fa0 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index f4d8f175f8..59bef4b3b7 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1359,7 +1359,7 @@ send(Socket, Data) ->
Data :: iodata(),
Flags :: send_flags(),
Reason :: term()
- ; (Socket, Data, nowait) -> ok |
+ ; (Socket, Data, Timeout :: nowait) -> ok |
{ok, SelectInfo} |
{ok, {RestData, SelectInfo}} |
{error, Reason} when
@@ -1488,7 +1488,7 @@ sendto(Socket, Data, Dest) ->
Dest :: null | sockaddr(),
Flags :: send_flags(),
Reason :: term()
- ; (Socket, Data, Dest, nowait) -> ok |
+ ; (Socket, Data, Dest, Timeout :: nowait) -> ok |
{ok, SelectInfo} |
{error, Reason} when
Socket :: socket(),
@@ -1630,7 +1630,7 @@ sendmsg(Socket, MsgHdr) ->
MsgHdr :: msghdr(),
Flags :: send_flags(),
Reason :: term()
- ; (Socket, MsgHdr, nowait) -> ok |
+ ; (Socket, MsgHdr, Timeout :: nowait) -> ok |
{ok, SelectInfo} |
{error, Reason} when
Socket :: socket(),
@@ -2162,7 +2162,7 @@ recvmsg(Socket) ->
Flags :: recv_flags(),
MsgHdr :: msghdr(),
Reason :: term()
- ; (Socket, nowait) -> {ok, MsgHdr} |
+ ; (Socket, Timeout :: nowait) -> {ok, MsgHdr} |
{ok, SelectInfo} |
{error, Reason} when
Socket :: socket(),
--
cgit v1.2.3
From fb6147dbbf43b454527d07e4d007f61e6771131d Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 29 Apr 2019 18:29:05 +0200
Subject: [esock|test] Add multi async recvfrom UDP test case
Add recvfrom test case testing the abort message received
when "another" process closes a socket. Multiple readers
for good measure.
---
erts/emulator/test/socket_SUITE.erl | 364 +++++++++++++++++++++++++++++++++++-
1 file changed, 363 insertions(+), 1 deletion(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index d4478bd9e3..1dfd541f27 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -91,6 +91,8 @@
api_a_accept_cancel_tcp4/1,
api_a_recv_cancel_tcp4/1,
api_a_recvmsg_cancel_tcp4/1,
+ api_a_mrecvfrom_cancel_udp4/1,
+
%% *** API Options ***
api_opt_simple_otp_options/1,
@@ -682,7 +684,8 @@ api_async_cases() ->
api_a_recvmsg_cancel_udp4,
api_a_accept_cancel_tcp4,
api_a_recv_cancel_tcp4,
- api_a_recvmsg_cancel_tcp4
+ api_a_recvmsg_cancel_tcp4,
+ api_a_mrecvfrom_cancel_udp4
].
api_options_cases() ->
@@ -4572,6 +4575,365 @@ api_a_recv_cancel_tcp(InitState) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make multiple async (Timeout = nowait) call to recvfrom
+%% (from *several* processes), wait some time and then cancel.
+%% This should result in abort messages to the 'other' processes.
+%%
+api_a_mrecvfrom_cancel_udp4(suite) ->
+ [];
+api_a_mrecvfrom_cancel_udp4(doc) ->
+ [];
+api_a_mrecvfrom_cancel_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_mrecvfrom_cancel_udp4,
+ fun() ->
+ Recv = fun(Sock) ->
+ case socket:recvfrom(Sock, 0, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ recv => Recv},
+ ok = api_a_mrecv_cancel_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_a_mrecv_cancel_udp(InitState) ->
+ ServerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind socket (to local address)",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, Port} ->
+ {ok, State#{port => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, sock := Sock}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Sock),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "try recv request (with nowait, expect select)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, _, _} = SelectInfo} ->
+ {ok, State#{recv_select_info => SelectInfo}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await abort message",
+ cmd => fun(#{sock := Sock,
+ recv_select_info := {select, _, Ref}} = State) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ {error, {unexpected_select, Ref}};
+ {'$socket', Sock, abort, {Ref, closed}} ->
+ {ok, maps:remove(sock, State)}
+ after 5000 ->
+ ?SEV_IPRINT("message queue: ~p",
+ [process_info(self(),
+ messages)]),
+ {error, timeout}
+ end
+ end},
+ #{desc => "announce ready (abort)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, abort),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ State2 = maps:remove(tester, State),
+ State3 = maps:remove(recv_stag, State2),
+ State4 = maps:remove(recv_sref, State3),
+ State5 = maps:remove(req_src, State4),
+ {ok, State5};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ AltServerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ {Tester, Sock} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, sock => Sock}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "try recv request (with nowait, expect select)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, _, _} = SelectInfo} ->
+ {ok, State#{recv_select_info => SelectInfo}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await abort message",
+ cmd => fun(#{sock := Sock,
+ recv_select_info := {select, _, Ref}} = State) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ {error, {unexpected_select, Ref}};
+ {'$socket', Sock, abort, {Ref, closed}} ->
+ {ok, maps:remove(sock, State)}
+ after 5000 ->
+ ?SEV_IPRINT("message queue: ~p",
+ [process_info(self(),
+ messages)]),
+ {error, timeout}
+ end
+ end},
+ #{desc => "announce ready (abort)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, abort),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ State1 = maps:remove(recv_select_info, State),
+ State2 = maps:remove(tester, State1),
+ State3 = maps:remove(sock, State2),
+ {ok, State3};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{sock => Sock}}
+ end},
+
+ %% Start the alt-server 1
+ #{desc => "order alt-server 1 start",
+ cmd => fun(#{alt_server1 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await alt-server 1 ready (init)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, alt_server1, init)
+ end},
+
+ %% Start the alt-server 2
+ #{desc => "order alt-server 2 start",
+ cmd => fun(#{alt_server2 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await alt-server 2 ready (init)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, alt_server2, init)
+ end},
+
+
+ %% The actual test
+ #{desc => "order server continue (recv)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await server ready (recv select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, recv_select)
+ end},
+
+ #{desc => "order alt-server 1 continue (recv)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await alt-server 1 ready (recv select)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server1, recv_select)
+ end},
+
+ #{desc => "order alt-server 2 continue (recv)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await alt-server 2 ready (recv select)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server2, recv_select)
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "close the socket",
+ cmd => fun(#{sock := Sock} = _State) ->
+ socket:close(Sock)
+ end},
+
+ #{desc => "await server ready (abort)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, abort)
+ end},
+ #{desc => "await alt-server 1 ready (abort)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort)
+ end},
+ #{desc => "await alt-server 2 ready (abort)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort)
+ end},
+
+ %% Terminations
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(server, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, InitState),
+
+ i("start alt-server 1 evaluator"),
+ AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState),
+
+ i("start alt-server 2 evaluator"),
+ AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState),
+
+ i("start 'tester' evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ alt_server1 => AltServer1#ev.pid,
+ alt_server2 => AltServer2#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Server, Tester]).
+
+
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--
cgit v1.2.3
From 6de8ee374ec1b1fba4acef1eef851c2634996fa2 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 29 Apr 2019 18:38:29 +0200
Subject: [esock|test] Add multi async recvmsg UDP test case
Add recvmsg test case testing the abort message received
when "another" process closes a socket. Multiple readers
for good measure.
---
erts/emulator/test/socket_SUITE.erl | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 1dfd541f27..079ed76f41 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -92,6 +92,7 @@
api_a_recv_cancel_tcp4/1,
api_a_recvmsg_cancel_tcp4/1,
api_a_mrecvfrom_cancel_udp4/1,
+ api_a_mrecvmsg_cancel_udp4/1,
%% *** API Options ***
@@ -685,7 +686,8 @@ api_async_cases() ->
api_a_accept_cancel_tcp4,
api_a_recv_cancel_tcp4,
api_a_recvmsg_cancel_tcp4,
- api_a_mrecvfrom_cancel_udp4
+ api_a_mrecvfrom_cancel_udp4,
+ api_a_mrecvmsg_cancel_udp4
].
api_options_cases() ->
@@ -4604,6 +4606,35 @@ api_a_mrecvfrom_cancel_udp4(_Config) when is_list(_Config) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make multiple async (Timeout = nowait) call to recvmsg
+%% (from *several* processes), wait some time and then cancel.
+%% This should result in abort messages to the 'other' processes.
+%%
+api_a_mrecvmsg_cancel_udp4(suite) ->
+ [];
+api_a_mrecvmsg_cancel_udp4(doc) ->
+ [];
+api_a_mrecvmsg_cancel_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_mrecvmsg_cancel_udp4,
+ fun() ->
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ recv => Recv},
+ ok = api_a_mrecv_cancel_udp(InitState)
+ end).
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
api_a_mrecv_cancel_udp(InitState) ->
--
cgit v1.2.3
From 39d587c6524ad55f34eda0ffc99252f54930ff50 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 30 Apr 2019 11:53:47 +0200
Subject: [esock|test] Add multi async accept TCP test case
Add accept test case testing the abort message received
when "another" process closes a socket. Multiple accept(ors)
for good measure.
---
erts/emulator/test/socket_SUITE.erl | 456 +++++++++++++++++++++++++++++++++++-
1 file changed, 451 insertions(+), 5 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 079ed76f41..bd5d408add 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -93,6 +93,7 @@
api_a_recvmsg_cancel_tcp4/1,
api_a_mrecvfrom_cancel_udp4/1,
api_a_mrecvmsg_cancel_udp4/1,
+ api_a_maccept_cancel_tcp4/1,
%% *** API Options ***
@@ -687,7 +688,8 @@ api_async_cases() ->
api_a_recv_cancel_tcp4,
api_a_recvmsg_cancel_tcp4,
api_a_mrecvfrom_cancel_udp4,
- api_a_mrecvmsg_cancel_udp4
+ api_a_mrecvmsg_cancel_udp4,
+ api_a_maccept_cancel_tcp4
].
api_options_cases() ->
@@ -4465,6 +4467,16 @@ api_a_recv_cancel_tcp(InitState) ->
_MRef = erlang:monitor(process, Pid),
ok
end},
+ #{desc => "monitor alt-server 1",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor alt-server 2",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
%% Start the server
#{desc => "order server start",
@@ -4579,7 +4591,7 @@ api_a_recv_cancel_tcp(InitState) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Basically we make multiple async (Timeout = nowait) call to recvfrom
+%% Basically we make multiple async (Timeout = nowait) call(s) to recvfrom
%% (from *several* processes), wait some time and then cancel.
%% This should result in abort messages to the 'other' processes.
%%
@@ -4608,7 +4620,7 @@ api_a_mrecvfrom_cancel_udp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Basically we make multiple async (Timeout = nowait) call to recvmsg
+%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
%% (from *several* processes), wait some time and then cancel.
%% This should result in abort messages to the 'other' processes.
%%
@@ -4811,6 +4823,7 @@ api_a_mrecv_cancel_udp(InitState) ->
cmd => fun(#{tester := Tester} = State) ->
case ?SEV_AWAIT_TERMINATE(Tester, tester) of
ok ->
+ ?SEV_IPRINT("terminating"),
State1 = maps:remove(recv_select_info, State),
State2 = maps:remove(tester, State1),
State3 = maps:remove(sock, State2),
@@ -4922,6 +4935,38 @@ api_a_mrecv_cancel_udp(InitState) ->
end},
%% Terminations
+ #{desc => "order alt-server 2 to terminate",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await alt-server 2 termination",
+ cmd => fun(#{alt_server2 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(alt_server2, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "order alt-server 1 to terminate",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await alt-server 1 termination",
+ cmd => fun(#{alt_server1 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(alt_server1, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
#{desc => "order server to terminate",
cmd => fun(#{server := Pid} = _State) ->
?SEV_ANNOUNCE_TERMINATE(Pid),
@@ -4958,10 +5003,411 @@ api_a_mrecv_cancel_udp(InitState) ->
alt_server2 => AltServer2#ev.pid},
Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
- i("await evaluator"),
- ok = ?SEV_AWAIT_FINISH([Server, Tester]).
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Tester]).
+
+
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make multiple async (Timeout = nowait) call(s) to accept
+%% (from *several* processes), wait some time and then cancel,
+%% This should result in abort messages to the 'other' processes.
+%%
+api_a_maccept_cancel_tcp4(suite) ->
+ [];
+api_a_maccept_cancel_tcp4(doc) ->
+ [];
+api_a_maccept_cancel_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_maccept_cancel_tcp4,
+ fun() ->
+ Accept = fun(Sock) ->
+ case socket:accept(Sock, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ accept => Accept},
+ ok = api_a_maccept_cancel_tcp(InitState)
+ end).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_a_maccept_cancel_tcp(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create listen socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{lsock := LSock, lsa := LSA} = State) ->
+ case socket:bind(LSock, LSA) of
+ {ok, Port} ->
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, lsock := Sock}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Sock),
+ ok
+ end},
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "await connection (nowait)",
+ cmd => fun(#{lsock := LSock, accept := Accept} = State) ->
+ case Accept(LSock) of
+ {ok, {select, T, R} = SelectInfo} ->
+ ?SEV_IPRINT("accept select: "
+ "~n T: ~p"
+ "~n R: ~p", [T, R]),
+ {ok, State#{accept_select_info => SelectInfo}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept_select),
+ ok
+ end},
+ #{desc => "await select message (without success)",
+ cmd => fun(#{lsock := Sock,
+ accept_select_info := {select, _, Ref}} = State) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ {error, {unexpected_select, Ref}};
+ {'$socket', Sock, abort, {Ref, closed}} ->
+ {ok, maps:remove(lsock, State)}
+ after 5000 ->
+ ?SEV_IPRINT("message queue: ~p",
+ [process_info(self(),
+ messages)]),
+ {error, timeout}
+ end
+ end},
+ #{desc => "announce ready (abort)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, abort),
+ ok
+ end},
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ AltServerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ {Tester, Sock} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, lsock => Sock}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "try accept request (with nowait, expect select)",
+ cmd => fun(#{lsock := Sock, accept := Accept} = State) ->
+ case Accept(Sock) of
+ {ok, {select, _, _} = SelectInfo} ->
+ {ok, State#{accept_select_info => SelectInfo}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept_select),
+ ok
+ end},
+ #{desc => "await abort message",
+ cmd => fun(#{lsock := Sock,
+ accept_select_info := {select, _, Ref}} = State) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ {error, {unexpected_select, Ref}};
+ {'$socket', Sock, abort, {Ref, closed}} ->
+ {ok, maps:remove(sock, State)}
+ after 5000 ->
+ ?SEV_IPRINT("message queue: ~p",
+ [process_info(self(),
+ messages)]),
+ {error, timeout}
+ end
+ end},
+ #{desc => "announce ready (abort)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, abort),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ ?SEV_IPRINT("terminating"),
+ State1 = maps:remove(tester, State),
+ State2 = maps:remove(accept_select_info, State1),
+ State3 = maps:remove(lsock, State2),
+ {ok, State3};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor alt-server 1",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor alt-server 2",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{sock => Sock}}
+ end},
+
+ %% Start the alt-server 1
+ #{desc => "order alt-server 1 start",
+ cmd => fun(#{alt_server1 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await alt-server 1 ready (init)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, alt_server1, init)
+ end},
+
+ %% Start the alt-server 2
+ #{desc => "order alt-server 2 start",
+ cmd => fun(#{alt_server2 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await alt-server 2 ready (init)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, alt_server2, init)
+ end},
+
+
+ %% *** The actual test ***
+ #{desc => "order server continue (accept)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ #{desc => "await server ready (accept select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, accept_select)
+ end},
+
+ #{desc => "order alt-server 1 continue (accept)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ #{desc => "await alt-server 1 ready (accept select)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server1, accept_select)
+ end},
+
+ #{desc => "order alt-server 2 continue (accept)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ #{desc => "await alt-server 2 ready (accept select)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server2, accept_select)
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "close the socket",
+ cmd => fun(#{sock := Sock} = _State) ->
+ socket:close(Sock)
+ end},
+
+ #{desc => "await server ready (abort)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, abort)
+ end},
+ #{desc => "await alt-server 1 ready (abort)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort)
+ end},
+ #{desc => "await alt-server 2 ready (abort)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort)
+ end},
+
+
+ %% *** Termination ***
+ #{desc => "order alt-server 2 to terminate",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await alt-server 2 termination",
+ cmd => fun(#{alt_server2 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(alt_server2, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "order alt-server 1 to terminate",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await alt-server 1 termination",
+ cmd => fun(#{alt_server1 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(alt_server1, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, InitState),
+
+ i("start alt-server 1 evaluator"),
+ AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState),
+
+ i("start alt-server 2 evaluator"),
+ AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ alt_server1 => AltServer1#ev.pid,
+ alt_server2 => AltServer2#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Tester]).
--
cgit v1.2.3
From 2d6f0763ddf80614d792cad96df38d0698eeb037 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 30 Apr 2019 12:43:44 +0200
Subject: [esock|test] Add multi async recv and recvmsg TCP test case(s)
Add recv and recvmsg test case testing the abort message
received when "another" process closes a socket. Multiple
readers for good measure.
---
erts/emulator/test/socket_SUITE.erl | 580 +++++++++++++++++++++++++++++++++++-
1 file changed, 579 insertions(+), 1 deletion(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index bd5d408add..52854cb7ad 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -94,6 +94,8 @@
api_a_mrecvfrom_cancel_udp4/1,
api_a_mrecvmsg_cancel_udp4/1,
api_a_maccept_cancel_tcp4/1,
+ api_a_mrecv_cancel_tcp4/1,
+ api_a_mrecvmsg_cancel_tcp4/1,
%% *** API Options ***
@@ -689,7 +691,9 @@ api_async_cases() ->
api_a_recvmsg_cancel_tcp4,
api_a_mrecvfrom_cancel_udp4,
api_a_mrecvmsg_cancel_udp4,
- api_a_maccept_cancel_tcp4
+ api_a_maccept_cancel_tcp4,
+ api_a_mrecv_cancel_tcp4,
+ api_a_mrecvmsg_cancel_tcp4
].
api_options_cases() ->
@@ -5412,6 +5416,580 @@ api_a_maccept_cancel_tcp(InitState) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make multiple async (Timeout = nowait) call(s) to recv
+%% (from *several* processes), wait some time and then cancel,
+%% This should result in abort messages to the 'other' processes.
+%%
+api_a_mrecv_cancel_tcp4(suite) ->
+ [];
+api_a_mrecv_cancel_tcp4(doc) ->
+ [];
+api_a_mrecv_cancel_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_mrecv_cancel_tcp4,
+ fun() ->
+ Recv = fun(Sock) ->
+ socket:recv(Sock, 0, nowait)
+ end,
+ InitState = #{domain => inet,
+ recv => Recv},
+ ok = api_a_mrecv_cancel_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
+%% (from *several* processes), wait some time and then cancel,
+%% This should result in abort messages to the 'other' processes.
+%%
+api_a_mrecvmsg_cancel_tcp4(suite) ->
+ [];
+api_a_mrecvmsg_cancel_tcp4(doc) ->
+ [];
+api_a_mrecvmsg_cancel_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_mrecvmsg_cancel_tcp4,
+ fun() ->
+ Recv = fun(Sock) ->
+ socket:recvmsg(Sock, nowait)
+ end,
+ InitState = #{domain => inet,
+ recv => Recv},
+ ok = api_a_mrecv_cancel_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_a_mrecv_cancel_tcp(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create listen socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{lsock := LSock, lsa := LSA} = State) ->
+ case socket:bind(LSock, LSA) of
+ {ok, Port} ->
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, lport := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "await connection (nowait)",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock) of
+ {ok, CSock} ->
+ {ok, State#{csock => CSock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester, csock := Sock}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept, Sock),
+ ok
+ end},
+
+ #{desc => "await continue (nowait recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "try recv request (with nowait, expect select)",
+ cmd => fun(#{csock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, T, R} = SelectInfo} ->
+ ?SEV_IPRINT("recv select: "
+ "~n Tag: ~p"
+ "~n Ref: ~p", [T, R]),
+ {ok, State#{recv_select_info => SelectInfo}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await select message",
+ cmd => fun(#{csock := Sock,
+ recv_select_info := {select, _, Ref}} = State) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ {error, {unexpected_select, Ref}};
+ {'$socket', Sock, abort, {Ref, closed}} ->
+ {ok, maps:remove(sock, State)}
+ after 5000 ->
+ ok
+ end
+ end},
+ #{desc => "announce ready (abort)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, abort),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := Sock}) ->
+ socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ AltServerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ {Tester, Sock} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, sock => Sock}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "try recv request (with nowait, expect select)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {select, _, _} = SelectInfo} ->
+ {ok, State#{recv_select_info => SelectInfo}};
+ {ok, X} ->
+ {error, {unexpected_select_info, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_select),
+ ok
+ end},
+ #{desc => "await abort message",
+ cmd => fun(#{sock := Sock,
+ recv_select_info := {select, _, Ref}} = State) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ {error, {unexpected_select, Ref}};
+ {'$socket', Sock, abort, {Ref, closed}} ->
+ {ok, maps:remove(sock, State)}
+ after 5000 ->
+ ?SEV_IPRINT("message queue: ~p",
+ [process_info(self(),
+ messages)]),
+ {error, timeout}
+ end
+ end},
+ #{desc => "announce ready (abort)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, abort),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ ?SEV_IPRINT("terminating"),
+ State1 = maps:remove(recv_select_info, State),
+ State2 = maps:remove(tester, State1),
+ State3 = maps:remove(sock, State2),
+ {ok, State3};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ ClientSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Port} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, server_port => Port}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ SSA = LSA#{port => Port},
+ {ok, State#{local_sa => LSA, server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (connect)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
+ end},
+ #{desc => "connect to server",
+ cmd => fun(#{sock := Sock, server_sa := SSA}) ->
+ socket:connect(Sock, SSA)
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock}) ->
+ socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor alt-server 1",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor alt-server 2",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client",
+ cmd => fun(#{client := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+
+ %% Start the client
+ #{desc => "order client start",
+ cmd => fun(#{client := Pid, server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Port),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, init)
+ end},
+
+ #{desc => "order server to continue (with accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept),
+ ok
+ end},
+ #{desc => "order client to continue (connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
+ ok
+ end},
+ #{desc => "await client ready (connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, client, connect)
+ end},
+ #{desc => "await server ready (accept)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, accept),
+ {ok, State#{sock => Sock}}
+ end},
+
+ %% Start the alt server 1
+ #{desc => "order alt-server 1 start",
+ cmd => fun(#{alt_server1 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await alt-server 1 ready (init)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server1, init)
+ end},
+
+ %% Start the alt server 2
+ #{desc => "order alt-server 2 start",
+ cmd => fun(#{alt_server2 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await alt-server 2 ready (init)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server2, init)
+ end},
+
+
+ %% *** The actual test ***
+ #{desc => "order server continue (recv)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await server ready (recv select)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, recv_select)
+ end},
+
+ #{desc => "order alt-server 1 continue (recv)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await alt-server 1 ready (recv select)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server1, recv_select)
+ end},
+
+ #{desc => "order alt-server 2 continue (recv)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await alt-server 2 ready (recv select)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server2, recv_select)
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "close the socket",
+ cmd => fun(#{sock := Sock} = _State) ->
+ socket:close(Sock)
+ end},
+
+ #{desc => "await server ready (abort)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, abort)
+ end},
+ #{desc => "await alt-server 1 ready (abort)",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort)
+ end},
+ #{desc => "await alt-server 2 ready (abort)",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort)
+ end},
+
+ %% Terminations
+ #{desc => "order client to terminate",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client termination",
+ cmd => fun(#{client := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client, State),
+ {ok, State1}
+ end},
+
+ #{desc => "order alt-server 2 to terminate",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await alt-server 2 termination",
+ cmd => fun(#{alt_server2 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(alt_server2, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "order alt-server 1 to terminate",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await alt-server 1 termination",
+ cmd => fun(#{alt_server1 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(alt_server1, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(server, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, InitState),
+
+ i("start alt-server 1 evaluator"),
+ AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState),
+
+ i("start alt-server 2 evaluator"),
+ AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState),
+
+ i("start client evaluator"),
+ Client = ?SEV_START("client", ClientSeq, InitState),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ alt_server1 => AltServer1#ev.pid,
+ alt_server2 => AltServer2#ev.pid,
+ client => Client#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Client, Tester]).
+
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
--
cgit v1.2.3
From 043cb47a1967a63b316c7bd4cb4232f2e9b0f895 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 30 Apr 2019 15:06:15 +0200
Subject: [esock|doc] Add text about the abort message
If another process closes the socket, the caller will
receive an abort message instead (of the select message).
OTP-15731
---
erts/doc/src/socket.xml | 16 ++++++++++++++--
erts/doc/src/socket_usage.xml | 13 ++++++++++++-
erts/preloaded/ebin/socket.beam | Bin 74628 -> 74636 bytes
erts/preloaded/src/socket.erl | 2 +-
4 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index 3ff0eb4e1b..f9dd9575f4 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -44,15 +44,27 @@
Some functions allow for an asynchronous call.
This is achieved by setting the Timeout argument to
nowait. For instance, if calling the
- recv/3
+ recv/3
function with Timeout set to nowait (recv(Sock, 0, nowait))
when there is actually nothing to read, it will return with
{ok, SelectInfo}. When data eventually arrives a 'select' message
will be sent to the caller:
- {'$socket', Sock, select, Info}. The caller can now make another
+
+ - {'$socket', Sock, select, Info}
+
+ The caller can now make another
call to the recv function and now expect data.
Note that all other users are locked out until the
'current user' has called the function (recv in this case).
+ Another message the user must be prepared for (when making asynchronous
+ calls) is the abort message:
+
+ - {'$socket', Sock, abort, Reason}
+
+ This message indicates
+ that the (asynchronous) operation has been aborted.
+ If, for instance, the socket has been
+ closed (by another process), Reason will be {Ref, closed}.
There is currently no support for Windows.
diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml
index 3d18bd1af7..fcfe9b54ec 100644
--- a/erts/doc/src/socket_usage.xml
+++ b/erts/doc/src/socket_usage.xml
@@ -56,8 +56,19 @@
when there is actually nothing to read, it will return with
{ok, SelectInfo}. When data eventually arrives a 'select message'
will be sent to the caller:
- {'$socket', Sock, select, Info}. The caller can make another
+
+ - {'$socket', Sock, select, Info}
+
+ The caller can then make another
call to the recv function and now expect data.
+ The user must also be prepared to receive an abort message:
+
+ - {'$socket', Sock, abort, Reason}
+
+ If the operation is aborted
+ for whatever reason (e.g. if the socket is closed "by someone else").
+ The Reason part contains the abort reason (in case the socket
+ has been closed Reason = {Ref, closed}).
Note that all other users are locked out until the
'current user' has called the function (recv in this case). So either
immediately call the function or
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 892aab1fa0..7f5a27c980 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 59bef4b3b7..ec63eeba64 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1794,7 +1794,7 @@ recv(Socket, Length) ->
Flags :: recv_flags(),
Data :: binary(),
Reason :: term()
- ; (Socket, Length, nowait) -> {ok, Data} |
+ ; (Socket, Length, Timeout :: nowait) -> {ok, Data} |
{ok, SelectInfo} |
{ok, {Data, SelectInfo}} |
{error, Reason} when
--
cgit v1.2.3
From 50620e6a44a9e0dc32d51a436b0cc05c44884d1b Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Thu, 2 May 2019 12:51:52 +0200
Subject: [esock] Changed return value for a selected async call
The return value for an async call (Timeout = nowait) will now
(normally) result in a {select, SelectInfo} instead (if a select
was performed).
OTP-15731
---
erts/emulator/test/socket_SUITE.erl | 84 ++++++++++++---------
.../emulator/test/socket_test_ttest_tcp_server.erl | 2 +-
erts/preloaded/ebin/socket.beam | Bin 74636 -> 74796 bytes
erts/preloaded/src/socket.erl | 66 ++++++++--------
4 files changed, 83 insertions(+), 69 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 52854cb7ad..66cbdda9c7 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -2708,6 +2708,8 @@ api_a_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) ->
{ok, {Source, Data}};
{ok, _} = OK ->
OK;
+ {select, _} = SELECT ->
+ SELECT;
{error, _} = ERROR ->
ERROR
end
@@ -2776,7 +2778,10 @@ api_a_send_and_recv_udp(InitState) ->
#{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, Tag, RecvRef}} ->
+ {select, {select_info, Tag, RecvRef}} ->
+ ?SEV_IPRINT("expected select: "
+ "~n Tag: ~p"
+ "~n Ref: ~p", [Tag, RecvRef]),
{ok, State#{recv_stag => Tag,
recv_sref => RecvRef}};
{ok, X} ->
@@ -2795,6 +2800,9 @@ api_a_send_and_recv_udp(InitState) ->
receive
{'$socket', Sock, select, RecvRef} ->
ok
+ after 5000 ->
+ ?SEV_EPRINT("message queue: ~p", [mq()]),
+ {error, timeout}
end
end},
#{desc => "announce ready (select)",
@@ -2919,7 +2927,7 @@ api_a_send_and_recv_udp(InitState) ->
#{desc => "try recv reply (with nowait)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, Tag, RecvRef}} ->
+ {select, {select_info, Tag, RecvRef}} ->
{ok, State#{recv_stag => Tag,
recv_sref => RecvRef}};
{ok, X} ->
@@ -3188,8 +3196,10 @@ api_a_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) ->
{ok, #{addr := undefined,
iov := [Data]}} ->
{ok, Data};
- {ok, _} = OK ->
+ {ok, _} = OK ->
OK;
+ {select, _} = SELECT ->
+ SELECT;
{error, _} = ERROR ->
ERROR
end
@@ -3261,7 +3271,7 @@ api_a_send_and_recv_tcp(InitState) ->
#{desc => "await connection (nowait)",
cmd => fun(#{lsock := LSock} = State) ->
case socket:accept(LSock, nowait) of
- {ok, {select, Tag, Ref}} ->
+ {select, {select_info, Tag, Ref}} ->
?SEV_IPRINT("accept select: "
"~n Tag: ~p"
"~n Ref: ~p", [Tag, Ref]),
@@ -3314,7 +3324,7 @@ api_a_send_and_recv_tcp(InitState) ->
#{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{csock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, Tag, Ref}} ->
+ {select, {select_info, Tag, Ref}} ->
?SEV_IPRINT("recv select: "
"~n Tag: ~p"
"~n Ref: ~p", [Tag, Ref]),
@@ -3473,7 +3483,7 @@ api_a_send_and_recv_tcp(InitState) ->
#{desc => "try recv reply (with nowait, expect select)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, Tag, Ref}} ->
+ {select, {select_info, Tag, Ref}} ->
?SEV_IPRINT("recv select: "
"~n Tag: ~p"
"~n Ref: ~p", [Tag, Ref]),
@@ -3715,6 +3725,8 @@ api_a_recvfrom_cancel_udp4(_Config) when is_list(_Config) ->
case socket:recvfrom(Sock, 0, nowait) of
{ok, _} = OK ->
OK;
+ {select, _} = SELECT ->
+ SELECT;
{error, _} = ERROR ->
ERROR
end
@@ -3743,6 +3755,8 @@ api_a_recvmsg_cancel_udp4(_Config) when is_list(_Config) ->
case socket:recvmsg(Sock, nowait) of
{ok, _} = OK ->
OK;
+ {select, _} = SELECT ->
+ SELECT;
{error, _} = ERROR ->
ERROR
end
@@ -3810,7 +3824,7 @@ api_a_recv_cancel_udp(InitState) ->
#{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, _, _} = SelectInfo} ->
+ {select, SelectInfo} ->
{ok, State#{recv_select_info => SelectInfo}};
{ok, X} ->
{error, {unexpected_select_info, X}};
@@ -3975,6 +3989,8 @@ api_a_accept_cancel_tcp4(_Config) when is_list(_Config) ->
case socket:accept(Sock, nowait) of
{ok, _} = OK ->
OK;
+ {select, _} = SELECT ->
+ SELECT;
{error, _} = ERROR ->
ERROR
end
@@ -4047,7 +4063,7 @@ api_a_accept_cancel_tcp(InitState) ->
#{desc => "await connection (nowait)",
cmd => fun(#{lsock := LSock, accept := Accept} = State) ->
case Accept(LSock) of
- {ok, {select, T, R} = SelectInfo} ->
+ {select, {select_info, T, R} = SelectInfo} ->
?SEV_IPRINT("accept select: "
"~n T: ~p"
"~n R: ~p", [T, R]),
@@ -4311,7 +4327,7 @@ api_a_recv_cancel_tcp(InitState) ->
#{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{csock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, T, R} = SelectInfo} ->
+ {select, {select_info, T, R} = SelectInfo} ->
?SEV_IPRINT("recv select: "
"~n Tag: ~p"
"~n Ref: ~p", [T, R]),
@@ -4611,6 +4627,8 @@ api_a_mrecvfrom_cancel_udp4(_Config) when is_list(_Config) ->
case socket:recvfrom(Sock, 0, nowait) of
{ok, _} = OK ->
OK;
+ {select, _} = SELECT ->
+ SELECT;
{error, _} = ERROR ->
ERROR
end
@@ -4640,6 +4658,8 @@ api_a_mrecvmsg_cancel_udp4(_Config) when is_list(_Config) ->
case socket:recvmsg(Sock, nowait) of
{ok, _} = OK ->
OK;
+ {select, _} = SELECT ->
+ SELECT;
{error, _} = ERROR ->
ERROR
end
@@ -4707,7 +4727,7 @@ api_a_mrecv_cancel_udp(InitState) ->
#{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, _, _} = SelectInfo} ->
+ {select, SelectInfo} ->
{ok, State#{recv_select_info => SelectInfo}};
{ok, X} ->
{error, {unexpected_select_info, X}};
@@ -4722,16 +4742,14 @@ api_a_mrecv_cancel_udp(InitState) ->
end},
#{desc => "await abort message",
cmd => fun(#{sock := Sock,
- recv_select_info := {select, _, Ref}} = State) ->
+ recv_select_info := {select_info, _, Ref}} = State) ->
receive
{'$socket', Sock, select, Ref} ->
{error, {unexpected_select, Ref}};
{'$socket', Sock, abort, {Ref, closed}} ->
{ok, maps:remove(sock, State)}
after 5000 ->
- ?SEV_IPRINT("message queue: ~p",
- [process_info(self(),
- messages)]),
+ ?SEV_EPRINT("message queue: ~p", [mq()]),
{error, timeout}
end
end},
@@ -4788,7 +4806,7 @@ api_a_mrecv_cancel_udp(InitState) ->
#{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, _, _} = SelectInfo} ->
+ {select, SelectInfo} ->
{ok, State#{recv_select_info => SelectInfo}};
{ok, X} ->
{error, {unexpected_select_info, X}};
@@ -4803,16 +4821,14 @@ api_a_mrecv_cancel_udp(InitState) ->
end},
#{desc => "await abort message",
cmd => fun(#{sock := Sock,
- recv_select_info := {select, _, Ref}} = State) ->
+ recv_select_info := {select_info, _, Ref}} = State) ->
receive
{'$socket', Sock, select, Ref} ->
{error, {unexpected_select, Ref}};
{'$socket', Sock, abort, {Ref, closed}} ->
{ok, maps:remove(sock, State)}
after 5000 ->
- ?SEV_IPRINT("message queue: ~p",
- [process_info(self(),
- messages)]),
+ ?SEV_EPRINT("message queue: ~p", [mq()]),
{error, timeout}
end
end},
@@ -5033,6 +5049,8 @@ api_a_maccept_cancel_tcp4(_Config) when is_list(_Config) ->
case socket:accept(Sock, nowait) of
{ok, _} = OK ->
OK;
+ {select, _} = SELECT ->
+ SELECT;
{error, _} = ERROR ->
ERROR
end
@@ -5106,7 +5124,7 @@ api_a_maccept_cancel_tcp(InitState) ->
#{desc => "await connection (nowait)",
cmd => fun(#{lsock := LSock, accept := Accept} = State) ->
case Accept(LSock) of
- {ok, {select, T, R} = SelectInfo} ->
+ {select, {select_info, T, R} = SelectInfo} ->
?SEV_IPRINT("accept select: "
"~n T: ~p"
"~n R: ~p", [T, R]),
@@ -5124,16 +5142,14 @@ api_a_maccept_cancel_tcp(InitState) ->
end},
#{desc => "await select message (without success)",
cmd => fun(#{lsock := Sock,
- accept_select_info := {select, _, Ref}} = State) ->
+ accept_select_info := {select_info, _, Ref}} = State) ->
receive
{'$socket', Sock, select, Ref} ->
{error, {unexpected_select, Ref}};
{'$socket', Sock, abort, {Ref, closed}} ->
{ok, maps:remove(lsock, State)}
after 5000 ->
- ?SEV_IPRINT("message queue: ~p",
- [process_info(self(),
- messages)]),
+ ?SEV_EPRINT("message queue: ~p", [mq()]),
{error, timeout}
end
end},
@@ -5186,7 +5202,7 @@ api_a_maccept_cancel_tcp(InitState) ->
#{desc => "try accept request (with nowait, expect select)",
cmd => fun(#{lsock := Sock, accept := Accept} = State) ->
case Accept(Sock) of
- {ok, {select, _, _} = SelectInfo} ->
+ {select, SelectInfo} ->
{ok, State#{accept_select_info => SelectInfo}};
{ok, X} ->
{error, {unexpected_select_info, X}};
@@ -5201,16 +5217,14 @@ api_a_maccept_cancel_tcp(InitState) ->
end},
#{desc => "await abort message",
cmd => fun(#{lsock := Sock,
- accept_select_info := {select, _, Ref}} = State) ->
+ accept_select_info := {select_info, _, Ref}} = State) ->
receive
{'$socket', Sock, select, Ref} ->
{error, {unexpected_select, Ref}};
{'$socket', Sock, abort, {Ref, closed}} ->
{ok, maps:remove(sock, State)}
after 5000 ->
- ?SEV_IPRINT("message queue: ~p",
- [process_info(self(),
- messages)]),
+ ?SEV_EPRINT("message queue: ~p", [mq()]),
{error, timeout}
end
end},
@@ -5544,7 +5558,7 @@ api_a_mrecv_cancel_tcp(InitState) ->
#{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{csock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, T, R} = SelectInfo} ->
+ {select, {select_info, T, R} = SelectInfo} ->
?SEV_IPRINT("recv select: "
"~n Tag: ~p"
"~n Ref: ~p", [T, R]),
@@ -5562,7 +5576,7 @@ api_a_mrecv_cancel_tcp(InitState) ->
end},
#{desc => "await select message",
cmd => fun(#{csock := Sock,
- recv_select_info := {select, _, Ref}} = State) ->
+ recv_select_info := {select_info, _, Ref}} = State) ->
receive
{'$socket', Sock, select, Ref} ->
{error, {unexpected_select, Ref}};
@@ -5624,7 +5638,7 @@ api_a_mrecv_cancel_tcp(InitState) ->
#{desc => "try recv request (with nowait, expect select)",
cmd => fun(#{sock := Sock, recv := Recv} = State) ->
case Recv(Sock) of
- {ok, {select, _, _} = SelectInfo} ->
+ {select, SelectInfo} ->
{ok, State#{recv_select_info => SelectInfo}};
{ok, X} ->
{error, {unexpected_select_info, X}};
@@ -5639,16 +5653,14 @@ api_a_mrecv_cancel_tcp(InitState) ->
end},
#{desc => "await abort message",
cmd => fun(#{sock := Sock,
- recv_select_info := {select, _, Ref}} = State) ->
+ recv_select_info := {select_info, _, Ref}} = State) ->
receive
{'$socket', Sock, select, Ref} ->
{error, {unexpected_select, Ref}};
{'$socket', Sock, abort, {Ref, closed}} ->
{ok, maps:remove(sock, State)}
after 5000 ->
- ?SEV_IPRINT("message queue: ~p",
- [process_info(self(),
- messages)]),
+ ?SEV_EPRINT("message queue: ~p", [mq()]),
{error, timeout}
end
end},
diff --git a/erts/emulator/test/socket_test_ttest_tcp_server.erl b/erts/emulator/test/socket_test_ttest_tcp_server.erl
index f0a2ae73ec..27b561d4b7 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_server.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_server.erl
@@ -169,7 +169,7 @@ server_init(Starter, Parent, Transport, Active) ->
end.
process_transport(Mod, _) when is_atom(Mod) ->
- {Mod, false, fun(Port) -> Mod:listen(Port) end, infinity};
+ {Mod, fun(Port) -> Mod:listen(Port) end, infinity};
process_transport({Mod, #{stats_interval := T} = Opts}, Active)
when (Active =/= false) ->
{Mod, fun(Port) -> Mod:listen(Port, Opts#{stats_to => self()}) end, T};
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 7f5a27c980..fb4d9850e5 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index ec63eeba64..60f162d37d 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -593,9 +593,13 @@
-type select_tag() :: atom().
-type select_ref() :: reference().
--type select_info() :: {select, select_tag(), select_ref()}.
--define(SELECT_INFO(T, R), {select, T, R}).
+-record(select_info, {tag :: select_tag(), ref :: select_ref()}).
+
+-type select_info() :: #select_info{}.
+
+-define(SELECT_INFO(T, R), #select_info{tag = T, ref = R}).
+-define(SELECT(T, R), {select, ?SELECT_INFO(T, R)}).
%% This is used in messages sent from the nif-code to erlang processes:
@@ -1283,7 +1287,7 @@ accept(Socket) ->
-spec accept(LSocket, nowait) ->
{ok, Socket} |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
LSocket :: socket(),
Socket :: socket(),
@@ -1314,7 +1318,7 @@ do_accept(LSockRef, Timeout) ->
{error, eagain} when (Timeout =:= nowait) ->
- {ok, ?SELECT_INFO(accept, AccRef)};
+ ?SELECT(accept, AccRef);
{error, eagain} ->
@@ -1360,7 +1364,7 @@ send(Socket, Data) ->
Flags :: send_flags(),
Reason :: term()
; (Socket, Data, Timeout :: nowait) -> ok |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{ok, {RestData, SelectInfo}} |
{error, Reason} when
Socket :: socket(),
@@ -1380,7 +1384,7 @@ send(Socket, Data, Timeout) ->
send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, Timeout).
-spec send(Socket, Data, Flags, nowait) -> ok |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{ok, {RestData, SelectInfo}} |
{error, Reason} when
Socket :: socket(),
@@ -1418,6 +1422,8 @@ do_send(SockRef, Data, EFlags, Timeout) ->
{ok, Written} when (Timeout =:= nowait) ->
<<_:Written/binary, Rest/binary>> = Data,
+ %% We are partially done, but the user don't want to wait (here)
+ %% for completion
{ok, {Rest, ?SELECT_INFO(send, SendRef)}};
@@ -1445,7 +1451,7 @@ do_send(SockRef, Data, EFlags, Timeout) ->
{error, eagain} when (Timeout =:= nowait) ->
- {ok, ?SELECT_INFO(send, SendRef)};
+ ?SELECT(send, SendRef);
{error, eagain} ->
@@ -1489,7 +1495,7 @@ sendto(Socket, Data, Dest) ->
Flags :: send_flags(),
Reason :: term()
; (Socket, Data, Dest, Timeout :: nowait) -> ok |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
Socket :: socket(),
Data :: iodata(),
@@ -1510,7 +1516,7 @@ sendto(Socket, Data, Dest, Timeout) ->
-spec sendto(Socket, Data, Dest, Flags, nowait) -> ok |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
Socket :: socket(),
Data :: binary(),
@@ -1584,7 +1590,7 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) ->
{error, eagain} when (Timeout =:= nowait) ->
- {ok, ?SELECT_INFO(sendto, SendRef)};
+ ?SELECT(sendto, SendRef);
{error, eagain} ->
@@ -1631,7 +1637,7 @@ sendmsg(Socket, MsgHdr) ->
Flags :: send_flags(),
Reason :: term()
; (Socket, MsgHdr, Timeout :: nowait) -> ok |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
Socket :: socket(),
MsgHdr :: msghdr(),
@@ -1653,7 +1659,7 @@ sendmsg(Socket, MsgHdr, Timeout)
-spec sendmsg(Socket, MsgHdr, Flags, nowait) ->
ok |
{ok, Remaining} |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
Socket :: socket(),
MsgHdr :: msghdr(),
@@ -1711,7 +1717,7 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) ->
{error, eagain} when (Timeout =:= nowait) ->
- {ok, ?SELECT_INFO(sendmsg, SendRef)};
+ ?SELECT(sendmsg, SendRef);
{error, eagain} ->
@@ -1795,7 +1801,7 @@ recv(Socket, Length) ->
Data :: binary(),
Reason :: term()
; (Socket, Length, Timeout :: nowait) -> {ok, Data} |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{ok, {Data, SelectInfo}} |
{error, Reason} when
Socket :: socket(),
@@ -1817,7 +1823,7 @@ recv(Socket, Length, Timeout) ->
recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
-spec recv(Socket, Length, Flags, nowait) -> {ok, Data} |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{ok, {Data, SelectInfo}} |
{error, Reason} when
Socket :: socket(),
@@ -1926,14 +1932,10 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
%% The user will be informed that there is something to read
%% via the select socket message (see below).
+ {error, eagain} when (Timeout =:= nowait) andalso (size(Acc) =:= 0) ->
+ ?SELECT(recv, RecvRef);
{error, eagain} when (Timeout =:= nowait) ->
- SelectInfo = ?SELECT_INFO(recv, RecvRef),
- if
- (size(Acc) =:= 0) ->
- {ok, SelectInfo};
- true ->
- {ok, {Acc, SelectInfo}}
- end;
+ {ok, {Acc, ?SELECT_INFO(recv, RecvRef)}};
%% We return with the accumulated binary (if its non-empty)
@@ -2028,7 +2030,7 @@ recvfrom(Socket, BufSz) ->
-spec recvfrom(Socket, Flags, nowait) ->
{ok, {Source, Data}} |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
Socket :: socket(),
Flags :: recv_flags(),
@@ -2055,7 +2057,7 @@ recvfrom(Socket, BufSz) ->
Reason :: term()
; (Socket, BufSz, nowait) ->
{ok, {Source, Data}} |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
Socket :: socket(),
BufSz :: non_neg_integer(),
@@ -2081,7 +2083,7 @@ recvfrom(Socket, BufSz, Timeout) ->
-spec recvfrom(Socket, BufSz, Flags, nowait) ->
{ok, {Source, Data}} |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
Socket :: socket(),
BufSz :: non_neg_integer(),
@@ -2119,7 +2121,7 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) ->
{error, eagain} when (Timeout =:= nowait) ->
- {ok, ?SELECT_INFO(recvfrom, RecvRef)};
+ ?SELECT(recvfrom, RecvRef);
{error, eagain} ->
@@ -2163,8 +2165,8 @@ recvmsg(Socket) ->
MsgHdr :: msghdr(),
Reason :: term()
; (Socket, Timeout :: nowait) -> {ok, MsgHdr} |
- {ok, SelectInfo} |
- {error, Reason} when
+ {select, SelectInfo} |
+ {error, Reason} when
Socket :: socket(),
MsgHdr :: msghdr(),
SelectInfo :: select_info(),
@@ -2181,7 +2183,7 @@ recvmsg(Socket, Timeout) ->
recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
-spec recvmsg(Socket, Flags, nowait) -> {ok, MsgHdr} |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
Socket :: socket(),
Flags :: recv_flags(),
@@ -2211,7 +2213,7 @@ recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz) andalso is_integer(CtrlSz)
-spec recvmsg(Socket,
BufSz, CtrlSz,
Flags, nowait) -> {ok, MsgHdr} |
- {ok, SelectInfo} |
+ {select, SelectInfo} |
{error, Reason} when
Socket :: socket(),
BufSz :: non_neg_integer(),
@@ -2250,7 +2252,7 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) ->
{error, eagain} when (Timeout =:= nowait) ->
- {ok, ?SELECT_INFO(recvmsg, RecvRef)};
+ ?SELECT(recvmsg, RecvRef);
{error, eagain} ->
@@ -2603,7 +2605,7 @@ peername(#socket{ref = SockRef}) ->
SelectInfo :: select_info(),
Reason :: term().
-cancel(#socket{ref = SockRef}, {select, Tag, Ref}) ->
+cancel(#socket{ref = SockRef}, #select_info{tag = Tag, ref = Ref}) ->
cancel(SockRef, Tag, Ref).
--
cgit v1.2.3
From 64c4668a65195283c7dbc24a677f8d3d46ac5513 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Thu, 2 May 2019 15:08:00 +0200
Subject: [esock|doc] Update with regard to the new select returns
---
erts/doc/src/socket.xml | 30 ++++++++++++++++-----
erts/doc/src/socket_usage.xml | 63 +++++++++++++++++++++++++++++++++++++------
2 files changed, 78 insertions(+), 15 deletions(-)
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index f9dd9575f4..48e91e93a7 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -47,24 +47,40 @@
recv/3
function with Timeout set to nowait (recv(Sock, 0, nowait))
when there is actually nothing to read, it will return with
- {ok, SelectInfo}. When data eventually arrives a 'select' message
- will be sent to the caller:
+ {ok,
+ SelectInfo}.
+ When data eventually arrives a 'select' message
+ will be sent to the caller:
+
+
+
+
+ - {'$socket', Sock, select, Info}
+
+ The caller can now make another
call to the recv function and now expect data.
Note that all other users are locked out until the
'current user' has called the function (recv in this case).
Another message the user must be prepared for (when making asynchronous
- calls) is the abort message:
+ calls) is the abort message:
+
+
+
+
+ - {'$socket', Sock, abort, Info}
+
+ This message indicates
that the (asynchronous) operation has been aborted.
If, for instance, the socket has been
- closed (by another process), Reason will be {Ref, closed}.
+ closed (by another process), Info will be {Ref, closed}.
There is currently no support for Windows.
diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml
index fcfe9b54ec..9d8141b822 100644
--- a/erts/doc/src/socket_usage.xml
+++ b/erts/doc/src/socket_usage.xml
@@ -54,25 +54,72 @@
recv/3
function with Timeout set to nowait (i.e. recv(Sock, 0, nowait))
when there is actually nothing to read, it will return with
- {ok, SelectInfo}. When data eventually arrives a 'select message'
- will be sent to the caller:
+ {ok,
+ SelectInfo}.
+ When data eventually arrives a 'select message'
+ will be sent to the caller:
+
+
+
+
+ - {'$socket', Sock, select, Info}
+
+ The caller can then make another
call to the recv function and now expect data.
- The user must also be prepared to receive an abort message:
+
The user must also be prepared to receive an abort message:
+
+
+
+
+ - {'$socket', Sock, abort, Info}
+
+ If the operation is aborted
for whatever reason (e.g. if the socket is closed "by someone else").
- The Reason part contains the abort reason (in case the socket
- has been closed Reason = {Ref, closed}).
+ The Info part contains the abort reason (in this case that
+ the socket has been closed Info = {Ref, closed}).
Note that all other users are locked out until the
'current user' has called the function (recv in this case). So either
immediately call the function or
cancel.
+ The general form of the 'socket' message is:
+
+
+
+
+ - {'$socket', Sock :: socket(), Tag :: atom(), Info :: term()}
+
+ Where the format of Info is a function of Tag:
+
+
+ Tag |
+ Info value type |
+
+
+ select |
+ reference() |
+
+
+ abort |
+ {reference(), Reason :: term()} |
+
+ socket message info value type
+
+ The reference() is the same as was received in the
+ SelectInfo.
--
cgit v1.2.3
From d5a78e4fab8e0039f59bfd20368e5c05a19b8439 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Thu, 2 May 2019 15:07:41 +0200
Subject: [esock|test] Fixed async ttest
Updated the ttest test(s) to handle async properly.
OTP-15731
---
erts/emulator/test/socket_SUITE.erl | 2 +-
.../emulator/test/socket_test_ttest_tcp_socket.erl | 42 +++++++++++-----------
2 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 66cbdda9c7..f7250f6725 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -22592,7 +22592,7 @@ which_ttest_runtime_env(false) ->
%% ms: milliseconds
%% s: seconds (default)
%% m: minutes
-which_ttest_runtime_env2([$m, $s | MS]) when (length(MS) > 0) ->
+which_ttest_runtime_env2([$s, $m | MS]) when (length(MS) > 0) ->
convert_time(MS, fun(X) -> X end);
which_ttest_runtime_env2([$m | M]) when (length(M) > 0) ->
convert_time(M, fun(X) -> ?MINS(X) end);
diff --git a/erts/emulator/test/socket_test_ttest_tcp_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_socket.erl
index 65fbba44c6..3aa3b2c504 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_socket.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_socket.erl
@@ -399,9 +399,9 @@ reader_loop(#{active := false,
end;
%% Read *once* and then change to false
-reader_loop(#{async := false,
- active := once,
- sock := Sock,
+reader_loop(#{active := once,
+ async := false,
+ sock := Sock,
method := Method,
ctrl_proc := Pid} = State) ->
case do_recv(Method, Sock) of
@@ -443,14 +443,14 @@ reader_loop(#{async := false,
Pid ! ?ERROR_MSG(Sock, Method, Reason),
reader_exit(State, E2)
end;
-reader_loop(#{async := true,
+reader_loop(#{active := once,
+ async := true,
select_info := undefined,
- active := once,
- sock := Sock,
+ sock := Sock,
method := Method,
ctrl_proc := Pid} = State) ->
case do_recv(Method, Sock, nowait) of
- {ok, {select, _, _} = SelectInfo} ->
+ {select, SelectInfo} ->
reader_loop(State#{select_info => SelectInfo});
{ok, Data} ->
Pid ! ?DATA_MSG(Sock, Method, Data),
@@ -464,11 +464,11 @@ reader_loop(#{async := true,
Pid ! ?ERROR_MSG(Sock, Method, Reason),
reader_exit(State, E2)
end;
-reader_loop(#{async := true,
- select_info := {select, _, Ref},
+reader_loop(#{active := once,
+ async := true,
+ select_info := {select_info, _, Ref},
select_num := N,
- active := once,
- sock := Sock,
+ sock := Sock,
method := Method,
ctrl_proc := Pid} = State) ->
receive
@@ -513,8 +513,8 @@ reader_loop(#{async := true,
end;
%% Read and forward data continuously
-reader_loop(#{async := false,
- active := true,
+reader_loop(#{active := true,
+ async := false,
sock := Sock,
method := Method,
ctrl_proc := Pid} = State) ->
@@ -557,14 +557,14 @@ reader_loop(#{async := false,
Pid ! ?ERROR_MSG(Sock, Method, Reason),
reader_exit(State, E2)
end;
-reader_loop(#{async := true,
+reader_loop(#{active := true,
+ async := true,
select_info := undefined,
- active := true,
sock := Sock,
method := Method,
ctrl_proc := Pid} = State) ->
case do_recv(Method, Sock) of
- {ok, {select, _, _} = SelectInfo} ->
+ {select, SelectInfo} ->
reader_loop(State#{select_info => SelectInfo});
{ok, Data} ->
Pid ! ?DATA_MSG(Sock, Method, Data),
@@ -578,10 +578,10 @@ reader_loop(#{async := true,
Pid ! ?ERROR_MSG(Sock, Method, Reason),
reader_exit(State, E2)
end;
-reader_loop(#{async := true,
- select_info := {select, _, Ref},
+reader_loop(#{active := true,
+ async := true,
+ select_info := {select_info, _, Ref},
select_num := N,
- active := true,
sock := Sock,
method := Method,
ctrl_proc := Pid} = State) ->
@@ -635,8 +635,8 @@ do_recv(msg, Sock, Timeout) ->
case socket:recvmsg(Sock, 0, 0, [], Timeout) of
{ok, #{iov := [Bin]}} ->
{ok, Bin};
- {ok, {select, _, _}} = OK ->
- OK;
+ {select, _} = SELECT ->
+ SELECT;
{error, _} = ERROR ->
ERROR
end.
--
cgit v1.2.3
From 27e7c94902e211276318652eb5d83ea18e5e70fd Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Fri, 3 May 2019 17:21:29 +0200
Subject: [esock|test] Attempt to create a ttest report
Attempted to implement a common report function for
the time test (ttest). Not complete.
---
erts/emulator/test/socket_SUITE.erl | 223 +++++++++++++++++++++++++++---
erts/emulator/test/socket_test_logger.erl | 2 +-
2 files changed, 205 insertions(+), 20 deletions(-)
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index f7250f6725..2d7d9c4e08 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -1580,11 +1580,12 @@ init_per_group(ttest = _GroupName, Config) ->
io:format("init_per_group(~w) -> entry with"
"~n Config: ~p"
"~n", [_GroupName, Config]),
+ ttest_manager_start(),
case lists:keysearch(esock_test_ttest_runtime, 1, Config) of
{value, _} ->
Config;
false ->
- [{esock_test_ttest_runtime, which_ttest_runtime_env()}|Config]
+ [{esock_test_ttest_runtime, which_ttest_runtime_env()} | Config]
end;
init_per_group(_GroupName, Config) ->
Config.
@@ -1593,6 +1594,7 @@ end_per_group(ttest = _GroupName, Config) ->
io:format("init_per_group(~w) -> entry with"
"~n Config: ~p"
"~n", [_GroupName, Config]),
+ ttest_manager_stop(),
lists:keydelete(esock_test_ttest_runtime, 1, Config);
end_per_group(_GroupName, Config) ->
Config.
@@ -1602,16 +1604,16 @@ init_per_testcase(_TC, Config) ->
io:format("init_per_testcase(~w) -> entry with"
"~n Config: ~p"
"~n", [_TC, Config]),
- case quiet_mode(Config) of
- default ->
- ?LOGGER:start();
- Quiet ->
- ?LOGGER:start(Quiet)
- end,
+ %% case quiet_mode(Config) of
+ %% default ->
+ %% ?LOGGER:start();
+ %% Quiet ->
+ %% ?LOGGER:start(Quiet)
+ %% end,
Config.
end_per_testcase(_TC, Config) ->
- ?LOGGER:stop(),
+ %% ?LOGGER:stop(),
Config.
@@ -4691,8 +4693,7 @@ api_a_mrecv_cancel_udp(InitState) ->
%% *** Init part ***
#{desc => "which local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{local_sa => LSA}}
end},
#{desc => "create socket",
@@ -5084,8 +5085,7 @@ api_a_maccept_cancel_tcp(InitState) ->
%% *** Init part ***
#{desc => "which local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{lsa => LSA}}
end},
#{desc => "create listen socket",
@@ -5499,8 +5499,7 @@ api_a_mrecv_cancel_tcp(InitState) ->
%% *** Init part ***
#{desc => "which local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{lsa => LSA}}
end},
#{desc => "create listen socket",
@@ -5706,10 +5705,8 @@ api_a_mrecv_cancel_tcp(InitState) ->
%% *** The init part ***
#{desc => "which server (local) address",
cmd => fun(#{domain := Domain, server_port := Port} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain,
- addr => LAddr},
- SSA = LSA#{port => Port},
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
{ok, State#{local_sa => LSA, server_sa => SSA}}
end},
#{desc => "create socket",
@@ -23017,12 +23014,23 @@ ttest_tcp(InitState) ->
%% Present the results
#{desc => "present the results",
- cmd => fun(#{result := Result} = State) ->
+ cmd => fun(#{result := Result,
+ domain := Domain,
+ server_mod := ServerTrans,
+ server_active := ServerActive,
+ client_mod := ClientTrans,
+ client_active := ClientActive,
+ msg_id := MsgID} = State) ->
case Result of
#{status := ok,
runtime := RunTime,
cnt := Cnt,
bcnt := BCnt} ->
+ ttest_report(Domain,
+ ServerTrans, ServerActive,
+ ClientTrans, ClientActive,
+ MsgID,
+ RunTime, BCnt, Cnt),
?SEV_IPRINT(
"TTest results: "
"~n Run Time: ~s"
@@ -23150,6 +23158,183 @@ ttest_tcp_client_start(Node,
MsgID, MaxOutstanding, RunTime).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(TTEST_MANAGER, esock_ttest_manager).
+
+-record(ttest_report_id,
+ {domain :: socket:domain(),
+ serv_trans :: gen | sock,
+ serv_active :: once | boolean(),
+ client_trans :: gen | sock,
+ client_active :: once | boolean(),
+ msg_id :: small | medium | large}).
+
+-record(ttest_report, {id :: #ttest_report_id{},
+ time :: non_neg_integer(),
+ bytes :: non_neg_integer(),
+ msgs :: non_neg_integer()}).
+
+-spec ttest_report(Domain :: socket:domain(),
+ ServTrans :: gen | sock, ServActive :: once | boolean(),
+ ClientTrans :: gen | sock, ClientActive :: once | boolean(),
+ MsgID :: 1 | 2 | 3,
+ RunTime :: non_neg_integer(),
+ NumBytes :: non_neg_integer(),
+ NumMsgs :: non_neg_integer()) -> ok.
+
+ttest_report(Domain,
+ ServTrans, ServActive,
+ ClientTrans, ClientActive,
+ MsgID,
+ RunTime,
+ NumBytes,
+ NumMsgs) ->
+ ID = #ttest_report_id{domain = Domain,
+ serv_trans = ServTrans,
+ serv_active = ServActive,
+ client_trans = ClientTrans,
+ client_active = ClientActive,
+ msg_id = ttest_msg_id_num_to_name(MsgID)},
+ Report = #ttest_report{id = ID,
+ time = RunTime,
+ bytes = NumBytes,
+ msgs = NumMsgs},
+ %% If we run just one test case, the group init has never been run
+ %% and therefor the ttest manager is not running (we also don't actually
+ %% care about collecting reports in that case).
+ (catch global:send(?TTEST_MANAGER, Report)),
+ ok.
+
+ttest_msg_id_num_to_name(1) ->
+ small;
+ttest_msg_id_num_to_name(2) ->
+ medium;
+ttest_msg_id_num_to_name(3) ->
+ large.
+
+ttest_manager_start() ->
+ Self = self(),
+ {Pid, MRef} = spawn_monitor(fun() -> ttest_manager_init(Self) end),
+ receive
+ {ttest_manager_started, Pid} ->
+ erlang:demonitor(MRef, [flush]),
+ ok;
+ {'DOWN', MRef, process, Pid, Reason} ->
+ exit({failed_starting, ttest_manager, Reason})
+ after 5000 ->
+ exit(Pid, kill),
+ exit({failed_starting, ttest_manager, timeout})
+ end.
+
+ttest_manager_stop() ->
+ case global:whereis_name(?TTEST_MANAGER) of
+ Pid when is_pid(Pid) ->
+ erlang:monitor(process, Pid),
+ global:send(?TTEST_MANAGER, stop),
+ receive
+ {'DOWN', _MRef, process, Pid, _} ->
+ ok
+ after 10000 ->
+ exit(Pid, kill),
+ ok
+ end;
+ _ ->
+ ok
+ end.
+
+ttest_manager_init(Parent) ->
+ yes = global:register_name(?TTEST_MANAGER, self()),
+ ets:new(?TTEST_MANAGER,
+ [{keypos, #ttest_report.id}, named_table, protected, ordered_set]),
+ Parent ! {ttest_manager_started, self()},
+ ttest_manager_loop().
+
+ttest_manager_loop() ->
+ receive
+ stop ->
+ ?LOGGER:format("manager stopping~n", []),
+ ttest_manager_done();
+
+ #ttest_report{id = _ID,
+ time = _RunTime,
+ bytes = _NumBytes,
+ msgs = _NumMsgs} = Report ->
+ true = ets:insert_new(?TTEST_MANAGER, Report),
+ ttest_manager_loop()
+ end.
+
+%% We are supposed to pretty print the result here...
+ttest_manager_done() ->
+ format_reports(inet),
+ %% format_reports(inet6),
+ ets:delete(?TTEST_MANAGER),
+ exit(normal).
+
+format_reports(Domain) ->
+ ?LOGGER:format("Domain ~w reports:~n~n", [Domain]),
+ format_reports(Domain, small),
+ format_reports(Domain, medium),
+ format_reports(Domain, large).
+
+format_reports(Domain, MsgID) when is_atom(MsgID) ->
+ case which_ttest_reports(Domain, MsgID) of
+ [] ->
+ ?LOGGER:format(" No ~w reports~n~n", [MsgID]);
+ Reports ->
+ ?LOGGER:format(" ~w reports: ~n", [MsgID]),
+ lists:foreach(fun(R) -> format_report(R) end, Reports)
+ end.
+
+%% This should really be a table like this:
+%%
+%% client
+%% server gen(false) gen(once) gen(true) sock(false) sock(once) sock(true)
+%% gen(false) nnn
+%% gen(once) nnn
+%% gen(true) nnn
+%% sock(false) nnn
+%% sock(once) nnn
+%% sock(true) nnn
+%%
+format_report(#ttest_report{id = #ttest_report_id{serv_trans = STrans,
+ serv_active = SActive,
+ client_trans = CTrans,
+ client_active = CActive},
+ time = RunTime,
+ bytes = BCnt,
+ msgs = MCnt}) ->
+ ?LOGGER:format(" server ~w[~w] - client ~w[~w] => "
+ "~n Run Time: ~s"
+ "~n Bytes: ~s"
+ "~n Messages: ~s"
+ "~n", [STrans, SActive, CTrans, CActive,
+ ?TTEST_LIB:format_time(RunTime),
+ if ((BCnt =:= 0) orelse (RunTime =:= 0)) ->
+ ?TTEST_LIB:format("~w, ~w",
+ [BCnt, RunTime]);
+ true ->
+ ?TTEST_LIB:format("~p => ~p byte / ms",
+ [BCnt, BCnt div RunTime])
+ end,
+ if (RunTime =:= 0) ->
+ "-";
+ true ->
+ ?TTEST_LIB:format("~p => ~p iterations / ms",
+ [MCnt, MCnt div RunTime])
+ end]),
+ ok.
+
+
+which_ttest_reports(Domain, all) ->
+ [R || R = #ttest_report{id = #ttest_report_id{domain = D}} <-
+ ets:tab2list(?TTEST_MANAGER), Domain =:= D];
+which_ttest_reports(Domain, MsgID) ->
+ [R || R = #ttest_report{id = #ttest_report_id{domain = D, msg_id = MID}} <-
+ ets:tab2list(?TTEST_MANAGER), (Domain =:= D) andalso (MsgID =:= MID)].
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This mechanism has only one purpose: So that we are able to kill
diff --git a/erts/emulator/test/socket_test_logger.erl b/erts/emulator/test/socket_test_logger.erl
index 26610e9ef3..f5d4c8c7b2 100644
--- a/erts/emulator/test/socket_test_logger.erl
+++ b/erts/emulator/test/socket_test_logger.erl
@@ -43,7 +43,7 @@ start(Quiet) ->
ok;
undefined ->
Self = self(),
- Pid = spawn_link(fun() -> init(Self, Quiet) end),
+ Pid = spawn(fun() -> init(Self, Quiet) end),
yes = global:register_name(?LOGGER, Pid),
ok
end.
--
cgit v1.2.3
From 88a1140f46b47e8a3526a7239f343ca344e9a3e1 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 6 May 2019 10:44:07 +0200
Subject: [esock|doc] Make select_ref opaque
OTP-15731
---
erts/doc/src/socket.xml | 20 ++++++--------------
erts/preloaded/ebin/socket.beam | Bin 74796 -> 74796 bytes
erts/preloaded/src/socket.erl | 2 +-
3 files changed, 7 insertions(+), 15 deletions(-)
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index 48e91e93a7..47e84090ee 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -51,15 +51,10 @@
SelectInfo}.
When data eventually arrives a 'select' message
will be sent to the caller:
-
- - {'$socket', Sock, select, Info}
+ - {'$socket', socket(), select, select_ref()}
The caller can now make another
call to the recv function and now expect data.
@@ -67,20 +62,15 @@
'current user' has called the function (recv in this case).
Another message the user must be prepared for (when making asynchronous
calls) is the abort message:
-
- - {'$socket', Sock, abort, Info}
+ - {'$socket', socket(), abort, Info}
This message indicates
that the (asynchronous) operation has been aborted.
- If, for instance, the socket has been
- closed (by another process), Info will be {Ref, closed}.
+ If, for instance, the socket has been closed (by another process),
+ Info will be {select_ref(), closed}.
There is currently no support for Windows.
@@ -111,6 +101,8 @@
+ A reference that uniquely identifies the (select) operation.
+
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index fb4d9850e5..0f438e4ca9 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 60f162d37d..0b1b49653c 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -592,7 +592,7 @@
-type select_tag() :: atom().
--type select_ref() :: reference().
+-opaque select_ref() :: reference().
-record(select_info, {tag :: select_tag(), ref :: select_ref()}).
--
cgit v1.2.3
From d6dddf8ec963d34c91ec92b4772ff34238f2c718 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 6 May 2019 12:19:41 +0200
Subject: [esock] Connect can now also take a nowait Timeout
Had forgot about the connect function. But it can now also
handle the Timeout = nowait, maybe resulting in a select.
Required some nif work also...
OTP-15731
---
erts/doc/src/socket.xml | 37 +++-
erts/doc/src/socket_usage.xml | 29 +--
erts/emulator/nifs/common/socket_nif.c | 73 ++++++-
erts/emulator/test/socket_SUITE.erl | 383 ++++++++++++++++++++++++++++++++-
erts/preloaded/ebin/socket.beam | Bin 74796 -> 75044 bytes
erts/preloaded/src/socket.erl | 30 ++-
6 files changed, 505 insertions(+), 47 deletions(-)
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index 47e84090ee..e5cf77663a 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -324,7 +324,7 @@
In the case when there is no connections waiting, the function
will return with the SelectInfo. The caller can then await a
- select message, {'$socket', Sock, select, Info} (where
+ select message, {'$socket', Socket, select, Info} (where
Info is the select_ref() from the SelectInfo),
when a client connects (a subsequent call to accept will then return
the socket).
@@ -377,7 +377,7 @@
-
+
Initiate a connection on a socket.
This function connects the socket to the address
@@ -385,6 +385,24 @@
+
+
+ Initiate a connection on a socket.
+
+ This function connects the socket to the address
+ specied by the SockAddr argument.
+
+ In the case when its not possible to immediately establish a
+ connection, the function will return with the
+ SelectInfo.
+ The caller can then await a
+ select message, {'$socket', Socket, select, Info} (where
+ Info is the select_ref() from the SelectInfo,
+ a subsequent call to connect will then
+ establish the connection).
+
+
+
@@ -505,7 +523,7 @@
In the case when there is no data waiting, the function
will return with the SelectInfo. The caller can then await a
- select message, {'$socket', Sock, select, Info} (where
+ select message, {'$socket', Socket, select, Info} (where
Info is the select_ref() from the SelectInfo),
when data has arrived (a subsequent call to recv will then return
the data).
@@ -564,7 +582,7 @@
In the case when there is no data waiting, the function
will return with the SelectInfo. The caller can then await a
- select message, {'$socket', Sock, select, Info} (where
+ select message, {'$socket', Socket, select, Info} (where
Info is the select_ref() from the SelectInfo),
when data has arrived (a subsequent call to recvfrom will then return
the data).
@@ -640,7 +658,7 @@
In the case when there is no data waiting, the function
will return with the SelectInfo. The caller can then await a
- select message, {'$socket', Sock, select, Info} (where
+ select message, {'$socket', Socket, select, Info} (where
Info is the select_ref() from the SelectInfo),
when data has arrived (a subsequent call to recvmsg will then return
the data).
@@ -667,7 +685,8 @@
In the case when there is no room in the (system-) buffers,
the function will return with the SelectInfo. The caller
- can then await a select message, {'$socket', Sock, select, Info}
+ can then await a select message,
+ {'$socket', Socket, select, Info}
(where Info is the select_ref() from the
SelectInfo), when there is room for more data (a subsequent
call to send will then send the data).
@@ -724,7 +743,8 @@
In the case when there is no room in the (system-) buffers,
the function will return with the SelectInfo. The caller
- can then await a select message, {'$socket', Sock, select, Info}
+ can then await a select message,
+ {'$socket', Socket, select, Info}
(where Info is the select_ref() from the
SelectInfo), when there is room for more data (a subsequent
call to sendmsg will then send the data).
@@ -751,7 +771,8 @@
In the case when there is no room in the (system-) buffers,
the function will return with the SelectInfo. The caller
- can then await a select message, {'$socket', Sock, select, Info}
+ can then await a select message,
+ {'$socket', Socket, select, Info}
(where Info is the select_ref() from the
SelectInfo), when there is room for more data (a subsequent
call to sendto will then send the data).
diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml
index 9d8141b822..7e65bcbf70 100644
--- a/erts/doc/src/socket_usage.xml
+++ b/erts/doc/src/socket_usage.xml
@@ -43,6 +43,7 @@
Asynchronous calls
Some functions allow for an asynchronous call
(accept/2,
+ connect/3,
recv/3,4,
recvfrom/3,4,
recvmsg/2,3,5,
@@ -52,21 +53,17 @@
This is achieved by setting the Timeout argument to
nowait. For instance, if calling the
recv/3
- function with Timeout set to nowait (i.e. recv(Sock, 0, nowait))
+ function with Timeout set to nowait (i.e.
+ recv(Sock, 0, nowait))
when there is actually nothing to read, it will return with
{ok,
SelectInfo}.
When data eventually arrives a 'select message'
will be sent to the caller:
-
- - {'$socket', Sock, select, Info}
+ - {'$socket', socket(), select, select_ref()}
The caller can then make another
call to the recv function and now expect data.
@@ -79,24 +76,18 @@
- - {'$socket', Sock, abort, Info}
+ - {'$socket', socket(), abort, Info}
If the operation is aborted
for whatever reason (e.g. if the socket is closed "by someone else").
The Info part contains the abort reason (in this case that
- the socket has been closed Info = {Ref, closed}).
+ the socket has been closed Info = {select_ref(), closed}).
Note that all other users are locked out until the
'current user' has called the function (recv in this case). So either
immediately call the function or
cancel.
+
The general form of the 'socket' message is:
-
@@ -110,15 +101,15 @@
select |
- reference() |
+ select_ref() |
abort |
- {reference(), Reason :: term()} |
+ {select_ref(), Reason :: term()} |
socket message info value type
- The reference() is the same as was received in the
+
The select_ref() is the same as was received in the
SelectInfo.
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index 25bc712949..1a8ce89b7b 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -825,6 +825,10 @@ typedef struct {
ErlNifPid ctrlPid;
ESockMonitor ctrlMon;
+ /* +++ Connector process +++ */
+ ErlNifPid connPid;
+ ESockMonitor connMon;
+
/* +++ Write stuff +++ */
ErlNifMutex* writeMtx;
ESockRequestor currentWriter;
@@ -2377,6 +2381,8 @@ static int socket_setopt(int sock,
const void* optVal,
const socklen_t optLen);
+static BOOLEAN_T is_connector(ErlNifEnv* env,
+ ESockDescriptor* descP);
static BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err);
static ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event);
@@ -4904,7 +4910,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
return esock_make_error(env, atom_eisconn);
}
- if (IS_CONNECTING(descP)) {
+ if (IS_CONNECTING(descP) && !is_connector(env, descP)) {
SSDBG( descP, ("SOCKET", "nif_connect -> already connecting\r\n") );
return esock_make_error(env, esock_atom_einval);
}
@@ -4926,19 +4932,41 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
((save_errno == ERRNO_BLOCK) || /* Winsock2 */
(save_errno == EINPROGRESS))) { /* Unix & OSE!! */
ref = MKREF(env);
- descP->state = SOCKET_STATE_CONNECTING;
- if ((sres = esock_select_write(env, descP->sock, descP, NULL,
- sockRef, ref)) < 0) {
- res = esock_make_error(env,
- MKT2(env,
- esock_atom_select_failed,
- MKI(env, sres)));
- } else {
+
+ if (IS_CONNECTING(descP)) {
+ /* Glitch */
res = esock_make_ok2(env, ref);
+ } else {
+
+ /* First time here */
+
+ if (enif_self(env, &descP->connPid) == NULL)
+ return esock_make_error(env, atom_exself);
+
+ if (MONP("nconnect -> conn",
+ env, descP,
+ &descP->connPid,
+ &descP->connMon) != 0)
+ return esock_make_error(env, atom_exmon);
+
+ descP->state = SOCKET_STATE_CONNECTING;
+
+ if ((sres = esock_select_write(env, descP->sock, descP, NULL,
+ sockRef, ref)) < 0) {
+ res = esock_make_error(env,
+ MKT2(env,
+ esock_atom_select_failed,
+ MKI(env, sres)));
+ } else {
+ res = esock_make_ok2(env, ref);
+ }
}
+
} else if (code == 0) { /* ok we are connected */
descP->state = SOCKET_STATE_CONNECTED;
+ enif_set_pid_undefined(&descP->connPid);
+ MON_INIT(&descP->connMon);
descP->isReadable = TRUE;
descP->isWritable = TRUE;
@@ -5069,6 +5097,29 @@ BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err)
+/* *** is_connector ***
+ * Check if the current process is the connector process.
+ */
+#if !defined(__WIN32__)
+static
+BOOLEAN_T is_connector(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ ErlNifPid caller;
+
+ if (enif_self(env, &caller) == NULL)
+ return FALSE;
+
+ if (COMPARE_PIDS(&descP->connPid, &caller) == 0)
+ return TRUE;
+ else
+ return FALSE;
+
+}
+#endif
+
+
+
/* ----------------------------------------------------------------------
* nif_listen
*
@@ -16873,7 +16924,9 @@ ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event)
char buf[64]; /* Buffer used for building the mutex name */
// This needs to be released when the socket is closed!
- // descP->env = enif_alloc_env();
+
+ enif_set_pid_undefined(&descP->connPid);
+ MON_INIT(&descP->connMon);
sprintf(buf, "esock[w,%d]", sock);
descP->writeMtx = MCREATE(buf);
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 2d7d9c4e08..0c29e3288c 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -82,6 +82,7 @@
api_b_sendmsg_and_recvmsg_tcpL/1,
%% *** API async ***
+ api_a_connect_tcp4/1,
api_a_sendto_and_recvfrom_udp4/1,
api_a_sendmsg_and_recvmsg_udp4/1,
api_a_send_and_recv_tcp4/1,
@@ -680,6 +681,7 @@ api_basic_cases() ->
api_async_cases() ->
[
+ api_a_connect_tcp4,
api_a_sendto_and_recvfrom_udp4,
api_a_sendmsg_and_recvmsg_udp4,
api_a_send_and_recv_tcp4,
@@ -2651,6 +2653,385 @@ api_b_send_and_recv_tcp(InitState) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically establish a TCP connection via an async connect.
+
+api_a_connect_tcp4(suite) ->
+ [];
+api_a_connect_tcp4(doc) ->
+ [];
+api_a_connect_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_connect_tcp4,
+ fun() ->
+ Connect = fun(Sock, SockAddr) ->
+ socket:connect(Sock, SockAddr, nowait)
+ end,
+ InitState = #{domain => inet,
+ connect => Connect},
+ ok = api_a_connect_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_a_connect_tcp(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create listen socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{lsock := LSock, lsa := LSA} = State) ->
+ case socket:bind(LSock, LSA) of
+ {ok, Port} ->
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, lport := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "await connection",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ ?SEV_IPRINT("accepted: ~n ~p", [Sock]),
+ {ok, State#{csock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close connection socket",
+ cmd => fun(#{csock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(csock, State)}
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(lsock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ ClientSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Port} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, server_port => Port}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ SSA = LSA#{port => Port},
+ {ok, State#{local_sa => LSA, server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (async connect)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, async_connect)
+ end},
+ #{desc => "connect (async) to server",
+ cmd => fun(#{sock := Sock,
+ server_sa := SSA,
+ connect := Connect} = State) ->
+ case Connect(Sock, SSA) of
+ ok ->
+ {error, unexpected_success};
+ {select, {select_info, ST, SR}} ->
+ ?SEV_IPRINT("select ->"
+ "~n tag: ~p"
+ "~n ref: ~p", [ST, SR]),
+ {ok, State#{connect_stag => ST,
+ connect_sref => SR}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (connect select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect_select),
+ ok
+ end},
+ #{desc => "await select message",
+ cmd => fun(#{sock := Sock, connect_sref := Ref}) ->
+ receive
+ {'$socket', Sock, select, Ref} ->
+ ?SEV_IPRINT("select message ->"
+ "~n ref: ~p", [Ref]),
+ ok
+ after 5000 ->
+ ?SEV_EPRINT("timeout: "
+ "~n message queue: ~p",
+ [mq()]),
+ {error, timeout}
+ end
+ end},
+ #{desc => "announce ready (select)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, select),
+ ok
+ end},
+ #{desc => "connect (async) to server",
+ cmd => fun(#{sock := Sock, server_sa := SSA, connect := Connect}) ->
+ case Connect(Sock, SSA) of
+ ok ->
+ ok = socket:setopt(Sock, otp, debug, false),
+ ok;
+ {select, SelectInfo} ->
+ {error, {unexpected_select, SelectInfo}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+ #{desc => "get peername",
+ cmd => fun(#{sock := Sock} = _State) ->
+ case socket:peername(Sock) of
+ {ok, SockAddr} ->
+ ?SEV_IPRINT("Peer Name: ~p", [SockAddr]),
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ State2 = maps:remove(sock, State),
+ State3 = maps:remove(connect_stag, State2),
+ State4 = maps:remove(connect_sref, State3),
+ {ok, State4}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client",
+ cmd => fun(#{client := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+
+ %% Start the client
+ #{desc => "order client start",
+ cmd => fun(#{client := Pid, server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Port),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, init)
+ end},
+
+
+ %% *** The actual test ***
+ #{desc => "order client to continue (async connect)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, async_connect),
+ ok
+ end},
+ #{desc => "await client ready (connect select)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, connect_select)
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "order server to continue (with accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept),
+ ok
+ end},
+ #{desc => "await client ready (select)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, select)
+ end},
+ #{desc => "await client ready (connect)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, connect)
+ end},
+ #{desc => "await server ready (accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, accept)
+ end},
+
+
+ %% *** Termination ***
+ #{desc => "order client to terminate",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client termination",
+ cmd => fun(#{client := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client, State),
+ {ok, State1}
+ end},
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, InitState),
+
+ i("start client evaluator"),
+ Client = ?SEV_START("client", ClientSeq, InitState),
+ i("await evaluator(s)"),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client => Client#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
+
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Basically send and receive on an IPv4 UDP (dgram) socket using
@@ -2787,7 +3168,7 @@ api_a_send_and_recv_udp(InitState) ->
{ok, State#{recv_stag => Tag,
recv_sref => RecvRef}};
{ok, X} ->
- {error, {unexpected_select_info, X}};
+ {error, {unexpected_succes, X}};
{error, _} = ERROR ->
ERROR
end
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 0f438e4ca9..fd8d489f36 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 0b1b49653c..c8d044209d 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -1206,18 +1206,24 @@ validate_inet6_addrs(Addrs) ->
%%
-spec connect(Socket, SockAddr) -> ok | {error, Reason} when
- Socket :: socket(),
- SockAddr :: sockaddr(),
- Reason :: term().
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Reason :: term().
connect(Socket, SockAddr) ->
connect(Socket, SockAddr, infinity).
--spec connect(Socket, SockAddr, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- SockAddr :: sockaddr(),
- Timeout :: timeout(),
- Reason :: term().
+-spec connect(Socket, SockAddr, nowait) ->
+ ok | {select, SelectInfo} | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ SelectInfo :: select_info(),
+ Reason :: term()
+ ; (Socket, SockAddr, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Timeout :: timeout(),
+ Reason :: term().
%%
%% Is it possible to connect with family = local for the (dest) sockaddr?
@@ -1227,12 +1233,18 @@ connect(_Socket, _SockAddr, Timeout)
{error, timeout};
connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout)
when ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso
- ((Timeout =:= infinity) orelse is_integer(Timeout)) ->
+ ((Timeout =:= nowait) orelse
+ (Timeout =:= infinity) orelse is_integer(Timeout)) ->
TS = timestamp(Timeout),
case nif_connect(SockRef, SockAddr) of
ok ->
%% Connected!
ok;
+
+ {ok, Ref} when (Timeout =:= nowait) ->
+ %% Connecting, but the caller does not want to wait...
+ ?SELECT(connect, Ref);
+
{ok, Ref} ->
%% Connecting...
NewTimeout = next_timeout(TS, Timeout),
--
cgit v1.2.3
From 6ede678f9e7a2ba1b15c276e0e401d7990e88c51 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Mon, 6 May 2019 18:21:38 +0200
Subject: [esock] Types and cleanup
Changed tag type tpo opaque. Also added connect (monitor and
process) cleanup.
OTP-15731
---
erts/doc/src/socket.xml | 6 +++++-
erts/emulator/nifs/common/socket_nif.c | 17 +++++++++++++++--
erts/preloaded/ebin/socket.beam | Bin 75044 -> 75044 bytes
erts/preloaded/src/socket.erl | 4 ++--
4 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index e5cf77663a..b4e22e86a8 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -98,10 +98,14 @@
+
+ A tag that describes the (select) operation.
+
- A reference that uniquely identifies the (select) operation.
+
+ A reference that uniquely identifies the (select) operation.
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index 1a8ce89b7b..4394e7bc7c 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -4966,7 +4966,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
descP->state = SOCKET_STATE_CONNECTED;
enif_set_pid_undefined(&descP->connPid);
- MON_INIT(&descP->connMon);
+ DEMONP("nconnect -> connected", env, descP, &descP->connMon);
descP->isReadable = TRUE;
descP->isWritable = TRUE;
@@ -5026,7 +5026,7 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env,
{
int error;
- if (descP->state != SOCKET_STATE_CONNECTING)
+ if (!IS_CONNECTING(descP))
return esock_make_error(env, atom_enotconn);
if (!verify_is_connected(descP, &error)) {
@@ -5035,6 +5035,8 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env,
}
descP->state = SOCKET_STATE_CONNECTED;
+ enif_set_pid_undefined(&descP->connPid);
+ DEMONP("nfinalize_connection -> connected", env, descP, &descP->connMon);
descP->isReadable = TRUE;
descP->isWritable = TRUE;
@@ -18705,6 +18707,17 @@ void socket_down(ErlNifEnv* env,
MON2T(env, mon));
}
+ } else if (COMPARE_PIDS(&descP->connPid, pid) == 0) {
+
+ /* The connPid is only set during the connection.
+ * The same goes for the monitor (connMon).
+ */
+
+ descP->state = SOCKET_STATE_OPEN; /* restore state */
+ enif_set_pid_undefined(&descP->connPid);
+ DEMONP("socket_down -> connector",
+ env, descP, &descP->connMon);
+
} else {
/* check all operation queue(s): acceptor, writer and reader.
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index fd8d489f36..134b4eac13 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index c8d044209d..ae1ffdb4ac 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -591,8 +591,8 @@
#{level := integer(), type := integer(), data := binary()}.
--type select_tag() :: atom().
--opaque select_ref() :: reference().
+-opaque select_tag() :: atom().
+-opaque select_ref() :: reference().
-record(select_info, {tag :: select_tag(), ref :: select_ref()}).
--
cgit v1.2.3
From 51a0daaec83e7e5794d74b7276b2d13f98ba48df Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 7 May 2019 10:36:56 +0200
Subject: [esock] Found and fixed some weirdness on darwin
Tests on Darwin Kernel Version 18.2.0 reveled some
problems handling connect (on that platform).
It seems that calling connect the second time is not needed
(it results in eisconn), so we needed to handle that case,
which we now do.
---
erts/emulator/nifs/common/socket_nif.c | 104 +++++++++++++++++++--------
erts/emulator/test/socket_SUITE.erl | 128 ++++++++++++++++++++++++++++++++-
2 files changed, 198 insertions(+), 34 deletions(-)
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index 4394e7bc7c..d24edde1e0 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -4901,17 +4901,17 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
return esock_make_error(env, atom_closed);
if (!IS_OPEN(descP)) {
- SSDBG( descP, ("SOCKET", "nif_connect -> not open\r\n") );
+ SSDBG( descP, ("SOCKET", "nconnect -> not open\r\n") );
return esock_make_error(env, atom_exbadstate);
}
if (IS_CONNECTED(descP)) {
- SSDBG( descP, ("SOCKET", "nif_connect -> already connected\r\n") );
+ SSDBG( descP, ("SOCKET", "nconnect -> already connected\r\n") );
return esock_make_error(env, atom_eisconn);
}
if (IS_CONNECTING(descP) && !is_connector(env, descP)) {
- SSDBG( descP, ("SOCKET", "nif_connect -> already connecting\r\n") );
+ SSDBG( descP, ("SOCKET", "nconnect -> already connecting\r\n") );
return esock_make_error(env, esock_atom_einval);
}
@@ -4925,45 +4925,79 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
descP->addrLen);
save_errno = sock_errno();
- SSDBG( descP, ("SOCKET", "nif_connect -> connect result: %d, %d\r\n",
+ SSDBG( descP, ("SOCKET", "nconnect -> connect result: %d, %d\r\n",
code, save_errno) );
- if (IS_SOCKET_ERROR(code) &&
- ((save_errno == ERRNO_BLOCK) || /* Winsock2 */
- (save_errno == EINPROGRESS))) { /* Unix & OSE!! */
- ref = MKREF(env);
+ if (IS_SOCKET_ERROR(code)) {
+ switch (save_errno) {
+ case ERRNO_BLOCK: /* Winsock2 */
+ case EINPROGRESS: /* Unix & OSE!! */
+ SSDBG( descP, ("SOCKET", "nconnect -> would block => select\r\n") );
- if (IS_CONNECTING(descP)) {
- /* Glitch */
- res = esock_make_ok2(env, ref);
- } else {
+ ref = MKREF(env);
- /* First time here */
+ if (IS_CONNECTING(descP)) {
+ /* Glitch */
+ res = esock_make_ok2(env, ref);
+ } else {
- if (enif_self(env, &descP->connPid) == NULL)
- return esock_make_error(env, atom_exself);
+ /* First time here */
- if (MONP("nconnect -> conn",
- env, descP,
- &descP->connPid,
- &descP->connMon) != 0)
- return esock_make_error(env, atom_exmon);
-
- descP->state = SOCKET_STATE_CONNECTING;
-
- if ((sres = esock_select_write(env, descP->sock, descP, NULL,
- sockRef, ref)) < 0) {
- res = esock_make_error(env,
- MKT2(env,
- esock_atom_select_failed,
- MKI(env, sres)));
- } else {
- res = esock_make_ok2(env, ref);
+ if (enif_self(env, &descP->connPid) == NULL)
+ return esock_make_error(env, atom_exself);
+
+ if (MONP("nconnect -> conn",
+ env, descP,
+ &descP->connPid,
+ &descP->connMon) != 0)
+ return esock_make_error(env, atom_exmon);
+
+ descP->state = SOCKET_STATE_CONNECTING;
+
+ if ((sres = esock_select_write(env, descP->sock, descP, NULL,
+ sockRef, ref)) < 0) {
+ res = esock_make_error(env,
+ MKT2(env,
+ esock_atom_select_failed,
+ MKI(env, sres)));
+ } else {
+ res = esock_make_ok2(env, ref);
+ }
}
+ break;
+
+ case EISCONN:
+ SSDBG( descP, ("SOCKET", "nconnect -> *already* connected\r\n") );
+ {
+ /* This is ***strange*** so make sure */
+ int err = 0;
+ if (!verify_is_connected(descP, &err)) {
+ descP->state = SOCKET_STATE_OPEN; /* restore state */
+ res = esock_make_error_errno(env, err);
+ } else {
+ descP->state = SOCKET_STATE_CONNECTED;
+ /* And just to be on the safe side, reset these */
+ enif_set_pid_undefined(&descP->connPid);
+ DEMONP("nconnect -> connected",
+ env, descP, &descP->connMon);
+ descP->isReadable = TRUE;
+ descP->isWritable = TRUE;
+ res = esock_atom_ok;
+ }
+ }
+ break;
+
+ default:
+ SSDBG( descP, ("SOCKET", "nconnect -> other error(1): %d\r\n",
+ save_errno) );
+ res = esock_make_error_errno(env, save_errno);
+ break;
}
} else if (code == 0) { /* ok we are connected */
+ SSDBG( descP, ("SOCKET", "nconnect -> connected\r\n") );
+
descP->state = SOCKET_STATE_CONNECTED;
enif_set_pid_undefined(&descP->connPid);
DEMONP("nconnect -> connected", env, descP, &descP->connMon);
@@ -4971,7 +5005,13 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
descP->isWritable = TRUE;
res = esock_atom_ok;
+
} else {
+ /* Do we really need this case? */
+
+ SSDBG( descP, ("SOCKET", "nconnect -> other error(2): %d\r\n",
+ save_errno) );
+
res = esock_make_error_errno(env, save_errno);
}
@@ -16412,6 +16452,8 @@ char* encode_cmsghdr_data_ipv6(ErlNifEnv* env,
#if defined(IPV6_PKTINFO)
case IPV6_PKTINFO:
{
+ char* xres;
+
struct in6_pktinfo* pktInfoP = (struct in6_pktinfo*) dataP;
ERL_NIF_TERM ifIndex = MKI(env, pktInfoP->ipi6_ifindex);
ERL_NIF_TERM addr;
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 0c29e3288c..d3ea02f166 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -2668,8 +2668,16 @@ api_a_connect_tcp4(_Config) when is_list(_Config) ->
Connect = fun(Sock, SockAddr) ->
socket:connect(Sock, SockAddr, nowait)
end,
+ Send = fun(Sock, Data) ->
+ socket:send(Sock, Data)
+ end,
+ Recv = fun(Sock) ->
+ socket:recv(Sock)
+ end,
InitState = #{domain => inet,
- connect => Connect},
+ connect => Connect,
+ send => Send,
+ recv => Recv},
ok = api_a_connect_tcp(InitState)
end).
@@ -2748,6 +2756,43 @@ api_a_connect_tcp(InitState) ->
ok
end},
+ #{desc => "await continue (recv_req)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv_req)
+ end},
+ #{desc => "recv req",
+ cmd => fun(#{csock := Sock, recv := Recv}) ->
+ case Recv(Sock) of
+ {ok, ?BASIC_REQ} ->
+ ok;
+ {ok, UnexpData} ->
+ {error, {unexpected_data, UnexpData}};
+ {error, _} = ERROR ->
+ %% At the moment there is no way to get
+ %% status or state for the socket...
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_req)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue (send_rep)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_rep)
+ end},
+ #{desc => "send rep",
+ cmd => fun(#{csock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REP)
+ end},
+ #{desc => "announce ready (send_rep)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_rep),
+ ok
+ end},
+
+
%% *** Termination ***
#{desc => "await terminate",
cmd => fun(#{tester := Tester} = State) ->
@@ -2831,7 +2876,10 @@ api_a_connect_tcp(InitState) ->
connect := Connect} = State) ->
case Connect(Sock, SSA) of
ok ->
- {error, unexpected_success};
+ ?SEV_IPRINT("ok -> "
+ "unexpected success => SKIP",
+ []),
+ {skip, unexpected_success};
{select, {select_info, ST, SR}} ->
?SEV_IPRINT("select ->"
"~n tag: ~p"
@@ -2870,7 +2918,6 @@ api_a_connect_tcp(InitState) ->
cmd => fun(#{sock := Sock, server_sa := SSA, connect := Connect}) ->
case Connect(Sock, SSA) of
ok ->
- ok = socket:setopt(Sock, otp, debug, false),
ok;
{select, SelectInfo} ->
{error, {unexpected_select, SelectInfo}};
@@ -2894,6 +2941,42 @@ api_a_connect_tcp(InitState) ->
end
end},
+ #{desc => "await continue (send_req)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send req",
+ cmd => fun(#{sock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REQ)
+ end},
+ #{desc => "announce ready (send_req)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await continue (recv_rep)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv_rep)
+ end},
+ #{desc => "recv rep",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ case Recv(Sock) of
+ {ok, ?BASIC_REP} ->
+ ok;
+ {ok, UnexpData} ->
+ {error, {unexpected_data, UnexpData}};
+ {error, _} = ERROR ->
+ %% At the moment there is no way to get
+ %% status or state for the socket...
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv_rep)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_rep),
+ ok
+ end},
+
%% *** Termination ***
#{desc => "await terminate",
cmd => fun(#{tester := Tester} = State) ->
@@ -2986,6 +3069,45 @@ api_a_connect_tcp(InitState) ->
?SEV_AWAIT_READY(Server, server, accept)
end},
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "order server to recv test req (recv req)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, recv_req),
+ ok
+ end},
+ #{desc => "order client to send test req (send req)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client ready (send_req)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, send_req)
+ end},
+ #{desc => "await server ready (recv_req)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order client to recv test rep (send rep)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, recv_rep),
+ ok
+ end},
+ #{desc => "order server to send test rep (send rep)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_rep),
+ ok
+ end},
+ #{desc => "await server ready (send_rep)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_rep)
+ end},
+ #{desc => "await client ready (recv_rep)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, recv_rep)
+ end},
+
%% *** Termination ***
#{desc => "order client to terminate",
--
cgit v1.2.3
From 8db5891cf7ab08980a2e4890dcd70311c21fe76e Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Wed, 8 May 2019 10:38:57 +0200
Subject: [esock|test] Fixed a ttest script
The script used the wrong variable for active (async).
---
erts/emulator/test/esock_ttest/esock-ttest-server-sock | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erts/emulator/test/esock_ttest/esock-ttest-server-sock b/erts/emulator/test/esock_ttest/esock-ttest-server-sock
index 4ea8ce0185..fc87499f09 100755
--- a/erts/emulator/test/esock_ttest/esock-ttest-server-sock
+++ b/erts/emulator/test/esock_ttest/esock-ttest-server-sock
@@ -37,7 +37,7 @@ if [ $# = 2 ]; then
ASYNC=
fi
- ACTIVE="--active $async"
+ ACTIVE="--active $active"
else
echo " Missing args: async and active"
--
cgit v1.2.3
From 0ea354febac9482d9ef703f3c169c6cc3e4fea43 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 4 Jun 2019 12:27:54 +0200
Subject: [esock] Post rebase cleanup
Rebase (on maint as of 20190529) resulted in a number of
issues. Mostly in the (esock) test suite.
OTP-15731
---
erts/emulator/nifs/common/socket_nif.c | 2 --
erts/emulator/test/socket_SUITE.erl | 29 +++++++++++++----------------
2 files changed, 13 insertions(+), 18 deletions(-)
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index d24edde1e0..a14d3c860b 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -16452,8 +16452,6 @@ char* encode_cmsghdr_data_ipv6(ErlNifEnv* env,
#if defined(IPV6_PKTINFO)
case IPV6_PKTINFO:
{
- char* xres;
-
struct in6_pktinfo* pktInfoP = (struct in6_pktinfo*) dataP;
ERL_NIF_TERM ifIndex = MKI(env, pktInfoP->ipi6_ifindex);
ERL_NIF_TERM addr;
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index d3ea02f166..0ec097836b 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -2703,8 +2703,7 @@ api_a_connect_tcp(InitState) ->
%% *** Init part ***
#{desc => "which local address",
cmd => fun(#{domain := Domain} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain, addr => LAddr},
+ LSA = which_local_socket_addr(Domain),
{ok, State#{lsa => LSA}}
end},
#{desc => "create listen socket",
@@ -2835,10 +2834,8 @@ api_a_connect_tcp(InitState) ->
%% *** The init part ***
#{desc => "which server (local) address",
cmd => fun(#{domain := Domain, server_port := Port} = State) ->
- LAddr = which_local_addr(Domain),
- LSA = #{family => Domain,
- addr => LAddr},
- SSA = LSA#{port => Port},
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
{ok, State#{local_sa => LSA, server_sa => SSA}}
end},
#{desc => "create socket",
@@ -4992,16 +4989,6 @@ api_a_recv_cancel_tcp(InitState) ->
_MRef = erlang:monitor(process, Pid),
ok
end},
- #{desc => "monitor alt-server 1",
- cmd => fun(#{alt_server1 := Pid} = _State) ->
- _MRef = erlang:monitor(process, Pid),
- ok
- end},
- #{desc => "monitor alt-server 2",
- cmd => fun(#{alt_server2 := Pid} = _State) ->
- _MRef = erlang:monitor(process, Pid),
- ok
- end},
%% Start the server
#{desc => "order server start",
@@ -5371,6 +5358,16 @@ api_a_mrecv_cancel_udp(InitState) ->
_MRef = erlang:monitor(process, Pid),
ok
end},
+ #{desc => "monitor alt-server 1",
+ cmd => fun(#{alt_server1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor alt-server 2",
+ cmd => fun(#{alt_server2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
%% Start the server
#{desc => "order server start",
--
cgit v1.2.3