From b98a1e312db1393aa5dd9cdd2e1a5f3daaf954bf Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 4 Oct 2018 18:16:38 +0200 Subject: [socket-nif|test] begin rework socket suite Begin rework socket test suite using a command evaluator. OTP-14831 --- lib/kernel/test/socket_SUITE.erl | 222 ++++++++++++++++++++++++++++++++++----- 1 file changed, 198 insertions(+), 24 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 1fff17cf8c..70810f5f3d 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -71,6 +71,17 @@ %% -export([]). +-type evaluator_state() :: term(). +-type command_fun() :: + fun((State :: evaluator_state()) -> ok) | + fun((State :: evaluator_state()) -> {ok, evaluator_state()}) | + fun((State :: evaluator_state()) -> {error, term()}). + +-type command() :: #{desc := string(), + cmd := command_fun() + }. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(BASIC_REQ, <<"hejsan">>). @@ -178,7 +189,10 @@ api_b_open_and_close_udp4(doc) -> []; api_b_open_and_close_udp4(_Config) when is_list(_Config) -> tc_begin(api_b_open_and_close_udp4), - ok = api_b_open_and_close(inet, dgram, udp), + State = #{domain => inet, + type => dgram, + protocol => udp}, + ok = api_b_open_and_close(State), tc_end(). @@ -192,34 +206,103 @@ api_b_open_and_close_tcp4(doc) -> []; api_b_open_and_close_tcp4(_Config) when is_list(_Config) -> tc_begin(api_b_open_and_close_tcp4), - ok = api_b_open_and_close(inet, stream, tcp), + State = #{domain => inet, + type => stream, + protocol => tcp}, + ok = api_b_open_and_close(State), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -api_b_open_and_close(Domain, Type, Proto) -> - Socket = case socket:open(Domain, Type, Proto) of - {ok, S} -> - S; - {error, Reason} -> - ?FAIL({open, Reason}) - end, - %% Domain is not available on all platforms: - case socket:getopt(Socket, socket, domain) of - {ok, Domain} -> - ok; - {error, einval} -> - ok; - Else -> - ?FAIL({getopt, domain, Else}) - end, - {ok, Type} = socket:getopt(Socket, socket, type), - {ok, Proto} = socket:getopt(Socket, socket, protocol), - Self = self(), - {ok, Self} = socket:getopt(Socket, otp, controlling_process), - ok = socket:close(Socket), - ok. +api_b_open_and_close(InitState) -> + Seq = + [ + #{desc => "open", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Protocol} = S) -> + Res = socket:open(Domain, Type, Protocol), + {ok, {S, Res}} + end}, + #{desc => "validate open", + cmd => fun({S, {ok, Sock}}) -> + NewS = S#{socket => Sock}, + {ok, NewS}; + ({_, {error, _} = ERROR}) -> + ERROR + end}, + #{desc => "get domain (maybe)", + cmd => fun(#{socket := Sock} = S) -> + Res = socket:getopt(Sock, socket, domain), + {ok, {S, Res}} + end}, + #{desc => "validate domain (maybe)", + cmd => fun({#{domain := Domain} = S, {ok, Domain}}) -> + {ok, S}; + ({#{domain := ExpDomain}, {ok, Domain}}) -> + {error, {unexpected_domain, ExpDomain, Domain}}; + %% Some platforms do not support this option + ({S, {error, einval}}) -> + {ok, S}; + ({_, {error, _} = ERROR}) -> + ERROR + end}, + #{desc => "get type", + cmd => fun(#{socket := Sock} = State) -> + Res = socket:getopt(Sock, socket, type), + {ok, {State, Res}} + end}, + #{desc => "validate type", + cmd => fun({#{type := Type} = State, {ok, Type}}) -> + {ok, State}; + ({#{type := ExpType}, {ok, Type}}) -> + {error, {unexpected_type, ExpType, Type}}; + ({_, {error, _} = ERROR}) -> + ERROR + end}, + #{desc => "get protocol", + cmd => fun(#{socket := Sock} = State) -> + Res = socket:getopt(Sock, socket, protocol), + {ok, {State, Res}} + end}, + #{desc => "validate protocol", + cmd => fun({#{protocol := Protocol} = State, {ok, Protocol}}) -> + {ok, State}; + ({#{protocol := ExpProtocol}, {ok, Protocol}}) -> + {error, {unexpected_type, ExpProtocol, Protocol}}; + ({_, {error, _} = ERROR}) -> + ERROR + end}, + #{desc => "get controlling-process", + cmd => fun(#{socket := Sock} = State) -> + Res = socket:getopt(Sock, otp, controlling_process), + {ok, {State, Res}} + end}, + #{desc => "validate controlling-process", + cmd => fun({State, {ok, Pid}}) -> + case self() of + Pid -> + {ok, State}; + _ -> + {error, {unexpected_owner, Pid}} + end; + ({_, {error, _} = ERROR}) -> + ERROR + end}, + #{desc => "close socket", + cmd => fun(#{socket := Sock} = State) -> + Res = socket:close(Sock), + {ok, {State, Res}} + end}, + #{desc => "validate socket close", + cmd => fun({_, ok}) -> + {ok, normal}; + ({_, {error, _} = ERROR}) -> + ERROR + end}], + Evaluator = evaluator_start("tester", Seq, InitState), + ok = await_evaluator_finish([Evaluator]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1230,6 +1313,97 @@ which_addr2(Domain, [_|IFO]) -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% An evaluator is a process that executes a command sequence. +%% A test case will consist of atleast one evaluator (one for +%% each actor). +%% The evaluator process *always* run locally. Which means that +%% it will act as a "proxy" for remote nodes in necessary. +%% When the command sequence has been processed, the final state +%% will be used as exit reason. +%% A successful command shall evaluate to ok | {ok, NewState} + +-spec evaluator_start(Name, Seq, Init) -> {Pid, MRef} when + Name :: string(), + Seq :: [command()], + Init :: evaluator_state(), + Pid :: pid(), + MRef :: reference(). + +evaluator_start(Name, Seq, Init) + when is_list(Name) andalso is_list(Seq) andalso (Seq =/= []) -> + erlang:spawn_monitor(fun() -> evaluator_init(Name, Seq, Init) end). + +evaluator_init(Name, Seq, Init) -> + put(sname, Name), + evaluator_loop(1, Seq, Init). + +evaluator_loop(_ID, [], FinalState) -> + exit(FinalState); +evaluator_loop(ID, [#{desc := Desc, + cmd := Cmd}|Cmds], State) when is_function(Cmd, 1) -> + ei("evaluate command ~2w: ~s", [ID, Desc]), + try Cmd(State) of + ok -> + evaluator_loop(ID + 1, Cmds, State); + {ok, NewState} -> + evaluator_loop(ID + 1, Cmds, NewState); + {error, Reason} -> + ee("command ~w failed: " + "~n Reason: ~p", [ID, Reason]), + exit({command_failed, ID, Reason, State}) + catch + C:E:S -> + ee("command ~w crashed: " + "~n Class: ~p" + "~n Error: ~p" + "~n Call Stack: ~p", [ID, C, E, S]), + exit({command_crashed, ID, {C,E,S}, State}) + end. + +await_evaluator_finish(Evs) -> + await_evaluator_finish(Evs, []). + +await_evaluator_finish([], []) -> + ok; +await_evaluator_finish([], Fails) -> + Fails; +await_evaluator_finish(Evs, Fails) -> + receive + {'DOWN', _MRef, process, Pid, normal} -> + case lists:keydelete(Pid, 1, Evs) of + Evs -> + p("unknown process ~p died (normal)", [Pid]), + await_evaluator_finish(Evs, Fails); + NewEvs -> + p("evaluator ~p success", [Pid]), + await_evaluator_finish(NewEvs, Fails) + end; + {'DOWN', _MRef, process, Pid, Reason} -> + case lists:keydelete(Pid, 1, Evs) of + Evs -> + p("unknown process ~p died: " + "~n ~p", [Pid, Reason]), + await_evaluator_finish(Evs, Fails); + NewEvs -> + p("Evaluator ~p failed", [Pid]), + await_evaluator_finish(NewEvs, [{Pid, Reason}|Fails]) + end + end. + + +ei(F, A) -> + eprint("", F, A). + +ee(F, A) -> + eprint(" ", F, A). + +eprint(Prefix, F, A) -> + io:format(user, "[~s][~p] ~s" ++ F ++ "~n", [get(sname), self(), Prefix | A]). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sock_open(Domain, Type, Proto) -> -- cgit v1.2.3 From 8210ffc460a0bfab8b3007afe3e1141c1bb9dec6 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 5 Oct 2018 11:07:55 +0200 Subject: [socket-nif|test] Two more test cases (udp) reworked Two test cases where reworked using the command evaluator. OTP-14831 --- lib/kernel/test/socket_SUITE.erl | 111 +++++++++++++++++++++++++++++++++------ 1 file changed, 94 insertions(+), 17 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 70810f5f3d..ed5722f008 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -305,6 +305,7 @@ api_b_open_and_close(InitState) -> ok = await_evaluator_finish([Evaluator]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Basically send and receive on an IPv4 UDP (dgram) socket using @@ -321,7 +322,10 @@ api_b_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) -> Recv = fun(Sock) -> socket:recvfrom(Sock) end, - ok = api_b_send_and_recv_udp(inet, Send, Recv), + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState), tc_end(). @@ -352,27 +356,100 @@ api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> ERROR end end, - ok = api_b_send_and_recv_udp(inet, Send, Recv), + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -api_b_send_and_recv_udp(Domain, Send, Recv) -> - SockSrc = sock_open(Domain, dgram, udp), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, - sock_bind(SockSrc, LSA), - SockDst = sock_open(Domain, dgram, udp), - sock_bind(SockDst, LSA), - Dst = sock_sockname(SockDst), - ok = Send(SockSrc, ?BASIC_REQ, Dst), - {ok, {Src, ?BASIC_REQ}} = Recv(SockDst), - ok = Send(SockDst, ?BASIC_REP, Src), - {ok, {Dst, ?BASIC_REP}} = Recv(SockSrc), - socket:close(SockSrc), - socket:close(SockDst), - ok. +api_b_send_and_recv_udp(InitState) -> + %% SockSrc = sock_open(Domain, dgram, udp), + %% LAddr = which_local_addr(Domain), + %% LSA = #{family => Domain, addr => LAddr}, + %% sock_bind(SockSrc, LSA), + %% SockDst = sock_open(Domain, dgram, udp), + %% sock_bind(SockDst, LSA), + %% Dst = sock_sockname(SockDst), + %% ok = Send(SockSrc, ?BASIC_REQ, Dst), + %% {ok, {Src, ?BASIC_REQ}} = Recv(SockDst), + %% ok = Send(SockDst, ?BASIC_REP, Src), + %% {ok, {Dst, ?BASIC_REP}} = Recv(SockSrc), + %% socket:close(SockSrc), + %% socket:close(SockDst), + Seq = + [ + #{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 src socket", + cmd => fun(#{domain := Domain} = State) -> + Sock = sock_open(Domain, dgram, udp), + SASrc = sock_sockname(Sock), + {ok, State#{sock_src => Sock, sa_src => SASrc}} + end}, + #{desc => "bind src", + cmd => fun(#{sock_src := Sock, lsa := LSA}) -> + sock_bind(Sock, LSA), + ok + end}, + #{desc => "sockname src socket", + cmd => fun(#{sock_src := Sock} = State) -> + SASrc = sock_sockname(Sock), + %% ei("src sockaddr: ~p", [SASrc]), + {ok, State#{sa_src => SASrc}} + end}, + #{desc => "open dst socket", + cmd => fun(#{domain := Domain} = State) -> + Sock = sock_open(Domain, dgram, udp), + {ok, State#{sock_dst => Sock}} + end}, + #{desc => "bind dst", + cmd => fun(#{sock_dst := Sock, lsa := LSA}) -> + sock_bind(Sock, LSA), + ok + end}, + #{desc => "sockname dst socket", + cmd => fun(#{sock_dst := Sock} = State) -> + SADst = sock_sockname(Sock), + %% ei("dst sockaddr: ~p", [SADst]), + {ok, State#{sa_dst => SADst}} + end}, + #{desc => "send req (to dst)", + cmd => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) -> + ok = Send(Sock, ?BASIC_REQ, Dst) + end}, + #{desc => "recv req (from src)", + cmd => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv} = State) -> + {ok, {Src, ?BASIC_REQ}} = Recv(Sock), + ok + end}, + #{desc => "send rep (to src)", + cmd => fun(#{sock_dst := Sock, sa_src := Src, send := Send}) -> + ok = Send(Sock, ?BASIC_REP, Src) + end}, + #{desc => "recv rep (from dst)", + cmd => fun(#{sock_src := Sock, sa_dst := Dst, recv := Recv}) -> + {ok, {Dst, ?BASIC_REP}} = Recv(Sock), + ok + end}, + #{desc => "close src socket", + cmd => fun(#{sock_src := Sock}) -> + ok = socket:close(Sock) + end}, + #{desc => "close dst socket", + cmd => fun(#{sock_dst := Sock}) -> + ok = socket:close(Sock), + {ok, normal} + end} + ], + Evaluator = evaluator_start("tester", Seq, InitState), + ok = await_evaluator_finish([Evaluator]). -- cgit v1.2.3 From d4cedb9fa86db720cc247b9201d5c7e1e8aab461 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 5 Oct 2018 14:23:47 +0200 Subject: [socket-nif|test] Two more test cases (tcp) reworked Two (tcp) test cases where reworked using the command evaluator. OTP-14831 --- lib/kernel/test/socket_SUITE.erl | 288 ++++++++++++++++++++++----------------- 1 file changed, 160 insertions(+), 128 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index ed5722f008..e50daaf367 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -71,6 +71,7 @@ %% -export([]). +-type initial_evaluator_state() :: map(). -type evaluator_state() :: term(). -type command_fun() :: fun((State :: evaluator_state()) -> ok) | @@ -78,8 +79,7 @@ fun((State :: evaluator_state()) -> {error, term()}). -type command() :: #{desc := string(), - cmd := command_fun() - }. + cmd := command_fun()}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -89,6 +89,8 @@ -define(FAIL(R), exit(R)). +-define(SLEEP(T), receive after T -> ok end). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -366,19 +368,6 @@ api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% api_b_send_and_recv_udp(InitState) -> - %% SockSrc = sock_open(Domain, dgram, udp), - %% LAddr = which_local_addr(Domain), - %% LSA = #{family => Domain, addr => LAddr}, - %% sock_bind(SockSrc, LSA), - %% SockDst = sock_open(Domain, dgram, udp), - %% sock_bind(SockDst, LSA), - %% Dst = sock_sockname(SockDst), - %% ok = Send(SockSrc, ?BASIC_REQ, Dst), - %% {ok, {Src, ?BASIC_REQ}} = Recv(SockDst), - %% ok = Send(SockDst, ?BASIC_REP, Src), - %% {ok, {Dst, ?BASIC_REP}} = Recv(SockSrc), - %% socket:close(SockSrc), - %% socket:close(SockDst), Seq = [ #{desc => "local address", @@ -425,7 +414,7 @@ api_b_send_and_recv_udp(InitState) -> ok = Send(Sock, ?BASIC_REQ, Dst) end}, #{desc => "recv req (from src)", - cmd => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv} = State) -> + cmd => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) -> {ok, {Src, ?BASIC_REQ}} = Recv(Sock), ok end}, @@ -469,7 +458,10 @@ api_b_send_and_recv_tcp4(_Config) when is_list(_Config) -> Recv = fun(Sock) -> socket:recv(Sock) end, - ok = api_b_send_and_recv_tcp(inet, Send, Recv), + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState), tc_end(). @@ -496,119 +488,157 @@ api_b_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) -> ERROR end end, - ok = api_b_send_and_recv_tcp(inet, Send, Recv), + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -api_b_send_and_recv_tcp(Domain, Send, Recv) -> +api_b_send_and_recv_tcp(InitState) -> process_flag(trap_exit, true), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, - Starter = self(), - ServerFun = fun() -> - put(sname, "server"), - %% Create the listen socket - ServerLSock = - case socket:open(Domain, stream, tcp) of - {ok, S1} -> - S1; - {error, ServerOR} -> - ?FAIL({server, open, ServerOR}) - end, - %% And bind it to the local address - SP = - case socket:bind(ServerLSock, LSA) of - {ok, P} -> - P; - {error, ServerBR} -> - ?FAIL({server, bind, ServerBR}) - end, - %% Listen for connecting clients - case socket:listen(ServerLSock) of - ok -> - ok; - {error, ServerLR} -> - ?FAIL({server, listen, ServerLR}) - end, - %% We are ready - Starter ! {self(), {ok, SP}}, - %% Accept connections - ServerSock = - case socket:accept(ServerLSock) of - {ok, Sock} -> - Sock; - {error, ServerAR} -> - ?FAIL({server, accept, ServerAR}) - end, - %% Wait for a message - case Recv(ServerSock) of - {ok, ?BASIC_REQ} -> - ok; - {error, ServerRR} -> - ?FAIL({server, recv, ServerRR}) - end, - %% Send the reply - case Send(ServerSock, ?BASIC_REP) of - ok -> - ok; - {error, ServerSR} -> - ?FAIL({server, send, ServerSR}) - end, - %% Close the sockets - socket:close(ServerSock), - socket:close(ServerLSock), - %% We are done - exit(normal) - end, - Server = spawn_link(ServerFun), - ServerPort = - receive - {Server, {ok, P}} -> - P; - {'EXIT', Server, ServerStartReason} -> - ?FAIL({server, start, ServerStartReason}) - end, - ClientSock = - case socket:open(Domain, stream, tcp) of - {ok, S2} -> - S2; - {error, ClientOR} -> - ?FAIL({client, open, ClientOR}) - end, - case socket:bind(ClientSock, LSA) of - {ok, _} -> - ok; - {error, ClientBR} -> - ?FAIL({client, bind, ClientBR}) - end, - case socket:connect(ClientSock, LSA#{port => ServerPort}) of - ok -> - ok; - {error, ClientCR} -> - ?FAIL({client, connect, ClientCR}) - end, - case Send(ClientSock, ?BASIC_REQ) of - ok -> - ok; - {error, ClientSR} -> - ?FAIL({client, send, ClientSR}) - end, - case Recv(ClientSock) of - {ok, ?BASIC_REP} -> - ok; - {ok, Msg} -> - ?FAIL({client, recv, {unexpected, Msg}}) - end, - receive - {'EXIT', Server, normal} -> - ok; - {'EXIT', Server, ServerStopReason} -> - ?FAIL({server, stop, ServerStopReason}) - end, - socket:close(ClientSock), - ok. + ServerSeq = + [ + #{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 server port", + cmd => fun(#{parent := Parent, lport := Port}) -> + ei("announcing port to parent (~p)", [Parent]), + Parent ! {server_port, self(), Port}, + ok + end}, + #{desc => "await connection", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, Sock} -> + ei("accepted: ~p", [Sock]), + {ok, State#{tsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "await request", + cmd => fun(#{tsock := Sock, recv := Recv}) -> + case Recv(Sock) of + {ok, ?BASIC_REQ} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "send reply", + cmd => fun(#{tsock := Sock, send := Send}) -> + Send(Sock, ?BASIC_REP) + end}, + #{desc => "sleep some", + cmd => fun(_) -> + ?SLEEP(1000), + ok + end}, + #{desc => "close traffic socket", + cmd => fun(#{tsock := Sock}) -> + socket:close(Sock) + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock}) -> + socket:close(Sock) + end}, + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + ClientSeq = + [ + #{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#{lsa => LSA, ssa => 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, lsa := LSA} = State) -> + case socket:bind(Sock, LSA) of + {ok, Port} -> + {ok, State#{port => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "connect to server", + cmd => fun(#{sock := Sock, ssa := SSA}) -> + socket:connect(Sock, SSA) + end}, + #{desc => "send request (to server)", + cmd => fun(#{sock := Sock, send := Send}) -> + Send(Sock, ?BASIC_REQ) + end}, + #{desc => "recv reply (from server)", + cmd => fun(#{sock := Sock, recv := Recv}) -> + {ok, ?BASIC_REP} = Recv(Sock), + ok + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock}) -> + socket:close(Sock) + end}, + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + p("start server evaluator"), + Server = evaluator_start("server", ServerSeq, InitState), + p("await server (~p) port", [Server]), + SPort = receive + {server_port, Server, Port} -> + Port + end, + p("start client evaluator"), + Client = evaluator_start("client", ClientSeq, InitState#{server_port => SPort}), + p("await evaluator(s)"), + ok = await_evaluator_finish([Server, Client]). @@ -1404,13 +1434,15 @@ which_addr2(Domain, [_|IFO]) -> -spec evaluator_start(Name, Seq, Init) -> {Pid, MRef} when Name :: string(), Seq :: [command()], - Init :: evaluator_state(), + Init :: initial_evaluator_state(), Pid :: pid(), MRef :: reference(). evaluator_start(Name, Seq, Init) when is_list(Name) andalso is_list(Seq) andalso (Seq =/= []) -> - erlang:spawn_monitor(fun() -> evaluator_init(Name, Seq, Init) end). + Init2 = Init#{parent => self()}, + {Pid, _} = erlang:spawn_monitor(fun() -> evaluator_init(Name, Seq, Init2) end), + Pid. evaluator_init(Name, Seq, Init) -> put(sname, Name), @@ -1449,7 +1481,7 @@ await_evaluator_finish([], Fails) -> await_evaluator_finish(Evs, Fails) -> receive {'DOWN', _MRef, process, Pid, normal} -> - case lists:keydelete(Pid, 1, Evs) of + case lists:delete(Pid, Evs) of Evs -> p("unknown process ~p died (normal)", [Pid]), await_evaluator_finish(Evs, Fails); @@ -1458,7 +1490,7 @@ await_evaluator_finish(Evs, Fails) -> await_evaluator_finish(NewEvs, Fails) end; {'DOWN', _MRef, process, Pid, Reason} -> - case lists:keydelete(Pid, 1, Evs) of + case lists:delete(Pid, Evs) of Evs -> p("unknown process ~p died: " "~n ~p", [Pid, Reason]), -- cgit v1.2.3 From 2a10a95dc380b41deffa060e30c0c461dfc3df8d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 10 Oct 2018 17:59:55 +0200 Subject: [socket-nif|test] Reworked simple otp options test case --- lib/kernel/test/socket_SUITE.erl | 295 +++++++++++++++++++++++++++++++-------- 1 file changed, 234 insertions(+), 61 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index e50daaf367..a9f51cd11f 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -652,10 +652,6 @@ api_opt_simple_otp_options(doc) -> api_opt_simple_otp_options(_Config) when is_list(_Config) -> tc_begin(api_opt_simple_otp_options), - p("Create sockets"), - S1 = sock_open(inet, stream, tcp), - S2 = sock_open(inet, dgram, udp), - Get = fun(S, Key) -> socket:getopt(S, otp, Key) end, @@ -663,68 +659,245 @@ api_opt_simple_otp_options(_Config) when is_list(_Config) -> socket:setopt(S, otp, Key, Val) end, - p("Create dummy process"), - Pid = spawn_link(fun() -> - put(sname, "dummy"), - receive - die -> - exit(normal) - end - end), - - F = fun(Sock) -> - p("Test IOW"), - {ok, IOW} = Get(Sock, iow), - NotIOW = not IOW, - ok = Set(Sock, iow, NotIOW), - {ok, NotIOW} = Get(Sock, iow), - - p("Test rcvbuf"), - {ok, RcvBuf} = Get(Sock, rcvbuf), - RcvBuf2 = RcvBuf*2, - ok = Set(Sock, rcvbuf, RcvBuf2), - {ok, RcvBuf2} = Get(Sock, rcvbuf), - ok = Set(Sock, rcvbuf, default), - {ok, RcvBuf} = Get(Sock, rcvbuf), - - p("Test rcvctrlbuf"), - {ok, RcvCtrlBuf} = Get(Sock, rcvctrlbuf), - RcvCtrlBuf2 = RcvCtrlBuf*2, - ok = Set(Sock, rcvctrlbuf, RcvCtrlBuf2), - {ok, RcvCtrlBuf2} = Get(Sock, rcvctrlbuf), - ok = Set(Sock, rcvctrlbuf, default), - {ok, RcvCtrlBuf} = Get(Sock, rcvctrlbuf), - - p("Test sndctrlbuf"), - {ok, SndCtrlBuf} = Get(Sock, sndctrlbuf), - SndCtrlBuf2 = SndCtrlBuf*2, - ok = Set(Sock, sndctrlbuf, SndCtrlBuf2), - {ok, SndCtrlBuf2} = Get(Sock, sndctrlbuf), - ok = Set(Sock, sndctrlbuf, default), - {ok, RcvCtrlBuf} = Get(Sock, sndctrlbuf), - - p("Test controlling-process"), - Self = self(), - {ok, Self} = Get(Sock, controlling_process), - ok = Set(Sock, controlling_process, Pid), - {ok, Pid} = Get(Sock, controlling_process) + Seq = + [ + %% *** Init part *** + #{desc => "create socket", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Protocol} = State) -> + Sock = sock_open(Domain, Type, Protocol), + {ok, State#{sock => Sock}} + end}, + #{desc => "create dummy process", + cmd => fun(State) -> + Pid = spawn_link(fun() -> + put(sname, "dummy"), + receive + die -> + exit(normal) + end + end), + {ok, State#{dummy => Pid}} + end}, - end, + %% *** Check iow part *** + #{desc => "get iow", + cmd => fun(#{sock := Sock} = State) -> + case Get(Sock, iow) of + {ok, IOW} when is_boolean(IOW) -> + {ok, State#{iow => IOW}}; + {ok, InvalidIOW} -> + {error, {invalid, InvalidIOW}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "set (new) iow", + cmd => fun(#{sock := Sock, iow := OldIOW} = State) -> + NewIOW = not OldIOW, + case Set(Sock, iow, NewIOW) of + ok -> + {ok, State#{iow => NewIOW}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "get (new) iow", + cmd => fun(#{sock := Sock, iow := IOW}) -> + case Get(Sock, iow) of + {ok, IOW} -> + ok; + {ok, InvalidIOW} -> + {error, {invalid, InvalidIOW}}; + {error, _} = ERROR -> + ERROR + end + end}, - p("Test stream/tcp "), - F(S1), + %% *** Check rcvbuf part *** + #{desc => "get rcvbuf", + cmd => fun(#{sock := Sock} = State) -> + case Get(Sock, rcvbuf) of + {ok, RcvBuf} when is_integer(RcvBuf) -> + {ok, State#{rcvbuf => RcvBuf}}; + {ok, InvalidRcvBuf} -> + {error, {invalid, InvalidRcvBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "set (new) rcvbuf", + cmd => fun(#{sock := Sock, rcvbuf := OldRcvBuf} = State) -> + NewRcvBuf = 2 * OldRcvBuf, + case Set(Sock, rcvbuf, NewRcvBuf) of + ok -> + {ok, State#{rcvbuf => NewRcvBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "get (new) rcvbuf", + cmd => fun(#{sock := Sock, rcvbuf := RcvBuf}) -> + case Get(Sock, rcvbuf) of + {ok, RcvBuf} -> + ok; + {ok, InvalidRcvBuf} -> + {error, {invalid, InvalidRcvBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, - p("Test dgram/udp "), - F(S2), + %% *** Check rcvctrlbuf part *** + #{desc => "get rcvctrlbuf", + cmd => fun(#{sock := Sock} = State) -> + case Get(Sock, rcvctrlbuf) of + {ok, RcvCtrlBuf} when is_integer(RcvCtrlBuf) -> + {ok, State#{rcvctrlbuf => RcvCtrlBuf}}; + {ok, InvalidRcvCtrlBuf} -> + {error, {invalid, InvalidRcvCtrlBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "set (new) rcvctrlbuf", + cmd => fun(#{sock := Sock, rcvctrlbuf := OldRcvCtrlBuf} = State) -> + NewRcvCtrlBuf = 2 * OldRcvCtrlBuf, + case Set(Sock, rcvctrlbuf, NewRcvCtrlBuf) of + ok -> + {ok, State#{rcvctrlbuf => NewRcvCtrlBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "get (new) rcvctrlbuf", + cmd => fun(#{sock := Sock, rcvctrlbuf := RcvCtrlBuf}) -> + case Get(Sock, rcvctrlbuf) of + {ok, RcvCtrlBuf} -> + ok; + {ok, InvalidRcvCtrlBuf} -> + {error, {invalid, InvalidRcvCtrlBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + %% *** Check rcvctrlbuf part *** + #{desc => "get rcvctrlbuf", + cmd => fun(#{sock := Sock} = State) -> + case Get(Sock, rcvctrlbuf) of + {ok, RcvCtrlBuf} when is_integer(RcvCtrlBuf) -> + {ok, State#{rcvctrlbuf => RcvCtrlBuf}}; + {ok, InvalidRcvCtrlBuf} -> + {error, {invalid, InvalidRcvCtrlBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "set (new) rcvctrlbuf", + cmd => fun(#{sock := Sock, rcvctrlbuf := OldRcvCtrlBuf} = State) -> + NewRcvCtrlBuf = 2 * OldRcvCtrlBuf, + case Set(Sock, rcvctrlbuf, NewRcvCtrlBuf) of + ok -> + {ok, State#{rcvctrlbuf => NewRcvCtrlBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "get (new) rcvctrlbuf", + cmd => fun(#{sock := Sock, rcvctrlbuf := RcvCtrlBuf}) -> + case Get(Sock, rcvctrlbuf) of + {ok, RcvCtrlBuf} -> + ok; + {ok, InvalidRcvCtrlBuf} -> + {error, {invalid, InvalidRcvCtrlBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, - p("kill dummy process"), - %% This will also close its sockets (S1 and S2), - %% This should really be tested explicitly... - Pid ! die, - %% p("close sockets"), - %% sock_close(S1), - %% sock_close(S2), + %% *** Check sndctrlbuf part *** + #{desc => "get sndctrlbuf", + cmd => fun(#{sock := Sock} = State) -> + case Get(Sock, sndctrlbuf) of + {ok, SndCtrlBuf} when is_integer(SndCtrlBuf) -> + {ok, State#{sndctrlbuf => SndCtrlBuf}}; + {ok, InvalidSndCtrlBuf} -> + {error, {invalid, InvalidSndCtrlBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "set (new) sndctrlbuf", + cmd => fun(#{sock := Sock, sndctrlbuf := OldSndCtrlBuf} = State) -> + NewSndCtrlBuf = 2 * OldSndCtrlBuf, + case Set(Sock, sndctrlbuf, NewSndCtrlBuf) of + ok -> + {ok, State#{sndctrlbuf => NewSndCtrlBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "get (new) sndctrlbuf", + cmd => fun(#{sock := Sock, sndctrlbuf := SndCtrlBuf}) -> + case Get(Sock, sndctrlbuf) of + {ok, SndCtrlBuf} -> + ok; + {ok, InvalidSndCtrlBuf} -> + {error, {invalid, InvalidSndCtrlBuf}}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** Check controlling-process part *** + #{desc => "verify self as controlling-process", + cmd => fun(#{sock := Sock}) -> + Self = self(), + case Get(Sock, controlling_process) of + {ok, Self} -> + ok; + {ok, InvalidPid} -> + {error, {invalid, InvalidPid}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "set dummy as controlling-process", + cmd => fun(#{sock := Sock, dummy := Dummy}) -> + Set(Sock, controlling_process, Dummy) + end}, + #{desc => "verify dummy as controlling-process", + cmd => fun(#{sock := Sock, dummy := Dummy}) -> + case Get(Sock, controlling_process) of + {ok, Dummy} -> + ok; + {ok, InvalidPid} -> + {error, {invalid, InvalidPid}}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + p("Run test for stream/tcp socket"), + InitState1 = #{domain => inet, type => stream, protocol => tcp}, + Tester1 = evaluator_start("tcp-tester", Seq, InitState1), + p("await evaluator 1"), + ok = await_evaluator_finish([Tester1]), + + p("Run test for dgram/udp socket"), + InitState2 = #{domain => inet, type => dgram, protocol => udp}, + Tester2 = evaluator_start("udp-tester", Seq, InitState2), + p("await evaluator 2"), + ok = await_evaluator_finish([Tester2]), tc_end(). -- cgit v1.2.3 From 8092cb3bb4e3a90be52df03fff211a2e29dc8784 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 10 Oct 2018 18:55:33 +0200 Subject: [socket-nif|test] Reworked otp controlling process option test case --- lib/kernel/test/socket_SUITE.erl | 327 +++++++++++++++++++++++++++------------ 1 file changed, 224 insertions(+), 103 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index a9f51cd11f..23a77f428c 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -912,10 +912,6 @@ api_opt_simple_otp_controlling_process(doc) -> api_opt_simple_otp_controlling_process(_Config) when is_list(_Config) -> tc_begin(api_opt_simple_otp_controlling_process), - p("Create sockets"), - S1 = sock_open(inet, stream, tcp), - S2 = sock_open(inet, dgram, udp), - Get = fun(S, Key) -> socket:getopt(S, otp, Key) end, @@ -923,110 +919,235 @@ api_opt_simple_otp_controlling_process(_Config) when is_list(_Config) -> socket:setopt(S, otp, Key, Val) end, - AwaitStart = - fun() -> - p("await start command"), - receive - {start, P, S} -> - {P, S} - end - end, - AwaitContinue = - fun(Pid) -> - p("await continue command"), - receive - {continue, Pid} -> - ok - end - end, - AwaitReady = - fun(Pid) -> - p("await ready confirmation from ~p", [Pid]), - receive - {ready, Pid} -> - ok - end - end, - AwaitDie = - fun(Pid) -> - p("await die command"), - receive - {die, Pid} -> - ok - end - end, - ClientStarter = - fun() -> - put(sname, "client"), - Self = self(), - {Parent, Sock} = AwaitStart(), - p("verify parent ~p controlling", [Parent]), - {ok, Parent} = Get(Sock, controlling_process), - p("attempt invalid control transfer (to self)"), - {error, not_owner} = Set(Sock, controlling_process, self()), - p("verify parent ~p (still) controlling", [Parent]), - {ok, Parent} = Get(Sock, controlling_process), - p("announce ready"), - Parent ! {ready, self()}, - - AwaitContinue(Parent), - p("verify self controlling"), - {ok, Self} = Get(Sock, controlling_process), - p("transfer control to parent ~p", [Parent]), - ok = Set(Sock, controlling_process, Parent), - p("attempt invalid control transfer (to self)"), - {error, not_owner} = Set(Sock, controlling_process, self()), - p("verify parent ~p controlling", [Parent]), - {ok, Parent} = Get(Sock, controlling_process), - p("announce ready"), - Parent ! {ready, self()}, - - AwaitDie(Parent), - p("done"), - exit(normal) - end, + ClientSeq = + [ + %% *** Init part *** + #{desc => "await start", + cmd => fun(State) -> + receive + {start, Tester, Socket} -> + {ok, State#{tester => Tester, + sock => Socket}} + end + end}, + #{desc => "verify tester as controlling-process", + cmd => fun(#{tester := Tester, sock := Sock} = _State) -> + case Get(Sock, controlling_process) of + {ok, Tester} -> + ok; + {ok, InvalidPid} -> + {error, {invalid, InvalidPid}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "attempt invalid controlling-process transfer (to self)", + cmd => fun(#{sock := Sock} = _State) -> + case Set(Sock, controlling_process, self()) of + {error, not_owner} -> + ok; + ok -> + {error, unexpected_success}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (1)", + cmd => fun(#{tester := Tester} = _State) -> + Tester ! {ready, self()}, + ok + end}, + #{desc => "await continue", + cmd => fun(#{tester := Tester} = _State) -> + receive + {continue, Tester} -> + ok + end + end}, + #{desc => "verify self as controlling-process", + cmd => fun(#{sock := Sock} = _State) -> + Self = self(), + case Get(Sock, controlling_process) of + {ok, Self} -> + ok; + {ok, InvalidPid} -> + {error, {invalid, InvalidPid}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "attempt controlling-process transfer to tester", + cmd => fun(#{tester := Tester, sock := Sock} = _State) -> + Set(Sock, controlling_process, Tester) + end}, + #{desc => "attempt invalid controlling-process transfer (to self)", + cmd => fun(#{sock := Sock} = _State) -> + case Set(Sock, controlling_process, self()) of + {error, not_owner} -> + ok; + ok -> + {error, unexpected_success}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (2)", + cmd => fun(#{tester := Tester} = _State) -> + Tester ! {ready, self()}, + ok + end}, + #{desc => "await termination", + cmd => fun(#{tester := Tester} = State) -> + receive + {terminate, Tester} -> + State1 = maps:remove(tester, State), + State2 = maps:remove(sock, State1), + {ok, State2} + end + end}, - Tester = - fun(Sock, Client) -> - p("start"), - Self = self(), - p("verify self controlling"), - {ok, Self} = Get(Sock, controlling_process), - p("announce start"), - Client ! {start, Self, Sock}, - AwaitReady(Client), - - p("transfer control to client ~p", [Client]), - ok = Set(Sock, controlling_process, Client), - p("verify client ~p controlling", [Client]), - {ok, Client} = Get(Sock, controlling_process), - p("attempt invalid control transfer (to self)"), - {error, not_owner} = Set(Sock, controlling_process, self()), - p("announce continue"), - Client ! {continue, Self}, - AwaitReady(Client), - - p("verify self controlling"), - {ok, Self} = Get(Sock, controlling_process), - p("announce die"), - Client ! {die, Self}, - p("done"), - ok - end, + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], - p("Create Worker Process(s)"), - Pid1 = spawn_link(ClientStarter), - Pid2 = spawn_link(ClientStarter), + TesterSeq = + [ + %% *** Init part *** + #{desc => "create socket", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Protocol} = State) -> + Sock = sock_open(Domain, Type, Protocol), + {ok, State#{sock => Sock}} + end}, + #{desc => "verify self as controlling-process", + cmd => fun(#{sock := Sock} = _State) -> + Self = self(), + case Get(Sock, controlling_process) of + {ok, Self} -> + ok; + {ok, InvalidPid} -> + {error, {invalid, InvalidPid}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "order (client) start", + cmd => fun(#{client := Client, sock := Sock} = _State) -> + Client ! {start, self(), Sock}, + ok + end}, + #{desc => "await (client) ready (1)", + cmd => fun(#{client := Client} = _State) -> + receive + {ready, Client} -> + ok + end + end}, + #{desc => "attempt controlling-process transfer to client", + cmd => fun(#{client := Client, sock := Sock} = _State) -> + Set(Sock, controlling_process, Client) + end}, + #{desc => "verify client as controlling-process", + cmd => fun(#{client := Client, sock := Sock} = _State) -> + case Get(Sock, controlling_process) of + {ok, Client} -> + ok; + {ok, InvalidPid} -> + {error, {invalid, InvalidPid}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "attempt invalid controlling-process transfer (to self)", + cmd => fun(#{sock := Sock} = _State) -> + case Set(Sock, controlling_process, self()) of + {error, not_owner} -> + ok; + ok -> + {error, unexpected_success}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "order (client) continue", + cmd => fun(#{client := Client} = _State) -> + Client ! {continue, self()}, + ok + end}, + #{desc => "await (client) ready (2)", + cmd => fun(#{client := Client} = _State) -> + receive + {ready, Client} -> + ok + end + end}, + #{desc => "verify self as controlling-process", + cmd => fun(#{sock := Sock} = _State) -> + Self = self(), + case Get(Sock, controlling_process) of + {ok, Self} -> + ok; + {ok, InvalidPid} -> + {error, {invalid, InvalidPid}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "monitor client", + cmd => fun(#{client := Client} = State) -> + MRef = erlang:monitor(process, Client), + {ok, State#{client_mref => MRef}} + end}, + #{desc => "order (client) terminate", + cmd => fun(#{client := Client} = _State) -> + Client ! {terminate, self()}, + ok + end}, + #{desc => "await (client) down", + cmd => fun(#{client := Client} = State) -> + receive + {'DOWN', _, process, Client, _} -> + {ok, maps:remove(client, State)} + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + sock_close(Sock), + {ok, maps:remove(sock, State)} + end}, - p("Test stream/tcp "), - Tester(S1, Pid1), + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], - p("Test dgram/udp "), - Tester(S2, Pid2), + p("Run test for stream/tcp socket"), + ClientInitState1 = #{}, + Client1 = evaluator_start("tcp-client", ClientSeq, ClientInitState1), + TesterInitState1 = #{domain => inet, + type => stream, + protocol => tcp, + client => Client1}, + Tester1 = evaluator_start("tcp-tester", TesterSeq, TesterInitState1), + p("await stream/tcp evaluator"), + ok = await_evaluator_finish([Tester1, Client1]), - p("close sockets"), - sock_close(S1), - sock_close(S2), + p("Run test for dgram/udp socket"), + ClientInitState2 = #{}, + Client2 = evaluator_start("udp-client", ClientSeq, ClientInitState2), + TesterInitState2 = #{domain => inet, + type => dgram, + protocol => udp, + client => Client2}, + Tester2 = evaluator_start("udp-tester", TesterSeq, TesterInitState2), + p("await dgram/udp evaluator"), + ok = await_evaluator_finish([Tester2, Client2]), tc_end(). -- cgit v1.2.3 From 3ccba221981a6ae61cb2b7222fe7ea56aa56a923 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 11 Oct 2018 12:06:41 +0200 Subject: [socket-nif|test] Reworked the connect timeout test case --- lib/kernel/test/socket_SUITE.erl | 281 +++++++++++++++++++++++++++++++-------- 1 file changed, 226 insertions(+), 55 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 23a77f428c..2d406458f0 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -1162,7 +1162,8 @@ api_to_connect_tcp4(doc) -> []; api_to_connect_tcp4(_Config) when is_list(_Config) -> tc_begin(api_to_connect_tcp4), - ok = api_to_connect_tcp(inet), + InitState = #{domain => inet}, + ok = api_to_connect_tcp(InitState), tc_end(). %% not_yet_implemented(). @@ -1177,7 +1178,8 @@ api_to_connect_tcp6(doc) -> []; api_to_connect_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_connect_tcp6), - %% ok = api_to_connect_tcp(inet6), + %% InitState = #{domain => inet6}, + %% ok = api_to_connect_tcp(InitState), %% tc_end(). not_yet_implemented(). @@ -1189,54 +1191,223 @@ api_to_connect_tcp6(_Config) when is_list(_Config) -> %% For instance, on FreeBSD (11.2) the reponse when the backlog is full %% is a econreset. -api_to_connect_tcp(Domain) -> +api_to_connect_tcp(InitState) -> process_flag(trap_exit, true), - p("init"), - Client = self(), - LocalAddr = which_local_addr(Domain), - LocalSA = #{family => Domain, addr => LocalAddr}, - ServerName = f("~s:server", [get_tc_name()]), - Server = spawn_link(fun() -> - put(sname, ServerName), - p("open"), - LSock = sock_open(Domain, stream, tcp), - p("bind"), - ServerLPort = sock_bind(LSock, LocalSA), - p("listen on ~w", [ServerLPort]), - sock_listen(LSock, 1), - p("inform client"), - Client ! {self(), ServerLPort}, - p("await termination command"), - receive - die -> - p("terminating"), - exit(normal) - end - end), - - p("await server port"), - ServerLPort = - receive - {Server, Port} -> - Port - end, - p("open(s)"), - CSock1 = sock_open(Domain, stream, tcp), - CSock2 = sock_open(Domain, stream, tcp), - CSock3 = sock_open(Domain, stream, tcp), - p("bind(s)"), - _ClientPort1 = sock_bind(CSock1, LocalSA), - _ClientPort2 = sock_bind(CSock2, LocalSA), - _ClientPort3 = sock_bind(CSock3, LocalSA), - ServerSA = LocalSA#{port => ServerLPort}, - api_to_connect_tcp_await_timeout([CSock1, CSock2, CSock3], ServerSA), - p("terminate server"), - Server ! die, - receive - {'EXIT', Server, _} -> - p("server terminated"), - ok - end, + + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + receive + {start, Tester} when is_pid(Tester) -> + {ok, State#{tester => Tester}} + end + 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 (with backlog = 1)", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock, 1) + end}, + #{desc => "monitor server", + cmd => fun(#{tester := Tester} = State) -> + MRef = erlang:monitor(process, Tester), + {ok, State#{tester_mref => MRef}} + end}, + #{desc => "announce ready", + cmd => fun(#{tester := Tester, lport := Port}) -> + ei("announcing ready to tester (~p)", [Tester]), + Tester ! {ready, self(), Port}, + ok + end}, + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester}) -> + receive + {'DOWN', _, process, Tester, Reason} -> + {error, {unexpected_tester_exit, Reason}}; + {terminate, Tester} -> + ok + end + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + TesterSeq = + [ + %% *** 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 socket 1", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 2", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 3", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock3 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket 1 to local address", + cmd => fun(#{sock1 := Sock, lsa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket 2 to local address", + cmd => fun(#{sock2 := Sock, lsa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket 3 to local address", + cmd => fun(#{sock3 := Sock, lsa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** Synchronize with the server *** + #{desc => "order (server) start", + cmd => fun(#{server := Server}) -> + Server ! {start, self()}, + ok + end}, + #{desc => "await ready (from server)", + cmd => fun(#{server := Server, lsa := LSA} = State) -> + receive + {ready, Server, Port} -> + {ok, State#{ssa => LSA#{port => Port}}} + end + end}, + + %% *** Connect sequence *** + #{desc => "order (server) start", + cmd => fun(#{sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + ssa := SSA}) -> + Socks = [Sock1, Sock2, Sock3], + api_to_connect_tcp_await_timeout(Socks, SSA) + end}, + + %% *** Terminate server *** + #{desc => "monitor server", + cmd => fun(#{server := Server} = State) -> + MRef = erlang:monitor(process, Server), + {ok, State#{server_mref => MRef}} + end}, + #{desc => "order (server) terminate", + cmd => fun(#{server := Server} = _State) -> + Server ! {terminate, self()}, + ok + end}, + #{desc => "await (server) down", + cmd => fun(#{server := Server} = State) -> + receive + {'DOWN', _, process, Server, _} -> + State1 = maps:remove(server, State), + State2 = maps:remove(ssa, State1), + {ok, State2} + end + end}, + + %% *** Close our sockets *** + #{desc => "close socket 3", + cmd => fun(#{sock3 := Sock} = State) -> + sock_close(Sock), + {ok, maps:remove(sock3, State)} + + end}, + #{desc => "close socket 2", + cmd => fun(#{sock2 := Sock} = State) -> + sock_close(Sock), + {ok, maps:remove(sock2, State)} + + end}, + #{desc => "close socket 1", + cmd => fun(#{sock1 := Sock} = State) -> + sock_close(Sock), + {ok, maps:remove(sock1, State)} + + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + p("create server evaluator"), + ServerInitState = InitState, + Server = evaluator_start("server", ServerSeq, ServerInitState), + + p("create tester evaluator"), + TesterInitState = InitState#{server => Server}, + Tester = evaluator_start("tester", TesterSeq, TesterInitState), + + p("await evaluator(s)"), + ok = await_evaluator_finish([Server, Tester]), ok. @@ -1246,19 +1417,19 @@ api_to_connect_tcp_await_timeout(Socks, ServerSA) -> api_to_connect_tcp_await_timeout([], _ServerSA, _ID) -> ?FAIL(unexpected_success); api_to_connect_tcp_await_timeout([Sock|Socks], ServerSA, ID) -> - p("~w: try connect", [ID]), + ei("~w: try connect", [ID]), case socket:connect(Sock, ServerSA, 5000) of {error, timeout} -> - p("expected timeout (~w)", [ID]), + ei("expected timeout (~w)", [ID]), ok; {error, econnreset = Reason} -> - p("failed connecting: ~p - giving up", [Reason]), + ei("failed connecting: ~p - giving up", [Reason]), ok; {error, Reason} -> - p("failed connecting: ~p", [Reason]), - ?FAIL({recv, Reason}); + ee("failed connecting: ~p", [Reason]), + ?FAIL({connect, Reason}); ok -> - p("unexpected success (~w) - try next", [ID]), + ei("unexpected success (~w) - try next", [ID]), api_to_connect_tcp_await_timeout(Socks, ServerSA, ID+1) end. -- cgit v1.2.3 From 51d0d80c96d29445713a026870e3e0124f3380a4 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 11 Oct 2018 15:06:54 +0200 Subject: [socket-nif|test] Reworked tcp (IPv4) recv timeout test case --- lib/kernel/test/socket_SUITE.erl | 384 ++++++++++++++++++++++++++++++++++----- 1 file changed, 334 insertions(+), 50 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 2d406458f0..9a4b263ea0 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -1246,12 +1246,12 @@ api_to_connect_tcp(InitState) -> ok end}, #{desc => "await terminate (from tester)", - cmd => fun(#{tester := Tester}) -> + cmd => fun(#{tester := Tester} = State) -> receive {'DOWN', _, process, Tester, Reason} -> - {error, {unexpected_tester_exit, Reason}}; + {error, {unexpected_exit, tester, Reason}}; {terminate, Tester} -> - ok + {ok, maps:remove(tester, State)} end end}, @@ -1597,7 +1597,8 @@ api_to_recv_tcp4(doc) -> []; api_to_recv_tcp4(_Config) when is_list(_Config) -> tc_begin(api_to_recv_tcp4), - ok = api_to_recv_tcp(inet), + InitState = #{domain => inet}, + ok = api_to_recv_tcp(InitState), tc_end(). @@ -1611,59 +1612,342 @@ api_to_recv_tcp6(doc) -> []; api_to_recv_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_recv_tcp6), - %% ok = api_to_recv_tcp(inet6), + %% InitState = #{domain => inet6}, + %% ok = api_to_recv_tcp(InitState), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -api_to_recv_tcp(Domain) -> +api_to_recv_tcp(InitState) -> process_flag(trap_exit, true), - p("server -> open"), - LSock = sock_open(Domain, stream, tcp), - LocalAddr = which_local_addr(Domain), - LocalSA = #{family => Domain, addr => LocalAddr}, - p("server -> bind"), - ServerLPort = sock_bind(LSock, LocalSA), - p("server(~w) -> listen", [ServerLPort]), - sock_listen(LSock), - ClientName = f("~s:client", [get_tc_name()]), - Client = spawn_link(fun() -> - put(sname, ClientName), - p("open"), - CSock = sock_open(Domain, stream, tcp), - p("bind"), - ClientPort = sock_bind(CSock, LocalSA), - p("[~w] connect to ~w", - [ClientPort, ServerLPort]), - sock_connect(CSock, LocalSA#{port => ServerLPort}), - p("await termination command"), - receive - die -> - p("terminating"), - exit(normal) - end - end), - p("server -> accept on ~w", [ServerLPort]), - Sock = sock_accept(LSock), - p("server -> recv"), - %% The zero (0) represents "give me everything you have" - case socket:recv(Sock, 0, 5000) of - {error, timeout} -> - p("server -> expected timeout"), - ok; - {ok, _Data} -> - ?FAIL(unexpected_success); - {error, Reason} -> - ?FAIL({recv, Reason}) - end, - Client ! die, - receive - {'EXIT', Client, _} -> - ok - end, - ok. + + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + receive + {start, Tester} when is_pid(Tester) -> + {ok, State#{tester => Tester}} + end + 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 (with backlog = 1)", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock, 1) + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = State) -> + MRef = erlang:monitor(process, Tester), + {ok, State#{tester_mref => MRef}} + end}, + #{desc => "announce ready", + cmd => fun(#{tester := Tester, lport := Port}) -> + Tester ! {ready, self(), Port}, + ok + end}, + #{desc => "await continue", + cmd => fun(#{tester := Tester}) -> + receive + {'DOWN', _, process, Tester, Reason} -> + {error, {unexpected_exit, tester, Reason}}; + {continue, Tester} -> + ok + end + end}, + + %% *** The actual test *** + #{desc => "await accept", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "attempt to recv (without success)", + cmd => fun(#{sock := Sock} = _State) -> + case socket:recv(Sock, 0, 5000) of + {error, timeout} -> + ok; + {ok, _Data} -> + {error, unexpected_success}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv timeout success)", + cmd => fun(#{tester := Tester} = _State) -> + Tester ! {ready, self()}, + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + {error, {unexpected_exit, tester, Reason}}; + {terminate, Tester} -> + {ok, maps:remove(tester, State)} + end + end}, + #{desc => "close (traffic) socket", + cmd => fun(#{sock := Sock} = State) -> + sock_close(Sock), + {ok, maps:remove(sock, State)} + end}, + #{desc => "close (listen) socket", + cmd => fun(#{lsock := LSock} = State) -> + sock_close(LSock), + {ok, maps:remove(lsock, State)} + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + receive + {start, Tester, Port} when is_pid(Tester) -> + {ok, State#{tester => Tester, + server_port => Port}} + end + end}, + + %% *** Init part *** + #{desc => "which 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#{lsa => LSA, ssa => 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, lsa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = State) -> + MRef = erlang:monitor(process, Tester), + {ok, State#{tester_mref => MRef}} + end}, + #{desc => "announce ready", + cmd => fun(#{tester := Tester} = _State) -> + Tester ! {ready, self()}, + ok + end}, + + %% *** The actual test *** + #{desc => "await continue (with connect)", + cmd => fun(#{tester := Tester} = _State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + {error, {unexpected_exit, tester, Reason}}; + {continue, Tester} -> + ok + end + end}, + #{desc => "connect", + cmd => fun(#{sock := Sock, ssa := SSA}) -> + sock_connect(Sock, SSA), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + {error, {unexpected_exit, tester, Reason}}; + {terminate, Tester} -> + {ok, maps:remove(tester, State)} + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + sock_close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Server} = State) -> + MRef = erlang:monitor(process, Server), + {ok, State#{server_mref => MRef}} + end}, + #{desc => "monitor client", + cmd => fun(#{client := Client} = State) -> + MRef = erlang:monitor(process, Client), + {ok, State#{client_mref => MRef}} + end}, + + %% *** Activate server *** + #{desc => "start server", + cmd => fun(#{server := Server} = _State) -> + Server ! {start, self()}, + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Server} = State) -> + receive + {'DOWN', _, process, Server, Reason} -> + {error, {unexpected_exit, server, Reason}}; + {ready, Server, Port} -> + {ok, State#{server_port => Port}} + end + end}, + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + Server ! {continue, self()}, + ok + end}, + + %% *** Activate client *** + #{desc => "start client", + cmd => fun(#{client := Client, server_port := Port} = _State) -> + Client ! {start, self(), Port}, + ok + end}, + #{desc => "await client ready", + cmd => fun(#{client := Client} = _State) -> + receive + {'DOWN', _, process, Client, Reason} -> + {error, {unexpected_exit, client, Reason}}; + {ready, Client} -> + ok + end + end}, + + %% *** The actual test *** + #{desc => "order client to continue (with connect)", + cmd => fun(#{client := Client} = _State) -> + Client ! {continue, self()}, + ok + end}, + #{desc => "await server ready (accept/recv)", + cmd => fun(#{server := Server} = _State) -> + receive + {'DOWN', _, process, Server, Reason} -> + {error, {unexpected_exit, server, Reason}}; + {ready, Server} -> + ok + end + end}, + + %% *** Termination *** + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + Client ! {terminate, self()}, + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + receive + {'DOWN', _, process, Client, _Reason} -> + State1 = maps:remove(client, State), + State2 = maps:remove(client_mref, State1), + {ok, State2} + end + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = State) -> + Server ! {terminate, self()}, + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + receive + {'DOWN', _, process, Server, _Reason} -> + State1 = maps:remove(server, State), + State2 = maps:remove(server_mref, State1), + State3 = maps:remove(server_port, State2), + {ok, State3} + end + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + + p("start server evaluator"), + ServerInitState = InitState, + Server = evaluator_start("server", ServerSeq, ServerInitState), + + p("start client evaluator"), + ClientInitState = InitState, + Client = evaluator_start("client", ClientSeq, ClientInitState), + + p("start tester evaluator"), + TesterInitState = #{server => Server, client => Client}, + Tester = evaluator_start("tester", TesterSeq, TesterInitState), + + p("await evaluator(s)"), + ok = await_evaluator_finish([Server, Client, Tester]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From d66f69c98404b8c299e98237839a35b351d454ca Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 11 Oct 2018 15:27:31 +0200 Subject: [socket-nif|test] Reworked (IPv4) tcp recvmsg timeout test case --- lib/kernel/test/socket_SUITE.erl | 152 +++++++++++++++------------------------ 1 file changed, 57 insertions(+), 95 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 9a4b263ea0..a432473f01 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -1597,8 +1597,9 @@ api_to_recv_tcp4(doc) -> []; api_to_recv_tcp4(_Config) when is_list(_Config) -> tc_begin(api_to_recv_tcp4), - InitState = #{domain => inet}, - ok = api_to_recv_tcp(InitState), + Recv = fun(Sock) -> socket:recv(Sock, 0, 5000) end, + InitState = #{domain => inet, recv => Recv}, + ok = api_to_receive_tcp(InitState), tc_end(). @@ -1612,15 +1613,22 @@ api_to_recv_tcp6(doc) -> []; api_to_recv_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_recv_tcp6), - %% InitState = #{domain => inet6}, - %% ok = api_to_recv_tcp(InitState), - %% tc_end(). + %% Res = case socket:supports(ipv6) of + %% true -> + %% Recv = fun(Sock) -> socket:recv(Sock, 0, 5000) end, + %% InitState = #{domain => inet6, recv => Recv}, + %% ok = api_to_receive_tcp(InitState); + %% false -> + %% {skip, ipv6_not_supported} + %% end, + %% tc_end(), + %% Res. not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -api_to_recv_tcp(InitState) -> +api_to_receive_tcp(InitState) -> process_flag(trap_exit, true), ServerSeq = @@ -1694,8 +1702,8 @@ api_to_recv_tcp(InitState) -> end end}, #{desc => "attempt to recv (without success)", - cmd => fun(#{sock := Sock} = _State) -> - case socket:recv(Sock, 0, 5000) of + cmd => fun(#{sock := Sock, recv := Recv} = _State) -> + case Recv(Sock) of {error, timeout} -> ok; {ok, _Data} -> @@ -1910,7 +1918,7 @@ api_to_recv_tcp(InitState) -> end end}, #{desc => "order server to terminate", - cmd => fun(#{server := Server} = State) -> + cmd => fun(#{server := Server} = _State) -> Server ! {terminate, self()}, ok end}, @@ -2067,9 +2075,10 @@ api_to_recvmsg_tcp4(doc) -> []; api_to_recvmsg_tcp4(_Config) when is_list(_Config) -> tc_begin(api_to_recvmsg_tcp4), - ok = api_to_recvmsg_tcp(inet), + Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, + InitState = #{domain => inet, recv => Recv}, + ok = api_to_receive_tcp(InitState), tc_end(). - %% not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2082,60 +2091,13 @@ api_to_recvmsg_tcp6(doc) -> []; api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_recvmsg_tcp6), - %% ok = api_to_recvmsg_tcp(inet6), + %% Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, + %% InitState = #{domain => inet6, recv => Recv}, + %% ok = api_to_receive_tcp(InitState), %% tc_end(). not_yet_implemented(). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -api_to_recvmsg_tcp(Domain) -> - process_flag(trap_exit, true), - p("server -> open"), - LSock = sock_open(Domain, stream, tcp), - LocalAddr = which_local_addr(Domain), - LocalSA = #{family => Domain, addr => LocalAddr}, - p("server -> bind"), - ServerLPort = sock_bind(LSock, LocalSA), - p("server(~w) -> listen", [ServerLPort]), - sock_listen(LSock), - ClientName = f("~s:client", [get_tc_name()]), - Client = spawn_link(fun() -> - put(sname, ClientName), - p("open"), - CSock = sock_open(Domain, stream, tcp), - p("bind"), - ClientPort = sock_bind(CSock, LocalSA), - p("[~w] connect to ~w", - [ClientPort, ServerLPort]), - sock_connect(CSock, LocalSA#{port => ServerLPort}), - p("await termination command"), - receive - die -> - p("terminating"), - exit(normal) - end - end), - p("server -> accept on ~w", [ServerLPort]), - Sock = sock_accept(LSock), - p("server -> recv"), - %% The zero (0) represents "give me everything you have" - case socket:recvmsg(Sock, 5000) of - {error, timeout} -> - p("server -> expected timeout"), - ok; - {ok, _Data} -> - ?FAIL(unexpected_success); - {error, Reason} -> - ?FAIL({recv, Reason}) - end, - Client ! die, - receive - {'EXIT', Client, _} -> - ok - end, - ok. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2312,36 +2274,36 @@ sock_sockname(Sock) -> end. -sock_listen(Sock) -> - sock_listen2(fun() -> socket:listen(Sock) end). - -sock_listen(Sock, BackLog) -> - sock_listen2(fun() -> socket:listen(Sock, BackLog) end). - -sock_listen2(Listen) -> - try Listen() of - ok -> - ok; - {error, Reason} -> - ?FAIL({listen, Reason}) - catch - C:E:S -> - ?FAIL({listen, C, E, S}) - end. - - -sock_accept(LSock) -> - try socket:accept(LSock) of - {ok, Sock} -> - Sock; - {error, Reason} -> - p("sock_accept -> error: ~p", [Reason]), - ?FAIL({accept, Reason}) - catch - C:E:S -> - p("sock_accept -> failed: ~p, ~p, ~p", [C, E, S]), - ?FAIL({accept, C, E, S}) - end. +%% sock_listen(Sock) -> +%% sock_listen2(fun() -> socket:listen(Sock) end). + +%% sock_listen(Sock, BackLog) -> +%% sock_listen2(fun() -> socket:listen(Sock, BackLog) end). + +%% sock_listen2(Listen) -> +%% try Listen() of +%% ok -> +%% ok; +%% {error, Reason} -> +%% ?FAIL({listen, Reason}) +%% catch +%% C:E:S -> +%% ?FAIL({listen, C, E, S}) +%% end. + + +%% sock_accept(LSock) -> +%% try socket:accept(LSock) of +%% {ok, Sock} -> +%% Sock; +%% {error, Reason} -> +%% p("sock_accept -> error: ~p", [Reason]), +%% ?FAIL({accept, Reason}) +%% catch +%% C:E:S -> +%% p("sock_accept -> failed: ~p, ~p, ~p", [C, E, S]), +%% ?FAIL({accept, C, E, S}) +%% end. sock_close(Sock) -> @@ -2372,8 +2334,8 @@ set_tc_name(N) when is_atom(N) -> set_tc_name(N) when is_list(N) -> put(tc_name, N). -get_tc_name() -> - get(tc_name). +%% get_tc_name() -> +%% get(tc_name). tc_begin(TC) -> set_tc_name(TC), @@ -2386,8 +2348,8 @@ tc_end() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -f(F, A) -> - lists:flatten(io_lib:format(F, A)). +%% f(F, A) -> +%% lists:flatten(io_lib:format(F, A)). p(F) -> p(F, []). -- cgit v1.2.3 From c4c1f1d2bd307a88c5668a489d131dc6b74bf3af Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 11 Oct 2018 19:13:31 +0200 Subject: [socket-nif|test] Wrapped each test case in try Each test case is wrapped in a try catch in the ttc_try function. It handles entry, exit and skip. --- lib/kernel/test/socket_SUITE.erl | 428 ++++++++++++++++++++++----------------- 1 file changed, 238 insertions(+), 190 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index a432473f01..dc9d5240ca 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -49,8 +49,8 @@ api_to_accept_tcp6/1, api_to_send_tcp4/1, api_to_send_tcp6/1, - api_to_sendapi_to_udp4/1, - api_to_sendapi_to_udp6/1, + api_to_sendto_udp4/1, + api_to_sendto_udp6/1, api_to_sendmsg_tcp4/1, api_to_sendmsg_tcp6/1, api_to_recv_udp4/1, @@ -143,8 +143,8 @@ api_op_with_timeout_cases() -> api_to_accept_tcp6, api_to_send_tcp4, api_to_send_tcp6, - api_to_sendapi_to_udp4, - api_to_sendapi_to_udp6, + api_to_sendto_udp4, + api_to_sendto_udp6, api_to_sendmsg_tcp4, api_to_sendmsg_tcp6, api_to_recv_udp4, @@ -190,12 +190,13 @@ api_b_open_and_close_udp4(suite) -> api_b_open_and_close_udp4(doc) -> []; api_b_open_and_close_udp4(_Config) when is_list(_Config) -> - tc_begin(api_b_open_and_close_udp4), - State = #{domain => inet, - type => dgram, - protocol => udp}, - ok = api_b_open_and_close(State), - tc_end(). + tc_try(api_b_open_and_close_udp4, + fun() -> + InitState = #{domain => inet, + type => dgram, + protocol => udp}, + ok = api_b_open_and_close(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -207,12 +208,13 @@ api_b_open_and_close_tcp4(suite) -> api_b_open_and_close_tcp4(doc) -> []; api_b_open_and_close_tcp4(_Config) when is_list(_Config) -> - tc_begin(api_b_open_and_close_tcp4), - State = #{domain => inet, - type => stream, - protocol => tcp}, - ok = api_b_open_and_close(State), - tc_end(). + tc_try(api_b_open_and_close_tcp4, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = api_b_open_and_close(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -317,18 +319,19 @@ api_b_sendto_and_recvfrom_udp4(suite) -> api_b_sendto_and_recvfrom_udp4(doc) -> []; api_b_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) -> - tc_begin(api_b_sendto_and_recvfrom_udp4), - Send = fun(Sock, Data, Dest) -> - socket:sendto(Sock, Data, Dest) - end, - Recv = fun(Sock) -> - socket:recvfrom(Sock) - end, - InitState = #{domain => inet, - send => Send, - recv => Recv}, - ok = api_b_send_and_recv_udp(InitState), - tc_end(). + tc_try(api_b_sendto_and_recvfrom_udp4, + fun() -> + Send = fun(Sock, Data, Dest) -> + socket:sendto(Sock, Data, Dest) + end, + Recv = fun(Sock) -> + socket:recvfrom(Sock) + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -340,29 +343,32 @@ api_b_sendmsg_and_recvmsg_udp4(suite) -> api_b_sendmsg_and_recvmsg_udp4(doc) -> []; api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> - tc_begin(api_b_sendmsg_and_recvmsg_udp4), - Send = fun(Sock, Data, Dest) -> - %% CMsgHdr = #{level => ip, type => tos, data => reliability}, - %% CMsgHdrs = [CMsgHdr], + tc_try(api_b_sendmsg_and_recvmsg_udp4, + fun() -> + Send = fun(Sock, Data, Dest) -> + %% CMsgHdr = #{level => ip, + %% type => tos, + %% data => reliability}, + %% CMsgHdrs = [CMsgHdr], MsgHdr = #{addr => Dest, %% ctrl => CMsgHdrs, iov => [Data]}, - socket:sendmsg(Sock, MsgHdr) - end, - Recv = fun(Sock) -> - case socket:recvmsg(Sock) of - {ok, #{addr := Source, - iov := [Data]}} -> - {ok, {Source, Data}}; - {error, _} = ERROR -> - ERROR - end - end, - InitState = #{domain => inet, - send => Send, - recv => Recv}, - ok = api_b_send_and_recv_udp(InitState), - tc_end(). + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock) of + {ok, #{addr := Source, + iov := [Data]}} -> + {ok, {Source, Data}}; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -451,18 +457,19 @@ api_b_send_and_recv_tcp4(suite) -> api_b_send_and_recv_tcp4(doc) -> []; api_b_send_and_recv_tcp4(_Config) when is_list(_Config) -> - tc_begin(api_b_send_and_recv_tcp4), - Send = fun(Sock, Data) -> - socket:send(Sock, Data) - end, - Recv = fun(Sock) -> - socket:recv(Sock) - end, - InitState = #{domain => inet, - send => Send, - recv => Recv}, - ok = api_b_send_and_recv_tcp(InitState), - tc_end(). + tc_try(api_b_send_and_recv_tcp4, + fun() -> + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + Recv = fun(Sock) -> + socket:recv(Sock) + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -474,25 +481,26 @@ api_b_sendmsg_and_recvmsg_tcp4(suite) -> api_b_sendmsg_and_recvmsg_tcp4(doc) -> []; api_b_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) -> - tc_begin(api_b_sendmsg_and_recvmsg_tcp4), - Send = fun(Sock, Data) -> - MsgHdr = #{iov => [Data]}, - socket:sendmsg(Sock, MsgHdr) - end, - Recv = fun(Sock) -> - case socket:recvmsg(Sock) of - {ok, #{addr := undefined, - iov := [Data]}} -> - {ok, Data}; - {error, _} = ERROR -> - ERROR - end - end, - InitState = #{domain => inet, - send => Send, - recv => Recv}, - ok = api_b_send_and_recv_tcp(InitState), - tc_end(). + tc_try(api_b_sendmsg_and_recvmsg_tcp4, + fun() -> + Send = fun(Sock, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock) of + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -650,8 +658,10 @@ api_opt_simple_otp_options(suite) -> api_opt_simple_otp_options(doc) -> []; api_opt_simple_otp_options(_Config) when is_list(_Config) -> - tc_begin(api_opt_simple_otp_options), + tc_try(api_opt_simple_otp_options, + fun() -> api_opt_simple_otp_options() end). +api_opt_simple_otp_options() -> Get = fun(S, Key) -> socket:getopt(S, otp, Key) end, @@ -897,9 +907,7 @@ api_opt_simple_otp_options(_Config) when is_list(_Config) -> InitState2 = #{domain => inet, type => dgram, protocol => udp}, Tester2 = evaluator_start("udp-tester", Seq, InitState2), p("await evaluator 2"), - ok = await_evaluator_finish([Tester2]), - - tc_end(). + ok = await_evaluator_finish([Tester2]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -910,8 +918,10 @@ api_opt_simple_otp_controlling_process(suite) -> api_opt_simple_otp_controlling_process(doc) -> []; api_opt_simple_otp_controlling_process(_Config) when is_list(_Config) -> - tc_begin(api_opt_simple_otp_controlling_process), + tc_try(api_opt_simple_otp_controlling_process, + fun() -> api_opt_simple_otp_controlling_process() end). +api_opt_simple_otp_controlling_process() -> Get = fun(S, Key) -> socket:getopt(S, otp, Key) end, @@ -1147,9 +1157,8 @@ api_opt_simple_otp_controlling_process(_Config) when is_list(_Config) -> client => Client2}, Tester2 = evaluator_start("udp-tester", TesterSeq, TesterInitState2), p("await dgram/udp evaluator"), - ok = await_evaluator_finish([Tester2, Client2]), + ok = await_evaluator_finish([Tester2, Client2]). - tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1161,11 +1170,11 @@ api_to_connect_tcp4(suite) -> api_to_connect_tcp4(doc) -> []; api_to_connect_tcp4(_Config) when is_list(_Config) -> - tc_begin(api_to_connect_tcp4), - InitState = #{domain => inet}, - ok = api_to_connect_tcp(InitState), - tc_end(). - %% not_yet_implemented(). + tc_try(api_to_connect_tcp4, + fun() -> + InitState = #{domain => inet}, + ok = api_to_connect_tcp(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1177,11 +1186,12 @@ api_to_connect_tcp6(suite) -> api_to_connect_tcp6(doc) -> []; api_to_connect_tcp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_connect_tcp6), - %% InitState = #{domain => inet6}, - %% ok = api_to_connect_tcp(InitState), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_connect_tcp6, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet6}, + ok = api_to_connect_tcp(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1429,7 +1439,7 @@ api_to_connect_tcp_await_timeout([Sock|Socks], ServerSA, ID) -> ee("failed connecting: ~p", [Reason]), ?FAIL({connect, Reason}); ok -> - ei("unexpected success (~w) - try next", [ID]), + ei("unexpected success (~w) - try next", [ID]), api_to_connect_tcp_await_timeout(Socks, ServerSA, ID+1) end. @@ -1444,10 +1454,11 @@ api_to_accept_tcp4(suite) -> api_to_accept_tcp4(doc) -> []; api_to_accept_tcp4(_Config) when is_list(_Config) -> - %% tc_begin(api_to_accept_tcp4), - %% ok = api_to_accept_tcp(inet), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_accept_tcp4, + fun() -> + not_yet_implemented()%% , + %% ok = api_to_accept_tcp(inet) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1459,10 +1470,11 @@ api_to_accept_tcp6(suite) -> api_to_accept_tcp6(doc) -> []; api_to_accept_tcp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_accept_tcp6), - %% ok = api_to_accept_tcp(inet6), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_accept_tcp4, + fun() -> + not_yet_implemented()%% , + %% ok = api_to_accept_tcp(inet6) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1474,10 +1486,11 @@ api_to_send_tcp4(suite) -> api_to_send_tcp4(doc) -> []; api_to_send_tcp4(_Config) when is_list(_Config) -> - %% tc_begin(api_to_send_tcp4), - %% ok = api_to_send_tcp(inet), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_send_tcp4, + fun() -> + not_yet_implemented()%% , + %% ok = api_to_send_tcp(inet) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1489,40 +1502,43 @@ api_to_send_tcp6(suite) -> api_to_send_tcp6(doc) -> []; api_to_send_tcp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_send_tcp6), - %% ok = api_to_send_tcp(inet6), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_send_tcp6, + fun() -> + not_yet_implemented()%% , + %% ok = api_to_send_tcp(inet6) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the sendto timeout option %% on an IPv4 UDP (dgram) socket. -api_to_sendapi_to_udp4(suite) -> +api_to_sendto_udp4(suite) -> []; -api_to_sendapi_to_udp4(doc) -> +api_to_sendto_udp4(doc) -> []; -api_to_sendapi_to_udp4(_Config) when is_list(_Config) -> - %% tc_begin(api_to_sendapi_to_udp4), - %% ok = api_to_sendapi_to_udp(inet), - %% tc_end(). - not_yet_implemented(). +api_to_sendto_udp4(_Config) when is_list(_Config) -> + tc_try(api_to_sendto_udp4, + fun() -> + not_yet_implemented()%% , + %% ok = api_to_sendto_to_udp(inet) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the sendto timeout option %% on an IPv6 UDP (dgram) socket. -api_to_sendapi_to_udp6(suite) -> +api_to_sendto_udp6(suite) -> []; -api_to_sendapi_to_udp6(doc) -> +api_to_sendto_udp6(doc) -> []; -api_to_sendapi_to_udp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_sendapi_to_udp6), - %% ok = api_to_sendapi_to_udp(inet6), - %% tc_end(). - not_yet_implemented(). +api_to_sendto_udp6(_Config) when is_list(_Config) -> + tc_try(api_to_sendto_udp6, + fun() -> + not_yet_implemented()%% , + %% ok = api_to_sendto_to_udp(inet6) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1534,10 +1550,11 @@ api_to_sendmsg_tcp4(suite) -> api_to_sendmsg_tcp4(doc) -> []; api_to_sendmsg_tcp4(_Config) when is_list(_Config) -> - %% tc_begin(api_to_sendmsg_tcp4), - %% ok = api_to_sendmsg_tcp(inet), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_sendmsg_tcp4, + fun() -> + not_yet_implemented()%% , + %% ok = api_to_sendmsg_tcp(inet) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1549,10 +1566,11 @@ api_to_sendmsg_tcp6(suite) -> api_to_sendmsg_tcp6(doc) -> []; api_to_sendmsg_tcp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_sendmsg_tcp6), - %% ok = api_to_sendmsg_tcp(inet6), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_sendmsg_tcp6, + fun() -> + not_yet_implemented()%% , + %% ok = api_to_sendmsg_tcp(inet6) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1565,10 +1583,11 @@ api_to_recv_udp4(suite) -> api_to_recv_udp4(doc) -> []; api_to_recv_udp4(_Config) when is_list(_Config) -> - %% tc_begin(api_to_recv_udp4), - %% ok = api_to_recv_udp(inet), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_recv_udp4, + fun() -> + not_yet_implemented()%%, + %%ok = api_to_recv_udp(inet) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1581,10 +1600,11 @@ api_to_recv_udp6(suite) -> api_to_recv_udp6(doc) -> []; api_to_recv_udp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_recv_udp6), - %% ok = api_to_recv_udp(inet6), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_recv_udp6, + fun() -> + not_yet_implemented()%% , + %% ok = api_to_recv_udp(inet6) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1596,11 +1616,12 @@ api_to_recv_tcp4(suite) -> api_to_recv_tcp4(doc) -> []; api_to_recv_tcp4(_Config) when is_list(_Config) -> - tc_begin(api_to_recv_tcp4), - Recv = fun(Sock) -> socket:recv(Sock, 0, 5000) end, - InitState = #{domain => inet, recv => Recv}, - ok = api_to_receive_tcp(InitState), - tc_end(). + tc_try(api_to_recv_tcp4, + fun() -> + Recv = fun(Sock) -> socket:recv(Sock, 0, 5000) end, + InitState = #{domain => inet, recv => Recv}, + ok = api_to_receive_tcp(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1612,18 +1633,20 @@ api_to_recv_tcp6(suite) -> api_to_recv_tcp6(doc) -> []; api_to_recv_tcp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_recv_tcp6), - %% Res = case socket:supports(ipv6) of - %% true -> - %% Recv = fun(Sock) -> socket:recv(Sock, 0, 5000) end, - %% InitState = #{domain => inet6, recv => Recv}, - %% ok = api_to_receive_tcp(InitState); - %% false -> - %% {skip, ipv6_not_supported} - %% end, - %% tc_end(), - %% Res. - not_yet_implemented(). + tc_try(api_to_recv_tcp6, + fun() -> + not_yet_implemented(), + case socket:supports(ipv6) of + true -> + Recv = fun(Sock) -> + socket:recv(Sock, 0, 5000) + end, + InitState = #{domain => inet6, recv => Recv}, + ok = api_to_receive_tcp(InitState); + false -> + skip("ipv6 not supported") + end + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1967,9 +1990,10 @@ api_to_recvfrom_udp4(suite) -> api_to_recvfrom_udp4(doc) -> []; api_to_recvfrom_udp4(_Config) when is_list(_Config) -> - tc_begin(api_to_recvfrom_udp4), - ok = api_to_recvfrom_udp(inet), - tc_end(). + tc_try(api_to_recvfrom_udp4, + fun() -> + ok = api_to_recvfrom_udp(inet) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1981,10 +2005,11 @@ api_to_recvfrom_udp6(suite) -> api_to_recvfrom_udp6(doc) -> []; api_to_recvfrom_udp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_recvfrom_udp6), - %% ok = api_to_recvfrom_udp(inet6), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_recvfrom_udp6, + fun() -> + not_yet_implemented(), + ok = api_to_recvfrom_udp(inet6) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2020,10 +2045,10 @@ api_to_recvmsg_udp4(suite) -> api_to_recvmsg_udp4(doc) -> []; api_to_recvmsg_udp4(_Config) when is_list(_Config) -> - %% not_yet_implemented(). - tc_begin(api_to_recvmsg_udp4), - ok = api_to_recvmsg_udp(inet), - tc_end(). + tc_try(api_to_recvmsg_udp4, + fun() -> + ok = api_to_recvmsg_udp(inet) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2035,10 +2060,11 @@ api_to_recvmsg_udp6(suite) -> api_to_recvmsg_udp6(doc) -> []; api_to_recvmsg_udp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_recvmsg_udp6), - %% ok = api_to_recvmsg_udp(inet6), - %% tc_end(). - not_yet_implemented(). + tc_try(api_to_recvmsg_udp6, + fun() -> + not_yet_implemented(), + ok = api_to_recvmsg_udp(inet6) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2074,11 +2100,12 @@ api_to_recvmsg_tcp4(suite) -> api_to_recvmsg_tcp4(doc) -> []; api_to_recvmsg_tcp4(_Config) when is_list(_Config) -> - tc_begin(api_to_recvmsg_tcp4), - Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, - InitState = #{domain => inet, recv => Recv}, - ok = api_to_receive_tcp(InitState), - tc_end(). + tc_try(api_to_recvmsg_tcp4, + fun() -> + Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, + InitState = #{domain => inet, recv => Recv}, + ok = api_to_receive_tcp(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2090,13 +2117,13 @@ api_to_recvmsg_tcp6(suite) -> api_to_recvmsg_tcp6(doc) -> []; api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> - %% tc_begin(api_to_recvmsg_tcp6), - %% Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, - %% InitState = #{domain => inet6, recv => Recv}, - %% ok = api_to_receive_tcp(InitState), - %% tc_end(). - not_yet_implemented(). - + tc_try(api_to_recvmsg_tcp6, + fun() -> + not_yet_implemented(), + Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, + InitState = #{domain => inet6, recv => Recv}, + ok = api_to_receive_tcp(InitState) + end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2324,7 +2351,10 @@ sock_close(Sock) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% not_yet_implemented() -> - {skip, "not yet implemented"}. + skip("not yet implemented"). + +skip(Reason) -> + throw({skip, Reason}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2341,11 +2371,29 @@ tc_begin(TC) -> set_tc_name(TC), p("begin ***"). -tc_end() -> - p("done ***"), +tc_end(Result) when is_list(Result) -> + p("done: ~s", [Result]), ok. +tc_try(Case, Fun) when is_atom(Case) andalso is_function(Fun, 0) -> + tc_begin(Case), + try + begin + Fun(), + tc_end("ok") + end + catch + throw:{skip, _} = SKIP -> + tc_end("skipping"), + SKIP; + Class:Error:Stack -> + tc_end("failed"), + erlang:raise(Class, Error, Stack) + end. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% f(F, A) -> -- cgit v1.2.3 From cb2d61a99ff3613f9b47598844a0274049859b7e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 12 Oct 2018 10:48:00 +0200 Subject: [socket-nif|test] Reworked (IPv4) udp recvfrom timeout test case --- lib/kernel/test/socket_SUITE.erl | 88 ++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index dc9d5240ca..724bbc9539 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -1992,7 +1992,8 @@ api_to_recvfrom_udp4(doc) -> api_to_recvfrom_udp4(_Config) when is_list(_Config) -> tc_try(api_to_recvfrom_udp4, fun() -> - ok = api_to_recvfrom_udp(inet) + InitState = #{domain => inet}, + ok = api_to_recvfrom_udp(InitState) end). @@ -2008,32 +2009,75 @@ api_to_recvfrom_udp6(_Config) when is_list(_Config) -> tc_try(api_to_recvfrom_udp6, fun() -> not_yet_implemented(), - ok = api_to_recvfrom_udp(inet6) + InitState = #{domain => inet6}, + ok = api_to_recvfrom_udp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -api_to_recvfrom_udp(Domain) -> - process_flag(trap_exit, true), - p("init"), - LocalAddr = which_local_addr(Domain), - LocalSA = #{family => Domain, addr => LocalAddr}, - p("open"), - Sock = sock_open(Domain, dgram, udp), - p("bind"), - _Port = sock_bind(Sock, LocalSA), - p("recv"), - case socket:recvfrom(Sock, 0, 5000) of - {error, timeout} -> - p("expected timeout"), - ok; - {ok, _SrcData} -> - ?FAIL(unexpected_success); - {error, Reason} -> - ?FAIL({recv, Reason}) - end, - ok. +api_to_recvfrom_udp(InitState) -> + TesterSeq = + [ + %% *** 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 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 to local address", + cmd => fun(#{sock := Sock, lsa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** The actual test *** + #{desc => "attempt to read (without success)", + cmd => fun(#{sock := Sock} = _State) -> + case socket:recvfrom(Sock, 0, 5000) of + {error, timeout} -> + ok; + {ok, _SrcData} -> + {error, unexpected_sucsess}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** Termination *** + #{desc => "close socket", + cmd => fun(#{sock := Sock} = _State) -> + sock_close(Sock), + ok + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + p("start tester evaluator"), + Tester = evaluator_start("tester", TesterSeq, InitState), + + p("await evaluator"), + ok = await_evaluator_finish([Tester]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From 227eb3efe1cd42324c315dc14d9945ffcd935069 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 12 Oct 2018 11:05:26 +0200 Subject: [socket-nif|test] Reworked (IPv4) udp recvmsg timeout test case --- lib/kernel/test/socket_SUITE.erl | 63 ++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 724bbc9539..82d79ef32b 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -1992,8 +1992,9 @@ api_to_recvfrom_udp4(doc) -> api_to_recvfrom_udp4(_Config) when is_list(_Config) -> tc_try(api_to_recvfrom_udp4, fun() -> - InitState = #{domain => inet}, - ok = api_to_recvfrom_udp(InitState) + Recv = fun(Sock) -> socket:recvfrom(Sock, 0, 5000) end, + InitState = #{domain => inet, recv => Recv}, + ok = api_to_receive_udp(InitState) end). @@ -2009,14 +2010,15 @@ api_to_recvfrom_udp6(_Config) when is_list(_Config) -> tc_try(api_to_recvfrom_udp6, fun() -> not_yet_implemented(), - InitState = #{domain => inet6}, - ok = api_to_recvfrom_udp(InitState) + Recv = fun(Sock) -> socket:recvfrom(Sock, 0, 5000) end, + InitState = #{domain => inet6, recv => Recv}, + ok = api_to_receive_udp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -api_to_recvfrom_udp(InitState) -> +api_to_receive_udp(InitState) -> TesterSeq = [ %% *** Init part *** @@ -2047,11 +2049,11 @@ api_to_recvfrom_udp(InitState) -> %% *** The actual test *** #{desc => "attempt to read (without success)", - cmd => fun(#{sock := Sock} = _State) -> - case socket:recvfrom(Sock, 0, 5000) of + cmd => fun(#{sock := Sock, recv := Recv} = _State) -> + case Recv(Sock) of {error, timeout} -> ok; - {ok, _SrcData} -> + {ok, _} -> {error, unexpected_sucsess}; {error, _} = ERROR -> ERROR @@ -2091,7 +2093,9 @@ api_to_recvmsg_udp4(doc) -> api_to_recvmsg_udp4(_Config) when is_list(_Config) -> tc_try(api_to_recvmsg_udp4, fun() -> - ok = api_to_recvmsg_udp(inet) + Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, + InitState = #{domain => inet, recv => Recv}, + ok = api_to_receive_udp(InitState) end). @@ -2107,34 +2111,12 @@ api_to_recvmsg_udp6(_Config) when is_list(_Config) -> tc_try(api_to_recvmsg_udp6, fun() -> not_yet_implemented(), - ok = api_to_recvmsg_udp(inet6) + Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, + InitState = #{domain => inet6, recv => Recv}, + ok = api_to_receive_udp(InitState) end). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -api_to_recvmsg_udp(Domain) -> - process_flag(trap_exit, true), - p("init"), - LocalAddr = which_local_addr(Domain), - LocalSA = #{family => Domain, addr => LocalAddr}, - p("open"), - Sock = sock_open(Domain, dgram, udp), - p("bind"), - _Port = sock_bind(Sock, LocalSA), - p("recv"), - case socket:recvmsg(Sock, 5000) of - {error, timeout} -> - p("expected timeout"), - ok; - {ok, _MsgHdr} -> - ?FAIL(unexpected_success); - {error, Reason} -> - ?FAIL({recv, Reason}) - end, - ok. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recvmsg timeout option @@ -2401,6 +2383,19 @@ skip(Reason) -> throw({skip, Reason}). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% t() -> +%% os:timestamp(). + + +%% tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) -> +%% T1 = A1*1000000000+B1*1000+(C1 div 1000), +%% T2 = A2*1000000000+B2*1000+(C2 div 1000), +%% T2 - T1. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% set_tc_name(N) when is_atom(N) -> -- cgit v1.2.3 From 22336422d7a014db53dcba7ff1b6e71a1729d89a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 12 Oct 2018 11:59:16 +0200 Subject: [socket-nif|test] Add accept timeout test case and update other timeout test cases(s) Added simple accept timeout testcase. Updated timeout test cases(s) with a timeout validation. OTP-14831 --- lib/kernel/test/socket_SUITE.erl | 249 ++++++++++++++++++++++++++++++--------- 1 file changed, 196 insertions(+), 53 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 82d79ef32b..de6a20e60b 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -1172,7 +1172,7 @@ api_to_connect_tcp4(doc) -> api_to_connect_tcp4(_Config) when is_list(_Config) -> tc_try(api_to_connect_tcp4, fun() -> - InitState = #{domain => inet}, + InitState = #{domain => inet, timeout => 5000}, ok = api_to_connect_tcp(InitState) end). @@ -1189,7 +1189,7 @@ api_to_connect_tcp6(_Config) when is_list(_Config) -> tc_try(api_to_connect_tcp6, fun() -> not_yet_implemented(), - InitState = #{domain => inet6}, + InitState = #{domain => inet6, timeout => 5000}, ok = api_to_connect_tcp(InitState) end). @@ -1352,12 +1352,13 @@ api_to_connect_tcp(InitState) -> %% *** Connect sequence *** #{desc => "order (server) start", - cmd => fun(#{sock1 := Sock1, - sock2 := Sock2, - sock3 := Sock3, - ssa := SSA}) -> + cmd => fun(#{sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + ssa := SSA, + timeout := To}) -> Socks = [Sock1, Sock2, Sock3], - api_to_connect_tcp_await_timeout(Socks, SSA) + api_to_connect_tcp_await_timeout(Socks, To, SSA) end}, %% *** Terminate server *** @@ -1417,21 +1418,28 @@ api_to_connect_tcp(InitState) -> Tester = evaluator_start("tester", TesterSeq, TesterInitState), p("await evaluator(s)"), - ok = await_evaluator_finish([Server, Tester]), - ok. + ok = await_evaluator_finish([Server, Tester]). -api_to_connect_tcp_await_timeout(Socks, ServerSA) -> - api_to_connect_tcp_await_timeout(Socks, ServerSA, 1). +api_to_connect_tcp_await_timeout(Socks, To, ServerSA) -> + api_to_connect_tcp_await_timeout(Socks, To, ServerSA, 1). -api_to_connect_tcp_await_timeout([], _ServerSA, _ID) -> +api_to_connect_tcp_await_timeout([], _To, _ServerSA, _ID) -> ?FAIL(unexpected_success); -api_to_connect_tcp_await_timeout([Sock|Socks], ServerSA, ID) -> +api_to_connect_tcp_await_timeout([Sock|Socks], To, ServerSA, ID) -> ei("~w: try connect", [ID]), - case socket:connect(Sock, ServerSA, 5000) of + Start = t(), + case socket:connect(Sock, ServerSA, To) of {error, timeout} -> ei("expected timeout (~w)", [ID]), - ok; + Stop = t(), + TDiff = tdiff(Start, Stop), + if + (TDiff >= To) -> + ok; + true -> + {error, {unexpected_timeout, TDiff, To}} + end; {error, econnreset = Reason} -> ei("failed connecting: ~p - giving up", [Reason]), ok; @@ -1440,7 +1448,7 @@ api_to_connect_tcp_await_timeout([Sock|Socks], ServerSA, ID) -> ?FAIL({connect, Reason}); ok -> ei("unexpected success (~w) - try next", [ID]), - api_to_connect_tcp_await_timeout(Socks, ServerSA, ID+1) + api_to_connect_tcp_await_timeout(Socks, To, ServerSA, ID+1) end. @@ -1456,8 +1464,8 @@ api_to_accept_tcp4(doc) -> api_to_accept_tcp4(_Config) when is_list(_Config) -> tc_try(api_to_accept_tcp4, fun() -> - not_yet_implemented()%% , - %% ok = api_to_accept_tcp(inet) + InitState = #{domain => inet, timeout => 5000}, + ok = api_to_accept_tcp(InitState) end). @@ -1472,11 +1480,94 @@ api_to_accept_tcp6(doc) -> api_to_accept_tcp6(_Config) when is_list(_Config) -> tc_try(api_to_accept_tcp4, fun() -> - not_yet_implemented()%% , - %% ok = api_to_accept_tcp(inet6) + not_yet_implemented(), + InitState = #{domain => inet6, timeout => 5000}, + ok = api_to_accept_tcp(InitState) end). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_to_accept_tcp(InitState) -> + TesterSeq = + [ + %% *** 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, _} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + + %% *** The actual test part *** + #{desc => "attempt to accept (without success)", + cmd => fun(#{lsock := LSock, timeout := To} = State) -> + Start = t(), + case socket:accept(LSock, To) of + {error, timeout} -> + {ok, State#{start => Start, stop => t()}}; + {ok, Sock} -> + (catch socket:close(Sock)), + {error, unexpected_success}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate timeout time", + cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) -> + TDiff = tdiff(Start, Stop), + if + (TDiff >= To) -> + ok; + true -> + {error, {unexpected_timeout, TDiff, To}} + end + end}, + + %% *** Close (listen) socket *** + #{desc => "close (listen) socket", + cmd => fun(#{lsock := LSock} = State) -> + sock_close(LSock), + {ok, maps:remove(sock3, State)} + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + p("create tester evaluator"), + Tester = evaluator_start("tester", TesterSeq, InitState), + + p("await evaluator"), + ok = await_evaluator_finish([Tester]). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the send timeout option @@ -1618,8 +1709,10 @@ api_to_recv_tcp4(doc) -> api_to_recv_tcp4(_Config) when is_list(_Config) -> tc_try(api_to_recv_tcp4, fun() -> - Recv = fun(Sock) -> socket:recv(Sock, 0, 5000) end, - InitState = #{domain => inet, recv => Recv}, + Recv = fun(Sock, To) -> socket:recv(Sock, 0, To) end, + InitState = #{domain => inet, + recv => Recv, + timeout => 5000}, ok = api_to_receive_tcp(InitState) end). @@ -1638,10 +1731,12 @@ api_to_recv_tcp6(_Config) when is_list(_Config) -> not_yet_implemented(), case socket:supports(ipv6) of true -> - Recv = fun(Sock) -> - socket:recv(Sock, 0, 5000) + Recv = fun(Sock, To) -> + socket:recv(Sock, 0, To) end, - InitState = #{domain => inet6, recv => Recv}, + InitState = #{domain => inet6, + recv => Recv, + timeout => 5000}, ok = api_to_receive_tcp(InitState); false -> skip("ipv6 not supported") @@ -1725,16 +1820,27 @@ api_to_receive_tcp(InitState) -> end end}, #{desc => "attempt to recv (without success)", - cmd => fun(#{sock := Sock, recv := Recv} = _State) -> - case Recv(Sock) of + cmd => fun(#{sock := Sock, recv := Recv, timeout := To} = State) -> + Start = t(), + case Recv(Sock, To) of {error, timeout} -> - ok; + {ok, State#{start => Start, stop => t()}}; {ok, _Data} -> {error, unexpected_success}; {error, _} = ERROR -> ERROR end end}, + #{desc => "validate timeout time", + cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) -> + TDiff = tdiff(Start, Stop), + if + (TDiff >= To) -> + ok; + true -> + {error, {unexpected_timeout, TDiff, To}} + end + end}, #{desc => "announce ready (recv timeout success)", cmd => fun(#{tester := Tester} = _State) -> Tester ! {ready, self()}, @@ -1992,8 +2098,10 @@ api_to_recvfrom_udp4(doc) -> api_to_recvfrom_udp4(_Config) when is_list(_Config) -> tc_try(api_to_recvfrom_udp4, fun() -> - Recv = fun(Sock) -> socket:recvfrom(Sock, 0, 5000) end, - InitState = #{domain => inet, recv => Recv}, + Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end, + InitState = #{domain => inet, + recv => Recv, + timeout => 5000}, ok = api_to_receive_udp(InitState) end). @@ -2010,8 +2118,10 @@ api_to_recvfrom_udp6(_Config) when is_list(_Config) -> tc_try(api_to_recvfrom_udp6, fun() -> not_yet_implemented(), - Recv = fun(Sock) -> socket:recvfrom(Sock, 0, 5000) end, - InitState = #{domain => inet6, recv => Recv}, + Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end, + InitState = #{domain => inet6, + recv => Recv, + timeout => 5000}, ok = api_to_receive_udp(InitState) end). @@ -2049,17 +2159,28 @@ api_to_receive_udp(InitState) -> %% *** The actual test *** #{desc => "attempt to read (without success)", - cmd => fun(#{sock := Sock, recv := Recv} = _State) -> - case Recv(Sock) of + cmd => fun(#{sock := Sock, recv := Recv, timeout := To} = State) -> + Start = t(), + case Recv(Sock, To) of {error, timeout} -> - ok; + {ok, State#{start => Start, stop => t()}}; {ok, _} -> {error, unexpected_sucsess}; {error, _} = ERROR -> ERROR end end}, - + #{desc => "validate timeout time", + cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) -> + TDiff = tdiff(Start, Stop), + if + (TDiff >= To) -> + ok; + true -> + {error, {unexpected_timeout, TDiff, To}} + end + end}, + %% *** Termination *** #{desc => "close socket", cmd => fun(#{sock := Sock} = _State) -> @@ -2093,8 +2214,10 @@ api_to_recvmsg_udp4(doc) -> api_to_recvmsg_udp4(_Config) when is_list(_Config) -> tc_try(api_to_recvmsg_udp4, fun() -> - Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, - InitState = #{domain => inet, recv => Recv}, + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => inet, + recv => Recv, + timeout => 5000}, ok = api_to_receive_udp(InitState) end). @@ -2111,8 +2234,10 @@ api_to_recvmsg_udp6(_Config) when is_list(_Config) -> tc_try(api_to_recvmsg_udp6, fun() -> not_yet_implemented(), - Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, - InitState = #{domain => inet6, recv => Recv}, + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => inet6, + recv => Recv, + timeout => 5000}, ok = api_to_receive_udp(InitState) end). @@ -2128,8 +2253,10 @@ api_to_recvmsg_tcp4(doc) -> api_to_recvmsg_tcp4(_Config) when is_list(_Config) -> tc_try(api_to_recvmsg_tcp4, fun() -> - Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, - InitState = #{domain => inet, recv => Recv}, + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => inet, + recv => Recv, + timeout => 5000}, ok = api_to_receive_tcp(InitState) end). @@ -2146,8 +2273,10 @@ api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> tc_try(api_to_recvmsg_tcp6, fun() -> not_yet_implemented(), - Recv = fun(Sock) -> socket:recvmsg(Sock, 5000) end, - InitState = #{domain => inet6, recv => Recv}, + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => inet6, + recv => Recv, + timeout => 5000}, ok = api_to_receive_tcp(InitState) end). @@ -2273,7 +2402,8 @@ ee(F, A) -> eprint(" ", F, A). eprint(Prefix, F, A) -> - io:format(user, "[~s][~p] ~s" ++ F ++ "~n", [get(sname), self(), Prefix | A]). + io:format(user, "[~s][~s][~p] ~s" ++ F ++ "~n", + [formated_timestamp(), get(sname), self(), Prefix | A]). @@ -2385,17 +2515,30 @@ skip(Reason) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% t() -> -%% os:timestamp(). +t() -> + os:timestamp(). -%% tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) -> -%% T1 = A1*1000000000+B1*1000+(C1 div 1000), -%% T2 = A2*1000000000+B2*1000+(C2 div 1000), -%% T2 - T1. +tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) -> + T1 = A1*1000000000+B1*1000+(C1 div 1000), + T2 = A2*1000000000+B2*1000+(C2 div 1000), + T2 - T1. - +formated_timestamp() -> + format_timestamp(os:timestamp()). + +format_timestamp({_N1, _N2, _N3} = TS) -> + {_Date, Time} = calendar:now_to_local_time(TS), + %% {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + %% FormatTS = + %% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w.~w", + %% [YYYY, MM, DD, Hour, Min, Sec, N3]), + FormatTS = io_lib:format("~.2.0w:~.2.0w:~.2.0w", [Hour, Min, Sec]), + lists:flatten(FormatTS). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% set_tc_name(N) when is_atom(N) -> @@ -2454,7 +2597,7 @@ p(F, A) -> Name when is_list(Name) -> Name end, - i("*** ~s[~p] " ++ F, [TcName,self()|A]). + i("*** [~s][~s][~p] " ++ F, [formated_timestamp(),TcName,self()|A]). %% i(F) -> -- cgit v1.2.3 From 09168a9d0a78319639d62254da717090e21c47ab Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 12 Oct 2018 15:54:46 +0200 Subject: [socket-nif|test] Add multi accept timeout test case Added simple multi-accept (multiple acceptors) timeout testcase. OTP-14831 --- lib/kernel/test/socket_SUITE.erl | 436 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 436 insertions(+) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index de6a20e60b..3f4347c52f 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -47,6 +47,8 @@ api_to_connect_tcp6/1, api_to_accept_tcp4/1, api_to_accept_tcp6/1, + api_to_maccept_tcp4/1, + api_to_maccept_tcp6/1, api_to_send_tcp4/1, api_to_send_tcp6/1, api_to_sendto_udp4/1, @@ -1568,6 +1570,440 @@ api_to_accept_tcp(InitState) -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This test case is intended to test the multi accept timeout option +%% on an IPv4 TCP (stream) socket with multiple acceptor processes +%% (three in this case). +api_to_maccept_tcp4(suite) -> + []; +api_to_maccept_tcp4(doc) -> + []; +api_to_maccept_tcp4(_Config) when is_list(_Config) -> + tc_try(api_to_maccept_tcp4, + fun() -> + InitState = #{domain => inet, timeout => 5000}, + ok = api_to_maccept_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This test case is intended to test the accept timeout option +%% on an IPv6 TCP (stream) socket. +api_to_maccept_tcp6(suite) -> + []; +api_to_maccept_tcp6(doc) -> + []; +api_to_maccept_tcp6(_Config) when is_list(_Config) -> + tc_try(api_to_maccept_tcp4, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet6, timeout => 5000}, + ok = api_to_maccept_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_to_maccept_tcp(InitState) -> + PrimAcceptorSeq = + [ + %% *** Init part *** + #{desc => "await start", + cmd => fun(State) -> + receive + {start, Tester} -> + MRef = erlang:monitor(process, Tester), + {ok, State#{tester => Tester, + tester_mref => MRef}} + end + end}, + #{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, _} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + + #{desc => "announce ready", + cmd => fun(#{lsock := LSock, tester := Tester}) -> + ei("announcing port to tester (~p)", [Tester]), + Tester ! {ready, self(), LSock}, + ok + end}, + #{desc => "await continue", + cmd => fun(#{tester := Tester} = _State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + {error, {unexpected_exit, tester, Reason}}; + {continue, Tester} -> + ok + end + end}, + + %% *** The actual test part *** + #{desc => "attempt to accept (without success)", + cmd => fun(#{lsock := LSock, timeout := To} = State) -> + Start = t(), + case socket:accept(LSock, To) of + {error, timeout} -> + {ok, State#{start => Start, stop => t()}}; + {ok, Sock} -> + (catch socket:close(Sock)), + {error, unexpected_success}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate timeout time", + cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) -> + TDiff = tdiff(Start, Stop), + if + (TDiff >= To) -> + ok; + true -> + {error, {unexpected_timeout, TDiff, To}} + end + end}, + #{desc => "announce ready", + cmd => fun(#{tester := Tester}) -> + ei("announcing port to tester (~p)", [Tester]), + Tester ! {ready, self()}, + ok + end}, + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = _State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + {error, {unexpected_exit, tester, Reason}}; + {terminate, Tester} -> + ok + end + end}, + + %% *** Close (listen) socket *** + #{desc => "close (listen) socket", + cmd => fun(#{lsock := LSock} = State) -> + sock_close(LSock), + {ok, maps:remove(sock3, State)} + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + + SecAcceptorSeq = + [ + %% *** Init part *** + #{desc => "await start", + cmd => fun(State) -> + receive + {start, Tester, LSock} -> + MRef = erlang:monitor(process, Tester), + {ok, State#{tester => Tester, + lsock => LSock, + tester_mref => MRef}} + end + end}, + #{desc => "announce ready (1)", + cmd => fun(#{tester := Tester} = _State) -> + Tester ! {ready, self()}, + ok + end}, + #{desc => "await continue", + cmd => fun(#{tester := Tester} = _State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester, Reason}}; + {continue, Tester} -> + ok + end + end}, + + %% *** The actual test part *** + #{desc => "attempt to accept (without success)", + cmd => fun(#{lsock := LSock, timeout := To} = State) -> + Start = t(), + case socket:accept(LSock, To) of + {error, timeout} -> + {ok, State#{start => Start, stop => t()}}; + {ok, Sock} -> + (catch socket:close(Sock)), + {error, unexpected_success}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate timeout time", + cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) -> + TDiff = tdiff(Start, Stop), + if + (TDiff >= To) -> + ok; + true -> + {error, {unexpected_timeout, TDiff, To}} + end + end}, + #{desc => "announce ready (2)", + cmd => fun(#{tester := Tester} = _State) -> + Tester ! {ready, self()}, + ok + end}, + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = _State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester, Reason}}; + {terminate, Tester} -> + ok + end + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + + TesterSeq = + [ + %% Init part + #{desc => "monitor prim-acceptor", + cmd => fun(#{prim_acceptor := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor sec-acceptor 1", + cmd => fun(#{sec_acceptor1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor sec-acceptor 2", + cmd => fun(#{sec_acceptor2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + + %% Start the prim-acceptor + #{desc => "start prim-acceptor", + cmd => fun(#{prim_acceptor := Pid} = _State) -> + Pid ! {start, self()}, + ok + end}, + #{desc => "await prim-acceptor ready (1)", + cmd => fun(#{prim_acceptor := Pid} = State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding prim-acceptor ~p:" + "~n ~p", [Reason]), + {error, {unexpected_exit, prim_acceptor}}; + {ready, Pid, LSock} -> + {ok, State#{lsock => LSock}} + end + end}, + + %% Start sec-acceptor-1 + #{desc => "start sec-acceptor 1", + cmd => fun(#{sec_acceptor1 := Pid, lsock := LSock} = _State) -> + Pid ! {start, self(), LSock}, + ok + end}, + #{desc => "await sec-acceptor 1 ready (1)", + cmd => fun(#{sec_acceptor1 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-acceptor 1 ~p:" + "~n ~p", [Reason]), + {error, {unexpected_exit, sec_acceptor_1}}; + {ready, Pid} -> + ok + end + end}, + + %% Start sec-acceptor-2 + #{desc => "start sec-acceptor 2", + cmd => fun(#{sec_acceptor2 := Pid, lsock := LSock} = _State) -> + Pid ! {start, self(), LSock}, + ok + end}, + #{desc => "await sec-acceptor 2 ready (1)", + cmd => fun(#{sec_acceptor2 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-acceptor 2 ~p:" + "~n ~p", [Reason]), + {error, {unexpected_exit, sec_acceptor_2}}; + {ready, Pid} -> + ok + end + end}, + + %% Activate the acceptor(s) + #{desc => "active prim-acceptor", + cmd => fun(#{prim_acceptor := Pid} = _State) -> + Pid ! {continue, self()}, + ok + end}, + #{desc => "active sec-acceptor 1", + cmd => fun(#{sec_acceptor1 := Pid} = _State) -> + Pid ! {continue, self()}, + ok + end}, + #{desc => "active sec-acceptor 2", + cmd => fun(#{sec_acceptor2 := Pid} = _State) -> + Pid ! {continue, self()}, + ok + end}, + + %% Await acceptor(s) completions + #{desc => "await prim-acceptor ready (2)", + cmd => fun(#{prim_acceptor := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding prim-acceptor ~p:" + "~n ~p", [Reason]), + {error, {unexpected_exit, prim_acceptor}}; + {ready, Pid} -> + ok + end + end}, + #{desc => "await sec-acceptor 1 ready (2)", + cmd => fun(#{sec_acceptor1 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-acceptor 1 ~p:" + "~n ~p", [Reason]), + {error, {unexpected_exit, sec_acceptor_1}}; + {ready, Pid} -> + ok + end + end}, + #{desc => "await sec-acceptor 2 ready (2)", + cmd => fun(#{sec_acceptor2 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-acceptor 2 ~p:" + "~n ~p", [Reason]), + {error, {unexpected_exit, sec_acceptor_2}}; + {ready, Pid} -> + ok + end + end}, + + + %% Terminate the acceptor(s) + #{desc => "order prim-acceptor to terminate", + cmd => fun(#{prim_acceptor := Pid} = _State) -> + ei("send terminate command to prim-acceptor (~p)", [Pid]), + Pid ! {terminate, self()}, + ok + end}, + #{desc => "order sec-acceptor 1 to terminate", + cmd => fun(#{sec_acceptor1 := Pid} = _State) -> + ei("send terminate command to sec-acceptor-1 (~p)", [Pid]), + Pid ! {terminate, self()}, + ok + end}, + #{desc => "order sec-acceptor 2 to terminate", + cmd => fun(#{sec_acceptor2 := Pid} = _State) -> + ei("send terminate command to sec-acceptor-2 (~p)", [Pid]), + Pid ! {terminate, self()}, + ok + end}, + + %% Await acceptor(s) termination + #{desc => "await prim-acceptor termination", + cmd => fun(#{prim_acceptor := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + State1 = maps:remove(prim_acceptor, State), + {ok, State1} + end + end}, + #{desc => "await sec-acceptor 1 termination", + cmd => fun(#{sec_acceptor1 := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + State1 = maps:remove(sec_acceptor1, State), + {ok, State1} + end + end}, + #{desc => "await sec-acceptor 2 termination", + cmd => fun(#{sec_acceptor2 := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + State1 = maps:remove(sec_acceptor2, State), + {ok, State1} + end + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + p("create prim-acceptor evaluator"), + PrimAInitState = InitState, + PrimAcceptor = evaluator_start("prim-acceptor", + PrimAcceptorSeq, PrimAInitState), + + p("create prim-acceptor 1 evaluator"), + SecAInitState1 = maps:remove(domain, InitState), + SecAcceptor1 = evaluator_start("sec-acceptor-1", + SecAcceptorSeq, SecAInitState1), + + p("create prim-acceptor 2 evaluator"), + SecAInitState2 = SecAInitState1, + SecAcceptor2 = evaluator_start("sec-acceptor-2", + SecAcceptorSeq, SecAInitState2), + + p("create tester evaluator"), + TesterInitState = #{prim_acceptor => PrimAcceptor, + sec_acceptor1 => SecAcceptor1, + sec_acceptor2 => SecAcceptor2}, + Tester = evaluator_start("tester", TesterSeq, TesterInitState), + + p("await evaluator(s)"), + ok = await_evaluator_finish([PrimAcceptor, SecAcceptor1, SecAcceptor2, Tester]). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the send timeout option -- cgit v1.2.3 From ac1e27ce82fde99519a9cf271144179e4a2ef373 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 12 Oct 2018 16:59:28 +0200 Subject: [socket-nif|test] Add skeletons for controlling-process test cases --- lib/kernel/test/socket_SUITE.erl | 560 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 556 insertions(+), 4 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 3f4347c52f..71e6dff174 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -64,7 +64,29 @@ api_to_recvmsg_udp4/1, api_to_recvmsg_udp6/1, api_to_recvmsg_tcp4/1, - api_to_recvmsg_tcp6/1 + api_to_recvmsg_tcp6/1, + + %% Controlling Process + socket_cleanup_tcp4/1, + socket_cleanup_tcp6/1, + socket_cleanup_udp4/1, + socket_cleanup_udp6/1, + socket_close_tcp4/1, + socket_close_tcp6/1, + socket_close_udp4/1, + socket_close_udp6/1, + recv_response_local_close_tcp4/1, + recv_response_local_close_tcp6/1, + recv_response_remote_close_tcp4/1, + recv_response_remote_close_tcp6/1, + recvmsg_response_local_close_tcp4/1, + recvmsg_response_local_close_tcp6/1, + recvmsg_response_remote_close_tcp4/1, + recvmsg_response_remote_close_tcp6/1, + acceptor_response_local_close_tcp4/1, + acceptor_response_local_close_tcp6/1, + acceptor_response_remote_close_tcp4/1, + acceptor_response_remote_close_tcp6/1 %% Tickets ]). @@ -102,15 +124,17 @@ suite() -> all() -> [ - {group, api} + {group, api}, + {group, controlling_process} %% {group, tickets} ]. groups() -> [{api, [], api_cases()}, {api_basic, [], api_basic_cases()}, + {api_options, [], api_options_cases()}, {api_op_with_timeout, [], api_op_with_timeout_cases()}, - {api_options, [], api_options_cases()} + {controlling_process, [], controlling_process_cases()} %% {tickets, [], ticket_cases()} ]. @@ -161,6 +185,34 @@ api_op_with_timeout_cases() -> api_to_recvmsg_tcp6 ]. +%% These cases tests what happens when the controlling process dies +controlling_process_cases() -> + [ + socket_cleanup_tcp4, + socket_cleanup_tcp6, + socket_cleanup_udp4, + socket_cleanup_udp6, + + socket_close_tcp4, + socket_close_tcp6, + socket_close_udp4, + socket_close_udp6, + + recv_response_local_close_tcp4, + recv_response_local_close_tcp6, + recv_response_remote_close_tcp4, + recv_response_remote_close_tcp6, + + recvmsg_response_local_close_tcp4, + recvmsg_response_local_close_tcp6, + recvmsg_response_remote_close_tcp4, + recvmsg_response_remote_close_tcp6, + + acceptor_response_local_close_tcp4, + acceptor_response_local_close_tcp6, + acceptor_response_remote_close_tcp4, + acceptor_response_remote_close_tcp6 + ]. %% ticket_cases() -> %% []. @@ -183,6 +235,14 @@ end_per_testcase(_TC, Config) -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% API BASIC %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Basically open (create) and close an IPv4 UDP (dgram) socket. @@ -549,7 +609,7 @@ api_b_send_and_recv_tcp(InitState) -> cmd => fun(#{lsock := LSock} = State) -> case socket:accept(LSock) of {ok, Sock} -> - ei("accepted: ~p", [Sock]), + ei("accepted: ~n ~p", [Sock]), {ok, State#{tsock => Sock}}; {error, _} = ERROR -> ERROR @@ -652,6 +712,14 @@ api_b_send_and_recv_tcp(InitState) -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% API OPTIONS %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Perform some simple getopt and setopt with the level = otp options @@ -1163,6 +1231,14 @@ api_opt_simple_otp_controlling_process() -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% API OPERATIONS WITH TIMEOUT %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the connect timeout option @@ -2717,6 +2793,482 @@ api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> end). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% CONTROLLING PROCESS %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% (removed) when the controlling process terminates (without explicitly +%% calling the close function). For a IPv4 TCP (stream) socket. + +socket_cleanup_tcp4(suite) -> + []; +socket_cleanup_tcp4(doc) -> + []; +socket_cleanup_tcp4(_Config) when is_list(_Config) -> + tc_try(socket_cleanup_tcp4, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = socket_cleanup(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% (removed) when the controlling process terminates (without explicitly +%% calling the close function). For a IPv6 TCP (stream) socket. + +socket_cleanup_tcp6(suite) -> + []; +socket_cleanup_tcp6(doc) -> + []; +socket_cleanup_tcp6(_Config) when is_list(_Config) -> + tc_try(socket_cleanup_tcp6, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet6, + type => stream, + protocol => tcp}, + ok = socket_cleanup(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% (removed) when the controlling process terminates (without explicitly +%% calling the close function). For a IPv4 UDP (dgram) socket. + +socket_cleanup_udp4(suite) -> + []; +socket_cleanup_udp4(doc) -> + []; +socket_cleanup_udp4(_Config) when is_list(_Config) -> + tc_try(socket_cleanup_udp4, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet, + type => dgram, + protocol => udp}, + ok = socket_cleanup(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% (removed) when the controlling process terminates (without explicitly +%% calling the close function). For a IPv6 UDP (dgram) socket. + +socket_cleanup_udp6(suite) -> + []; +socket_cleanup_udp6(doc) -> + []; +socket_cleanup_udp6(_Config) when is_list(_Config) -> + tc_try(socket_cleanup_udp6, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet6, + type => dgram, + protocol => udp}, + ok = socket_cleanup(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +socket_cleanup(_InitState) -> + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a process other +%% than the controlling process closes a socket. +%% For a IPv4 TCP (stream) socket. + +socket_close_tcp4(suite) -> + []; +socket_close_tcp4(doc) -> + []; +socket_close_tcp4(_Config) when is_list(_Config) -> + tc_try(socket_close_tcp4, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = socket_close(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a process other +%% than the controlling process closes a socket. +%% For a IPv6 TCP (stream) socket. + +socket_close_tcp6(suite) -> + []; +socket_close_tcp6(doc) -> + []; +socket_close_tcp6(_Config) when is_list(_Config) -> + tc_try(socket_close_tcp6, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet6, + type => stream, + protocol => tcp}, + ok = socket_close(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a process other +%% than the controlling process closes a socket. +%% For a IPv4 UDP (dgram) socket. + +socket_close_udp4(suite) -> + []; +socket_close_udp4(doc) -> + []; +socket_close_udp4(_Config) when is_list(_Config) -> + tc_try(socket_close_udp4, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet, + type => dgram, + protocol => udp}, + ok = socket_close(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a process other +%% than the controlling process closes a socket. +%% For a IPv6 UDP (dgram) socket. + +socket_close_udp6(suite) -> + []; +socket_close_udp6(doc) -> + []; +socket_close_udp6(_Config) when is_list(_Config) -> + tc_try(socket_close_udp6, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet6, + type => dgram, + protocol => udp}, + ok = socket_close(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +socket_close(_InitState) -> + ok. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recv function. +%% Socket is IPv4. + +recv_response_local_close_tcp4(suite) -> + []; +recv_response_local_close_tcp4(doc) -> + []; +recv_response_local_close_tcp4(_Config) when is_list(_Config) -> + tc_try(recv_response_local_close_tcp4, + fun() -> + not_yet_implemented(), + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => inet, + type => stream, + protocol => tcp, + recv => Recv}, + ok = receive_response_local_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recv function. +%% Socket is IPv6. + +recv_response_local_close_tcp6(suite) -> + []; +recv_response_local_close_tcp6(doc) -> + []; +recv_response_local_close_tcp6(_Config) when is_list(_Config) -> + tc_try(recv_response_local_close_tcp6, + fun() -> + not_yet_implemented(), + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => inet6, + type => stream, + protocol => tcp, + recv => Recv}, + ok = receive_response_local_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +receive_response_local_close_tcp(_InitState) -> + ok. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recv function. +%% Socket is IPv4. + +recv_response_remote_close_tcp4(suite) -> + []; +recv_response_remote_close_tcp4(doc) -> + []; +recv_response_remote_close_tcp4(_Config) when is_list(_Config) -> + tc_try(recv_response_remote_close_tcp4, + fun() -> + not_yet_implemented(), + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => inet, + type => stream, + protocol => tcp, + recv => Recv}, + ok = receive_response_remote_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recv function. +%% Socket is IPv6. + +recv_response_remote_close_tcp6(suite) -> + []; +recv_response_remote_close_tcp6(doc) -> + []; +recv_response_remote_close_tcp6(_Config) when is_list(_Config) -> + tc_try(recv_response_remote_close_tcp6, + fun() -> + not_yet_implemented(), + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => inet6, + type => stream, + protocol => tcp, + recv => Recv}, + ok = receive_response_remote_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +receive_response_remote_close_tcp(_InitState) -> + ok. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recvmsg function. +%% Socket is IPv4. + +recvmsg_response_local_close_tcp4(suite) -> + []; +recvmsg_response_local_close_tcp4(doc) -> + []; +recvmsg_response_local_close_tcp4(_Config) when is_list(_Config) -> + tc_try(recvmsg_response_local_close_tcp4, + fun() -> + not_yet_implemented(), + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => inet, + type => stream, + protocol => tcp, + recv => Recv}, + ok = receive_response_local_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recvmsg function. +%% Socket is IPv6. + +recvmsg_response_local_close_tcp6(suite) -> + []; +recvmsg_response_local_close_tcp6(doc) -> + []; +recvmsg_response_local_close_tcp6(_Config) when is_list(_Config) -> + tc_try(recvmsg_response_local_close_tcp6, + fun() -> + not_yet_implemented(), + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => inet6, + type => stream, + protocol => tcp, + recv => Recv}, + ok = receive_response_local_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recvmsg function. +%% Socket is IPv4. + +recvmsg_response_remote_close_tcp4(suite) -> + []; +recvmsg_response_remote_close_tcp4(doc) -> + []; +recvmsg_response_remote_close_tcp4(_Config) when is_list(_Config) -> + tc_try(recvmsg_response_remote_close_tcp4, + fun() -> + not_yet_implemented(), + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => inet, + type => stream, + protocol => tcp, + recv => Recv}, + ok = receive_response_remote_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recvmsg function. +%% Socket is IPv6. + +recvmsg_response_remote_close_tcp6(suite) -> + []; +recvmsg_response_remote_close_tcp6(doc) -> + []; +recvmsg_response_remote_close_tcp6(_Config) when is_list(_Config) -> + tc_try(recvmsg_response_remote_close_tcp6, + fun() -> + not_yet_implemented(), + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => inet6, + type => stream, + protocol => tcp, + recv => Recv}, + ok = receive_response_remote_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the accept function. +%% We test what happens with a non-controlling_process also, since we +%% git the setup anyway. +%% Socket is IPv4. + +acceptor_response_local_close_tcp4(suite) -> + []; +acceptor_response_local_close_tcp4(doc) -> + []; +acceptor_response_local_close_tcp4(_Config) when is_list(_Config) -> + tc_try(acceptor_response_local_close_tcp4, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = acceptor_response_local_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the accept function. +%% We test what happens with a non-controlling_process also, since we +%% git the setup anyway. +%% Socket is IPv6. + +acceptor_response_local_close_tcp6(suite) -> + []; +acceptor_response_local_close_tcp6(doc) -> + []; +acceptor_response_local_close_tcp6(_Config) when is_list(_Config) -> + tc_try(acceptor_response_local_close_tcp6, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = acceptor_response_local_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +acceptor_response_local_close_tcp(_InitState) -> + ok. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the accept function. +%% We test what happens with a non-controlling_process also, since we +%% git the setup anyway. +%% Socket is IPv4. + +acceptor_response_remote_close_tcp4(suite) -> + []; +acceptor_response_remote_close_tcp4(doc) -> + []; +acceptor_response_remote_close_tcp4(_Config) when is_list(_Config) -> + tc_try(acceptor_response_remote_close_tcp4, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = acceptor_response_remote_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the accept function. +%% We test what happens with a non-controlling_process also, since we +%% git the setup anyway. +%% Socket is IPv6. + +acceptor_response_remote_close_tcp6(suite) -> + []; +acceptor_response_remote_close_tcp6(doc) -> + []; +acceptor_response_remote_close_tcp6(_Config) when is_list(_Config) -> + tc_try(acceptor_response_remote_close_tcp6, + fun() -> + not_yet_implemented(), + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = acceptor_response_remote_close_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +acceptor_response_remote_close_tcp(_InitState) -> + ok. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From 468c633a6e96a689532957e299ce7fb7c67a5d58 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 15 Oct 2018 18:29:03 +0200 Subject: [socket-nif] Fix socket close when owner process exits The handling of terminating controlling-process was incomplete. Also added state check (CLOSED and CLOSING) in all nif-functions to prevent access for a closed (or closing) socket. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 180 +++++++++++++++++++++++++++++++-- 1 file changed, 173 insertions(+), 7 deletions(-) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 0ef88162bc..f09f618653 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -390,7 +390,18 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define SOCKET_STATE_ACCEPTING (SOCKET_STATE_LISTENING | SOCKET_FLAG_ACC) #define SOCKET_STATE_CLOSING (SOCKET_FLAG_CLOSE) -#define IS_OPEN(d) \ +#define IS_CLOSED(d) \ + ((d)->state == SOCKET_STATE_CLOSED) + +/* +#define IS_STATE(d, f) \ + (((d)->state & (f)) == (f)) +*/ + +#define IS_CLOSING(d) \ + (((d)->state & SOCKET_STATE_CLOSING) == SOCKET_STATE_CLOSING) + +#define IS_OPEN(d) \ (((d)->state & SOCKET_FLAG_OPEN) == SOCKET_FLAG_OPEN) #define IS_CONNECTED(d) \ @@ -2945,6 +2956,9 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, } eSockAddr = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_bind -> args when sock = %d (0x%lX)" "\r\n Socket: %T" @@ -3034,6 +3048,9 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, } eSockAddr = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_connect -> args when sock = %d:" "\r\n Socket: %T" @@ -3235,6 +3252,9 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_listen -> args when sock = %d:" "\r\n Socket: %T" @@ -3296,6 +3316,9 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, } ref = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_accept -> args when sock = %d:" "\r\n Socket: %T" @@ -3676,6 +3699,9 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, } sendRef = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_send -> args when sock = %d:" "\r\n Socket: %T" @@ -3800,6 +3826,9 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, sendRef = argv[1]; eSockAddr = argv[3]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_sendto -> args when sock = %d:" "\r\n Socket: %T" @@ -3923,6 +3952,9 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, sendRef = argv[1]; eMsgHdr = argv[2]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_sendmsg -> args when sock = %d:" "\r\n Socket: %T" @@ -4212,6 +4244,9 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, } recvRef = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + if (!IS_CONNECTED(descP)) return esock_make_error(env, atom_enotconn); @@ -4351,6 +4386,9 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, } recvRef = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_recvfrom -> args when sock = %d:" "\r\n Socket: %T" @@ -4511,6 +4549,9 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, } recvRef = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_recvmsg -> args when sock = %d:" "\r\n Socket: %T" @@ -4669,6 +4710,9 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + return nclose(env, descP); } @@ -4807,10 +4851,10 @@ ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, { ERL_NIF_TERM reply; - if (descP->state == SOCKET_STATE_CLOSED) + if (IS_CLOSED(descP)) return esock_atom_ok; - if (descP->state != SOCKET_STATE_CLOSING) + if (!IS_CLOSING(descP)) return esock_make_error(env, atom_enotclosing); /* This nif is executed in a dirty scheduler just so that @@ -4873,6 +4917,9 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + if (!ehow2how(ehow, &how)) return enif_make_badarg(env); @@ -4957,6 +5004,9 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, eIsEncoded = argv[1]; eVal = argv[4]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + isEncoded = esock_decode_bool(eIsEncoded); /* SGDBG( ("SOCKET", "nif_setopt -> eIsDecoded (%T) decoded: %d\r\n", */ @@ -8214,6 +8264,9 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, eIsEncoded = argv[1]; eOpt = argv[3]; // Is "normally" an int, but if raw mode: {Int, ValueSz} + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_getopt -> args when sock = %d:" "\r\n Socket: %T" @@ -10807,6 +10860,9 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_sockname -> args when sock = %d:" "\r\n Socket: %T" @@ -10872,6 +10928,9 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_peername -> args when sock = %d:" "\r\n Socket: %T" @@ -10940,6 +10999,9 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, op = argv[1]; opRef = argv[2]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_cancel -> args when sock = %d:" "\r\n op: %T" @@ -14955,6 +15017,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) MLOCK(descP->accMtx); MLOCK(descP->closeMtx); + SSDBG( descP, ("SOCKET", "socket_stop -> all mutex(s) locked\r\n") ); descP->state = SOCKET_STATE_CLOSING; // Just in case...??? descP->isReadable = FALSE; @@ -14964,7 +15027,13 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) /* We should check that we actually have a monitor. * This *should* be done with a "NULL" monitor value, * which there currently is none... + * If we got here because the controlling process died, + * its no point in demonitor. Also, we not actually have + * a monitor in that case... */ + SSDBG( descP, + ("SOCKET", + "socket_stop -> demonitor (maybe) controlling process\r\n") ); DEMONP(env, descP, &descP->ctrlMon); if (descP->currentWriterP != NULL) { @@ -14972,6 +15041,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * writers waiting. */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle writer(s)\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentWriter.pid) && @@ -14998,6 +15068,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * readers waiting. */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle reader(s)\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentReader.pid) && @@ -15023,6 +15094,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * acceptors waiting. */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle acceptor(s)\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentAcceptor.pid) && @@ -15085,7 +15157,15 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) } } - + + if (!is_direct_call) { + descP->sock = INVALID_SOCKET; + descP->event = INVALID_EVENT; + descP->state = SOCKET_STATE_CLOSED; + } + + SSDBG( descP, ("SOCKET", "socket_stop -> unlock all mutex(s)\r\n") ); + MUNLOCK(descP->closeMtx); MUNLOCK(descP->accMtx); MUNLOCK(descP->readMtx); @@ -15163,22 +15243,108 @@ void socket_down(ErlNifEnv* env, if (compare_pids(env, &descP->ctrlPid, pid)) { + int selectRes; + /* We don't bother with the queue cleanup here - * we leave it to the stop callback function. */ + SSDBG( descP, ("SOCKET", "socket_down -> controlling process term\r\n") ); + descP->state = SOCKET_STATE_CLOSING; descP->closeLocal = TRUE; descP->closerPid = *pid; descP->closerMon = *mon; - descP->closeRef = MKREF(env); - enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), - descP, NULL, descP->closeRef); + descP->closeRef = MKREF(env); // Do we really need this in this case? + + selectRes = enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), + descP, NULL, descP->closeRef); + + if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { + /* We are done - wwe can finalize (socket close) directly */ + SSDBG( descP, + ("SOCKET", "socket_down -> [%d] stop called\r\n", descP->sock) ); + dec_socket(descP->domain, descP->type, descP->protocol); + descP->state = SOCKET_STATE_CLOSED; + + /* And finally close the socket. + * Since we close the socket because of an exiting owner, + * we do not need to wait for buffers to sync (linger). + * If the owner wish to ensure the buffer are written, + * it should have closed teh socket explicitly... + */ + if (sock_close(descP->sock) != 0) { + int save_errno = sock_errno(); + + esock_warning_msg("Failed closing socket for terminating " + "controlling process: " + "\r\n Controlling Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d" + "\r\n", pid, descP->sock, save_errno); + } + sock_close_event(descP->event); + + descP->sock = INVALID_SOCKET; + descP->event = INVALID_EVENT; + + descP->state = SOCKET_STATE_CLOSED; + + } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { + /* The stop callback function has been *scheduled* which means that + * "should" wait for it to complete. But since we are in a callback + * (down) function, we cannot... + * So, we must close the socket + */ + SSDBG( descP, + ("SOCKET", + "socket_down -> [%d] stop scheduled\r\n", descP->sock) ); + dec_socket(descP->domain, descP->type, descP->protocol); + + /* And now what? We can't wait for the stop function here... + * So, we simply close it here and leave the rest of the "close" + * for later (when the stop function actually gets called... + */ + + if (sock_close(descP->sock) != 0) { + int save_errno = sock_errno(); + + esock_warning_msg("Failed closing socket for terminating " + "controlling process: " + "\r\n Controlling Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d" + "\r\n", pid, descP->sock, save_errno); + } + sock_close_event(descP->event); + + } else { + + /* + * + * + * WE SHOULD REALLY HAVE A WAY TO CLOBBER THE SOCKET, + * SO WE DON'T LET STUFF LEAK. + * NOW, BECAUSE WE FAILED TO SELECT, WE CANNOT FINISH + * THE CLOSE, WHAT TO DO? ABORT? + * + * + */ + esock_warning_msg("Failed selecting stop when handling down " + "of controlling process: " + "\r\n Select Res: %d" + "\r\n Controlling Process: %T" + "\r\n Descriptor: %d" + "\r\n", selectRes, pid, descP->sock); + } + } else { /* check all operation queue(s): acceptor, writer and reader. */ + SSDBG( descP, ("SOCKET", "socket_down -> other process term\r\n") ); + MLOCK(descP->accMtx); if (descP->currentAcceptorP != NULL) socket_down_acceptor(env, descP, pid); -- cgit v1.2.3 From f2481d58a2fb64b8f3c7cbb26e09cb17c04726e0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 15 Oct 2018 18:31:43 +0200 Subject: [socket-nif|test] Added two test cases regarding exiting owner Added two (working) test cases for testing "socket cleanup" when the controlling-process exits. OTP-14831 --- lib/kernel/test/socket_SUITE.erl | 487 +++++++++++++++++++++------------------ 1 file changed, 265 insertions(+), 222 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 71e6dff174..42420a37dc 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -67,26 +67,22 @@ api_to_recvmsg_tcp6/1, %% Controlling Process - socket_cleanup_tcp4/1, - socket_cleanup_tcp6/1, - socket_cleanup_udp4/1, - socket_cleanup_udp6/1, - socket_close_tcp4/1, - socket_close_tcp6/1, - socket_close_udp4/1, - socket_close_udp6/1, - recv_response_local_close_tcp4/1, - recv_response_local_close_tcp6/1, - recv_response_remote_close_tcp4/1, - recv_response_remote_close_tcp6/1, - recvmsg_response_local_close_tcp4/1, - recvmsg_response_local_close_tcp6/1, - recvmsg_response_remote_close_tcp4/1, - recvmsg_response_remote_close_tcp6/1, - acceptor_response_local_close_tcp4/1, - acceptor_response_local_close_tcp6/1, - acceptor_response_remote_close_tcp4/1, - acceptor_response_remote_close_tcp6/1 + sc_socket_cleanup_tcp4/1, + sc_socket_cleanup_tcp6/1, + sc_socket_cleanup_udp4/1, + sc_socket_cleanup_udp6/1, + sc_recv_response_local_close_tcp4/1, + sc_recv_response_local_close_tcp6/1, + sc_recv_response_remote_close_tcp4/1, + sc_recv_response_remote_close_tcp6/1, + sc_recvmsg_response_local_close_tcp4/1, + sc_recvmsg_response_local_close_tcp6/1, + sc_recvmsg_response_remote_close_tcp4/1, + sc_recvmsg_response_remote_close_tcp6/1, + sc_acceptor_response_local_close_tcp4/1, + sc_acceptor_response_local_close_tcp6/1, + sc_acceptor_response_remote_close_tcp4/1, + sc_acceptor_response_remote_close_tcp6/1 %% Tickets ]). @@ -125,7 +121,7 @@ suite() -> all() -> [ {group, api}, - {group, controlling_process} + {group, socket_closure} %% {group, tickets} ]. @@ -134,7 +130,7 @@ groups() -> {api_basic, [], api_basic_cases()}, {api_options, [], api_options_cases()}, {api_op_with_timeout, [], api_op_with_timeout_cases()}, - {controlling_process, [], controlling_process_cases()} + {socket_closure, [], socket_closure_cases()} %% {tickets, [], ticket_cases()} ]. @@ -185,33 +181,29 @@ api_op_with_timeout_cases() -> api_to_recvmsg_tcp6 ]. -%% These cases tests what happens when the controlling process dies -controlling_process_cases() -> +%% These cases tests what happens when the socket is closed, locally or +%% remotely. +socket_closure_cases() -> [ - socket_cleanup_tcp4, - socket_cleanup_tcp6, - socket_cleanup_udp4, - socket_cleanup_udp6, - - socket_close_tcp4, - socket_close_tcp6, - socket_close_udp4, - socket_close_udp6, - - recv_response_local_close_tcp4, - recv_response_local_close_tcp6, - recv_response_remote_close_tcp4, - recv_response_remote_close_tcp6, - - recvmsg_response_local_close_tcp4, - recvmsg_response_local_close_tcp6, - recvmsg_response_remote_close_tcp4, - recvmsg_response_remote_close_tcp6, - - acceptor_response_local_close_tcp4, - acceptor_response_local_close_tcp6, - acceptor_response_remote_close_tcp4, - acceptor_response_remote_close_tcp6 + sc_socket_cleanup_tcp4, + sc_socket_cleanup_tcp6, + sc_socket_cleanup_udp4, + sc_socket_cleanup_udp6, + + sc_recv_response_local_close_tcp4, + sc_recv_response_local_close_tcp6, + sc_recv_response_remote_close_tcp4, + sc_recv_response_remote_close_tcp6, + + sc_recvmsg_response_local_close_tcp4, + sc_recvmsg_response_local_close_tcp6, + sc_recvmsg_response_remote_close_tcp4, + sc_recvmsg_response_remote_close_tcp6, + + sc_acceptor_response_local_close_tcp4, + sc_acceptor_response_local_close_tcp6, + sc_acceptor_response_remote_close_tcp4, + sc_acceptor_response_remote_close_tcp6 ]. %% ticket_cases() -> @@ -1337,7 +1329,9 @@ api_to_connect_tcp(InitState) -> cmd => fun(#{tester := Tester} = State) -> receive {'DOWN', _, process, Tester, Reason} -> - {error, {unexpected_exit, tester, Reason}}; + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; {terminate, Tester} -> {ok, maps:remove(tester, State)} end @@ -1734,7 +1728,9 @@ api_to_maccept_tcp(InitState) -> cmd => fun(#{tester := Tester} = _State) -> receive {'DOWN', _, process, Tester, Reason} -> - {error, {unexpected_exit, tester, Reason}}; + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; {continue, Tester} -> ok end @@ -1774,7 +1770,9 @@ api_to_maccept_tcp(InitState) -> cmd => fun(#{tester := Tester} = _State) -> receive {'DOWN', _, process, Tester, Reason} -> - {error, {unexpected_exit, tester, Reason}}; + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; {terminate, Tester} -> ok end @@ -2797,68 +2795,67 @@ api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% -%% CONTROLLING PROCESS %% +%% SOCKET CLOSURE %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sockets are cleaned up -%% (removed) when the controlling process terminates (without explicitly +%% ("removed") when the controlling process terminates (without explicitly %% calling the close function). For a IPv4 TCP (stream) socket. -socket_cleanup_tcp4(suite) -> +sc_socket_cleanup_tcp4(suite) -> []; -socket_cleanup_tcp4(doc) -> +sc_socket_cleanup_tcp4(doc) -> []; -socket_cleanup_tcp4(_Config) when is_list(_Config) -> - tc_try(socket_cleanup_tcp4, +sc_socket_cleanup_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_socket_cleanup_tcp4, fun() -> - not_yet_implemented(), + %% not_yet_implemented(), InitState = #{domain => inet, type => stream, protocol => tcp}, - ok = socket_cleanup(InitState) + ok = sc_socket_cleanup(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sockets are cleaned up -%% (removed) when the controlling process terminates (without explicitly +%% ("removed") when the controlling process terminates (without explicitly %% calling the close function). For a IPv6 TCP (stream) socket. -socket_cleanup_tcp6(suite) -> +sc_socket_cleanup_tcp6(suite) -> []; -socket_cleanup_tcp6(doc) -> +sc_socket_cleanup_tcp6(doc) -> []; -socket_cleanup_tcp6(_Config) when is_list(_Config) -> - tc_try(socket_cleanup_tcp6, +sc_socket_cleanup_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_socket_cleanup_tcp6, fun() -> not_yet_implemented(), InitState = #{domain => inet6, type => stream, protocol => tcp}, - ok = socket_cleanup(InitState) + ok = sc_socket_cleanup(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sockets are cleaned up -%% (removed) when the controlling process terminates (without explicitly +%% ("removed") when the controlling process terminates (without explicitly %% calling the close function). For a IPv4 UDP (dgram) socket. -socket_cleanup_udp4(suite) -> +sc_socket_cleanup_udp4(suite) -> []; -socket_cleanup_udp4(doc) -> +sc_socket_cleanup_udp4(doc) -> []; -socket_cleanup_udp4(_Config) when is_list(_Config) -> - tc_try(socket_cleanup_udp4, +sc_socket_cleanup_udp4(_Config) when is_list(_Config) -> + tc_try(sc_socket_cleanup_udp4, fun() -> - not_yet_implemented(), InitState = #{domain => inet, type => dgram, protocol => udp}, - ok = socket_cleanup(InitState) + ok = sc_socket_cleanup(InitState) end). @@ -2868,113 +2865,159 @@ socket_cleanup_udp4(_Config) when is_list(_Config) -> %% (removed) when the controlling process terminates (without explicitly %% calling the close function). For a IPv6 UDP (dgram) socket. -socket_cleanup_udp6(suite) -> +sc_socket_cleanup_udp6(suite) -> []; -socket_cleanup_udp6(doc) -> +sc_socket_cleanup_udp6(doc) -> []; -socket_cleanup_udp6(_Config) when is_list(_Config) -> - tc_try(socket_cleanup_udp6, +sc_socket_cleanup_udp6(_Config) when is_list(_Config) -> + tc_try(sc_socket_cleanup_udp6, fun() -> not_yet_implemented(), InitState = #{domain => inet6, type => dgram, protocol => udp}, - ok = socket_cleanup(InitState) - end). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -socket_cleanup(_InitState) -> - ok. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to test what happens when a process other -%% than the controlling process closes a socket. -%% For a IPv4 TCP (stream) socket. - -socket_close_tcp4(suite) -> - []; -socket_close_tcp4(doc) -> - []; -socket_close_tcp4(_Config) when is_list(_Config) -> - tc_try(socket_close_tcp4, - fun() -> - not_yet_implemented(), - InitState = #{domain => inet, - type => stream, - protocol => tcp}, - ok = socket_close(InitState) + ok = sc_socket_cleanup(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to test what happens when a process other -%% than the controlling process closes a socket. -%% For a IPv6 TCP (stream) socket. - -socket_close_tcp6(suite) -> - []; -socket_close_tcp6(doc) -> - []; -socket_close_tcp6(_Config) when is_list(_Config) -> - tc_try(socket_close_tcp6, - fun() -> - not_yet_implemented(), - InitState = #{domain => inet6, - type => stream, - protocol => tcp}, - ok = socket_close(InitState) - end). +sc_socket_cleanup(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + receive + {start, Tester} when is_pid(Tester) -> + {ok, State#{tester => Tester}} + end + end}, + %% *** Init part *** + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready", + cmd => fun(#{tester := Tester, sock := Sock} = _State) -> + Tester ! {ready, self(), Sock}, + ok + end}, -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to test what happens when a process other -%% than the controlling process closes a socket. -%% For a IPv4 UDP (dgram) socket. - -socket_close_udp4(suite) -> - []; -socket_close_udp4(doc) -> - []; -socket_close_udp4(_Config) when is_list(_Config) -> - tc_try(socket_close_udp4, - fun() -> - not_yet_implemented(), - InitState = #{domain => inet, - type => dgram, - protocol => udp}, - ok = socket_close(InitState) - end). - + %% *** The actual test *** + %% We intentially leave the socket "as is", no explicit close + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {terminate, Tester} -> + {ok, maps:remove(tester, State)} + end + end}, + #{desc => "enable (otp) debug", + cmd => fun(#{sock := Sock} = _State) -> + ok = socket:setopt(Sock, otp, debug, true) + end}, -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to test what happens when a process other -%% than the controlling process closes a socket. -%% For a IPv6 UDP (dgram) socket. + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], -socket_close_udp6(suite) -> - []; -socket_close_udp6(doc) -> - []; -socket_close_udp6(_Config) when is_list(_Config) -> - tc_try(socket_close_udp6, - fun() -> - not_yet_implemented(), - InitState = #{domain => inet6, - type => dgram, - protocol => udp}, - ok = socket_close(InitState) - end). + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor owner", + cmd => fun(#{owner := Owner} = _State) -> + _MRef = erlang:monitor(process, Owner), + ok + end}, + #{desc => "order (owner) start", + cmd => fun(#{owner := Pid} = _State) -> + Pid ! {start, self()}, + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Owner} = State) -> + receive + {'DOWN', _, process, Owner, Reason} -> + ee("Unexpected DOWN regarding owner ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, owner}}; + {ready, Owner, Sock} -> + {ok, State#{sock => Sock}} + end + end}, + #{desc => "verify owner as controlling-process", + cmd => fun(#{owner := Owner, sock := Sock} = _State) -> + case socket:getopt(Sock, otp, controlling_process) of + {ok, Owner} -> + ok; + {ok, Other} -> + {error, {unexpected_owner, Other}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + Pid ! {terminate, self()}, + ok + end}, + #{desc => "await (owner) termination", + cmd => fun(#{owner := Owner} = _State) -> + receive + {'DOWN', _, process, Owner, _} -> + ok + end + end}, + #{desc => "verify no socket (closed)", + cmd => fun(#{owner := Owner, sock := Sock} = _State) -> + case socket:getopt(Sock, otp, controlling_process) of + {ok, Pid} -> + {error, {unexpected_success, Owner, Pid}}; + {error, closed} -> + ok; + {error, Reason} -> + ei("expected failure: ~p", [Reason]), + {error, {unexpected_failure, Reason}} + end + end}, + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + p("start (socket) owner evaluator"), + Owner = evaluator_start("owner", OwnerSeq, InitState), -socket_close(_InitState) -> - ok. + p("start tester evaluator"), + TesterInitState = #{owner => Owner}, + Tester = evaluator_start("tester", TesterSeq, TesterInitState), + p("await evaluator"), + ok = await_evaluator_finish([Owner, Tester]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2982,12 +3025,12 @@ socket_close(_InitState) -> %% locally closed while the process is calling the recv function. %% Socket is IPv4. -recv_response_local_close_tcp4(suite) -> +sc_recv_response_local_close_tcp4(suite) -> []; -recv_response_local_close_tcp4(doc) -> +sc_recv_response_local_close_tcp4(doc) -> []; -recv_response_local_close_tcp4(_Config) when is_list(_Config) -> - tc_try(recv_response_local_close_tcp4, +sc_recv_response_local_close_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_recv_response_local_close_tcp4, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recv(Sock) end, @@ -2995,7 +3038,7 @@ recv_response_local_close_tcp4(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = receive_response_local_close_tcp(InitState) + ok = sc_receive_response_local_close_tcp(InitState) end). @@ -3004,12 +3047,12 @@ recv_response_local_close_tcp4(_Config) when is_list(_Config) -> %% locally closed while the process is calling the recv function. %% Socket is IPv6. -recv_response_local_close_tcp6(suite) -> +sc_recv_response_local_close_tcp6(suite) -> []; -recv_response_local_close_tcp6(doc) -> +sc_recv_response_local_close_tcp6(doc) -> []; -recv_response_local_close_tcp6(_Config) when is_list(_Config) -> - tc_try(recv_response_local_close_tcp6, +sc_recv_response_local_close_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_recv_response_local_close_tcp6, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recv(Sock) end, @@ -3017,13 +3060,13 @@ recv_response_local_close_tcp6(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = receive_response_local_close_tcp(InitState) + ok = sc_receive_response_local_close_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -receive_response_local_close_tcp(_InitState) -> +sc_receive_response_local_close_tcp(_InitState) -> ok. @@ -3033,12 +3076,12 @@ receive_response_local_close_tcp(_InitState) -> %% remotely closed while the process is calling the recv function. %% Socket is IPv4. -recv_response_remote_close_tcp4(suite) -> +sc_recv_response_remote_close_tcp4(suite) -> []; -recv_response_remote_close_tcp4(doc) -> +sc_recv_response_remote_close_tcp4(doc) -> []; -recv_response_remote_close_tcp4(_Config) when is_list(_Config) -> - tc_try(recv_response_remote_close_tcp4, +sc_recv_response_remote_close_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_recv_response_remote_close_tcp4, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recv(Sock) end, @@ -3046,7 +3089,7 @@ recv_response_remote_close_tcp4(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = receive_response_remote_close_tcp(InitState) + ok = sc_receive_response_remote_close_tcp(InitState) end). @@ -3055,12 +3098,12 @@ recv_response_remote_close_tcp4(_Config) when is_list(_Config) -> %% remotely closed while the process is calling the recv function. %% Socket is IPv6. -recv_response_remote_close_tcp6(suite) -> +sc_recv_response_remote_close_tcp6(suite) -> []; -recv_response_remote_close_tcp6(doc) -> +sc_recv_response_remote_close_tcp6(doc) -> []; -recv_response_remote_close_tcp6(_Config) when is_list(_Config) -> - tc_try(recv_response_remote_close_tcp6, +sc_recv_response_remote_close_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_recv_response_remote_close_tcp6, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recv(Sock) end, @@ -3068,13 +3111,13 @@ recv_response_remote_close_tcp6(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = receive_response_remote_close_tcp(InitState) + ok = sc_receive_response_remote_close_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -receive_response_remote_close_tcp(_InitState) -> +sc_receive_response_remote_close_tcp(_InitState) -> ok. @@ -3084,12 +3127,12 @@ receive_response_remote_close_tcp(_InitState) -> %% locally closed while the process is calling the recvmsg function. %% Socket is IPv4. -recvmsg_response_local_close_tcp4(suite) -> +sc_recvmsg_response_local_close_tcp4(suite) -> []; -recvmsg_response_local_close_tcp4(doc) -> +sc_recvmsg_response_local_close_tcp4(doc) -> []; -recvmsg_response_local_close_tcp4(_Config) when is_list(_Config) -> - tc_try(recvmsg_response_local_close_tcp4, +sc_recvmsg_response_local_close_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_recvmsg_response_local_close_tcp4, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recvmsg(Sock) end, @@ -3097,7 +3140,7 @@ recvmsg_response_local_close_tcp4(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = receive_response_local_close_tcp(InitState) + ok = sc_receive_response_local_close_tcp(InitState) end). @@ -3106,12 +3149,12 @@ recvmsg_response_local_close_tcp4(_Config) when is_list(_Config) -> %% locally closed while the process is calling the recvmsg function. %% Socket is IPv6. -recvmsg_response_local_close_tcp6(suite) -> +sc_recvmsg_response_local_close_tcp6(suite) -> []; -recvmsg_response_local_close_tcp6(doc) -> +sc_recvmsg_response_local_close_tcp6(doc) -> []; -recvmsg_response_local_close_tcp6(_Config) when is_list(_Config) -> - tc_try(recvmsg_response_local_close_tcp6, +sc_recvmsg_response_local_close_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_recvmsg_response_local_close_tcp6, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recvmsg(Sock) end, @@ -3119,7 +3162,7 @@ recvmsg_response_local_close_tcp6(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = receive_response_local_close_tcp(InitState) + ok = sc_receive_response_local_close_tcp(InitState) end). @@ -3128,12 +3171,12 @@ recvmsg_response_local_close_tcp6(_Config) when is_list(_Config) -> %% remotely closed while the process is calling the recvmsg function. %% Socket is IPv4. -recvmsg_response_remote_close_tcp4(suite) -> +sc_recvmsg_response_remote_close_tcp4(suite) -> []; -recvmsg_response_remote_close_tcp4(doc) -> +sc_recvmsg_response_remote_close_tcp4(doc) -> []; -recvmsg_response_remote_close_tcp4(_Config) when is_list(_Config) -> - tc_try(recvmsg_response_remote_close_tcp4, +sc_recvmsg_response_remote_close_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_recvmsg_response_remote_close_tcp4, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recvmsg(Sock) end, @@ -3141,7 +3184,7 @@ recvmsg_response_remote_close_tcp4(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = receive_response_remote_close_tcp(InitState) + ok = sc_receive_response_remote_close_tcp(InitState) end). @@ -3150,12 +3193,12 @@ recvmsg_response_remote_close_tcp4(_Config) when is_list(_Config) -> %% remotely closed while the process is calling the recvmsg function. %% Socket is IPv6. -recvmsg_response_remote_close_tcp6(suite) -> +sc_recvmsg_response_remote_close_tcp6(suite) -> []; -recvmsg_response_remote_close_tcp6(doc) -> +sc_recvmsg_response_remote_close_tcp6(doc) -> []; -recvmsg_response_remote_close_tcp6(_Config) when is_list(_Config) -> - tc_try(recvmsg_response_remote_close_tcp6, +sc_recvmsg_response_remote_close_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_recvmsg_response_remote_close_tcp6, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recvmsg(Sock) end, @@ -3163,7 +3206,7 @@ recvmsg_response_remote_close_tcp6(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = receive_response_remote_close_tcp(InitState) + ok = sc_receive_response_remote_close_tcp(InitState) end). @@ -3174,18 +3217,18 @@ recvmsg_response_remote_close_tcp6(_Config) when is_list(_Config) -> %% git the setup anyway. %% Socket is IPv4. -acceptor_response_local_close_tcp4(suite) -> +sc_acceptor_response_local_close_tcp4(suite) -> []; -acceptor_response_local_close_tcp4(doc) -> +sc_acceptor_response_local_close_tcp4(doc) -> []; -acceptor_response_local_close_tcp4(_Config) when is_list(_Config) -> - tc_try(acceptor_response_local_close_tcp4, +sc_acceptor_response_local_close_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_acceptor_response_local_close_tcp4, fun() -> not_yet_implemented(), InitState = #{domain => inet, type => stream, protocol => tcp}, - ok = acceptor_response_local_close_tcp(InitState) + ok = sc_acceptor_response_local_close_tcp(InitState) end). @@ -3196,24 +3239,24 @@ acceptor_response_local_close_tcp4(_Config) when is_list(_Config) -> %% git the setup anyway. %% Socket is IPv6. -acceptor_response_local_close_tcp6(suite) -> +sc_acceptor_response_local_close_tcp6(suite) -> []; -acceptor_response_local_close_tcp6(doc) -> +sc_acceptor_response_local_close_tcp6(doc) -> []; -acceptor_response_local_close_tcp6(_Config) when is_list(_Config) -> - tc_try(acceptor_response_local_close_tcp6, +sc_acceptor_response_local_close_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_acceptor_response_local_close_tcp6, fun() -> not_yet_implemented(), InitState = #{domain => inet, type => stream, protocol => tcp}, - ok = acceptor_response_local_close_tcp(InitState) + ok = sc_acceptor_response_local_close_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -acceptor_response_local_close_tcp(_InitState) -> +sc_acceptor_response_local_close_tcp(_InitState) -> ok. @@ -3225,18 +3268,18 @@ acceptor_response_local_close_tcp(_InitState) -> %% git the setup anyway. %% Socket is IPv4. -acceptor_response_remote_close_tcp4(suite) -> +sc_acceptor_response_remote_close_tcp4(suite) -> []; -acceptor_response_remote_close_tcp4(doc) -> +sc_acceptor_response_remote_close_tcp4(doc) -> []; -acceptor_response_remote_close_tcp4(_Config) when is_list(_Config) -> - tc_try(acceptor_response_remote_close_tcp4, +sc_acceptor_response_remote_close_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_acceptor_response_remote_close_tcp4, fun() -> not_yet_implemented(), InitState = #{domain => inet, type => stream, protocol => tcp}, - ok = acceptor_response_remote_close_tcp(InitState) + ok = sc_acceptor_response_remote_close_tcp(InitState) end). @@ -3247,24 +3290,24 @@ acceptor_response_remote_close_tcp4(_Config) when is_list(_Config) -> %% git the setup anyway. %% Socket is IPv6. -acceptor_response_remote_close_tcp6(suite) -> +sc_acceptor_response_remote_close_tcp6(suite) -> []; -acceptor_response_remote_close_tcp6(doc) -> +sc_acceptor_response_remote_close_tcp6(doc) -> []; -acceptor_response_remote_close_tcp6(_Config) when is_list(_Config) -> +sc_acceptor_response_remote_close_tcp6(_Config) when is_list(_Config) -> tc_try(acceptor_response_remote_close_tcp6, fun() -> not_yet_implemented(), InitState = #{domain => inet, type => stream, protocol => tcp}, - ok = acceptor_response_remote_close_tcp(InitState) + ok = sc_acceptor_response_remote_close_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -acceptor_response_remote_close_tcp(_InitState) -> +sc_acceptor_response_remote_close_tcp(_InitState) -> ok. -- cgit v1.2.3 From 043624804888dc021a75b01c0a3d8c1c0a1fde23 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 16 Oct 2018 15:53:37 +0200 Subject: [socket-nif] Recv handling of closing sockets Fixed more issues regarding closing sockets. Specifically with respect to recv calls. Also, added demonitor for all *current* processes. Also fixed a buffer overflow problem when writing an warning message (the timestamp buffer was not large enough). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 61 +++++++++++++++++++++++++++------ erts/emulator/nifs/common/socket_util.c | 2 +- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f09f618653..3ec19c5a60 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -4776,17 +4776,23 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { /* Prep done - inform the caller it can finalize (close) directly */ SSDBG( descP, - ("SOCKET", "nclose -> [%d] stop called\r\n", descP->sock) ); + ("SOCKET", "nclose -> [%d] stop was called\r\n", descP->sock) ); dec_socket(domain, type, protocol); reply = esock_atom_ok; } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { /* The stop callback function has been *scheduled* which means that we * have to wait for it to complete. */ SSDBG( descP, - ("SOCKET", "nclose -> [%d] stop scheduled\r\n", descP->sock) ); + ("SOCKET", "nclose -> [%d] stop was scheduled\r\n", + descP->sock) ); dec_socket(domain, type, protocol); // SHALL WE DO THIS AT finalize? reply = esock_make_ok2(env, descP->closeRef); } else { + + SSDBG( descP, + ("SOCKET", "nclose -> [%d] stop failed: %d\r\n", + descP->sock, selectRes) ); + /* * * WE SHOULD REALLY HAVE A WAY TO CLOBBER THE SOCKET, @@ -4796,6 +4802,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, * * */ + reason = MKT2(env, atom_select, MKI(env, selectRes)); reply = esock_make_error(env, reason); } @@ -12000,6 +12007,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] eagain\r\n", toRead) ); + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) + return esock_make_error_str(env, xres); + SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); @@ -15005,6 +15015,7 @@ static void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) { SocketDescriptor* descP = (SocketDescriptor*) obj; + int dres; SSDBG( descP, ("SOCKET", "socket_stop -> entry when" @@ -15033,15 +15044,24 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) */ SSDBG( descP, ("SOCKET", - "socket_stop -> demonitor (maybe) controlling process\r\n") ); - DEMONP(env, descP, &descP->ctrlMon); + "socket_stop -> demonitor (maybe) controlling process (%T)\r\n", + descP->ctrlPid) ); + dres = DEMONP(env, descP, &descP->ctrlMon); + SSDBG( descP, ("SOCKET", "socket_stop -> demonitor result: %d\r\n", dres) ); + if (descP->currentWriterP != NULL) { /* We have a (current) writer and *may* therefor also have * writers waiting. */ - SSDBG( descP, ("SOCKET", "socket_stop -> handle writer(s)\r\n") ); + SSDBG( descP, + ("SOCKET", + "socket_stop -> demonitor (maybe) current writer: %T, %T\r\n", + descP->currentWriter.pid, descP->currentWriter.ref) ); + DEMONP(env, descP, &descP->currentWriter.mon); + + SSDBG( descP, ("SOCKET", "socket_stop -> handle current writer\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentWriter.pid) && @@ -15059,6 +15079,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) } /* And also deal with the waiting writers (in the same way) */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting writer(s)\r\n") ); inform_waiting_procs(env, descP, &descP->writersQ, TRUE, atom_closed); } @@ -15068,7 +15089,13 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * readers waiting. */ - SSDBG( descP, ("SOCKET", "socket_stop -> handle reader(s)\r\n") ); + SSDBG( descP, + ("SOCKET", + "socket_stop -> demonitor (maybe) current reader: %T, %T\r\n", + descP->currentReader.pid, descP->currentReader.ref) ); + DEMONP(env, descP, &descP->currentReader.mon); + + SSDBG( descP, ("SOCKET", "socket_stop -> handle current reader\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentReader.pid) && @@ -15086,6 +15113,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) } /* And also deal with the waiting readers (in the same way) */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting reader(s)\r\n") ); inform_waiting_procs(env, descP, &descP->readersQ, TRUE, atom_closed); } @@ -15094,7 +15122,13 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * acceptors waiting. */ - SSDBG( descP, ("SOCKET", "socket_stop -> handle acceptor(s)\r\n") ); + SSDBG( descP, + ("SOCKET", + "socket_stop -> demonitor (maybe) current acceptor: %T, %T\r\n", + descP->currentAcceptor.pid, descP->currentAcceptor.ref) ); + DEMONP(env, descP, &descP->currentAcceptor.mon); + + SSDBG( descP, ("SOCKET", "socket_stop -> handle current acceptor\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentAcceptor.pid) && @@ -15112,6 +15146,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) } /* And also deal with the waiting acceptors (in the same way) */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting acceptor(s)\r\n") ); inform_waiting_procs(env, descP, &descP->acceptorsQ, TRUE, atom_closed); } @@ -15237,9 +15272,13 @@ void socket_down(ErlNifEnv* env, SocketDescriptor* descP = (SocketDescriptor*) obj; SSDBG( descP, ("SOCKET", "socket_down -> entry with" - "\r\n sock: %d" - "\r\n pid: %T" - "\r\n", descP->sock, *pid) ); + "\r\n sock: %d" + "\r\n pid: %T" + "\r\n Closed: %s" + "\r\n Closing: %s" + "\r\n", + descP->sock, *pid, + B2S(IS_CLOSED(descP)), B2S(IS_CLOSING(descP))) ); if (compare_pids(env, &descP->ctrlPid, pid)) { @@ -15249,7 +15288,7 @@ void socket_down(ErlNifEnv* env, * we leave it to the stop callback function. */ - SSDBG( descP, ("SOCKET", "socket_down -> controlling process term\r\n") ); + SSDBG( descP, ("SOCKET", "socket_down -> controlling process exit\r\n") ); descP->state = SOCKET_STATE_CLOSING; descP->closeLocal = TRUE; diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index ff50fd2384..766d3724c1 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1506,7 +1506,7 @@ void esock_warning_msg( const char* format, ... ) { va_list args; char f[512 + sizeof(format)]; // This has to suffice... - char stamp[32]; + char stamp[64]; // Just in case... struct timespec ts; int res; -- cgit v1.2.3 From f945aa4a8067d745ee75fe695272104e220d7bc3 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 16 Oct 2018 15:57:17 +0200 Subject: [socket-nif|test] Added test case for closed socket while recv Added a test cases for testing "socket cleanup" while process is reading using recv. OTP-14831 --- lib/kernel/test/socket_SUITE.erl | 632 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 596 insertions(+), 36 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 42420a37dc..8ab39f6ffe 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -66,11 +66,11 @@ api_to_recvmsg_tcp4/1, api_to_recvmsg_tcp6/1, - %% Controlling Process - sc_socket_cleanup_tcp4/1, - sc_socket_cleanup_tcp6/1, - sc_socket_cleanup_udp4/1, - sc_socket_cleanup_udp6/1, + %% Socket Closure + sc_cpe_socket_cleanup_tcp4/1, + sc_cpe_socket_cleanup_tcp6/1, + sc_cpe_socket_cleanup_udp4/1, + sc_cpe_socket_cleanup_udp6/1, sc_recv_response_local_close_tcp4/1, sc_recv_response_local_close_tcp6/1, sc_recv_response_remote_close_tcp4/1, @@ -130,7 +130,8 @@ groups() -> {api_basic, [], api_basic_cases()}, {api_options, [], api_options_cases()}, {api_op_with_timeout, [], api_op_with_timeout_cases()}, - {socket_closure, [], socket_closure_cases()} + {socket_closure, [], socket_closure_cases()}, + {sc_ctrl_proc_exit, [], sc_cp_exit_cases()} %% {tickets, [], ticket_cases()} ]. @@ -185,10 +186,7 @@ api_op_with_timeout_cases() -> %% remotely. socket_closure_cases() -> [ - sc_socket_cleanup_tcp4, - sc_socket_cleanup_tcp6, - sc_socket_cleanup_udp4, - sc_socket_cleanup_udp6, + {group, sc_ctrl_proc_exit}, sc_recv_response_local_close_tcp4, sc_recv_response_local_close_tcp6, @@ -206,6 +204,16 @@ socket_closure_cases() -> sc_acceptor_response_remote_close_tcp6 ]. +%% These cases are all about socket cleanup after the controlling process +%% exits *without* calling socket:close/1. +sc_cp_exit_cases() -> + [ + sc_cpe_socket_cleanup_tcp4, + sc_cpe_socket_cleanup_tcp6, + sc_cpe_socket_cleanup_udp4, + sc_cpe_socket_cleanup_udp6 + ]. + %% ticket_cases() -> %% []. @@ -1782,7 +1790,7 @@ api_to_maccept_tcp(InitState) -> #{desc => "close (listen) socket", cmd => fun(#{lsock := LSock} = State) -> sock_close(LSock), - {ok, maps:remove(sock3, State)} + {ok, maps:remove(lsock, State)} end}, %% *** We are done *** @@ -2805,18 +2813,18 @@ api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> %% ("removed") when the controlling process terminates (without explicitly %% calling the close function). For a IPv4 TCP (stream) socket. -sc_socket_cleanup_tcp4(suite) -> +sc_cpe_socket_cleanup_tcp4(suite) -> []; -sc_socket_cleanup_tcp4(doc) -> +sc_cpe_socket_cleanup_tcp4(doc) -> []; -sc_socket_cleanup_tcp4(_Config) when is_list(_Config) -> - tc_try(sc_socket_cleanup_tcp4, +sc_cpe_socket_cleanup_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_cpe_socket_cleanup_tcp4, fun() -> %% not_yet_implemented(), InitState = #{domain => inet, type => stream, protocol => tcp}, - ok = sc_socket_cleanup(InitState) + ok = sc_cpe_socket_cleanup(InitState) end). @@ -2825,18 +2833,18 @@ sc_socket_cleanup_tcp4(_Config) when is_list(_Config) -> %% ("removed") when the controlling process terminates (without explicitly %% calling the close function). For a IPv6 TCP (stream) socket. -sc_socket_cleanup_tcp6(suite) -> +sc_cpe_socket_cleanup_tcp6(suite) -> []; -sc_socket_cleanup_tcp6(doc) -> +sc_cpe_socket_cleanup_tcp6(doc) -> []; -sc_socket_cleanup_tcp6(_Config) when is_list(_Config) -> - tc_try(sc_socket_cleanup_tcp6, +sc_cpe_socket_cleanup_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_cpe_socket_cleanup_tcp6, fun() -> not_yet_implemented(), InitState = #{domain => inet6, type => stream, protocol => tcp}, - ok = sc_socket_cleanup(InitState) + ok = sc_cpe_socket_cleanup(InitState) end). @@ -2845,17 +2853,17 @@ sc_socket_cleanup_tcp6(_Config) when is_list(_Config) -> %% ("removed") when the controlling process terminates (without explicitly %% calling the close function). For a IPv4 UDP (dgram) socket. -sc_socket_cleanup_udp4(suite) -> +sc_cpe_socket_cleanup_udp4(suite) -> []; -sc_socket_cleanup_udp4(doc) -> +sc_cpe_socket_cleanup_udp4(doc) -> []; -sc_socket_cleanup_udp4(_Config) when is_list(_Config) -> - tc_try(sc_socket_cleanup_udp4, +sc_cpe_socket_cleanup_udp4(_Config) when is_list(_Config) -> + tc_try(sc_cpe_socket_cleanup_udp4, fun() -> InitState = #{domain => inet, type => dgram, protocol => udp}, - ok = sc_socket_cleanup(InitState) + ok = sc_cpe_socket_cleanup(InitState) end). @@ -2865,24 +2873,24 @@ sc_socket_cleanup_udp4(_Config) when is_list(_Config) -> %% (removed) when the controlling process terminates (without explicitly %% calling the close function). For a IPv6 UDP (dgram) socket. -sc_socket_cleanup_udp6(suite) -> +sc_cpe_socket_cleanup_udp6(suite) -> []; -sc_socket_cleanup_udp6(doc) -> +sc_cpe_socket_cleanup_udp6(doc) -> []; -sc_socket_cleanup_udp6(_Config) when is_list(_Config) -> - tc_try(sc_socket_cleanup_udp6, +sc_cpe_socket_cleanup_udp6(_Config) when is_list(_Config) -> + tc_try(sc_cpe_socket_cleanup_udp6, fun() -> not_yet_implemented(), InitState = #{domain => inet6, type => dgram, protocol => udp}, - ok = sc_socket_cleanup(InitState) + ok = sc_cpe_socket_cleanup(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sc_socket_cleanup(InitState) -> +sc_cpe_socket_cleanup(InitState) -> OwnerSeq = [ %% *** Wait for start order part *** @@ -3022,8 +3030,16 @@ sc_socket_cleanup(InitState) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test what happens when a socket is -%% locally closed while the process is calling the recv function. +%% locally closed while a process is calling the recv function. %% Socket is IPv4. +%% +%% +%% +%% We should really have a similar test cases for when the controlling +%% process exits and there are other processes in recv, accept, and +%% all the other functions. +%% +%% sc_recv_response_local_close_tcp4(suite) -> []; @@ -3032,7 +3048,7 @@ sc_recv_response_local_close_tcp4(doc) -> sc_recv_response_local_close_tcp4(_Config) when is_list(_Config) -> tc_try(sc_recv_response_local_close_tcp4, fun() -> - not_yet_implemented(), + %% not_yet_implemented(), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet, type => stream, @@ -3066,8 +3082,548 @@ sc_recv_response_local_close_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sc_receive_response_local_close_tcp(_InitState) -> - ok. +sc_receive_response_local_close_tcp(InitState) -> + %% This is the server that accepts connections. + %% But it is also suppose to close the connection socket, + %% and trigger the read failure for the handler process. + AcceptorSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + receive + {start, Tester} when is_pid(Tester) -> + {ok, State#{tester => Tester}} + end + 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#{lsa => LSA}} + end}, + #{desc => "create (listen) socket", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) 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}) -> + Tester ! {ready, self(), Port}, + ok + end}, + + %% The actual test + #{desc => "await continue (connection)", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {continue, Tester, Handler} -> + {ok, State#{handler => Handler}} + end + end}, + #{desc => "await connection", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, Sock} -> + ei("connection accepted"), + {ok, State#{csock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "transfer new connection to handler", + cmd => fun(#{handler := Handler, csock := Sock}) -> + ok = socket:setopt(Sock, + otp, controlling_process, + Handler), + Handler ! {connection, Sock}, + ok + end}, + #{desc => "announce ready (connection)", + cmd => fun(#{tester := Tester}) -> + Tester ! {ready, self()}, + ok + end}, + #{desc => "await continue (close)", + cmd => fun(#{tester := Tester} = _State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {continue, Tester} -> + ok + end + end}, + %% #{desc => "enable debug", + %% cmd => fun(#{csock := Sock}) -> + %% socket:setopt(Sock, otp, debug, true) + %% end}, + #{desc => "close (the connection) socket", + cmd => fun(#{csock := Sock}) -> + socket:close(Sock) + end}, + + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {terminate, Tester} -> + {ok, maps:remove(tester, State)} + end + end}, + #{desc => "socket cleanup", + cmd => fun(#{lsock := Sock} = State) -> + ok = socket:close(Sock), + State1 = maps:remove(csock, State), + State2 = maps:remove(lsock, State1), + State3 = maps:remove(lport, State2), + {ok, State3} + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + %% The point of this is to perform the recv for which we are testing the reponse + HandlerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + receive + {start, Tester} when is_pid(Tester) -> + {ok, State#{tester => Tester}} + end + end}, + #{desc => "monitor server", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + Tester ! {ready, self()}, + ok + end}, + + %% The actual test + #{desc => "await connection socket", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {connection, Sock} -> + {ok, State#{sock => Sock}} + end + end}, + #{desc => "announce ready (connection)", + cmd => fun(#{tester := Tester}) -> + Tester ! {ready, self()}, + ok + end}, + %% #{desc => "enable debug", + %% cmd => fun(#{sock := Sock}) -> + %% socket:setopt(Sock, otp, debug, true) + %% end}, + %% #{desc => "monitored-by", + %% cmd => fun(_) -> + %% {_, Mons} = process_info(self(), monitored_by), + %% ei("Monitored By: ~p", [Mons]), + %% ok + %% end}, + #{desc => "attempt recv", + cmd => fun(#{sock := Sock} = State) -> + case socket:recv(Sock) of + {ok, _Data} -> + ee("Unexpected data received"), + {error, unexpected_data}; + {error, closed} -> + State1 = maps:remove(sock, State), + {ok, State1}; + {error, Reason} = ERROR -> + ee("Unexpected read faulure: " + "~n ~p", [Reason]), + ERROR + end + end}, + %% #{desc => "monitored-by", + %% cmd => fun(_) -> + %% {_, Mons} = process_info(self(), monitored_by), + %% ei("Monitored By: ~p", [Mons]), + %% ok + %% end}, + #{desc => "announce ready (close)", + cmd => fun(#{tester := Tester}) -> + Tester ! {ready, self()}, + ok + end}, + #{desc => "sleep some", + cmd => fun(_) -> + ?SLEEP(1000), + ok + end}, + %% #{desc => "monitored-by", + %% cmd => fun(_) -> + %% {_, Mons} = process_info(self(), monitored_by), + %% ei("Monitored By: ~p", [Mons]), + %% ok + %% end}, + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = _State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {terminate, Tester} -> + ok + end + end}, + + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + %% The point of this is basically just to create the connection. + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + receive + {start, Tester} when is_pid(Tester) -> + {ok, State#{tester => Tester}} + end + end}, + + %% Init + #{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 socket", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket to local address", + cmd => fun(#{sock := Sock, lsa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready", + cmd => fun(#{tester := Tester} = _State) -> + Tester ! {ready, self()}, + ok + end}, + + %% The actual test + #{desc => "await continue", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Tester, Reason]), + {error, {unexpected_exit, tester, Reason}}; + {continue, Tester, Port} -> + {ok, State#{lport => Port}} + end + end}, + #{desc => "connect to server", + cmd => fun(#{sock := Sock, lsa := LSA, lport := LPort}) -> + socket:connect(Sock, LSA#{port => LPort}) + end}, + #{desc => "announce ready (connection)", + cmd => fun(#{tester := Tester} = _State) -> + Tester ! {ready, self()}, + ok + end}, + + %% Cleaning up + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Tester, Reason]), + {error, {unexpected_exit, tester}}; + {terminate, Tester} -> + {ok, maps:remove(tester, State)} + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + sock_close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor acceptor", + cmd => fun(#{acceptor := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor handler", + cmd => fun(#{handler := 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 acceptor + #{desc => "order acceptor start", + cmd => fun(#{acceptor := Pid} = _State) -> + Pid ! {start, self()}, + ok + end}, + #{desc => "await acceptor ready (init)", + cmd => fun(#{acceptor := Pid} = State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding acceptor ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, acceptor}}; + {ready, Pid, Port} -> + {ok, State#{lport => Port}} + end + end}, + + %% Start the handler + #{desc => "order handler start", + cmd => fun(#{handler := Pid} = _State) -> + Pid ! {start, self()}, + ok + end}, + #{desc => "await handler ready (init)", + cmd => fun(#{handler := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding handler ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, acceptor}}; + {ready, Pid} -> + ok + end + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid} = _State) -> + Pid ! {start, self()}, + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding cient ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, acceptor}}; + {ready, Pid} -> + ok + end + end}, + + %% The actual test + #{desc => "order acceptor to continue", + cmd => fun(#{acceptor := Pid, handler := Handler} = _State) -> + Pid ! {continue, self(), Handler}, + ok + end}, + #{desc => "order client to continue", + cmd => fun(#{client := Pid, lport := Port} = _State) -> + Pid ! {continue, self(), Port}, + ok + end}, + #{desc => "await acceptor ready (connection)", + cmd => fun(#{acceptor := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding acceptor ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, acceptor}}; + {ready, Pid} -> + ok + end + end}, + #{desc => "await client ready (connection)", + cmd => fun(#{client := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding client ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, acceptor}}; + {ready, Pid} -> + ok + end + end}, + #{desc => "await handler ready (connection)", + cmd => fun(#{handler := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding handler ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, acceptor}}; + {ready, Pid} -> + ok + end + end}, + #{desc => "sleep some", + cmd => fun(_State) -> + ?SLEEP(1000), + ok + end}, + #{desc => "order acceptor to continue (close)", + cmd => fun(#{acceptor := Pid} = _State) -> + Pid ! {continue, self()}, + ok + end}, + #{desc => "await handler ready (close)", + cmd => fun(#{handler := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding handler ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, acceptor}}; + {ready, Pid} -> + ok + end + end}, + + %% Terminations + #{desc => "order handler to terminate", + cmd => fun(#{handler := Pid} = _State) -> + Pid ! {terminate, self()}, + ok + end}, + #{desc => "await handler termination", + cmd => fun(#{handler := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + {ok, maps:remove(handler, State)} + end + end}, + #{desc => "order client to terminate", + cmd => fun(#{client := Pid} = _State) -> + Pid ! {terminate, self()}, + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + {ok, maps:remove(client, State)} + end + end}, + #{desc => "order acceptor to terminate", + cmd => fun(#{acceptor := Pid} = _State) -> + Pid ! {terminate, self()}, + ok + end}, + #{desc => "await acceptor termination", + cmd => fun(#{acceptor := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + {ok, maps:remove(acceptor, State)} + end + end}, + + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + p("start acceptor evaluator"), + AccInitState = InitState, + Acceptor = evaluator_start("acceptor", AcceptorSeq, AccInitState), + + p("start handler evaluator"), + HandlerInitState = #{}, + Handler = evaluator_start("handler", HandlerSeq, HandlerInitState), + + p("start client evaluator"), + ClientInitState = InitState, + Client = evaluator_start("client", ClientSeq, ClientInitState), + + p("start tester evaluator"), + TesterInitState = #{acceptor => Acceptor, + handler => Handler, + client => Client}, + Tester = evaluator_start("tester", TesterSeq, TesterInitState), + + p("await evaluator"), + ok = await_evaluator_finish([Acceptor, Handler, Client, Tester]). @@ -3426,9 +3982,13 @@ await_evaluator_finish(Evs, Fails) -> end. +ei(F) -> + ei(F, []). ei(F, A) -> eprint("", F, A). +ee(F) -> + ee(F, []). ee(F, A) -> eprint(" ", F, A). -- cgit v1.2.3 From 6a5013d89a02401b132483711325ca07b8357020 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 18 Oct 2018 16:17:42 +0200 Subject: [socket-nif] Socket close Fixed a number of issues regarding monitor handling. Monitors are now wrapped in its own type (ESockMonitor). This is hopefully temporary! The reason for this is that there is no way to "initialize" a monitor after a demonitor. This could mean that after a successful demonitor, you could still get a down-callback, and thenh compare with, for instance, ctrlMOn field and get a match, even though it was just (successfully) demonitor'ed. To make debugging easier, the monitor and demonitor calls are now wrapped in their own functions, with debug printouts before and in case of failure, also after the actual calls Also, fixed a number of problems with cancel, where monitors where left still active after cleaning up current and active reader, writer and acceptor. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 8 +- erts/emulator/nifs/common/socket_nif.c | 412 ++++++++++++++++++++++++++------- 2 files changed, 329 insertions(+), 91 deletions(-) diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index f9246856fa..d89970ecd6 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -216,8 +216,12 @@ extern ERL_NIF_TERM esock_atom_einval; #define MLOCK(M) enif_mutex_lock((M)) #define MUNLOCK(M) enif_mutex_unlock((M)) -#define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) -#define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) +// #define MONP(S,E,D,P,M) enif_monitor_process((E), (D), (P), (M)) +// #define DEMONP(S,E,D,M) enif_demonitor_process((E), (D), (M)) +#define MONP(S,E,D,P,M) esock_monitor((S), (E), (D), (P), (M)) +#define DEMONP(S,E,D,M) esock_demonitor((S), (E), (D), (M)) +#define MON_INIT(M) esock_monitor_init((M)) +// #define MON_COMP(M1, M2) esock_monitor_compare((M1), (M2)) #define SELECT(E,FD,M,O,P,R) \ if (enif_select((E), (FD), (M), (O), (P), (R)) < 0) \ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 3ec19c5a60..5d2cfbc10b 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -725,9 +725,16 @@ static unsigned long one_value = 1; ((sap)->in4.sin_port) : -1) +typedef union { + ErlNifMonitor mon; + uint32_t raw[4]; +} ESockMonitor; + + typedef struct { ErlNifPid pid; // PID of the requesting process - ErlNifMonitor mon; // Monitor to the requesting process + // ErlNifMonitor mon; Monitor to the requesting process + ESockMonitor mon; // Monitor to the requesting process ERL_NIF_TERM ref; // The (unique) reference (ID) of the request } SocketRequestor; @@ -760,7 +767,8 @@ typedef struct { /* +++ Controller (owner) process +++ */ ErlNifPid ctrlPid; - ErlNifMonitor ctrlMon; + // ErlNifMonitor ctrlMon; + ESockMonitor ctrlMon; /* +++ Write stuff +++ */ ErlNifMutex* writeMtx; @@ -803,7 +811,8 @@ typedef struct { /* +++ Close stuff +++ */ ErlNifMutex* closeMtx; ErlNifPid closerPid; - ErlNifMonitor closerMon; + // ErlNifMonitor closerMon; + ESockMonitor closerMon; ERL_NIF_TERM closeRef; BOOLEAN_T closeLocal; @@ -2185,7 +2194,8 @@ static ERL_NIF_TERM acceptor_push(ErlNifEnv* env, static BOOLEAN_T acceptor_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref); static BOOLEAN_T acceptor_unqueue(ErlNifEnv* env, SocketDescriptor* descP, @@ -2201,7 +2211,8 @@ static ERL_NIF_TERM writer_push(ErlNifEnv* env, static BOOLEAN_T writer_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref); static BOOLEAN_T writer_unqueue(ErlNifEnv* env, SocketDescriptor* descP, @@ -2217,7 +2228,8 @@ static ERL_NIF_TERM reader_push(ErlNifEnv* env, static BOOLEAN_T reader_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref); static BOOLEAN_T reader_unqueue(ErlNifEnv* env, SocketDescriptor* descP, @@ -2230,8 +2242,27 @@ static void qpush(SocketRequestQueue* q, SocketRequestQueueElement* e); static SocketRequestQueueElement* qpop(SocketRequestQueue* q); static BOOLEAN_T qunqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const char* slogan, SocketRequestQueue* q, const ErlNifPid* pid); + +static int esock_monitor(const char* slogan, + ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid, + ESockMonitor* mon); +static int esock_demonitor(const char* slogan, + ErlNifEnv* env, + SocketDescriptor* descP, + ESockMonitor* monP); +static void esock_monitor_init(ESockMonitor* mon); +/* +static int esock_monitor_compare(const ErlNifMonitor* mon1, + const ESockMonitor* mon2); +*/ + + /* #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) static size_t my_strnlen(const char *s, size_t maxlen); @@ -2282,6 +2313,7 @@ static BOOLEAN_T extract_iow(ErlNifEnv* env, static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); + #if HAVE_IN6 # if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY # if HAVE_DECL_IN6ADDR_ANY_INIT @@ -2786,7 +2818,6 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, res = enif_make_resource(env, descP); enif_release_resource(descP); // We should really store a reference ... - /* Keep track of the creator * This should not be a problem but just in case * the *open* function is used with the wrong kind @@ -2795,9 +2826,10 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, if (enif_self(env, &descP->ctrlPid) == NULL) return esock_make_error(env, atom_exself); - if (MONP(env, descP, + if (MONP("nopen -> ctrl", + env, descP, &descP->ctrlPid, - &descP->ctrlMon) > 0) + &descP->ctrlMon) != 0) return esock_make_error(env, atom_exmon); @@ -3396,9 +3428,10 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "naccept_listening -> would block\r\n") ); descP->currentAcceptor.pid = caller; - if (MONP(env, descP, + if (MONP("naccept_listening -> current acceptor", + env, descP, &descP->currentAcceptor.pid, - &descP->currentAcceptor.mon) > 0) + &descP->currentAcceptor.mon) != 0) return esock_make_error(env, atom_exmon); descP->currentAcceptor.ref = enif_make_copy(descP->env, ref); @@ -3473,9 +3506,10 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, enif_release_resource(accDescP); // We should really store a reference ... accDescP->ctrlPid = caller; - if (MONP(env, accDescP, + if (MONP("naccept_listening -> ctrl", + env, accDescP, &accDescP->ctrlPid, - &accDescP->ctrlMon) > 0) { + &accDescP->ctrlMon) != 0) { sock_close(accSock); return esock_make_error(env, atom_exmon); } @@ -3589,7 +3623,8 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "naccept_accepting -> accept success\r\n") ); - DEMONP(env, descP, &descP->currentAcceptor.mon); + DEMONP("naccept_accepting -> current acceptor", + env, descP, &descP->currentAcceptor.mon); if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) { save_errno = sock_errno(); @@ -3611,9 +3646,10 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, enif_release_resource(accDescP); // We should really store a reference ... accDescP->ctrlPid = caller; - if (MONP(env, accDescP, + if (MONP("naccept_accepting -> ctrl", + env, accDescP, &accDescP->ctrlPid, - &accDescP->ctrlMon) > 0) { + &accDescP->ctrlMon) != 0) { sock_close(accSock); return esock_make_error(env, atom_exmon); } @@ -4728,7 +4764,11 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, int type = descP->type; int protocol = descP->protocol; - SSDBG( descP, ("SOCKET", "nclose -> [%d] entry\r\n", descP->sock) ); + SSDBG( descP, ("SOCKET", "nclose -> [%d] entry (0x%lX, 0x%lX, 0x%lX)\r\n", + descP->sock, + descP->currentWriterP, + descP->currentReaderP, + descP->currentAcceptorP) ); MLOCK(descP->closeMtx); @@ -4753,11 +4793,18 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, /* Monitor the caller, since we should complete this operation even if * the caller dies (for whatever reason). + * + * + * + * Can we actiually use this for anything? + * + * */ - if (MONP(env, descP, - &descP->closerPid, - &descP->closerMon) > 0) { + if (MONP("nclose -> closer", + env, descP, + &descP->closerPid, + &descP->closerMon) != 0) { MUNLOCK(descP->closeMtx); return esock_make_error(env, atom_exmon); } @@ -4778,6 +4825,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nclose -> [%d] stop was called\r\n", descP->sock) ); dec_socket(domain, type, protocol); + DEMONP("nclose -> closer", env, descP, &descP->closerMon); reply = esock_atom_ok; } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { /* The stop callback function has been *scheduled* which means that we @@ -4803,6 +4851,9 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, * */ + // No point in having this? + DEMONP("nclose -> closer", env, descP, &descP->closerMon); + reason = MKT2(env, atom_select, MKI(env, selectRes)); reply = esock_make_error(env, reason); } @@ -5160,7 +5211,8 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, ERL_NIF_TERM eVal) { ErlNifPid caller, newCtrlPid; - ErlNifMonitor newCtrlMon; + // ErlNifMonitor newCtrlMon; + ESockMonitor newCtrlMon; int xres; SSDBG( descP, @@ -5183,20 +5235,22 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, return esock_make_error(env, esock_atom_einval); } - if ((xres = MONP(env, descP, &newCtrlPid, &newCtrlMon)) != 0) { + if ((xres = MONP("nsetopt_otp_ctrl_proc -> (new) ctrl", + env, descP, &newCtrlPid, &newCtrlMon)) != 0) { esock_warning_msg("Failed monitor %d) (new) controlling process\r\n", xres); return esock_make_error(env, esock_atom_einval); } - if ((xres = DEMONP(env, descP, &descP->ctrlMon)) != 0) { + if ((xres = DEMONP("nsetopt_otp_ctrl_proc -> (old) ctrl", + env, descP, &descP->ctrlMon)) != 0) { esock_warning_msg("Failed demonitor (%d) " "old controlling process %T (%T)\r\n", xres, descP->ctrlPid, descP->ctrlMon); } descP->ctrlPid = newCtrlPid; - descP->ctrlMon = newCtrlMon; - + descP->ctrlMon = newCtrlMon; + SSDBG( descP, ("SOCKET", "nsetopt_otp_ctrl_proc -> done\r\n") ); return esock_atom_ok; @@ -11137,6 +11191,8 @@ ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "ncancel_accept_current -> entry\r\n") ); + DEMONP("ncancel_accept_current -> current acceptor", + env, descP, &descP->currentAcceptor.mon); res = ncancel_read_select(env, descP, descP->currentAcceptor.ref); SSDBG( descP, ("SOCKET", "ncancel_accept_current -> cancel res: %T\r\n", res) ); @@ -11255,6 +11311,8 @@ ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "ncancel_send_current -> entry\r\n") ); + DEMONP("ncancel_recv_current -> current writer", + env, descP, &descP->currentWriter.mon); res = ncancel_write_select(env, descP, descP->currentWriter.ref); SSDBG( descP, ("SOCKET", "ncancel_send_current -> cancel res: %T\r\n", res) ); @@ -11371,6 +11429,8 @@ ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "ncancel_recv_current -> entry\r\n") ); + DEMONP("ncancel_recv_current -> current reader", + env, descP, &descP->currentReader.mon); res = ncancel_read_select(env, descP, descP->currentReader.ref); SSDBG( descP, ("SOCKET", "ncancel_recv_current -> cancel res: %T\r\n", res) ); @@ -11568,7 +11628,8 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, cnt_inc(&descP->writePkgCnt, 1); cnt_inc(&descP->writeByteCnt, written); if (descP->currentWriterP != NULL) - DEMONP(env, descP, &descP->currentWriter.mon); + DEMONP("send_check_result -> current writer", + env, descP, &descP->currentWriter.mon); SSDBG( descP, ("SOCKET", "send_check_result -> " @@ -11607,7 +11668,8 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, if ((saveErrno != EAGAIN) && (saveErrno != EINTR)) { ErlNifPid pid; - ErlNifMonitor mon; + // ErlNifMonitor mon; + ESockMonitor mon; ERL_NIF_TERM ref, res; /* @@ -11622,13 +11684,15 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, res = esock_make_error_errno(env, saveErrno); if (descP->currentWriterP != NULL) { - DEMONP(env, descP, &descP->currentWriter.mon); + DEMONP("send_check_result -> current writer", + env, descP, &descP->currentWriter.mon); while (writer_pop(env, descP, &pid, &mon, &ref)) { SSDBG( descP, ("SOCKET", "send_check_result -> abort %T\r\n", pid) ); send_msg_nif_abort(env, ref, res, &pid); - DEMONP(env, descP, &mon); + DEMONP("send_check_result -> pop'ed writer", + env, descP, &mon); } } @@ -11660,9 +11724,10 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, if (enif_self(env, &caller) == NULL) return esock_make_error(env, atom_exself); descP->currentWriter.pid = caller; - if (MONP(env, descP, + if (MONP("send_check_result -> current writer", + env, descP, &descP->currentWriter.pid, - &descP->currentWriter.mon) > 0) + &descP->currentWriter.mon) != 0) return esock_make_error(env, atom_exmon); descP->currentWriter.ref = enif_make_copy(descP->env, sendRef); descP->currentWriterP = &descP->currentWriter; @@ -11747,9 +11812,10 @@ char* recv_init_current_reader(ErlNifEnv* env, return str_exself; descP->currentReader.pid = caller; - if (MONP(env, descP, + if (MONP("recv_init_current_reader -> current reader", + env, descP, &descP->currentReader.pid, - &descP->currentReader.mon) > 0) { + &descP->currentReader.mon) != 0) { return str_exmon; } descP->currentReader.ref = enif_make_copy(descP->env, recvRef); @@ -11774,7 +11840,8 @@ ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, { if (descP->currentReaderP != NULL) { - DEMONP(env, descP, &descP->currentReader.mon); + DEMONP("recv_update_current_reader -> current reader", + env, descP, &descP->currentReader.mon); if (reader_pop(env, descP, &descP->currentReader.pid, @@ -11822,16 +11889,19 @@ void recv_error_current_reader(ErlNifEnv* env, { if (descP->currentReaderP != NULL) { ErlNifPid pid; - ErlNifMonitor mon; + // ErlNifMonitor mon; + ESockMonitor mon; ERL_NIF_TERM ref; - DEMONP(env, descP, &descP->currentReader.mon); + DEMONP("recv_error_current_reader -> current reader", + env, descP, &descP->currentReader.mon); while (reader_pop(env, descP, &pid, &mon, &ref)) { SSDBG( descP, ("SOCKET", "recv_error_current_reader -> abort %T\r\n", pid) ); send_msg_nif_abort(env, ref, reason, &pid); - DEMONP(env, descP, &mon); + DEMONP("recv_error_current_reader -> pop'ed reader", + env, descP, &mon); } } } @@ -13975,6 +14045,11 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->sock = sock; descP->event = event; + MON_INIT(&descP->currentWriter.mon); + MON_INIT(&descP->currentReader.mon); + MON_INIT(&descP->currentAcceptor.mon); + MON_INIT(&descP->ctrlMon); + MON_INIT(&descP->closerMon); } return descP; @@ -14593,7 +14668,8 @@ ERL_NIF_TERM acceptor_push(ErlNifEnv* env, reqP->pid = pid; reqP->ref = enif_make_copy(descP->env, ref); - if (MONP(env, descP, &pid, &reqP->mon) > 0) { + if (MONP("acceptor_push -> acceptor request", + env, descP, &pid, &reqP->mon) != 0) { FREE(reqP); return esock_make_error(env, atom_exmon); } @@ -14613,7 +14689,8 @@ static BOOLEAN_T acceptor_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref) { SocketRequestQueueElement* e = qpop(&descP->acceptorsQ); @@ -14644,7 +14721,8 @@ BOOLEAN_T acceptor_unqueue(ErlNifEnv* env, SocketDescriptor* descP, const ErlNifPid* pid) { - return qunqueue(env, &descP->acceptorsQ, pid); + return qunqueue(env, descP, "qunqueue -> waiting acceptor", + &descP->acceptorsQ, pid); } @@ -14679,7 +14757,8 @@ ERL_NIF_TERM writer_push(ErlNifEnv* env, reqP->pid = pid; reqP->ref = enif_make_copy(descP->env, ref); - if (MONP(env, descP, &pid, &reqP->mon) > 0) { + if (MONP("writer_push -> writer request", + env, descP, &pid, &reqP->mon) != 0) { FREE(reqP); return esock_make_error(env, atom_exmon); } @@ -14699,7 +14778,8 @@ static BOOLEAN_T writer_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref) { SocketRequestQueueElement* e = qpop(&descP->writersQ); @@ -14730,7 +14810,8 @@ BOOLEAN_T writer_unqueue(ErlNifEnv* env, SocketDescriptor* descP, const ErlNifPid* pid) { - return qunqueue(env, &descP->writersQ, pid); + return qunqueue(env, descP, "qunqueue -> waiting writer", + &descP->writersQ, pid); } @@ -14765,7 +14846,8 @@ ERL_NIF_TERM reader_push(ErlNifEnv* env, reqP->pid = pid; reqP->ref = enif_make_copy(descP->env, ref); - if (MONP(env, descP, &pid, &reqP->mon) > 0) { + if (MONP("reader_push -> reader request", + env, descP, &pid, &reqP->mon) != 0) { FREE(reqP); return esock_make_error(env, atom_exmon); } @@ -14785,7 +14867,8 @@ static BOOLEAN_T reader_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref) { SocketRequestQueueElement* e = qpop(&descP->readersQ); @@ -14816,7 +14899,8 @@ BOOLEAN_T reader_unqueue(ErlNifEnv* env, SocketDescriptor* descP, const ErlNifPid* pid) { - return qunqueue(env, &descP->readersQ, pid); + return qunqueue(env, descP, "qunqueue -> waiting reader", + &descP->readersQ, pid); } @@ -14880,6 +14964,8 @@ SocketRequestQueueElement* qpop(SocketRequestQueue* q) static BOOLEAN_T qunqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const char* slogan, SocketRequestQueue* q, const ErlNifPid* pid) { @@ -14892,6 +14978,8 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, /* We have a match */ + DEMONP(slogan, env, descP, &e->data.mon); + if (p != NULL) { /* Not the first, but could be the last */ if (q->last == e) { @@ -14965,6 +15053,97 @@ void cnt_dec(uint32_t* cnt, uint32_t dec) +/* ---------------------------------------------------------------------- + * M o n i t o r W r a p p e r F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +static +int esock_monitor(const char* slogan, + ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid, + ESockMonitor* monP) +{ + int res; + + SSDBG( descP, ("SOCKET", "[%d] %s: try monitor", descP->sock, slogan) ); + // esock_dbg_printf("MONP", "[%d] %s\r\n", descP->sock, slogan); + res = enif_monitor_process(env, descP, pid, &monP->mon); + + if (res != 0) { + SSDBG( descP, ("SOCKET", "[%d] monitor failed: %d", descP->sock, res) ); + // esock_dbg_printf("MONP", "[%d] failed: %d\r\n", descP->sock, res); + } /* else { + esock_dbg_printf("MONP", + "[%d] success: " + "%u,%u,%u,%u\r\n", + descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + } */ + + return res; +} + + +static +int esock_demonitor(const char* slogan, + ErlNifEnv* env, + SocketDescriptor* descP, + ESockMonitor* monP) +{ + int res; + + SSDBG( descP, ("SOCKET", "[%d] %s: try demonitor\r\n", descP->sock, slogan) ); + /* + esock_dbg_printf("DEMONP", "[%d] %s: %u,%u,%u,%u\r\n", + descP->sock, slogan, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + */ + + res = enif_demonitor_process(env, descP, &monP->mon); + + if (res == 0) { + esock_monitor_init(monP); + } else { + SSDBG( descP, + ("SOCKET", "[%d] demonitor failed: %d\r\n", descP->sock, res) ); + /* + esock_dbg_printf("DEMONP", "[%d] failed: %d\r\n", descP->sock, res); + */ + } + + return res; +} + + +static +void esock_monitor_init(ESockMonitor* monP) +{ + int i; + + /* + * UGLY, + * but since we don't have a ERL_NIF_MONITOR_NULL, + * this will have to do for now... + */ + for (i = 0; i < 4; i++) + monP->raw[i] = 0; + +} + +/* +static +int esock_monitor_compare(const ErlNifMonitor* mon1, + const ESockMonitor* mon2) +{ + return enif_compare_monitors(mon1, &mon2->mon); +} +*/ + + /* ---------------------------------------------------------------------- * C a l l b a c k F u n c t i o n s * ---------------------------------------------------------------------- @@ -15015,13 +15194,15 @@ static void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) { SocketDescriptor* descP = (SocketDescriptor*) obj; - int dres; - + + /* + esock_dbg_printf("STOP", "[%d] begin\r\n", descP->sock); + */ + SSDBG( descP, - ("SOCKET", "socket_stop -> entry when" - "\r\n sock: %d (%d)" - "\r\n Is Direct Call: %s" - "\r\n", descP->sock, fd, B2S(is_direct_call)) ); + ("SOCKET", "socket_stop -> entry when %s" + "\r\n sock: %d (%d)" + "\r\n", ((is_direct_call) ? "called" : "scheduled"), descP->sock, fd) ); MLOCK(descP->writeMtx); MLOCK(descP->readMtx); @@ -15033,8 +15214,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) descP->state = SOCKET_STATE_CLOSING; // Just in case...??? descP->isReadable = FALSE; descP->isWritable = FALSE; - - + /* We should check that we actually have a monitor. * This *should* be done with a "NULL" monitor value, * which there currently is none... @@ -15042,24 +15222,19 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * its no point in demonitor. Also, we not actually have * a monitor in that case... */ - SSDBG( descP, - ("SOCKET", - "socket_stop -> demonitor (maybe) controlling process (%T)\r\n", - descP->ctrlPid) ); - dres = DEMONP(env, descP, &descP->ctrlMon); - SSDBG( descP, ("SOCKET", "socket_stop -> demonitor result: %d\r\n", dres) ); - - + DEMONP("socket_stop -> ctrl", env, descP, &descP->ctrlMon); + + /* + esock_dbg_printf("STOP", "[%d] maybe handle current writer (0x%lX)\r\n", + descP->sock, descP->currentReaderP); + */ if (descP->currentWriterP != NULL) { /* We have a (current) writer and *may* therefor also have * writers waiting. */ - SSDBG( descP, - ("SOCKET", - "socket_stop -> demonitor (maybe) current writer: %T, %T\r\n", - descP->currentWriter.pid, descP->currentWriter.ref) ); - DEMONP(env, descP, &descP->currentWriter.mon); + DEMONP("socket_stop -> current writer", + env, descP, &descP->currentWriter.mon); SSDBG( descP, ("SOCKET", "socket_stop -> handle current writer\r\n") ); if (!compare_pids(env, @@ -15082,18 +15257,19 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting writer(s)\r\n") ); inform_waiting_procs(env, descP, &descP->writersQ, TRUE, atom_closed); } - + + /* + esock_dbg_printf("STOP", "[%d] maybe handle current reader (0x%lX)\r\n", + descP->sock, descP->currentReaderP); + */ if (descP->currentReaderP != NULL) { /* We have a (current) reader and *may* therefor also have * readers waiting. */ - SSDBG( descP, - ("SOCKET", - "socket_stop -> demonitor (maybe) current reader: %T, %T\r\n", - descP->currentReader.pid, descP->currentReader.ref) ); - DEMONP(env, descP, &descP->currentReader.mon); + DEMONP("socket_stop -> current reader", + env, descP, &descP->currentReader.mon); SSDBG( descP, ("SOCKET", "socket_stop -> handle current reader\r\n") ); if (!compare_pids(env, @@ -15116,17 +15292,18 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting reader(s)\r\n") ); inform_waiting_procs(env, descP, &descP->readersQ, TRUE, atom_closed); } - + + /* + esock_dbg_printf("STOP", "[%d] maybe handle current acceptor (0x%lX)\r\n", + descP->sock, descP->currentReaderP); + */ if (descP->currentAcceptorP != NULL) { /* We have a (current) acceptor and *may* therefor also have * acceptors waiting. */ - SSDBG( descP, - ("SOCKET", - "socket_stop -> demonitor (maybe) current acceptor: %T, %T\r\n", - descP->currentAcceptor.pid, descP->currentAcceptor.ref) ); - DEMONP(env, descP, &descP->currentAcceptor.mon); + DEMONP("socket_stop -> current acceptor", + env, descP, &descP->currentAcceptor.mon); SSDBG( descP, ("SOCKET", "socket_stop -> handle current acceptor\r\n") ); if (!compare_pids(env, @@ -15178,7 +15355,8 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) send_msg(env, MKT2(env, atom_close, descP->closeRef), &descP->closerPid); - DEMONP(env, descP, &descP->closerMon); + DEMONP("socket_stop -> closer", + env, descP, &descP->closerMon); } else { @@ -15194,6 +15372,10 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) if (!is_direct_call) { + if (descP->closeLocal) { + DEMONP("socket_stop -> closer", + env, descP, &descP->closerMon); + } descP->sock = INVALID_SOCKET; descP->event = INVALID_EVENT; descP->state = SOCKET_STATE_CLOSED; @@ -15206,6 +15388,10 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) MUNLOCK(descP->readMtx); MUNLOCK(descP->writeMtx); + /* + esock_dbg_printf("STOP", "[%d] end\r\n", descP->sock); + */ + SSDBG( descP, ("SOCKET", "socket_stop -> done (%d, %d)\r\n", descP->sock, fd) ); @@ -15245,7 +15431,8 @@ void inform_waiting_procs(ErlNifEnv* env, currentP->data.ref, reason, ¤tP->data.pid)) ); - DEMONP(env, descP, ¤tP->data.mon); + DEMONP("inform_waiting_procs -> current 'request'", + env, descP, ¤tP->data.mon); nextP = currentP->nextP; if (free) FREE(currentP); currentP = nextP; @@ -15270,16 +15457,43 @@ void socket_down(ErlNifEnv* env, const ErlNifMonitor* mon) { SocketDescriptor* descP = (SocketDescriptor*) obj; + ESockMonitor* monP = (ESockMonitor*) mon; SSDBG( descP, ("SOCKET", "socket_down -> entry with" - "\r\n sock: %d" - "\r\n pid: %T" - "\r\n Closed: %s" - "\r\n Closing: %s" + "\r\n sock: %d" + "\r\n pid: %T" + "\r\n Close: %s (%s)" "\r\n", descP->sock, *pid, - B2S(IS_CLOSED(descP)), B2S(IS_CLOSING(descP))) ); + B2S(IS_CLOSED(descP)), + B2S(IS_CLOSING(descP))) ); + + /* + esock_dbg_printf("DOWN", + "[%d] begin %u,%u,%u,%d\r\n", + descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + */ + /* + if (MON_COMP(mon, &descP->ctrlMon) == 0) { + SSDBG( descP, ("SOCKET", "socket_down -> controlling process mon\r\n") ); + } else if (MON_COMP(mon, &descP->closerMon) == 0) { + SSDBG( descP, ("SOCKET", "socket_down -> closer mon\r\n") ); + } else if ((descP->currentWriterP != NULL) && + (MON_COMP(mon, &descP->currentWriter.mon) == 0)) { + SSDBG( descP, ("SOCKET", "socket_down -> current writer mon\r\n") ); + } else if ((descP->currentReaderP != NULL) && + (MON_COMP(mon, &descP->currentReader.mon) == 0)) { + SSDBG( descP, ("SOCKET", "socket_down -> current reader mon\r\n") ); + } else if ((descP->currentAcceptorP != NULL) && + (MON_COMP(mon, &descP->currentAcceptor.mon) == 0)) { + SSDBG( descP, ("SOCKET", "socket_down -> current acceptor mon\r\n") ); + } else { + SSDBG( descP, ("SOCKET", "socket_down -> OTHER mon\r\n") ); + } + */ if (compare_pids(env, &descP->ctrlPid, pid)) { int selectRes; @@ -15288,14 +15502,23 @@ void socket_down(ErlNifEnv* env, * we leave it to the stop callback function. */ - SSDBG( descP, ("SOCKET", "socket_down -> controlling process exit\r\n") ); + SSDBG( descP, + ("SOCKET", "socket_down -> controlling process exit\r\n") ); descP->state = SOCKET_STATE_CLOSING; descP->closeLocal = TRUE; descP->closerPid = *pid; - descP->closerMon = *mon; + descP->closerMon = (ESockMonitor) *mon; descP->closeRef = MKREF(env); // Do we really need this in this case? + /* + esock_dbg_printf("DOWN", + "[%d] select stop %u,%u,%u,%d\r\n", + descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + */ + selectRes = enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), descP, NULL, descP->closeRef); @@ -15374,7 +15597,10 @@ void socket_down(ErlNifEnv* env, "\r\n Select Res: %d" "\r\n Controlling Process: %T" "\r\n Descriptor: %d" - "\r\n", selectRes, pid, descP->sock); + "\r\n Monitor: %u.%u.%u.%u" + "\r\n", selectRes, pid, descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); } @@ -15400,6 +15626,14 @@ void socket_down(ErlNifEnv* env, MUNLOCK(descP->readMtx); } + + /* + esock_dbg_printf("DOWN", + "[%d] end %u,%u,%u,%d\r\n", + descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + */ SSDBG( descP, ("SOCKET", "socket_down -> done\r\n") ); -- cgit v1.2.3 From 598ecec8b59509f223807d36e9dd1244d7d80fa2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 18 Oct 2018 16:25:47 +0200 Subject: [socket-nif|test] Some minor test case re-grouping --- lib/kernel/test/socket_SUITE.erl | 267 +++++++++++++++++++-------------------- 1 file changed, 128 insertions(+), 139 deletions(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 8ab39f6ffe..4ee2eec39a 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -71,18 +71,16 @@ sc_cpe_socket_cleanup_tcp6/1, sc_cpe_socket_cleanup_udp4/1, sc_cpe_socket_cleanup_udp6/1, - sc_recv_response_local_close_tcp4/1, - sc_recv_response_local_close_tcp6/1, - sc_recv_response_remote_close_tcp4/1, - sc_recv_response_remote_close_tcp6/1, - sc_recvmsg_response_local_close_tcp4/1, - sc_recvmsg_response_local_close_tcp6/1, - sc_recvmsg_response_remote_close_tcp4/1, - sc_recvmsg_response_remote_close_tcp6/1, - sc_acceptor_response_local_close_tcp4/1, - sc_acceptor_response_local_close_tcp6/1, - sc_acceptor_response_remote_close_tcp4/1, - sc_acceptor_response_remote_close_tcp6/1 + sc_lc_recv_response_tcp4/1, + sc_lc_recv_response_tcp6/1, + sc_lc_recvmsg_response_tcp4/1, + sc_lc_recvmsg_response_tcp6/1, + sc_lc_acceptor_response_tcp4/1, + sc_lc_acceptor_response_tcp6/1, + sc_rc_recv_response_tcp4/1, + sc_rc_recv_response_tcp6/1, + sc_rc_recvmsg_response_tcp4/1, + sc_rc_recvmsg_response_tcp6/1 %% Tickets ]). @@ -131,7 +129,9 @@ groups() -> {api_options, [], api_options_cases()}, {api_op_with_timeout, [], api_op_with_timeout_cases()}, {socket_closure, [], socket_closure_cases()}, - {sc_ctrl_proc_exit, [], sc_cp_exit_cases()} + {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, + {sc_local_close, [], sc_lc_cases()}, + {sc_remote_close, [], sc_rc_cases()} %% {tickets, [], ticket_cases()} ]. @@ -187,21 +187,8 @@ api_op_with_timeout_cases() -> socket_closure_cases() -> [ {group, sc_ctrl_proc_exit}, - - sc_recv_response_local_close_tcp4, - sc_recv_response_local_close_tcp6, - sc_recv_response_remote_close_tcp4, - sc_recv_response_remote_close_tcp6, - - sc_recvmsg_response_local_close_tcp4, - sc_recvmsg_response_local_close_tcp6, - sc_recvmsg_response_remote_close_tcp4, - sc_recvmsg_response_remote_close_tcp6, - - sc_acceptor_response_local_close_tcp4, - sc_acceptor_response_local_close_tcp6, - sc_acceptor_response_remote_close_tcp4, - sc_acceptor_response_remote_close_tcp6 + {group, sc_local_close}, + {group, sc_remote_close} ]. %% These cases are all about socket cleanup after the controlling process @@ -214,6 +201,30 @@ sc_cp_exit_cases() -> sc_cpe_socket_cleanup_udp6 ]. +%% These cases tests what happens when the socket is closed locally. +sc_lc_cases() -> + [ + sc_lc_recv_response_tcp4, + sc_lc_recv_response_tcp6, + + sc_lc_recvmsg_response_tcp4, + sc_lc_recvmsg_response_tcp6, + + sc_lc_acceptor_response_tcp4, + sc_lc_acceptor_response_tcp6 + ]. + +%% These cases tests what happens when the socket is closed remotely. +sc_rc_cases() -> + [ + sc_rc_recv_response_tcp4, + sc_rc_recv_response_tcp6, + + sc_rc_recvmsg_response_tcp4, + sc_rc_recvmsg_response_tcp6 + ]. + + %% ticket_cases() -> %% []. @@ -2332,6 +2343,7 @@ api_to_receive_tcp(InitState) -> cmd => fun(#{lsock := LSock} = State) -> case socket:accept(LSock) of {ok, Sock} -> + %% ok = socket:setopt(Sock, otp, debug, true), {ok, State#{sock => Sock}}; {error, _} = ERROR -> ERROR @@ -2375,16 +2387,44 @@ api_to_receive_tcp(InitState) -> {ok, maps:remove(tester, State)} end end}, + %% #{desc => "sleep some (before traffic close)", + %% cmd => fun(_) -> + %% ?SLEEP(1000), + %% ok + %% end}, + %% #{desc => "monitored-by", + %% cmd => fun(_) -> + %% {_, Mons} = process_info(self(), monitored_by), + %% ei("Monitored By: ~p", [Mons]), + %% ok + %% end}, #{desc => "close (traffic) socket", cmd => fun(#{sock := Sock} = State) -> + %% ok = socket:setopt(Sock, otp, debug, true), sock_close(Sock), {ok, maps:remove(sock, State)} end}, + %% #{desc => "monitored-by", + %% cmd => fun(_) -> + %% {_, Mons} = process_info(self(), monitored_by), + %% ei("Monitored By: ~p", [Mons]), + %% ok + %% end}, + %% #{desc => "sleep some (before listen close)", + %% cmd => fun(_) -> + %% ?SLEEP(1000), + %% ok + %% end}, #{desc => "close (listen) socket", cmd => fun(#{lsock := LSock} = State) -> sock_close(LSock), {ok, maps:remove(lsock, State)} end}, + %% #{desc => "sleep some (after listen close)", + %% cmd => fun(_) -> + %% ?SLEEP(1000), + %% ok + %% end}, %% *** We are done *** #{desc => "finish", @@ -2938,10 +2978,10 @@ sc_cpe_socket_cleanup(InitState) -> {ok, maps:remove(tester, State)} end end}, - #{desc => "enable (otp) debug", - cmd => fun(#{sock := Sock} = _State) -> - ok = socket:setopt(Sock, otp, debug, true) - end}, + %% #{desc => "enable (otp) debug", + %% cmd => fun(#{sock := Sock} = _State) -> + %% ok = socket:setopt(Sock, otp, debug, true) + %% end}, %% *** We are done *** #{desc => "finish", @@ -3041,12 +3081,12 @@ sc_cpe_socket_cleanup(InitState) -> %% %% -sc_recv_response_local_close_tcp4(suite) -> +sc_lc_recv_response_tcp4(suite) -> []; -sc_recv_response_local_close_tcp4(doc) -> +sc_lc_recv_response_tcp4(doc) -> []; -sc_recv_response_local_close_tcp4(_Config) when is_list(_Config) -> - tc_try(sc_recv_response_local_close_tcp4, +sc_lc_recv_response_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_lc_recv_response_tcp4, fun() -> %% not_yet_implemented(), Recv = fun(Sock) -> socket:recv(Sock) end, @@ -3054,7 +3094,7 @@ sc_recv_response_local_close_tcp4(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = sc_receive_response_local_close_tcp(InitState) + ok = sc_lc_receive_response_tcp(InitState) end). @@ -3063,12 +3103,12 @@ sc_recv_response_local_close_tcp4(_Config) when is_list(_Config) -> %% locally closed while the process is calling the recv function. %% Socket is IPv6. -sc_recv_response_local_close_tcp6(suite) -> +sc_lc_recv_response_tcp6(suite) -> []; -sc_recv_response_local_close_tcp6(doc) -> +sc_lc_recv_response_tcp6(doc) -> []; -sc_recv_response_local_close_tcp6(_Config) when is_list(_Config) -> - tc_try(sc_recv_response_local_close_tcp6, +sc_lc_recv_response_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_lc_recv_response_tcp6, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recv(Sock) end, @@ -3076,13 +3116,13 @@ sc_recv_response_local_close_tcp6(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = sc_receive_response_local_close_tcp(InitState) + ok = sc_lc_receive_response_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sc_receive_response_local_close_tcp(InitState) -> +sc_lc_receive_response_tcp(InitState) -> %% This is the server that accepts connections. %% But it is also suppose to close the connection socket, %% and trigger the read failure for the handler process. @@ -3271,8 +3311,8 @@ sc_receive_response_local_close_tcp(InitState) -> %% ok %% end}, #{desc => "attempt recv", - cmd => fun(#{sock := Sock} = State) -> - case socket:recv(Sock) of + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of {ok, _Data} -> ee("Unexpected data received"), {error, unexpected_data}; @@ -3609,7 +3649,7 @@ sc_receive_response_local_close_tcp(InitState) -> Acceptor = evaluator_start("acceptor", AcceptorSeq, AccInitState), p("start handler evaluator"), - HandlerInitState = #{}, + HandlerInitState = #{recv => maps:get(recv, InitState)}, Handler = evaluator_start("handler", HandlerSeq, HandlerInitState), p("start client evaluator"), @@ -3632,12 +3672,12 @@ sc_receive_response_local_close_tcp(InitState) -> %% remotely closed while the process is calling the recv function. %% Socket is IPv4. -sc_recv_response_remote_close_tcp4(suite) -> +sc_rc_recv_response_tcp4(suite) -> []; -sc_recv_response_remote_close_tcp4(doc) -> +sc_rc_recv_response_tcp4(doc) -> []; -sc_recv_response_remote_close_tcp4(_Config) when is_list(_Config) -> - tc_try(sc_recv_response_remote_close_tcp4, +sc_rc_recv_response_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_rc_recv_response_tcp4, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recv(Sock) end, @@ -3645,7 +3685,7 @@ sc_recv_response_remote_close_tcp4(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = sc_receive_response_remote_close_tcp(InitState) + ok = sc_rc_receive_response_tcp(InitState) end). @@ -3654,12 +3694,12 @@ sc_recv_response_remote_close_tcp4(_Config) when is_list(_Config) -> %% remotely closed while the process is calling the recv function. %% Socket is IPv6. -sc_recv_response_remote_close_tcp6(suite) -> +sc_rc_recv_response_tcp6(suite) -> []; -sc_recv_response_remote_close_tcp6(doc) -> +sc_rc_recv_response_tcp6(doc) -> []; -sc_recv_response_remote_close_tcp6(_Config) when is_list(_Config) -> - tc_try(sc_recv_response_remote_close_tcp6, +sc_rc_recv_response_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_rc_recv_response_tcp6, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recv(Sock) end, @@ -3667,13 +3707,13 @@ sc_recv_response_remote_close_tcp6(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = sc_receive_response_remote_close_tcp(InitState) + ok = sc_rc_receive_response_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sc_receive_response_remote_close_tcp(_InitState) -> +sc_rc_receive_response_tcp(_InitState) -> ok. @@ -3683,12 +3723,12 @@ sc_receive_response_remote_close_tcp(_InitState) -> %% locally closed while the process is calling the recvmsg function. %% Socket is IPv4. -sc_recvmsg_response_local_close_tcp4(suite) -> +sc_lc_recvmsg_response_tcp4(suite) -> []; -sc_recvmsg_response_local_close_tcp4(doc) -> +sc_lc_recvmsg_response_tcp4(doc) -> []; -sc_recvmsg_response_local_close_tcp4(_Config) when is_list(_Config) -> - tc_try(sc_recvmsg_response_local_close_tcp4, +sc_lc_recvmsg_response_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_lc_recvmsg_response_tcp4, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recvmsg(Sock) end, @@ -3696,7 +3736,7 @@ sc_recvmsg_response_local_close_tcp4(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = sc_receive_response_local_close_tcp(InitState) + ok = sc_lc_receive_response_tcp(InitState) end). @@ -3705,12 +3745,12 @@ sc_recvmsg_response_local_close_tcp4(_Config) when is_list(_Config) -> %% locally closed while the process is calling the recvmsg function. %% Socket is IPv6. -sc_recvmsg_response_local_close_tcp6(suite) -> +sc_lc_recvmsg_response_tcp6(suite) -> []; -sc_recvmsg_response_local_close_tcp6(doc) -> +sc_lc_recvmsg_response_tcp6(doc) -> []; -sc_recvmsg_response_local_close_tcp6(_Config) when is_list(_Config) -> - tc_try(sc_recvmsg_response_local_close_tcp6, +sc_lc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_recvmsg_response_tcp6, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recvmsg(Sock) end, @@ -3718,7 +3758,7 @@ sc_recvmsg_response_local_close_tcp6(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = sc_receive_response_local_close_tcp(InitState) + ok = sc_lc_receive_response_tcp(InitState) end). @@ -3727,12 +3767,12 @@ sc_recvmsg_response_local_close_tcp6(_Config) when is_list(_Config) -> %% remotely closed while the process is calling the recvmsg function. %% Socket is IPv4. -sc_recvmsg_response_remote_close_tcp4(suite) -> +sc_rc_recvmsg_response_tcp4(suite) -> []; -sc_recvmsg_response_remote_close_tcp4(doc) -> +sc_rc_recvmsg_response_tcp4(doc) -> []; -sc_recvmsg_response_remote_close_tcp4(_Config) when is_list(_Config) -> - tc_try(sc_recvmsg_response_remote_close_tcp4, +sc_rc_recvmsg_response_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_rc_recvmsg_response_tcp4, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recvmsg(Sock) end, @@ -3740,7 +3780,7 @@ sc_recvmsg_response_remote_close_tcp4(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = sc_receive_response_remote_close_tcp(InitState) + ok = sc_rc_receive_response_tcp(InitState) end). @@ -3749,12 +3789,12 @@ sc_recvmsg_response_remote_close_tcp4(_Config) when is_list(_Config) -> %% remotely closed while the process is calling the recvmsg function. %% Socket is IPv6. -sc_recvmsg_response_remote_close_tcp6(suite) -> +sc_rc_recvmsg_response_tcp6(suite) -> []; -sc_recvmsg_response_remote_close_tcp6(doc) -> +sc_rc_recvmsg_response_tcp6(doc) -> []; -sc_recvmsg_response_remote_close_tcp6(_Config) when is_list(_Config) -> - tc_try(sc_recvmsg_response_remote_close_tcp6, +sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_rc_recvmsg_response_tcp6, fun() -> not_yet_implemented(), Recv = fun(Sock) -> socket:recvmsg(Sock) end, @@ -3762,7 +3802,7 @@ sc_recvmsg_response_remote_close_tcp6(_Config) when is_list(_Config) -> type => stream, protocol => tcp, recv => Recv}, - ok = sc_receive_response_remote_close_tcp(InitState) + ok = sc_rc_receive_response_tcp(InitState) end). @@ -3773,18 +3813,18 @@ sc_recvmsg_response_remote_close_tcp6(_Config) when is_list(_Config) -> %% git the setup anyway. %% Socket is IPv4. -sc_acceptor_response_local_close_tcp4(suite) -> +sc_lc_acceptor_response_tcp4(suite) -> []; -sc_acceptor_response_local_close_tcp4(doc) -> +sc_lc_acceptor_response_tcp4(doc) -> []; -sc_acceptor_response_local_close_tcp4(_Config) when is_list(_Config) -> - tc_try(sc_acceptor_response_local_close_tcp4, +sc_lc_acceptor_response_tcp4(_Config) when is_list(_Config) -> + tc_try(sc_lc_acceptor_response_tcp4, fun() -> not_yet_implemented(), InitState = #{domain => inet, type => stream, protocol => tcp}, - ok = sc_acceptor_response_local_close_tcp(InitState) + ok = sc_lc_acceptor_response_tcp(InitState) end). @@ -3795,75 +3835,24 @@ sc_acceptor_response_local_close_tcp4(_Config) when is_list(_Config) -> %% git the setup anyway. %% Socket is IPv6. -sc_acceptor_response_local_close_tcp6(suite) -> - []; -sc_acceptor_response_local_close_tcp6(doc) -> - []; -sc_acceptor_response_local_close_tcp6(_Config) when is_list(_Config) -> - tc_try(sc_acceptor_response_local_close_tcp6, - fun() -> - not_yet_implemented(), - InitState = #{domain => inet, - type => stream, - protocol => tcp}, - ok = sc_acceptor_response_local_close_tcp(InitState) - end). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -sc_acceptor_response_local_close_tcp(_InitState) -> - ok. - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to test what happens when a socket is -%% remotely closed while the process is calling the accept function. -%% We test what happens with a non-controlling_process also, since we -%% git the setup anyway. -%% Socket is IPv4. - -sc_acceptor_response_remote_close_tcp4(suite) -> - []; -sc_acceptor_response_remote_close_tcp4(doc) -> - []; -sc_acceptor_response_remote_close_tcp4(_Config) when is_list(_Config) -> - tc_try(sc_acceptor_response_remote_close_tcp4, - fun() -> - not_yet_implemented(), - InitState = #{domain => inet, - type => stream, - protocol => tcp}, - ok = sc_acceptor_response_remote_close_tcp(InitState) - end). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to test what happens when a socket is -%% remotely closed while the process is calling the accept function. -%% We test what happens with a non-controlling_process also, since we -%% git the setup anyway. -%% Socket is IPv6. - -sc_acceptor_response_remote_close_tcp6(suite) -> +sc_lc_acceptor_response_tcp6(suite) -> []; -sc_acceptor_response_remote_close_tcp6(doc) -> +sc_lc_acceptor_response_tcp6(doc) -> []; -sc_acceptor_response_remote_close_tcp6(_Config) when is_list(_Config) -> - tc_try(acceptor_response_remote_close_tcp6, +sc_lc_acceptor_response_tcp6(_Config) when is_list(_Config) -> + tc_try(sc_lc_acceptor_response_tcp6, fun() -> not_yet_implemented(), InitState = #{domain => inet, type => stream, protocol => tcp}, - ok = sc_acceptor_response_remote_close_tcp(InitState) + ok = sc_lc_acceptor_response_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sc_acceptor_response_remote_close_tcp(_InitState) -> +sc_lc_acceptor_response_tcp(_InitState) -> ok. -- cgit v1.2.3 From 3eb4d5160f6a9f7cca799c97daf33b1a8b154f5d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 18 Oct 2018 17:18:10 +0200 Subject: [socket-nif] Initiation of "current reader" missing for recvmsg When calling the recvmsg function when there is no data, we should be wait (for the specified amount of time). But this not not wotk because the "current reader" structure was not initiated. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 5d2cfbc10b..2eddcb5658 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -12334,9 +12334,13 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, } else if ((saveErrno == ERRNO_BLOCK) || (saveErrno == EAGAIN)) { + char* xres; SSDBG( descP, ("SOCKET", "recvmsg_check_result -> eagain\r\n") ); + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) + return esock_make_error_str(env, xres); + SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); -- cgit v1.2.3 From 3c4c36587df5a847dd6f03011a4ecb76d0b70b40 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 18 Oct 2018 17:21:11 +0200 Subject: [socket-nif|test] Enables test case sc_lc_recvmsg_response_tcp4 --- lib/kernel/test/socket_SUITE.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 4ee2eec39a..022e83a944 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -3730,7 +3730,6 @@ sc_lc_recvmsg_response_tcp4(doc) -> sc_lc_recvmsg_response_tcp4(_Config) when is_list(_Config) -> tc_try(sc_lc_recvmsg_response_tcp4, fun() -> - not_yet_implemented(), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet, type => stream, -- cgit v1.2.3