diff options
author | Micael Karlberg <bmk@erlang.org> | 2021-04-30 15:47:35 +0200 |
---|---|---|
committer | Micael Karlberg <bmk@erlang.org> | 2021-04-30 15:47:35 +0200 |
commit | b0acad58bd52685009439cc563d1e5548b0fbb21 (patch) | |
tree | 38e8354eb03443304fc37a05e322c152bb2ed6b1 /lib/kernel | |
parent | 549d0c2f2092ce2b47a35c3aba8a96ca2770e8b5 (diff) | |
parent | fa5d955e4e818a16a3bffdf061a50a3d72ffa033 (diff) | |
download | erlang-b0acad58bd52685009439cc563d1e5548b0fbb21.tar.gz |
Merge branch 'bmk/kernel/20210426/socket_monitor/OTP-17155'
OTP-17155
Diffstat (limited to 'lib/kernel')
-rw-r--r-- | lib/kernel/doc/src/inet.xml | 65 | ||||
-rw-r--r-- | lib/kernel/doc/src/socket.xml | 63 | ||||
-rw-r--r-- | lib/kernel/src/gen_tcp_socket.erl | 47 | ||||
-rw-r--r-- | lib/kernel/src/inet.erl | 63 | ||||
-rw-r--r-- | lib/kernel/src/inet_db.erl | 69 | ||||
-rw-r--r-- | lib/kernel/src/socket.erl | 161 | ||||
-rw-r--r-- | lib/kernel/test/gen_tcp_misc_SUITE.erl | 482 | ||||
-rw-r--r-- | lib/kernel/test/kernel_test_lib.erl | 11 | ||||
-rw-r--r-- | lib/kernel/test/kernel_test_lib.hrl | 3 | ||||
-rw-r--r-- | lib/kernel/test/socket_SUITE.erl | 4688 |
10 files changed, 5620 insertions, 32 deletions
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 5a0529eb00..437381d3a9 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2020</year> + <year>1997</year><year>2021</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -351,6 +351,36 @@ fe80::204:acff:fe17:bf38 </func> <func> + <name name="cancel_monitor" arity="1" since="OTP @OTP-17155@"/> + <fsummary>Stop socket monitoring</fsummary> + <desc> + <p>If <c><anno>MRef</anno></c> is a reference that the + calling process obtained by calling + <seemfa marker="#monitor/1"><c>monitor/1</c></seemfa>, + this monitor is turned off. + If the monitoring is already turned off, nothing happens.</p> + <p>The returned value is one of the following:</p> + <taglist> + <tag><c>true</c></tag> + <item> + <p>The monitor was found and removed. In this case, + no <c>'DOWN'</c> message corresponding to this + monitor has been delivered and will not be delivered. </p> + </item> + <tag><c>false</c></tag> + <item> + <p>The monitor was not found and could not be removed. + This probably because a <c>'DOWN'</c> message corresponding + to this monitor has already been placed in the caller + message queue.</p> + </item> + </taglist> + <p>Failure: It is an error if <c><anno>MRef</anno></c> refers to a + monitor started by another process. </p> + </desc> + </func> + + <func> <name name="format_error" arity="1" since=""/> <fsummary>Return a descriptive string for an error reason.</fsummary> <desc> @@ -696,6 +726,39 @@ get_tcpi_sacked(Sock) -> </func> <func> + <name name="monitor" arity="1" since="OTP @OTP-17155@"/> + <fsummary>Start socket monitoring</fsummary> + <desc> + <p>Start monitor the socket <c><anno>Socket</anno></c>. </p> + <p>If the monitored socket does not exist or when the + monitor is triggered, a <c>'DOWN'</c> message is sent + that has the following pattern:</p> + <code type="none"> + {'DOWN', MonitorRef, socket, Object, Info} + </code> + <p>In the monitor message <c>MonitorRef</c> and <c>Type</c> are the + same as described earlier, and:</p> + <taglist> + <tag><c>Object</c></tag> + <item> + <p>The monitored entity, socket, which triggered the event. </p> + </item> + <tag><c>Info</c></tag> + <item> + <p>Either the termination reason of the socket or <c>nosock</c> + (socket <c><anno>Socket</anno></c> did not exist at the time + of monitor creation). </p> + </item> + </taglist> + + <p>Making several calls to <c>inet:monitor/1</c> for the same + <c><anno>Socket</anno></c> is not an error; it results in + as many independent monitoring instances. </p> + + </desc> + </func> + + <func> <name name="ntoa" arity="1" since="OTP R16B02"/> <fsummary>Convert IPv6/IPV4 address to ASCII.</fsummary> <desc> diff --git a/lib/kernel/doc/src/socket.xml b/lib/kernel/doc/src/socket.xml index 972cb7a15c..0707e5f3c0 100644 --- a/lib/kernel/doc/src/socket.xml +++ b/lib/kernel/doc/src/socket.xml @@ -1597,6 +1597,36 @@ </func> <func> + <name name="cancel_monitor" arity="1" since="OTP @OTP-17155@"/> + <fsummary>Stop socket monitoring</fsummary> + <desc> + <p>If <c><anno>MRef</anno></c> is a reference that the + calling process obtained by calling + <seemfa marker="#monitor/1"><c>monitor/1</c></seemfa>, + this monitor is turned off. + If the monitoring is already turned off, nothing happens.</p> + <p>The returned value is one of the following:</p> + <taglist> + <tag><c>true</c></tag> + <item> + <p>The monitor was found and removed. In this case, + no <c>'DOWN'</c> message corresponding to this + monitor has been delivered and will not be delivered. </p> + </item> + <tag><c>false</c></tag> + <item> + <p>The monitor was not found and could not be removed. + This probably because a <c>'DOWN'</c> message corresponding + to this monitor has already been placed in the caller + message queue.</p> + </item> + </taglist> + <p>Failure: It is an error if <c><anno>MRef</anno></c> refers to a + monitor started by another process. </p> + </desc> + </func> + + <func> <name name="getopt" arity="2" clause_i="1" since="OTP @OTP-17154@"/> <fsummary> Get a socket option from the protocol level <c>otp</c>. @@ -1817,6 +1847,39 @@ </func> <func> + <name name="monitor" arity="1" since="OTP @OTP-17155@"/> + <fsummary>Start socket monitoring</fsummary> + <desc> + <p>Start monitor the socket <c><anno>Socket</anno></c>. </p> + <p>If the monitored socket does not exist or when the + monitor is triggered, a <c>'DOWN'</c> message is sent + that has the following pattern:</p> + <code type="none"> + {'DOWN', MonitorRef, socket, Object, Info} + </code> + <p>In the monitor message <c>MonitorRef</c> and <c>Type</c> are the + same as described earlier, and:</p> + <taglist> + <tag><c>Object</c></tag> + <item> + <p>The monitored entity, socket, which triggered the event. </p> + </item> + <tag><c>Info</c></tag> + <item> + <p>Either the termination reason of the socket or <c>nosock</c> + (socket <c><anno>Socket</anno></c> did not exist at the time + of monitor creation). </p> + </item> + </taglist> + + <p>Making several calls to <c>socket:monitor/1</c> for the same + <c><anno>Socket</anno></c> is not an error; it results in + as many independent monitoring instances. </p> + + </desc> + </func> + + <func> <name name="number_of" arity="0" since="OTP 22.3"/> <fsummary>Get the number of active sockets.</fsummary> <desc> diff --git a/lib/kernel/src/gen_tcp_socket.erl b/lib/kernel/src/gen_tcp_socket.erl index ed6451a328..d395987f2c 100644 --- a/lib/kernel/src/gen_tcp_socket.erl +++ b/lib/kernel/src/gen_tcp_socket.erl @@ -21,18 +21,23 @@ -module(gen_tcp_socket). -behaviour(gen_statem). +-compile({no_auto_import, [monitor/1]}). + %% gen_tcp -export([connect/4, listen/2, accept/2, send/2, recv/3, sendfile/4, shutdown/2, close/1, controlling_process/2]). %% inet --export([setopts/2, getopts/2, +-export([ + monitor/1, cancel_monitor/1, + setopts/2, getopts/2, sockname/1, peername/1, - getstat/2]). + getstat/2 + ]). %% Utility --export([info/1]). +-export([info/1, socket_to_list/1]). -ifdef(undefined). -export([unrecv/2]). @@ -490,13 +495,34 @@ controlling_process(S, NewOwner, Server, Msg) -> NewOwner ! Msg, controlling_process(S, NewOwner, Server). + %% ------------------------------------------------------------------------- %% Module inet backends %% ------------------------------------------------------------------------- +monitor(?MODULE_socket(_Server, ESock) = Socket) -> + %% The socket that is part of the down message: + case socket_registry:monitor(ESock, #{msocket => Socket}) of + {error, Reason} -> + erlang:error({invalid, Reason}); + MRef when is_reference(MRef) -> + MRef + end; +monitor(Socket) -> + erlang:error(badarg, [Socket]). + +cancel_monitor(MRef) when is_reference(MRef) -> + socket:cancel_monitor(MRef); +cancel_monitor(MRef) -> + erlang:error(badarg, [MRef]). + + +%% ------------------------------------------------------------------------- + setopts(?MODULE_socket(Server, _Socket), Opts) when is_list(Opts) -> call(Server, {setopts, Opts}). + %% ------------------------------------------------------------------------- getopts(?MODULE_socket(Server, _Socket), Opts) when is_list(Opts) -> @@ -530,6 +556,15 @@ info(?MODULE_socket(Server, _Socket)) -> call(Server, info). +%% ------------------------------------------------------------------------- + +socket_to_list(?MODULE_socket(_Server, Socket)) -> + "#Socket" ++ Id = socket:to_list(Socket), + "#InetSocket" ++ Id; +socket_to_list(Socket) -> + erlang:error(badarg, [Socket]). + + %%% ======================================================================== %%% Socket glue code %%% @@ -1250,8 +1285,8 @@ handle_event( #controlling_process{owner = NewOwner, state = State}, {#params{owner = Owner, owner_mon = OwnerMon} = P, D}) -> %% - NewOwnerMon = monitor(process, NewOwner), - true = demonitor(OwnerMon, [flush]), + NewOwnerMon = erlang:monitor(process, NewOwner), + true = erlang:demonitor(OwnerMon, [flush]), {next_state, State, {P#params{owner = NewOwner, owner_mon = NewOwnerMon}, D}, [{reply, From, ok}]}; @@ -1296,7 +1331,7 @@ handle_event({call, From}, {getopts, Opts}, State, {P, D}) -> %% Call: setopts/1 handle_event({call, From}, {setopts, Opts}, State, {P, D}) -> - %% ?DBG([{opts, Opts}, {state, State}, {d, D}]), + %% ?DBG([{setopts, Opts}, {state, State}, {d, D}]), {Result, D_1} = state_setopts(P, D, State, Opts), %% ?DBG([{result, Result}, {d1, D_1}]), case Result of diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index c41e57345f..17c278923f 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -31,7 +31,7 @@ getif/1, getif/0, getiflist/0, getiflist/1, ifget/3, ifget/2, ifset/3, ifset/2, getstat/1, getstat/2, - info/1, + info/1, socket_to_list/1, ip/1, stats/0, options/0, pushf/3, popf/1, close/1, gethostname/0, gethostname/1, parse_ipv4_address/1, parse_ipv6_address/1, parse_ipv4strict_address/1, @@ -75,6 +75,9 @@ %% timer interface -export([start_timer/1, timeout/1, timeout/2, stop_timer/1]). +%% Socket monitoring +-export([monitor/1, cancel_monitor/1]). + -export_type([address_family/0, socket_protocol/0, hostent/0, hostname/0, ip4_address/0, ip6_address/0, ip_address/0, port_number/0, family_address/0, local_address/0, @@ -208,6 +211,53 @@ close(Socket) -> end. +%% -- Socket monitor + +-spec monitor(Socket) -> reference() when + Socket :: socket(). + +monitor({'$inet', GenSocketMod, _} = Socket) when is_atom(GenSocketMod) -> + MRef = GenSocketMod:?FUNCTION_NAME(Socket), + case inet_db:put_socket_type(MRef, {socket, GenSocketMod}) of + ok -> + MRef; + error -> + GenSocketMod:cancel_monitor(MRef), + erlang:error({invalid, Socket}) + end; +monitor(Socket) when is_port(Socket) -> + MRef = erlang:monitor(port, Socket), + case inet_db:put_socket_type(MRef, port) of + ok -> + MRef; + error -> + erlang:demonitor(MRef, [flush]), + erlang:error({invalid, Socket}) + end; +monitor(Socket) -> + erlang:error(badarg, [Socket]). + + +%% -- Cancel socket monitor + +-spec cancel_monitor(MRef) -> boolean() when + MRef :: reference(). + +cancel_monitor(MRef) when is_reference(MRef) -> + case inet_db:take_socket_type(MRef) of + {ok, port} -> + erlang:demonitor(MRef, [info]); + {ok, {socket, GenSocketMod}} -> + GenSocketMod:?FUNCTION_NAME(MRef); + error -> % Assume it has the monitor has already been cancel'ed + false + end; +cancel_monitor(MRef) -> + erlang:error(badarg, [MRef]). + + +%% -- Socket peername + -spec peername(Socket :: socket()) -> {ok, {ip_address(), port_number()} | @@ -605,6 +655,17 @@ gethostbyaddr_tm(Address,Timer) -> gethostbyaddr_tm(Address, Timer, inet_db:res_option(lookup)). +-spec socket_to_list(Socket) -> list() when + Socket :: socket(). + +socket_to_list({'$inet', GenSocketMod, _} = Socket) + when is_atom(GenSocketMod) -> + GenSocketMod:to_list(Socket); +socket_to_list(Socket) when is_port(Socket) -> + erlang:port_to_list(Socket). + + + -spec info(Socket) -> Info when Socket :: socket(), Info :: term(). diff --git a/lib/kernel/src/inet_db.erl b/lib/kernel/src/inet_db.erl index 17f31ed92e..939cbf6b17 100644 --- a/lib/kernel/src/inet_db.erl +++ b/lib/kernel/src/inet_db.erl @@ -21,7 +21,8 @@ -module(inet_db). %% Store info about ip addresses, names, aliases host files resolver -%% options +%% options. +%% Also miscellaneous "stuff" related to sockets. %% If the macro DEBUG is defined during compilation, %% debug printouts are done through erlang:display/1. @@ -51,7 +52,8 @@ -export([tcp_module/0, set_tcp_module/1]). -export([udp_module/0, set_udp_module/1]). -export([sctp_module/0,set_sctp_module/1]). --export([register_socket/2, unregister_socket/1, lookup_socket/1]). +-export([register_socket/2, unregister_socket/1, lookup_socket/1, + put_socket_type/2, take_socket_type/1]). %% Host name & domain -export([set_hostname/1, set_domain/1]). @@ -88,6 +90,7 @@ hosts_byaddr, %% hosts table hosts_file_byname, %% hosts table from system file hosts_file_byaddr, %% hosts table from system file + sockets, %% hosts table from system file cache_timer %% timer reference for refresh }). -type state() :: #state{}. @@ -815,6 +818,14 @@ lookup_socket(Socket) when is_port(Socket) -> error:badarg -> {error,closed} end. + +put_socket_type(MRef, Type) -> + call({put_socket_type, MRef, Type}). + +take_socket_type(MRef) -> + call({take_socket_type, MRef}). + + %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- @@ -872,6 +883,10 @@ lookup_socket(Socket) when is_port(Socket) -> %% node_auth Ls - Default authenication %% node_crypt Ls - Default encryption %% +%% Socket type (used for socket monitors) +%% -------------------------------------- +%% reference() inet | {socket, Module} - Type of socket being monitored +%% -spec init([]) -> {'ok', state()}. @@ -892,13 +907,16 @@ init([]) -> HostsByaddr = ets:new(inet_hosts_byaddr, [named_table]), HostsFileByname = ets:new(inet_hosts_file_byname, [named_table]), HostsFileByaddr = ets:new(inet_hosts_file_byaddr, [named_table]), - {ok, #state{db = Db, - cache = Cache, - hosts_byname = HostsByname, - hosts_byaddr = HostsByaddr, + %% Miscellaneous stuff related to sockets (monitoring, ...) + Sockets = ets:new(inet_sockets, [protected, set, named_table]), + {ok, #state{db = Db, + cache = Cache, + hosts_byname = HostsByname, + hosts_byaddr = HostsByaddr, hosts_file_byname = HostsFileByname, hosts_file_byaddr = HostsFileByaddr, - cache_timer = init_timer() }}. + sockets = Sockets, + cache_timer = init_timer() }}. reset_db(Db) -> ets:insert( @@ -1164,6 +1182,18 @@ handle_call(Request, From, #state{db=Db}=State) -> {add_rc_list, List} -> handle_rc_list(List, From, State); + %% Store the type of socket this monitor (reference) refers to + {put_socket_type, MRef, Type} -> + Reply = handle_put_socket_type(State#state.sockets, MRef, Type), + {reply, Reply, State}; + + %% Take (in the 'maps' sence of the word) the socket type of + %% this socket monitor (reference). + {take_socket_type, MRef} -> + Reply = handle_take_socket_type(State#state.sockets, MRef), + {reply, Reply, State}; + + stop -> {stop, normal, ok, State}; @@ -1171,6 +1201,7 @@ handle_call(Request, From, #state{db=Db}=State) -> {reply, error, State} end. + %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | @@ -1934,3 +1965,27 @@ lists_nth(_N, [], Default) -> Default; lists_nth(N, [_ | T], Default) -> lists_nth(N - 1, T, Default). + + +%%---------------------------------------------------------------------- +%% Socket related functions +%%---------------------------------------------------------------------- + +handle_put_socket_type(Db, MRef, Type) -> + Key = {type, MRef}, + case ets:lookup(Db, Key) of + [_] -> % "Should" be impossible... + error; + [] -> + ets:insert(Db, {Key, Type}), + ok + end. + +handle_take_socket_type(Db, MRef) -> + Key = {type, MRef}, + case ets:take(Db, Key) of + [{Key, Type}] -> + {ok, Type}; + [] -> % Already demonitor'ed + error + end. diff --git a/lib/kernel/src/socket.erl b/lib/kernel/src/socket.erl index 8da1fb4adf..3d14627669 100644 --- a/lib/kernel/src/socket.erl +++ b/lib/kernel/src/socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2021. All Rights Reserved. +%% Copyright Ericsson AB 2020-2021. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -20,17 +20,26 @@ -module(socket). --compile({no_auto_import,[error/1]}). +-compile({no_auto_import, [error/1, monitor/1]}). %% Administrative and "global" utility functions -export([ + %% (registry) Socket functions number_of/0, which_sockets/0, which_sockets/1, + %% (registry) Socket monitor functions + number_of_monitors/0, number_of_monitors/1, + which_monitors/1, + monitored_by/1, + debug/1, socket_debug/1, use_registry/1, info/0, info/1, + monitor/1, cancel_monitor/1, supports/0, supports/1, supports/2, - is_supported/1, is_supported/2, is_supported/3 + is_supported/1, is_supported/2, is_supported/3, + + to_list/1 ]). -export([ @@ -164,7 +173,7 @@ -type socket_info() :: #{domain := domain() | integer(), type := type() | integer(), protocol := protocol() | integer(), - ctrl := pid(), + owner := pid(), ctype := normal | fromfd | {fromfd, integer()}, counters := socket_counters(), num_readers := non_neg_integer(), @@ -514,9 +523,10 @@ %% Messages sent from the nif-code to erlang processes: -define(socket_msg(Socket, Tag, Info), {?socket_tag, (Socket), (Tag), (Info)}). --type socket() :: ?socket(socket_handle()). +-type socket() :: ?socket(socket_handle()). -opaque socket_handle() :: reference(). + %% Some flags are used for send, others for recv, and yet again %% others are found in a cmsg(). They may occur in multiple locations.. -type msg_flag() :: @@ -737,17 +747,18 @@ number_of() -> %% Interface function to the socket registry %% Returns a list of all the sockets, accoring to the filter rule. %% + -spec which_sockets() -> [socket()]. which_sockets() -> ?REGISTRY:which_sockets(true). -spec which_sockets(FilterRule) -> [socket()] when - FilterRule :: 'inet' | 'inet6' | - 'stream' | 'dgram' | 'seqpacket' | - 'sctp' | 'tcp' | 'udp' | - pid() | - fun((socket_info()) -> boolean()). + FilterRule :: 'inet' | 'inet6' | + 'stream' | 'dgram' | 'seqpacket' | + 'sctp' | 'tcp' | 'udp' | + pid() | + fun((socket_info()) -> boolean()). which_sockets(Domain) when Domain =:= inet; @@ -778,6 +789,73 @@ which_sockets(Other) -> + +%% *** number_of_monitors *** +%% +%% Interface function to the socket registry +%% returns the number of existing socket monitors. +%% + +-spec number_of_monitors() -> non_neg_integer(). + +number_of_monitors() -> + ?REGISTRY:number_of_monitors(). + +-spec number_of_monitors(pid()) -> non_neg_integer(). + +number_of_monitors(Pid) when is_pid(Pid) -> + ?REGISTRY:number_of_monitors(Pid). + + +%% *** which_monitors/1 *** +%% +%% Interface function to the socket registry +%% Returns a list of all the monitors of the process or socket. +%% + +-spec which_monitors(Pid) -> [reference()] when + Pid :: pid(); + (Socket) -> [reference()] when + Socket :: socket(). + +which_monitors(Pid) when is_pid(Pid) -> + ?REGISTRY:which_monitors(Pid); +which_monitors(?socket(SockRef) = Socket) when is_reference(SockRef) -> + ?REGISTRY:which_monitors(Socket); +which_monitors(Socket) -> + erlang:error(badarg, [Socket]). + + +%% *** monitor_by/1 *** +%% +%% Interface function to the socket registry +%% Returns a list of all the process'es monitoring the socket. +%% + +-spec monitored_by(Socket) -> [reference()] when + Socket :: socket(). + +monitored_by(?socket(SockRef) = Socket) when is_reference(SockRef) -> + ?REGISTRY:monitored_by(Socket); +monitored_by(Socket) -> + erlang:error(badarg, [Socket]). + + +%% *** to_list/1 *** +%% +%% This is intended to convert a socket() to a printable string. +%% + +-spec to_list(Socket) -> list() when + Socket :: socket(). + +to_list(?socket(SockRef)) when is_reference(SockRef) -> + "#Ref" ++ Id = erlang:ref_to_list(SockRef), + "#Socket" ++ Id; +to_list(Socket) -> + erlang:error(badarg, [Socket]). + + %% =========================================================================== %% %% Debug features @@ -836,6 +914,69 @@ info(Socket) -> %% =========================================================================== %% +%% monitor - Monitor a socket +%% +%% If a socket "dies", a down message, similar to erlang:monitor, will be +%% sent to the requesting process: +%% +%% {'DOWN', MonitorRef, socket, Socket, Info} +%% +%% =========================================================================== + +-spec monitor(Socket) -> reference() when + Socket :: socket(). + +%% Should it be possible to specify a modification of the 'Socket' part +%% of the DOWN-message? The point would be to make it possible for +%% a gen_tcp_socket-socket to use this and get the proper 'socket' +%% as part of the message. + +monitor(?socket(SockRef) = Socket) when is_reference(SockRef) -> + case prim_socket:setopt(SockRef, {otp, use_registry}, true) of + ok -> + case socket_registry:monitor(Socket) of + {error, MReason} -> + erlang:error({invalid, MReason}); + MRef when is_reference(MRef) -> + MRef + end; + {error, SReason} -> + erlang:error({invalid, SReason}) + end; +monitor(Socket) -> + erlang:error(badarg, [Socket]). + + +%% =========================================================================== +%% +%% cancel_monitor - Cancel a socket monitor +%% +%% If MRef is a reference that the socket obtained +%% by calling monitor/1, this monitoring is turned off. +%% If the monitoring is already turned off, nothing happens. +%% +%% =========================================================================== + +-spec cancel_monitor(MRef) -> boolean() when + MRef :: reference(). + +cancel_monitor(MRef) when is_reference(MRef) -> + case socket_registry:cancel_monitor(MRef) of + ok -> + true; + {error, unknown_monitor} -> + false; + {error, not_owner} -> + erlang:error(badarg, [MRef]); + {error, Reason} -> + erlang:error({invalid, Reason}) + end; +cancel_monitor(MRef) -> + erlang:error(badarg, [MRef]). + + +%% =========================================================================== +%% %% supports - get information about what the platform "supports". %% %% Generates a list of various info about what the plaform can support. diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index d099cc0864..8cd7d1f21e 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -22,7 +22,8 @@ -include_lib("common_test/include/ct.hrl"). -include("kernel_test_lib.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, +-export([ + all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, controlling_process/1, controlling_process_self/1, no_accept/1, close_with_pending_output/1, active_n/1, @@ -58,7 +59,15 @@ active_once_closed/1, send_timeout/1, send_timeout_active/1, otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, wrapping_oct/0, wrapping_oct/1, otp_9389/1, otp_13939/1, - otp_12242/1, delay_send_error/1, bidirectional_traffic/1]). + otp_12242/1, delay_send_error/1, bidirectional_traffic/1, + socket_monitor1/1, + socket_monitor1_manys/1, + socket_monitor1_manyc/1, + socket_monitor1_demon_after/1, + socket_monitor2/1, + socket_monitor2_manys/1, + socket_monitor2_manyc/1 + ]). %% Internal exports. -export([sender/4, @@ -142,7 +151,8 @@ groups() -> {partial_recv_and_close, [], partial_recv_and_close_cases()}, {pktoptions, [], pktoptions_cases()}, {accept, [], accept_cases()}, - {send_timeout, [], send_timeout_cases()} + {send_timeout, [], send_timeout_cases()}, + {socket_monitor, [], socket_monitor_cases()} ]. inet_backend_default_cases() -> @@ -177,7 +187,8 @@ all_cases() -> otp_7816, otp_8102, otp_9389, otp_12242, delay_send_error, - bidirectional_traffic + bidirectional_traffic, + {group, socket_monitor} ]. close_cases() -> @@ -275,6 +286,17 @@ send_timeout_cases() -> send_timeout_active ]. +socket_monitor_cases() -> + [ + socket_monitor1, + socket_monitor1_manys, + socket_monitor1_manyc, + socket_monitor1_demon_after, + socket_monitor2, + socket_monitor2_manys, + socket_monitor2_manyc + ]. + init_per_suite(Config0) -> ?P("init_per_suite -> entry with" @@ -448,7 +470,7 @@ do_delay_send_1(Config) -> {B1,B2,B3}. do_delay_send_2(Config) -> - {ok, LS} = ?LISTEN(Config, 0, []), + {ok, LS} = ?LISTEN(Config), {ok, {{0,0,0,0},PortNum}} = inet:sockname(LS), {ok, S} = ?CONNECT(Config, "localhost",PortNum,[]), {ok, S2} = gen_tcp:accept(LS), @@ -6633,6 +6655,456 @@ recv(Socket, Total, Control) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This is the most basic of tests. +%% We create a listen socket, then spawns processes that create +%% monitors to it... +socket_monitor1(Config) when is_list(Config) -> + ct:timetrap(?MINS(1)), + ?TC_TRY(socket_monitor1, + fun() -> do_socket_monitor1(Config) end). + +do_socket_monitor1(Config) -> + ?P("begin"), + Self = self(), + Type = ?SOCKET_TYPE(Config), + {ok, LSock1} = ?LISTEN(Config), + F = fun(S, Fun) -> spawn_monitor(fun() -> Fun(S, Self) end) end, + F1 = fun(Socket, Parent) when is_pid(Parent) -> + ?P("[client] create monitor"), + MRef = inet:monitor(Socket), + Parent ! {self(), ready}, + sm_await_socket_down(MRef, Socket, Type) + end, + ?P("spawn client"), + {Pid1, Mon1} = F(LSock1, F1), + ?P("await client ready"), + sm_await_client_ready(Pid1), + ?P("close socket"), + gen_tcp:close(LSock1), + ?P("await client termination"), + sm_await_down(Pid1, Mon1, ok), + ?P("done"), + ok. + +sm_await_socket_down(ExpMon, ExpSock, ExpType) -> + sm_await_socket_down(ExpMon, ExpSock, ExpType, "client"). + +sm_await_socket_down(ExpMon, ExpSock, ExpType, Name) -> + receive + {'DOWN', Mon, Type, Sock, Info} when (Type =:= ExpType) andalso + (Mon =:= ExpMon) andalso + (Sock =:= ExpSock) -> + ?P("[~s] received expected (socket) down message: " + "~n Mon: ~p" + "~n Type: ~p" + "~n Sock: ~p" + "~n Info: ~p", [Name, Mon, Type, Sock, Info]), + exit(ok); + + Any -> + ?P("[~s] received unexpected message: " + "~n ~p", [Name, Any]), + exit({unexpected_message, Any}) + end. + +sm_await_client_ready(Pid) -> + sm_await_client_ready(Pid, "client"). + +sm_await_client_ready(Pid, Name) -> + receive + {Pid, ready} -> + ?P("received ~s ready", [Name]) + end. + +sm_await_down(Pid, Mon, ExpRes) -> + receive + {'DOWN', Mon, process, Pid, ExpRes} -> + ?P("received expected process down message from ~p", [Pid]), + ok; + {'DOWN', Mon, process, Pid, UnexpRes} -> + ?P("received unexpected process down message from ~p: " + "~n ~p", [Pid, UnexpRes]), + ct:fail({unexpected_down, UnexpRes}) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This is the most basic of tests. +%% We create "many" listen socket(s), then spawns processes that create +%% monitors to them... +socket_monitor1_manys(Config) when is_list(Config) -> + ct:timetrap(?MINS(1)), + ?TC_TRY(socket_monitor1_manys, + fun() -> do_socket_monitor1_manys(Config) end). + +do_socket_monitor1_manys(Config) -> + ?P("begin"), + Self = self(), + Type = ?SOCKET_TYPE(Config), + ?P("[client] create socket(s)"), + {ok, LSock1} = ?LISTEN(Config), + {ok, LSock2} = ?LISTEN(Config), + {ok, LSock3} = ?LISTEN(Config), + {ok, LSock4} = ?LISTEN(Config), + {ok, LSock5} = ?LISTEN(Config), + F = fun(S, Fun) -> spawn_monitor(fun() -> Fun(S, Self) end) end, + F1 = fun(Sockets, Parent) when is_list(Sockets) andalso is_pid(Parent) -> + ?P("[client] create monitor(s)"), + Monitors = [{inet:monitor(Socket), Socket} || + Socket <- Sockets], + Parent ! {self(), ready}, + sm_await_socket_down2(Monitors, Type) + end, + ?P("spawn client"), + {Pid1, Mon1} = F([LSock1, LSock2, LSock3, LSock4, LSock5], F1), + ?P("await client ready"), + sm_await_client_ready(Pid1), + ?P("close socket(s)"), + gen_tcp:close(LSock1), + gen_tcp:close(LSock2), + gen_tcp:close(LSock3), + gen_tcp:close(LSock4), + gen_tcp:close(LSock5), + ?P("await client termination"), + sm_await_down(Pid1, Mon1, ok), + ?P("done"), + ok. + + +sm_await_socket_down2(Monitors, ExpType) -> + sm_await_socket_down2(Monitors, ExpType, "client"). + +sm_await_socket_down2([], _ExpType, Name) -> + ?P("[~s] all sockets down", [Name]), + exit(ok); +sm_await_socket_down2(Mons, ExpType, Name) when is_list(Mons) -> + ?P("[~s] await socket down", [Name]), + receive + {'DOWN', Mon, Type, Sock, Info} when (Type =:= ExpType) -> + ?P("[~s] received expected (socket) down message: " + "~n Mon: ~p" + "~n Type: ~p" + "~n Sock: ~p" + "~n Info: ~p", [Name, Mon, Type, Sock, Info]), + case lists:keysearch(Mon, 1, Mons) of + {value, {Mon, Sock}} -> + Mons2 = lists:keydelete(Mon, 1, Mons), + sm_await_socket_down2(Mons2, ExpType, Name); + {value, Value} -> + ?P("[~s] Unexpected socket down: " + "~n Value: ~p", [Name, Value]), + ct:fail({unexpected_monitor, Mon, Value}); + false -> + ct:fail({unknown_monitor, Mon}) + end + end. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This is the most basic of tests. +%% We create a listen socket, then spawn client process(es) that create +%% monitors to it... +socket_monitor1_manyc(Config) when is_list(Config) -> + ct:timetrap(?MINS(1)), + ?TC_TRY(socket_monitor1_manyc, + fun() -> do_socket_monitor1_manyc(Config) end). + +do_socket_monitor1_manyc(Config) -> + ?P("begin"), + Self = self(), + Type = ?SOCKET_TYPE(Config), + {ok, LSock1} = ?LISTEN(Config), + F = fun(S, Fun, Name) -> + spawn_monitor(fun() -> Fun(S, Name, Self) end) + end, + F1 = fun(Socket, Name, Parent) when is_list(Name) andalso is_pid(Parent) -> + ?P("[~s] monitor socket", [Name]), + MRef = inet:monitor(Socket), + Parent ! {self(), ready}, + sm_await_socket_down(MRef, Socket, Type) + end, + ?P("spawn client(s)"), + {Pid1, Mon1} = F(LSock1, F1, "client1"), + {Pid2, Mon2} = F(LSock1, F1, "client2"), + {Pid3, Mon3} = F(LSock1, F1, "client3"), + {Pid4, Mon4} = F(LSock1, F1, "client4"), + {Pid5, Mon5} = F(LSock1, F1, "client5"), + ?P("await client(s) ready"), + sm_await_client_ready(Pid1, "client1"), + sm_await_client_ready(Pid2, "client2"), + sm_await_client_ready(Pid3, "client3"), + sm_await_client_ready(Pid4, "client4"), + sm_await_client_ready(Pid5, "client5"), + ?P("close socket"), + gen_tcp:close(LSock1), + ?P("await client(s) termination"), + sm_await_down(Pid1, Mon1, ok), + sm_await_down(Pid2, Mon2, ok), + sm_await_down(Pid3, Mon3, ok), + sm_await_down(Pid4, Mon4, ok), + sm_await_down(Pid5, Mon5, ok), + ?P("done"), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This is the most basic of tests. +%% We create a listen socket, then spawns processes that create +%% monitors to it... +socket_monitor1_demon_after(Config) when is_list(Config) -> + ct:timetrap(?MINS(1)), + ?TC_TRY(socket_monitor1_demon_after, + fun() -> do_socket_monitor1_demon_after(Config) end). + +do_socket_monitor1_demon_after(Config) -> + ?P("begin"), + Self = self(), + Type = ?SOCKET_TYPE(Config), + {ok, LSock1} = ?LISTEN(Config), + F = fun(S, Fun) -> spawn_monitor(fun() -> Fun(S, Self) end) end, + F1 = fun(Socket, Parent) when is_pid(Parent) -> + ?P("[client] create monitor"), + MRef = inet:monitor(Socket), + ?P("[client] sleep some"), + ?SLEEP(?SECS(1)), + ?P("[client] cancel (socket) monitor"), + inet:cancel_monitor(MRef), + ?P("[client] announce ready"), + Parent ! {self(), ready}, + sm_await_no_socket_down(MRef, Socket, Type) + end, + ?P("spawn client"), + {Pid1, Mon1} = F(LSock1, F1), + ?P("await client ready"), + sm_await_client_ready(Pid1), + ?P("close socket"), + gen_tcp:close(LSock1), + ?P("await client termination"), + sm_await_down(Pid1, Mon1, ok), + ?P("done"), + ok. + + +sm_await_no_socket_down(ExpMon, ExpSock, ExpType) -> + sm_await_no_socket_down(ExpMon, ExpSock, ExpType, "client"). + +sm_await_no_socket_down(ExpMon, ExpSock, ExpType, Name) -> + receive + {'DOWN', Mon, Type, Sock, Info} when (Type =:= ExpType) andalso + (Mon =:= ExpMon) andalso + (Sock =:= ExpSock) -> + ?P("[~s] received unexpected (socket) down message: " + "~n Mon: ~p" + "~n Type: ~p" + "~n Sock: ~p" + "~n Info: ~p", [Name, Mon, Type, Sock, Info]), + exit({unexpected_down, Mon, Type, Sock, Info}); + + Any -> + ?P("[~s] received unexpected message: " + "~n ~p", [Name, Any]), + exit({unexpected_message, Any}) + + after 1000 -> + ?P("[~s] expected message timeout", [Name]), + exit(ok) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This is the most basic of tests. +%% Spawn a process that creates a (listen) socket, then spawns processes +%% that create monitors to it... +socket_monitor2(Config) when is_list(Config) -> + ct:timetrap(?MINS(1)), + ?TC_TRY(socket_monitor2, + fun() -> do_socket_monitor2(Config) end). + +do_socket_monitor2(Config) -> + ?P("begin"), + Type = ?SOCKET_TYPE(Config), + Self = self(), + {OwnerPid, OwnerMon} = + spawn_monitor(fun() -> + ?P("[owner] create (listen) socket"), + {ok, L} = ?LISTEN(Config), + ?P("[owner] send (listen) socket to ctrl"), + Self ! {socket, L}, + ?P("[owner] ready"), + receive + {Self, die} -> + exit(normal) + end + end), + LSock1 = receive + {socket, L} -> + ?P("received socket from owner"), + L; + {'DOWN', OwnerMon, process, OwnerPid, OwnerReason} -> + ?P("received unexpected owner termination: " + "~n ~p", [OwnerReason]), + ct:fail({unexpected_owner_termination, OwnerReason}) + end, + F = fun(S, Fun) -> spawn_monitor(fun() -> Fun(S, Self) end) end, + F1 = fun(Socket, Parent) when is_pid(Parent) -> + ?P("[client] create monitor"), + MRef = inet:monitor(Socket), + Parent ! {self(), ready}, + sm_await_socket_down(MRef, Socket, Type) + end, + ?P("spawn client"), + {Pid1, Mon1} = F(LSock1, F1), + ?P("spawn client"), + sm_await_client_ready(Pid1), + ?P("kill owner"), + exit(OwnerPid, kill), + ?P("await owner termination"), + sm_await_down(OwnerPid, OwnerMon, killed), + ?P("await client termination"), + sm_await_down(Pid1, Mon1, ok), + ?P("done"), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This is the most basic of tests. +%% Spawn a process that creates "many" (listen) socket(s), then spawns +%% a process that create monitors to them... + +socket_monitor2_manys(Config) when is_list(Config) -> + ct:timetrap(?MINS(1)), + ?TC_TRY(socket_monitor2_manys, + fun() -> do_socket_monitor2_manys(Config) end). + +do_socket_monitor2_manys(Config) -> + ?P("begin"), + Type = ?SOCKET_TYPE(Config), + Self = self(), + ?P("spawn owner"), + {OwnerPid, OwnerMon} = + spawn_monitor(fun() -> + ?P("[owner] create (listen) socket(s)"), + {ok, L1} = ?LISTEN(Config), + {ok, L2} = ?LISTEN(Config), + {ok, L3} = ?LISTEN(Config), + {ok, L4} = ?LISTEN(Config), + {ok, L5} = ?LISTEN(Config), + ?P("[owner] send (listen) socket(s) to ctrl"), + Self ! {socket, [L1, L2, L3, L4, L5]}, + ?P("[owner] ready"), + receive + {Self, die} -> + exit(normal) + end + end), + ?P("await sockets (from owner)"), + LSocks = receive + {socket, Socks} -> + ?P("received socket(s) from owner"), + Socks; + {'DOWN', OwnerMon, process, OwnerPid, OwnerReason} -> + ?P("received unexpected owner termination: " + "~n ~p", [OwnerReason]), + ct:fail({unexpected_owner_termination, OwnerReason}) + end, + F = fun(S, Fun) -> spawn_monitor(fun() -> Fun(S, Self) end) end, + F1 = fun(Sockets, Parent) when is_list(Sockets) andalso is_pid(Parent) -> + ?P("[client] create monitor(s)"), + Monitors = [{inet:monitor(Socket), Socket} || + Socket <- Sockets], + ?P("[client] announce ready"), + Parent ! {self(), ready}, + sm_await_socket_down2(Monitors, Type) + end, + ?P("spawn client"), + {Pid1, Mon1} = F(LSocks, F1), + ?P("await client ready"), + sm_await_client_ready(Pid1), + ?P("kill owner"), + exit(OwnerPid, kill), + ?P("await owner (~p) termination", [OwnerPid]), + sm_await_down(OwnerPid, OwnerMon, killed), + ?P("await client termination"), + sm_await_down(Pid1, Mon1, ok), + ?P("done"), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This is the most basic of tests. +%% Spawn a process that creates a (listen) socket, then spawns (client) +%% processes that create monitors to it... +socket_monitor2_manyc(Config) when is_list(Config) -> + ct:timetrap(?MINS(1)), + ?TC_TRY(socket_monitor2_manyc, + fun() -> do_socket_monitor2_manyc(Config) end). + +do_socket_monitor2_manyc(Config) -> + ?P("begin"), + Type = ?SOCKET_TYPE(Config), + Self = self(), + {OwnerPid, OwnerMon} = + spawn_monitor(fun() -> + {ok, L} = ?LISTEN(Config), + Self ! {socket, L}, + receive + {Self, die} -> + exit(normal) + end + end), + LSock1 = receive + {socket, L} -> + ?P("received socket from owner"), + L; + {'DOWN', OwnerMon, process, OwnerPid, OwnerReason} -> + ?P("received unexpected owner termination: " + "~n ~p", [OwnerReason]), + ct:fail({unexpected_owner_termination, OwnerReason}) + end, + F = fun(S, Fun, Name) -> + spawn_monitor(fun() -> Fun(S, Name, Self) end) + end, + F1 = fun(Socket, Name, Parent) when is_list(Name) andalso is_pid(Parent) -> + ?P("[~s] create monitor", [Name]), + MRef = inet:monitor(Socket), + Parent ! {self(), ready}, + sm_await_socket_down(MRef, Socket, Type, Name) + end, + ?P("spawn client(s)"), + {Pid1, Mon1} = F(LSock1, F1, "client1"), + {Pid2, Mon2} = F(LSock1, F1, "client2"), + {Pid3, Mon3} = F(LSock1, F1, "client3"), + {Pid4, Mon4} = F(LSock1, F1, "client4"), + {Pid5, Mon5} = F(LSock1, F1, "client5"), + ?P("await client(s) ready"), + sm_await_client_ready(Pid1, "client1"), + sm_await_client_ready(Pid2, "client2"), + sm_await_client_ready(Pid3, "client3"), + sm_await_client_ready(Pid4, "client4"), + sm_await_client_ready(Pid5, "client5"), + ?P("kill owner"), + exit(OwnerPid, kill), + ?P("await owner termination"), + sm_await_down(OwnerPid, OwnerMon, killed), + ?P("await client(s) termination"), + sm_await_down(Pid1, Mon1, ok), + sm_await_down(Pid2, Mon2, ok), + sm_await_down(Pid3, Mon3, ok), + sm_await_down(Pid4, Mon4, ok), + sm_await_down(Pid5, Mon5, ok), + ?P("done"), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + pi(Item) -> {Item, Val} = process_info(self(), Item), Val. diff --git a/lib/kernel/test/kernel_test_lib.erl b/lib/kernel/test/kernel_test_lib.erl index 268b97ca3f..36ae71067d 100644 --- a/lib/kernel/test/kernel_test_lib.erl +++ b/lib/kernel/test/kernel_test_lib.erl @@ -23,7 +23,8 @@ -export([init_per_suite/1, end_per_suite/1]). -export([tc_try/3]). --export([listen/3, +-export([socket_type/1, + listen/3, connect/4, connect/5, is_socket_backend/1, inet_backend_opts/1, @@ -1726,6 +1727,14 @@ proxy_call(F, Timeout, Default) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +socket_type(Config) -> + case is_socket_backend(Config) of + true -> + socket; + false -> + port + end. + listen(Config, Port, Opts) -> InetBackendOpts = inet_backend_opts(Config), gen_tcp:listen(Port, InetBackendOpts ++ Opts). diff --git a/lib/kernel/test/kernel_test_lib.hrl b/lib/kernel/test/kernel_test_lib.hrl index d49c31f4d6..9dc99fdd22 100644 --- a/lib/kernel/test/kernel_test_lib.hrl +++ b/lib/kernel/test/kernel_test_lib.hrl @@ -33,6 +33,9 @@ -define(TC_TRY(Case, TC), ?TC_TRY(Case, fun() -> ok end, TC)). -define(TC_TRY(Case, Cond, TC), ?LIB:tc_try(Case, Cond, TC)). +-define(SOCKET_TYPE(C), ?LIB:socket_type(C)). +-define(LISTEN(C), ?LIB:listen(C, 0, [])). +-define(LISTEN(C, P), ?LIB:listen(C, P, [])). -define(LISTEN(C, P, O), ?LIB:listen(C, P, O)). -define(CONNECT(__C__, __H__, __P__, __O__), ?LIB:connect(__C__, __H__, __P__, __O__)). diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 6f5e778047..9efe99229a 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -235,7 +235,19 @@ %% Socket Registry reg_s_single_open_and_close_and_count/1, reg_s_optional_open_and_close_and_count/1, - + + + %% Socket Monitor + monitor_simple_open_and_close/1, + monitor_simple_open_and_exit/1, + monitor_simple_open_and_demon_and_close/1, + monitor_open_and_close_multi_socks/1, + monitor_open_and_exit_multi_socks/1, + monitor_open_and_demon_and_close_multi_socks/1, + monitor_open_and_close_multi_mon/1, + monitor_open_and_exit_multi_mon/1, + monitor_open_and_close_multi_socks_and_mon/1, + monitor_open_and_exit_multi_socks_and_mon/1, %% *** Socket Closure *** sc_cpe_socket_cleanup_tcp4/1, @@ -685,6 +697,7 @@ suite() -> all() -> Groups = [{api, "ESOCK_TEST_API", include}, {reg, undefined, include}, + {monitor, undefined, include}, {socket_close, "ESOCK_TEST_SOCK_CLOSE", include}, {traffic, "ESOCK_TEST_TRAFFIC", include}, {ttest, "ESOCK_TEST_TTEST", exclude}, @@ -737,6 +750,7 @@ groups() -> %% {api_options_sctp, [], api_options_sctp_cases()}, {api_op_with_timeout, [], api_op_with_timeout_cases()}, {reg, [], reg_simple_cases()}, + {monitor, [], monitor_cases()}, {socket_close, [], socket_close_cases()}, {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, {sc_local_close, [], sc_lc_cases()}, @@ -1080,6 +1094,22 @@ reg_simple_cases() -> ]. +%% Socket monitor test cases +monitor_cases() -> + [ + monitor_simple_open_and_close, + monitor_simple_open_and_exit, + monitor_simple_open_and_demon_and_close, + monitor_open_and_close_multi_socks, + monitor_open_and_exit_multi_socks, + monitor_open_and_demon_and_close_multi_socks, + monitor_open_and_close_multi_mon, + monitor_open_and_exit_multi_mon, + monitor_open_and_close_multi_socks_and_mon, + monitor_open_and_exit_multi_socks_and_mon + ]. + + %% These cases tests what happens when the socket is closed/shutdown, %% locally or remotely. socket_close_cases() -> @@ -11084,6 +11114,8 @@ api_opt_simple_otp_meta_option() -> i("await tcp evaluators"), ok = ?SEV_AWAIT_FINISH([Helper, Main]). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Perform some simple operations with the rcvbuf otp option @@ -25679,6 +25711,4660 @@ reg_s_optional_open_and_close_and_count() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% +%% SOCKET MONITOR %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create one socket, monitor from a different process then close socket. +%% The process that did the monitor shall receive a socket DOWN. + +monitor_simple_open_and_close(suite) -> + []; +monitor_simple_open_and_close(doc) -> + []; +monitor_simple_open_and_close(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(monitor_simple_open_and_close, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_simple_open_and_close(InitState) + end). + + +mon_simple_open_and_close(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "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 (init)", + cmd => fun(#{tester := Tester, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_READY(Tester, init, Sock), + ok + end}, + + %% The actual test + #{desc => "await continue (close)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close) + end}, + + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock => Sock}} + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket - create two", + cmd => fun(#{sock := Sock} = State) -> + MRef1 = socket:monitor(Sock), + MRef2 = socket:monitor(Sock), + ?SEV_IPRINT("Monitor(s): " + "~n 1: ~p" + "~n 2: ~p", [MRef1, MRef2]), + {ok, State#{mon1 => MRef1, mon2 => MRef2}} + end}, + #{desc => "verify total number of monitors (=2)", + cmd => fun(_State) -> + 2 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=2)", + cmd => fun(_State) -> + 2 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "verify which monitors - (self) two", + cmd => fun(#{mon1 := MRef1, + mon2 := MRef2} = _State) -> + Mons = lists:sort([MRef1, MRef2]), + case lists:sort(socket:which_monitors(self())) of + Mons -> + ok; + SMons -> + ?SEV_EPRINT("Unexpected (self) monitors: " + "~n Expected: ~p" + "~n Actual: ~p", + [Mons, SMons]), + {error, unexpected_monitors} + end + end}, + #{desc => "verify which monitors - (sock) two", + cmd => fun(#{sock := Sock, + mon1 := MRef1, + mon2 := MRef2} = _State) -> + Mons = lists:sort([MRef1, MRef2]), + case lists:sort(socket:which_monitors(Sock)) of + Mons -> + ok; + SMons -> + ?SEV_EPRINT("Unexpected (sock) monitors: " + "~n Expected: ~p" + "~n Actual: ~p", + [Mons, SMons]), + {error, unexpected_monitors} + end + end}, + #{desc => "verify monitored by - only us", + cmd => fun(#{sock := Sock} = _State) -> + Self = self(), + [Self] = socket:monitored_by(Sock), + ok + end}, + + %% The actual test + #{desc => "order owner to close", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close), + ok + end}, + #{desc => "await socket down 1", + cmd => fun(#{sock := Sock, + mon1 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~s" + "~n Info: ~p", + [MRef, + socket:to_list(Sock), + Info]), + {ok, maps:remove(mon1, State)} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "await socket down 2", + cmd => fun(#{sock := Sock, + mon2 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~s" + "~n Info: ~p", + [MRef, + socket:to_list(Sock), + Info]), + {ok, maps:remove(mon2, State)} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "verify which monitors - only one", + cmd => fun(#{sock := Sock} = _State) -> + [] = socket:which_monitors(self()), + [] = socket:which_monitors(Sock), + ok + end}, + #{desc => "verify monitored by - none", + cmd => fun(#{sock := Sock} = _State) -> + [] = socket:monitored_by(Sock), + ok + end}, + + %% Cleanup + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = _State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create one socket, monitor from a different process then stop the +%% owner process. +%% The process that did the monitor shall receive a socket DOWN. + +monitor_simple_open_and_exit(suite) -> + []; +monitor_simple_open_and_exit(doc) -> + []; +monitor_simple_open_and_exit(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(monitor_simple_open_and_exit, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_simple_open_and_exit(InitState) + end). + + +mon_simple_open_and_exit(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "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 (init)", + cmd => fun(#{tester := Tester, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_READY(Tester, init, Sock), + ok + end}, + + %% The actual test + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock => Sock}} + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket", + cmd => fun(#{sock := Sock} = State) -> + MRef = socket:monitor(Sock), + ?SEV_IPRINT("Monitor: ~p", [MRef]), + {ok, State#{mon => MRef}} + end}, + #{desc => "verify total number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "verify which monitors - only one", + cmd => fun(#{sock := Sock, + mon := MRef} = _State) -> + [MRef] = socket:which_monitors(self()), + [MRef] = socket:which_monitors(Sock), + ok + end}, + #{desc => "verify monitored by - only us", + cmd => fun(#{sock := Sock} = _State) -> + Self = self(), + [Self] = socket:monitored_by(Sock), + ok + end}, + + %% The actual test + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = _State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "await socket down", + cmd => fun(#{sock := Sock, + mon := MRef} = _State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + ok + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "verify which monitors - only one", + cmd => fun(#{sock := Sock} = _State) -> + [] = socket:which_monitors(self()), + [] = socket:which_monitors(Sock), + ok + end}, + #{desc => "verify monitored by - none", + cmd => fun(#{sock := Sock} = _State) -> + [] = socket:monitored_by(Sock), + ok + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create one socket, monitor from a different process, cancel_montor +%% (demonitor) and then close socket. +%% The process that did the monitor shall *not* receive a socket DOWN. + +monitor_simple_open_and_demon_and_close(suite) -> + []; +monitor_simple_open_and_demon_and_close(doc) -> + []; +monitor_simple_open_and_demon_and_close(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(monitor_simple_open_and_demon_and_close, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_simple_open_and_demon_and_close(InitState) + end). + + +mon_simple_open_and_demon_and_close(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "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 (init)", + cmd => fun(#{tester := Tester, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_READY(Tester, init, Sock), + ok + end}, + + %% The actual test + #{desc => "await continue (close)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close) + end}, + + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock => Sock}} + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket", + cmd => fun(#{sock := Sock} = State) -> + MRef = socket:monitor(Sock), + ?SEV_IPRINT("Monitor: ~p", [MRef]), + {ok, State#{mon => MRef}} + end}, + #{desc => "verify total number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(self()), + ok + end}, + + %% The actual test + #{desc => "demonitor socket", + cmd => fun(#{mon := MRef} = State) -> + true = socket:cancel_monitor(MRef), + {ok, maps:remove(mon, State)} + end}, + + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "order owner to close", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close), + ok + end}, + + #{desc => "await socket down", + cmd => fun(#{sock := Sock} = _State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_EPRINT("received UNEXPECTED down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + {error, unexpected_down} + after 5000 -> + ?SEV_IPRINT("expected socket down timeout"), + ok + end + end}, + + %% Cleanup + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = _State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create several sockets, monitor from a different process then close +%% socket. The process that did the monitor shall receive a socket DOWN. + +monitor_open_and_close_multi_socks(suite) -> + []; +monitor_open_and_close_multi_socks(doc) -> + []; +monitor_open_and_close_multi_socks(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(monitor_open_and_close_multi_socks, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_open_and_close_multi_socks(InitState) + end). + + +mon_open_and_close_multi_socks(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "create socket 1", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 2", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 3", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock3 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 4", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock4 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 5", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock5 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_READY(Tester, init, Socks), + ok + end}, + + %% The actual test + #{desc => "await continue (close1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close1) + end}, + #{desc => "close socket 1", + cmd => fun(#{sock1 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock1, State)} + end}, + + #{desc => "await continue (close2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close2) + end}, + #{desc => "close socket 2", + cmd => fun(#{sock2 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock2, State)} + end}, + + #{desc => "await continue (close3)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close3) + end}, + #{desc => "close socket 3", + cmd => fun(#{sock3 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock3, State)} + end}, + + #{desc => "await continue (close4)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close4) + end}, + #{desc => "close socket 4", + cmd => fun(#{sock4 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock4, State)} + end}, + + #{desc => "await continue (close5)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close5) + end}, + #{desc => "close socket 5", + cmd => fun(#{sock5 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock5, State)} + end}, + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, [Sock1, Sock2, Sock3, Sock4, Sock5]} = + ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock1 => Sock1, + sock2 => Sock2, + sock3 => Sock3, + sock4 => Sock4, + sock5 => Sock5}} + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket", + cmd => fun(#{sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = State) -> + MRef1 = socket:monitor(Sock1), + MRef2 = socket:monitor(Sock2), + MRef3 = socket:monitor(Sock3), + MRef4 = socket:monitor(Sock4), + MRef5 = socket:monitor(Sock5), + ?SEV_IPRINT("Monitors:" + "~n 1: ~p" + "~n 2: ~p" + "~n 3: ~p" + "~n 4: ~p" + "~n 5: ~p", + [MRef1, MRef2, MRef3, MRef4, MRef5]), + {ok, State#{mon1 => MRef1, + mon2 => MRef2, + mon3 => MRef3, + mon4 => MRef4, + mon5 => MRef5}} + end}, + #{desc => "verify total number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(self()), + ok + end}, + + %% The actual test + #{desc => "order owner to close socket 1", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close1), + ok + end}, + #{desc => "await socket 1 down", + cmd => fun(#{sock1 := Sock, + mon1 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~s" + "~n Info: ~p", + [MRef, + socket:to_list(Sock), + Info]), + State2 = maps:remove(sock1, State), + State3 = maps:remove(mon1, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=4)", + cmd => fun(_State) -> + 4 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=4)", + cmd => fun(_State) -> + 4 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "order owner to close socket 2", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close2), + ok + end}, + #{desc => "await socket 2 down", + cmd => fun(#{sock2 := Sock, + mon2 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~s" + "~n Info: ~p", + [MRef, + socket:to_list(Sock), + Info]), + State2 = maps:remove(sock2, State), + State3 = maps:remove(mon2, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=3)", + cmd => fun(_State) -> + 3 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=3)", + cmd => fun(_State) -> + 3 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "order owner to close socket 3", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close3), + ok + end}, + #{desc => "await socket 3 down", + cmd => fun(#{sock3 := Sock, + mon3 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~s" + "~n Info: ~p", + [MRef, + socket:to_list(Sock), + Info]), + State2 = maps:remove(sock3, State), + State3 = maps:remove(mon3, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=2)", + cmd => fun(_State) -> + 2 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=2)", + cmd => fun(_State) -> + 2 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "order owner to close socket 4", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close4), + ok + end}, + #{desc => "await socket 4 down", + cmd => fun(#{sock4 := Sock, + mon4 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock4, State), + State3 = maps:remove(mon4, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "order owner to close socket 5", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close5), + ok + end}, + #{desc => "await socket 5 down", + cmd => fun(#{sock5 := Sock, + mon5 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock5, State), + State3 = maps:remove(mon5, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + + %% Cleanup + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = _State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create several sockets, monitor from a different process then exit +%% the owner process. +%% The process that did the monitor shall receive a socket DOWN. + +monitor_open_and_exit_multi_socks(suite) -> + []; +monitor_open_and_exit_multi_socks(doc) -> + []; +monitor_open_and_exit_multi_socks(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(monitor_open_and_exit_multi_socks, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_open_and_exit_multi_socks(InitState) + end). + + +mon_open_and_exit_multi_socks(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "create socket 1", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 2", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 3", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock3 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 4", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock4 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 5", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock5 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_READY(Tester, init, Socks), + ok + end}, + + + %% The actual test + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, [Sock1, Sock2, Sock3, Sock4, Sock5]} = + ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock1 => Sock1, + sock2 => Sock2, + sock3 => Sock3, + sock4 => Sock4, + sock5 => Sock5}} + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket", + cmd => fun(#{sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = State) -> + MRef1 = socket:monitor(Sock1), + MRef2 = socket:monitor(Sock2), + MRef3 = socket:monitor(Sock3), + MRef4 = socket:monitor(Sock4), + MRef5 = socket:monitor(Sock5), + ?SEV_IPRINT("Monitors:" + "~n 1: ~p" + "~n 2: ~p" + "~n 3: ~p" + "~n 4: ~p" + "~n 5: ~p", + [MRef1, MRef2, MRef3, MRef4, MRef5]), + {ok, State#{mon1 => MRef1, + mon2 => MRef2, + mon3 => MRef3, + mon4 => MRef4, + mon5 => MRef5}} + end}, + #{desc => "verify total number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(self()), + ok + end}, + + %% The actual test + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = _State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "await socket 1 down", + cmd => fun(#{sock1 := Sock, + mon1 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock1, State), + State3 = maps:remove(mon1, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "await socket 2 down", + cmd => fun(#{sock2 := Sock, + mon2 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock2, State), + State3 = maps:remove(mon2, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "await socket 3 down", + cmd => fun(#{sock3 := Sock, + mon3 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock3, State), + State3 = maps:remove(mon3, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "await socket 4 down", + cmd => fun(#{sock4 := Sock, + mon4 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock4, State), + State3 = maps:remove(mon4, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "await socket 5 down", + cmd => fun(#{sock5 := Sock, + mon5 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock5, State), + State3 = maps:remove(mon5, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create several sockets, monitor from a different process, demonitor +%% two of them then close socket. +%% The process that did the monitor shall receive a socket DOWN for +%% the sockets that are still monitored. + +monitor_open_and_demon_and_close_multi_socks(suite) -> + []; +monitor_open_and_demon_and_close_multi_socks(doc) -> + []; +monitor_open_and_demon_and_close_multi_socks(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(monitor_open_and_demon_and_close_multi_socks, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_open_and_demon_and_close_multi_socks(InitState) + end). + + +mon_open_and_demon_and_close_multi_socks(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "create socket 1", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 2", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 3", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock3 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 4", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock4 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 5", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock5 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_READY(Tester, init, Socks), + ok + end}, + + %% The actual test + #{desc => "await continue (close1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close1) + end}, + #{desc => "close socket 1", + cmd => fun(#{sock1 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock1, State)} + end}, + + #{desc => "await continue (close3)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close3) + end}, + #{desc => "close socket 3", + cmd => fun(#{sock3 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock3, State)} + end}, + + #{desc => "await continue (close5)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close5) + end}, + #{desc => "close socket 5", + cmd => fun(#{sock5 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock5, State)} + end}, + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, [Sock1, Sock2, Sock3, Sock4, Sock5]} = + ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock1 => Sock1, + sock2 => Sock2, + sock3 => Sock3, + sock4 => Sock4, + sock5 => Sock5}} + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket", + cmd => fun(#{sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = State) -> + MRef1 = socket:monitor(Sock1), + MRef2 = socket:monitor(Sock2), + MRef3 = socket:monitor(Sock3), + MRef4 = socket:monitor(Sock4), + MRef5 = socket:monitor(Sock5), + ?SEV_IPRINT("Monitors:" + "~n 1: ~p" + "~n 2: ~p" + "~n 3: ~p" + "~n 4: ~p" + "~n 5: ~p", + [MRef1, MRef2, MRef3, MRef4, MRef5]), + {ok, State#{mon1 => MRef1, + mon2 => MRef2, + mon3 => MRef3, + mon4 => MRef4, + mon5 => MRef5}} + end}, + #{desc => "verify total number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(self()), + ok + end}, + + %% The actual test + #{desc => "demonitor socket(s)", + cmd => fun(#{mon2 := MRef2, + mon4 := MRef4} = State) -> + true = socket:cancel_monitor(MRef2), + true = socket:cancel_monitor(MRef4), + ?SEV_IPRINT("cancel socket monitor 2 and 4"), + State2 = maps:remove(mon2, State), + State3 = maps:remove(sock2, State2), + State4 = maps:remove(mon4, State3), + State5 = maps:remove(sock4, State4), + {ok, State5} + end}, + #{desc => "verify total number of monitors (=3)", + cmd => fun(_State) -> + 3 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=3)", + cmd => fun(_State) -> + 3 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "order owner to close socket 1", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close1), + ok + end}, + #{desc => "await socket 1 down", + cmd => fun(#{sock1 := Sock, + mon1 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock1, State), + State3 = maps:remove(mon1, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=2)", + cmd => fun(_State) -> + 2 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=2)", + cmd => fun(_State) -> + 2 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "order owner to close socket 3", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close3), + ok + end}, + #{desc => "await socket 3 down", + cmd => fun(#{sock3 := Sock, + mon3 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock3, State), + State3 = maps:remove(mon3, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "order owner to close socket 5", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close5), + ok + end}, + #{desc => "await socket 5 down", + cmd => fun(#{sock5 := Sock, + mon5 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(sock5, State), + State3 = maps:remove(mon5, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + + %% Cleanup + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = _State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create one socket (by 'owner' process), monitor from several different +%% processes, then close socket (from 'owner'). +%% The processes that did the monitor shall receive a socket DOWN. + +monitor_open_and_close_multi_mon(suite) -> + []; +monitor_open_and_close_multi_mon(doc) -> + []; +monitor_open_and_close_multi_mon(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(monitor_open_and_close_multi_mon, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_open_and_close_multi_mon(InitState) + end). + + +mon_open_and_close_multi_mon(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "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 (init)", + cmd => fun(#{tester := Tester, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_READY(Tester, init, Sock), + ok + end}, + + %% The actual test + #{desc => "await continue (close)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close) + end}, + + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + #{desc => "await continue (socket)", + cmd => fun(#{tester := Tester} = State) -> + {ok, Sock} = + ?SEV_AWAIT_CONTINUE(Tester, tester, socket), + ?SEV_IPRINT("Socket: ~p", [Sock]), + {ok, State#{sock => Sock}} + end}, + + + %% The actual test + #{desc => "await continue (monitor)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, monitor) + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket", + cmd => fun(#{sock := Sock} = State) -> + MRef = socket:monitor(Sock), + ?SEV_IPRINT("Monitor: ~p", [MRef]), + {ok, State#{mon => MRef}} + end}, + #{desc => "verify own number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "announce ready (monitor)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, monitor), + ok + end}, + + #{desc => "await continue (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, down) + end}, + + #{desc => "await socket down", + cmd => fun(#{sock := Sock, + mon := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon, State), + State3 = maps:remove(sock, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "announce ready (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, down), + ok + end}, + + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock => Sock}} + end}, + + #{desc => "monitor 'client 1'", + cmd => fun(#{client1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 1) start", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 1) ready", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, init) + end}, + #{desc => "send socket to client 1", + cmd => fun(#{client1 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + #{desc => "monitor 'client 2'", + cmd => fun(#{client2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 2) start", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 2) ready", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, init) + end}, + #{desc => "send socket to client 2", + cmd => fun(#{client2 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + #{desc => "monitor 'client 3'", + cmd => fun(#{client3 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 3) start", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 3) ready", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, init) + end}, + #{desc => "send socket to client 3", + cmd => fun(#{client3 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + #{desc => "monitor 'client 4'", + cmd => fun(#{client4 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 4) start", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 4) ready", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, init) + end}, + #{desc => "send socket to client 4", + cmd => fun(#{client4 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + #{desc => "monitor 'client 5'", + cmd => fun(#{client5 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 5) start", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 5) ready", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, init) + end}, + #{desc => "send socket to client 5", + cmd => fun(#{client5 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + + %% The actual test + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 1 to monitor", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 1) ready", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, monitor) + end}, + #{desc => "verify total number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 2 to monitor", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 2) ready", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, monitor) + end}, + #{desc => "verify total number of monitors (=2)", + cmd => fun(_State) -> + 2 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 3 to monitor", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 3) ready", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, monitor) + end}, + #{desc => "verify total number of monitors (=3)", + cmd => fun(_State) -> + 3 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 4 to monitor", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 4) ready", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, monitor) + end}, + #{desc => "verify total number of monitors (=4)", + cmd => fun(_State) -> + 4 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 5 to monitor", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 5) ready", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, monitor) + end}, + #{desc => "verify total number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(), + ok + end}, + #{desc => "verify monitored by - none", + cmd => fun(#{sock := Sock, + client1 := Pid1, + client2 := Pid2, + client3 := Pid3, + client4 := Pid4, + client5 := Pid5} = _State) -> + Clients = lists:sort([Pid1, Pid2, Pid3, Pid4, Pid5]), + case lists:sort(socket:monitored_by(Sock)) of + Clients -> + ok; + SClients -> + ?SEV_EPRINT("Unexpected clients: " + "~n Expected: ~p" + "~n Actual: ~p", + [Clients, SClients]), + {error, unexpected_clients} + end + end}, + + #{desc => "order client 1 to await down", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 2 to await down", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 3 to await down", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 4 to await down", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 5 to await down", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + + #{desc => "order owner to close", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close), + ok + end}, + + #{desc => "await (client 1) down received", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, down) + end}, + #{desc => "await (client 2) down received", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, down) + end}, + #{desc => "await (client 3) down received", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, down) + end}, + #{desc => "await (client 4) down received", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, down) + end}, + #{desc => "await (client 5) down received", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, down) + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + + %% Cleanup + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(owner, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 1) terminate", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 1) termination", + cmd => fun(#{client1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client1, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 2) terminate", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 2) termination", + cmd => fun(#{client2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client2, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 3) terminate", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 3) termination", + cmd => fun(#{client3 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client3, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 4) terminate", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 4) termination", + cmd => fun(#{client4 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client4, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 5) terminate", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 5) termination", + cmd => fun(#{client5 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client5, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start client 1 evaluator"), + Client1 = ?SEV_START("client-1", ClientSeq, InitState), + + i("start client 2 evaluator"), + Client2 = ?SEV_START("client-2", ClientSeq, InitState), + + i("start client 3 evaluator"), + Client3 = ?SEV_START("client-3", ClientSeq, InitState), + + i("start client 4 evaluator"), + Client4 = ?SEV_START("client-4", ClientSeq, InitState), + + i("start client 5 evaluator"), + Client5 = ?SEV_START("client-5", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid, + client1 => Client1#ev.pid, + client2 => Client2#ev.pid, + client3 => Client3#ev.pid, + client4 => Client4#ev.pid, + client5 => Client5#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create one socket (by 'owner' process), monitor from several different +%% processes, then close socket (from 'owner'). +%% The processes that did the monitor shall receive a socket DOWN. + +monitor_open_and_exit_multi_mon(suite) -> + []; +monitor_open_and_exit_multi_mon(doc) -> + []; +monitor_open_and_exit_multi_mon(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(monitor_open_and_exit_multi_mon, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_open_and_exit_multi_mon(InitState) + end). + + +mon_open_and_exit_multi_mon(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "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 (init)", + cmd => fun(#{tester := Tester, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_READY(Tester, init, Sock), + ok + end}, + + %% The actual test + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + #{desc => "await continue (socket)", + cmd => fun(#{tester := Tester} = State) -> + {ok, Sock} = + ?SEV_AWAIT_CONTINUE(Tester, tester, socket), + ?SEV_IPRINT("Socket: ~p", [Sock]), + {ok, State#{sock => Sock}} + end}, + + + %% The actual test + #{desc => "await continue (monitor)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, monitor) + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket", + cmd => fun(#{sock := Sock} = State) -> + MRef = socket:monitor(Sock), + ?SEV_IPRINT("Monitor: ~p", [MRef]), + {ok, State#{mon => MRef}} + end}, + #{desc => "verify own number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "announce ready (monitor)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, monitor), + ok + end}, + + #{desc => "await continue (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, down) + end}, + + #{desc => "await socket down", + cmd => fun(#{sock := Sock, + mon := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon, State), + State3 = maps:remove(sock, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "announce ready (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, down), + ok + end}, + + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock => Sock}} + end}, + + #{desc => "monitor 'client 1'", + cmd => fun(#{client1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 1) start", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 1) ready", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, init) + end}, + #{desc => "send socket to client 1", + cmd => fun(#{client1 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + #{desc => "monitor 'client 2'", + cmd => fun(#{client2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 2) start", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 2) ready", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, init) + end}, + #{desc => "send socket to client 2", + cmd => fun(#{client2 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + #{desc => "monitor 'client 3'", + cmd => fun(#{client3 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 3) start", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 3) ready", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, init) + end}, + #{desc => "send socket to client 3", + cmd => fun(#{client3 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + #{desc => "monitor 'client 4'", + cmd => fun(#{client4 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 4) start", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 4) ready", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, init) + end}, + #{desc => "send socket to client 4", + cmd => fun(#{client4 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + #{desc => "monitor 'client 5'", + cmd => fun(#{client5 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 5) start", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 5) ready", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, init) + end}, + #{desc => "send socket to client 5", + cmd => fun(#{client5 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock), + ok + end}, + + + %% The actual test + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 1 to monitor", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 1) ready", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, monitor) + end}, + #{desc => "verify total number of monitors (=1)", + cmd => fun(_State) -> + 1 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 2 to monitor", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 2) ready", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, monitor) + end}, + #{desc => "verify total number of monitors (=2)", + cmd => fun(_State) -> + 2 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 3 to monitor", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 3) ready", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, monitor) + end}, + #{desc => "verify total number of monitors (=3)", + cmd => fun(_State) -> + 3 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 4 to monitor", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 4) ready", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, monitor) + end}, + #{desc => "verify total number of monitors (=4)", + cmd => fun(_State) -> + 4 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 5 to monitor", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 5) ready", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, monitor) + end}, + #{desc => "verify total number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 1 to await down", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 2 to await down", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 3 to await down", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 4 to await down", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 5 to await down", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + + + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(owner, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "await (client 1) down received", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, down) + end}, + #{desc => "await (client 2) down received", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, down) + end}, + #{desc => "await (client 3) down received", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, down) + end}, + #{desc => "await (client 4) down received", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, down) + end}, + #{desc => "await (client 5) down received", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, down) + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + + %% Cleanup + #{desc => "order (client 1) terminate", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 1) termination", + cmd => fun(#{client1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client1, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 2) terminate", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 2) termination", + cmd => fun(#{client2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client2, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 3) terminate", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 3) termination", + cmd => fun(#{client3 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client3, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 4) terminate", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 4) termination", + cmd => fun(#{client4 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client4, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 5) terminate", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 5) termination", + cmd => fun(#{client5 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client5, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start client 1 evaluator"), + Client1 = ?SEV_START("client-1", ClientSeq, InitState), + + i("start client 2 evaluator"), + Client2 = ?SEV_START("client-2", ClientSeq, InitState), + + i("start client 3 evaluator"), + Client3 = ?SEV_START("client-3", ClientSeq, InitState), + + i("start client 4 evaluator"), + Client4 = ?SEV_START("client-4", ClientSeq, InitState), + + i("start client 5 evaluator"), + Client5 = ?SEV_START("client-5", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid, + client1 => Client1#ev.pid, + client2 => Client2#ev.pid, + client3 => Client3#ev.pid, + client4 => Client4#ev.pid, + client5 => Client5#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create several sockets (by 'owner' process), monitor each from several +%% different processes, then close each socket (from 'owner'). +%% The processes that did the monitor shall receive one socket DOWN for +%% each socket. + +monitor_open_and_close_multi_socks_and_mon(suite) -> + []; +monitor_open_and_close_multi_socks_and_mon(doc) -> + []; +monitor_open_and_close_multi_socks_and_mon(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(monitor_open_and_close_multi_socks_and_mon, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_open_and_close_multi_socks_and_mon(InitState) + end). + + +mon_open_and_close_multi_socks_and_mon(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "create socket 1", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 2", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 3", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock3 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 4", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock4 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 5", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock5 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_READY(Tester, init, Socks), + ok + end}, + + %% The actual test + #{desc => "await continue (close1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close1) + end}, + #{desc => "close socket 1", + cmd => fun(#{sock1 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock1, State)} + end}, + + #{desc => "await continue (close2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close2) + end}, + #{desc => "close socket 2", + cmd => fun(#{sock2 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock2, State)} + end}, + + #{desc => "await continue (close3)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close3) + end}, + #{desc => "close socket 3", + cmd => fun(#{sock3 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock3, State)} + end}, + + #{desc => "await continue (close4)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close4) + end}, + #{desc => "close socket 4", + cmd => fun(#{sock4 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock4, State)} + end}, + + #{desc => "await continue (close5)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, close5) + end}, + #{desc => "close socket 5", + cmd => fun(#{sock5 := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock5, State)} + end}, + + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + #{desc => "await continue (socket)", + cmd => fun(#{tester := Tester} = State) -> + {ok, [Sock1, Sock2, Sock3, Sock4, Sock5]} = + ?SEV_AWAIT_CONTINUE(Tester, tester, socket), + ?SEV_IPRINT("Sockets: " + "~n 1: ~p" + "~n 2: ~p" + "~n 3: ~p" + "~n 4: ~p" + "~n 5: ~p", + [Sock1, Sock2, Sock3, Sock4, Sock5]), + {ok, State#{sock1 => Sock1, + sock2 => Sock2, + sock3 => Sock3, + sock4 => Sock4, + sock5 => Sock5}} + end}, + + + %% The actual test + #{desc => "await continue (monitor)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, monitor) + end}, + + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket", + cmd => fun(#{sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = State) -> + MRef1 = socket:monitor(Sock1), + MRef2 = socket:monitor(Sock2), + MRef3 = socket:monitor(Sock3), + MRef4 = socket:monitor(Sock4), + MRef5 = socket:monitor(Sock5), + ?SEV_IPRINT("Monitors: " + "~n 1: ~p" + "~n 2: ~p" + "~n 3: ~p" + "~n 4: ~p" + "~n 5: ~p", + [MRef1, MRef2, MRef3, MRef4, MRef5]), + {ok, State#{mon1 => MRef1, + mon2 => MRef2, + mon3 => MRef3, + mon4 => MRef4, + mon5 => MRef5}} + end}, + #{desc => "verify own number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(self()), + ok + end}, + + #{desc => "announce ready (monitor)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, monitor), + ok + end}, + + #{desc => "await continue (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, down) + end}, + + #{desc => "await socket 1 down", + cmd => fun(#{sock1 := Sock, + mon1 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon1, State), + State3 = maps:remove(sock1, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "announce ready (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, down), + ok + end}, + + #{desc => "await socket 2 down", + cmd => fun(#{sock2 := Sock, + mon2 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon2, State), + State3 = maps:remove(sock2, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "announce ready (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, down), + ok + end}, + + #{desc => "await socket 3 down", + cmd => fun(#{sock3 := Sock, + mon3 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon3, State), + State3 = maps:remove(sock3, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "announce ready (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, down), + ok + end}, + + #{desc => "await socket 4 down", + cmd => fun(#{sock4 := Sock, + mon4 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon4, State), + State3 = maps:remove(sock4, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "announce ready (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, down), + ok + end}, + + #{desc => "await socket 5 down", + cmd => fun(#{sock5 := Sock, + mon5 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon5, State), + State3 = maps:remove(sock5, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "announce ready (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, down), + ok + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, [Sock1, Sock2, Sock3, Sock4, Sock5]} = + ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock1 => Sock1, + sock2 => Sock2, + sock3 => Sock3, + sock4 => Sock4, + sock5 => Sock5}} + end}, + + #{desc => "monitor 'client 1'", + cmd => fun(#{client1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 1) start", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 1) ready", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, init) + end}, + #{desc => "send socket to client 1", + cmd => fun(#{client1 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + #{desc => "monitor 'client 2'", + cmd => fun(#{client2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 2) start", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 2) ready", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, init) + end}, + #{desc => "send socket to client 2", + cmd => fun(#{client2 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + #{desc => "monitor 'client 3'", + cmd => fun(#{client3 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 3) start", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 3) ready", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, init) + end}, + #{desc => "send socket to client 3", + cmd => fun(#{client3 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + #{desc => "monitor 'client 4'", + cmd => fun(#{client4 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 4) start", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 4) ready", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, init) + end}, + #{desc => "send socket to client 4", + cmd => fun(#{client4 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + #{desc => "monitor 'client 5'", + cmd => fun(#{client5 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 5) start", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 5) ready", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, init) + end}, + #{desc => "send socket to client 5", + cmd => fun(#{client5 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + + %% The actual test + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 1 to monitor", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 1) ready", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, monitor) + end}, + #{desc => "verify total number of monitors (=1*5)", + cmd => fun(_State) -> + 1*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 2 to monitor", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 2) ready", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, monitor) + end}, + #{desc => "verify total number of monitors (=2*5)", + cmd => fun(_State) -> + 2*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 3 to monitor", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 3) ready", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, monitor) + end}, + #{desc => "verify total number of monitors (=3*5)", + cmd => fun(_State) -> + 3*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 4 to monitor", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 4) ready", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, monitor) + end}, + #{desc => "verify total number of monitors (=4*5)", + cmd => fun(_State) -> + 4*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 5 to monitor", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 5) ready", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, monitor) + end}, + #{desc => "verify total number of monitors (=5*5)", + cmd => fun(_State) -> + 5*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 1 to await down", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 2 to await down", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 3 to await down", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 4 to await down", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 5 to await down", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order owner to close socket 1", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close1), + ok + end}, + #{desc => "await (client 1) down received", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, down) + end}, + #{desc => "await (client 2) down received", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, down) + end}, + #{desc => "await (client 3) down received", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, down) + end}, + #{desc => "await (client 4) down received", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, down) + end}, + #{desc => "await (client 5) down received", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, down) + end}, + #{desc => "verify total number of monitors (=5*4)", + cmd => fun(_State) -> + 5*4 = socket:number_of_monitors(), + ok + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order owner to close socket 2", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close2), + ok + end}, + #{desc => "await (client 1) down received", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, down) + end}, + #{desc => "await (client 2) down received", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, down) + end}, + #{desc => "await (client 3) down received", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, down) + end}, + #{desc => "await (client 4) down received", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, down) + end}, + #{desc => "await (client 5) down received", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, down) + end}, + #{desc => "verify total number of monitors (=5*3)", + cmd => fun(_State) -> + 5*3 = socket:number_of_monitors(), + ok + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order owner to close socket 3", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close3), + ok + end}, + #{desc => "await (client 1) down received", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, down) + end}, + #{desc => "await (client 2) down received", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, down) + end}, + #{desc => "await (client 3) down received", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, down) + end}, + #{desc => "await (client 4) down received", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, down) + end}, + #{desc => "await (client 5) down received", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, down) + end}, + #{desc => "verify total number of monitors (=5*2)", + cmd => fun(_State) -> + 5*2 = socket:number_of_monitors(), + ok + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order owner to close socket 4", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close4), + ok + end}, + #{desc => "await (client 1) down received", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, down) + end}, + #{desc => "await (client 2) down received", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, down) + end}, + #{desc => "await (client 3) down received", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, down) + end}, + #{desc => "await (client 4) down received", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, down) + end}, + #{desc => "await (client 5) down received", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, down) + end}, + #{desc => "verify total number of monitors (=5*1)", + cmd => fun(_State) -> + 5*1 = socket:number_of_monitors(), + ok + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order owner to close socket 5", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, close5), + ok + end}, + #{desc => "await (client 1) down received", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, down) + end}, + #{desc => "await (client 2) down received", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, down) + end}, + #{desc => "await (client 3) down received", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, down) + end}, + #{desc => "await (client 4) down received", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, down) + end}, + #{desc => "await (client 5) down received", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, down) + end}, + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + + ?SEV_SLEEP(?SECS(1)), + + %% Cleanup + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(owner, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 1) terminate", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 1) termination", + cmd => fun(#{client1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client1, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 2) terminate", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 2) termination", + cmd => fun(#{client2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client2, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 3) terminate", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 3) termination", + cmd => fun(#{client3 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client3, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 4) terminate", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 4) termination", + cmd => fun(#{client4 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client4, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 5) terminate", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 5) termination", + cmd => fun(#{client5 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client5, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start client 1 evaluator"), + Client1 = ?SEV_START("client-1", ClientSeq, InitState), + + i("start client 2 evaluator"), + Client2 = ?SEV_START("client-2", ClientSeq, InitState), + + i("start client 3 evaluator"), + Client3 = ?SEV_START("client-3", ClientSeq, InitState), + + i("start client 4 evaluator"), + Client4 = ?SEV_START("client-4", ClientSeq, InitState), + + i("start client 5 evaluator"), + Client5 = ?SEV_START("client-5", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid, + client1 => Client1#ev.pid, + client2 => Client2#ev.pid, + client3 => Client3#ev.pid, + client4 => Client4#ev.pid, + client5 => Client5#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Create several sockets (by 'owner' process), monitor each from several +%% different processes, then exit the owner. +%% The processes that did the monitor shall receive one socket DOWN for +%% each socket. + +monitor_open_and_exit_multi_socks_and_mon(suite) -> + []; +monitor_open_and_exit_multi_socks_and_mon(doc) -> + []; +monitor_open_and_exit_multi_socks_and_mon(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(monitor_open_and_exit_multi_socks_and_mon, + fun() -> + InitState = #{domain => inet, + type => stream, + protocol => tcp}, + ok = mon_open_and_exit_multi_socks_and_mon(InitState) + end). + + +mon_open_and_exit_multi_socks_and_mon(InitState) -> + OwnerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "create socket 1", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 2", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 3", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock3 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 4", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock4 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create socket 5", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Proto} = State) -> + case socket:open(Domain, Type, Proto) of + {ok, Sock} -> + {ok, State#{sock5 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_READY(Tester, init, Socks), + ok + end}, + + %% The actual test + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + #{desc => "await continue (socket)", + cmd => fun(#{tester := Tester} = State) -> + {ok, [Sock1, Sock2, Sock3, Sock4, Sock5]} = + ?SEV_AWAIT_CONTINUE(Tester, tester, socket), + ?SEV_IPRINT("Sockets: " + "~n 1: ~p" + "~n 2: ~p" + "~n 3: ~p" + "~n 4: ~p" + "~n 5: ~p", + [Sock1, Sock2, Sock3, Sock4, Sock5]), + {ok, State#{sock1 => Sock1, + sock2 => Sock2, + sock3 => Sock3, + sock4 => Sock4, + sock5 => Sock5}} + end}, + + + %% The actual test + #{desc => "await continue (monitor)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, monitor) + end}, + + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "monitor socket", + cmd => fun(#{sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = State) -> + MRef1 = socket:monitor(Sock1), + MRef2 = socket:monitor(Sock2), + MRef3 = socket:monitor(Sock3), + MRef4 = socket:monitor(Sock4), + MRef5 = socket:monitor(Sock5), + ?SEV_IPRINT("Monitors: " + "~n 1: ~p" + "~n 2: ~p" + "~n 3: ~p" + "~n 4: ~p" + "~n 5: ~p", + [MRef1, MRef2, MRef3, MRef4, MRef5]), + {ok, State#{mon1 => MRef1, + mon2 => MRef2, + mon3 => MRef3, + mon4 => MRef4, + mon5 => MRef5}} + end}, + #{desc => "verify own number of monitors (=5)", + cmd => fun(_State) -> + 5 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "announce ready (monitor)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, monitor), + ok + end}, + + #{desc => "await continue (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, down) + end}, + + #{desc => "await socket 1 down", + cmd => fun(#{sock1 := Sock, + mon1 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon1, State), + State3 = maps:remove(sock1, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + + #{desc => "await socket 2 down", + cmd => fun(#{sock2 := Sock, + mon2 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon2, State), + State3 = maps:remove(sock2, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + + #{desc => "await socket 3 down", + cmd => fun(#{sock3 := Sock, + mon3 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon3, State), + State3 = maps:remove(sock3, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + + #{desc => "await socket 4 down", + cmd => fun(#{sock4 := Sock, + mon4 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon4, State), + State3 = maps:remove(sock4, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + + #{desc => "await socket 5 down", + cmd => fun(#{sock5 := Sock, + mon5 := MRef} = State) -> + receive + {'DOWN', MRef, socket, Sock, Info} -> + ?SEV_IPRINT("received expected down: " + "~n MRef: ~p" + "~n Socket: ~p" + "~n Info: ~p", + [MRef, Sock, Info]), + State2 = maps:remove(mon5, State), + State3 = maps:remove(sock5, State2), + {ok, State3} + after 5000 -> + ?SEV_EPRINT("socket down timeout"), + {error, timeout} + end + end}, + #{desc => "verify own number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(self()), + ok + end}, + #{desc => "announce ready (down)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, down), + ok + end}, + + + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + 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) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (owner) ready", + cmd => fun(#{owner := Pid} = State) -> + {ok, [Sock1, Sock2, Sock3, Sock4, Sock5]} = + ?SEV_AWAIT_READY(Pid, owner, init), + {ok, State#{sock1 => Sock1, + sock2 => Sock2, + sock3 => Sock3, + sock4 => Sock4, + sock5 => Sock5}} + end}, + + #{desc => "monitor 'client 1'", + cmd => fun(#{client1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 1) start", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 1) ready", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, init) + end}, + #{desc => "send socket to client 1", + cmd => fun(#{client1 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + #{desc => "monitor 'client 2'", + cmd => fun(#{client2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 2) start", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 2) ready", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, init) + end}, + #{desc => "send socket to client 2", + cmd => fun(#{client2 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + #{desc => "monitor 'client 3'", + cmd => fun(#{client3 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 3) start", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 3) ready", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, init) + end}, + #{desc => "send socket to client 3", + cmd => fun(#{client3 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + #{desc => "monitor 'client 4'", + cmd => fun(#{client4 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 4) start", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 4) ready", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, init) + end}, + #{desc => "send socket to client 4", + cmd => fun(#{client4 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + #{desc => "monitor 'client 5'", + cmd => fun(#{client5 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "order (client 5) start", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await (client 5) ready", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, init) + end}, + #{desc => "send socket to client 5", + cmd => fun(#{client5 := Pid, + sock1 := Sock1, + sock2 := Sock2, + sock3 := Sock3, + sock4 := Sock4, + sock5 := Sock5} = _State) -> + Socks = [Sock1, Sock2, Sock3, Sock4, Sock5], + ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks), + ok + end}, + + + %% The actual test + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 1 to monitor", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 1) ready", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, monitor) + end}, + #{desc => "verify total number of monitors (=1*5)", + cmd => fun(_State) -> + 1*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 2 to monitor", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 2) ready", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, monitor) + end}, + #{desc => "verify total number of monitors (=2*5)", + cmd => fun(_State) -> + 2*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 3 to monitor", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 3) ready", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, monitor) + end}, + #{desc => "verify total number of monitors (=3*5)", + cmd => fun(_State) -> + 3*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 4 to monitor", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 4) ready", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, monitor) + end}, + #{desc => "verify total number of monitors (=4*5)", + cmd => fun(_State) -> + 4*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 5 to monitor", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, monitor), + ok + end}, + #{desc => "await (client 5) ready", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, monitor) + end}, + #{desc => "verify total number of monitors (=5*5)", + cmd => fun(_State) -> + 5*5 = socket:number_of_monitors(), + ok + end}, + + #{desc => "order client 1 to await down", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 2 to await down", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 3 to await down", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 4 to await down", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + #{desc => "order client 5 to await down", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, down), + ok + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order (owner) terminate", + cmd => fun(#{owner := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (owner) termination", + cmd => fun(#{owner := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(owner, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "await (client 1) ready", + cmd => fun(#{client1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client1, down) + end}, + #{desc => "await (client 2) ready", + cmd => fun(#{client2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client2, down) + end}, + #{desc => "await (client 3) ready", + cmd => fun(#{client3 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client3, down) + end}, + #{desc => "await (client 4) ready", + cmd => fun(#{client4 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client4, down) + end}, + #{desc => "await (client 5) ready", + cmd => fun(#{client5 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client5, down) + end}, + + #{desc => "verify total number of monitors (=0)", + cmd => fun(_State) -> + 0 = socket:number_of_monitors(), + ok + end}, + + ?SEV_SLEEP(?SECS(1)), + + %% Cleanup + #{desc => "order (client 1) terminate", + cmd => fun(#{client1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 1) termination", + cmd => fun(#{client1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client1, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 2) terminate", + cmd => fun(#{client2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 2) termination", + cmd => fun(#{client2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client2, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 3) terminate", + cmd => fun(#{client3 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 3) termination", + cmd => fun(#{client3 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client3, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 4) terminate", + cmd => fun(#{client4 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 4) termination", + cmd => fun(#{client4 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client4, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order (client 5) terminate", + cmd => fun(#{client5 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await (client 5) termination", + cmd => fun(#{client5 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + {ok, maps:remove(client5, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start (socket) owner evaluator"), + Owner = ?SEV_START("owner", OwnerSeq, InitState), + + i("start client 1 evaluator"), + Client1 = ?SEV_START("client-1", ClientSeq, InitState), + + i("start client 2 evaluator"), + Client2 = ?SEV_START("client-2", ClientSeq, InitState), + + i("start client 3 evaluator"), + Client3 = ?SEV_START("client-3", ClientSeq, InitState), + + i("start client 4 evaluator"), + Client4 = ?SEV_START("client-4", ClientSeq, InitState), + + i("start client 5 evaluator"), + Client5 = ?SEV_START("client-5", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{owner => Owner#ev.pid, + client1 => Client1#ev.pid, + client2 => Client2#ev.pid, + client3 => Client3#ev.pid, + client4 => Client4#ev.pid, + client5 => Client5#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Owner, Tester]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% %% SOCKET CLOSURE %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |