Skip to content

Instantly share code, notes, and snippets.

@RoadRunnr RoadRunnr/stest.escript
Last active Dec 10, 2019

Embed
What would you like to do?
#!/usr/bin/env escript
%% -*- erlang -*-
%%! +sbtu +A1
-mode(compile).
-export([init_server/1]).
-define(UNIT, nanosecond).
%%-define(UNIT, millisecond).
main(_) ->
{ok, Server, SockAddr} = start_server(),
io:format("Server Pid ~p~nServer Addr ~p~n", [Server, SockAddr]),
{ok, Socket} = gen_udp:open(0, [binary, inet, {active, false}]),
Load = [proc_lib:spawn_link(fun busy_loop/0) ||
_ <- lists:seq(0, erlang:system_info(schedulers) * 2)],
Stats = ping(Server, Socket, SockAddr, 1000, []),
io:format(" round trip Clnt/Srvr Srvr/Clnt ProcPing~n"),
[io:format("~8w: ~8w ns, ~8w ns, ~8w ns, ~8w ns~n",
[Cnt, RecvTS - SendTS, RecvTS - SrvTS,
SrvTS - SendTS, PingTS - RecvTS]) ||
{Cnt, RecvTS, SendTS, SrvTS, PingTS} <- Stats],
[P ! stop || P <- Load],
Server ! stop.
ping(Server) ->
erlang:send(Server, {ping, self()}, [noconnect]),
receive
pong -> ok
after 2000 ->
io:format("ping timeout~n")
end.
ping(_Server, _Socket, _Dest, 0, Stats) ->
lists:reverse(Stats);
ping(Server, Socket, #{addr := Addr, port := Port} = Dest, Cnt, Stats0) ->
%%timer:sleep(rand:uniform(1000)),
%%timer:sleep(2000),
SendTS = erlang:monotonic_time(?UNIT),
Fill = binary:list_to_bin(lists:duplicate(1000, 0)),
Data = term_to_binary({Cnt, SendTS, Fill}),
ping(Server),
ok = gen_udp:send(Socket, Addr, Port, Data),
ping(Server),
{ok, {_, _, Answer}} = gen_udp:recv(Socket, 0, infinity),
erlang:send(Server, hi, [noconnect]),
RecvTS = erlang:monotonic_time(?UNIT),
Mref = erlang:monitor(process, Server),
ping(Server),
erlang:demonitor(Mref, [flush]),
PingTS = erlang:monotonic_time(?UNIT),
{Cnt, _, SrvTS, _} = binary_to_term(Answer),
Stats = [{Cnt, RecvTS, SendTS, SrvTS, PingTS}|Stats0],
ping(Server, Socket, Dest, Cnt - 1, Stats).
busy_loop() ->
receive
stop ->
ok
after
1 ->
busy_loop()
end.
start_server() ->
{ok, _, _} = proc_lib:start_link(?MODULE, init_server, [self()]).
init_server(Parent) ->
{ok, Socket} = socket:open(inet, dgram, udp),
{ok, _} = socket:bind(Socket, loopback),
{ok, SockAddr} = socket:sockname(Socket),
proc_lib:init_ack(Parent, {ok, self(), SockAddr}),
server_loop(Socket).
server_loop(Socket) ->
server_loop(Socket, 10).
server_loop(Socket, 0) ->
self() ! {'$socket', Socket, select, undefined},
select_loop(Socket);
server_loop(Socket, BusyCnt) ->
case socket:recvfrom(Socket, 0, [], nowait) of
{ok, {Source, Data}} ->
SrvTS = erlang:monotonic_time(?UNIT),
{Cnt, SendTS, Fill} = binary_to_term(Data),
Answer = term_to_binary({Cnt, SendTS, SrvTS, Fill}),
socket:sendto(Socket, Answer, Source, [], nowait),
server_loop(Socket, BusyCnt - 1);
{select, _} ->
select_loop(Socket);
{error, Reason} ->
exit(Reason)
end.
select_loop(Socket) ->
receive
hi ->
select_loop(Socket);
{ping, Pid} ->
Pid ! pong,
select_loop(Socket);
stop ->
ok;
{'$socket', Socket, abort, _} ->
io:format("Socket Error"),
ok;
{'$socket', Socket, select, _} ->
server_loop(Socket)
end.
@bmk

This comment has been minimized.

Copy link

bmk commented Oct 24, 2019

It looks like you have both proc's running in the same VM.
Have you tried it in different VMs?

/BMK

@RoadRunnr

This comment has been minimized.

Copy link
Owner Author

RoadRunnr commented Oct 25, 2019

No. But why would it matter if it does or does not work with two VMs? Using socket.erl should not break Erlang message passing in either case, yet it clearly does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.