have channel_man spawn its own router

Once I added dispatch_fallback to channel_man, it became clear that
the router is actually subordinate to the channel_man.
This commit is contained in:
Jarvis Carroll 2025-10-26 11:14:09 +00:00
parent cfd9eb748f
commit 47612c2775
3 changed files with 39 additions and 32 deletions

View File

@ -9,7 +9,7 @@
-copyright("Jarvis Carroll <spiveehere@gmail.com>").
-license("MIT").
-export([update_router/5, create_channel/3, dispatch_fallback/5]).
-export([add_route/4, create_channel/3, dispatch_fallback/5]).
%% Worker interface
-export([enroll/0]).
%% gen_server
@ -41,9 +41,17 @@
%%% Service Interface
update_router(OurPort, TheirIP, TheirPort, Router, Sock) ->
Route = #route{from = OurPort, to = {TheirIP, TheirPort}},
gen_server:cast(?MODULE, {update_router, Route, Router, Sock}).
% A pretty complex function for an interface, but we need to transfer ownership
% of the socket.
add_route(Sock, TheirIP, TheirPort, Side) ->
% Transfer the socket to the manager. We will then transfer it on to the
% router, once it is spawned.
case gen_udp:controlling_process(Sock, whereis(?MODULE)) of
ok ->
gen_server:cast(?MODULE, {add_route, Sock, {TheirIP, TheirPort}, Side});
{error, Reason} ->
{error, Reason}
end.
create_channel(OurPort, TheirIP, TheirPort) ->
Route = #route{from = OurPort, to = {TheirIP, TheirPort}},
@ -89,8 +97,8 @@ handle_call(Unexpected, From, State) ->
{noreply, State}.
handle_cast({update_router, Route, Router, Sock}, State) ->
NewState = do_update_router(Route, Router, Sock, State),
handle_cast({add_route, Sock, Route, Side}, State) ->
NewState = do_add_route(Sock, Route, Side, State),
{noreply, NewState};
handle_cast({dispatch, Route, ID, Packet}, State) ->
NewState = do_dispatch(Route, ID, Packet, State),
@ -149,19 +157,21 @@ do_enroll2(PID, State, Channels) ->
State
end.
do_update_router(Route, Router, Sock, State = #s{connections = Conns}) ->
case maps:find(Route, Conns) of
{ok, _Info} ->
io:format("Updating routes not yet implemented.~n", []),
init:stop();
error ->
NewConn = #route_info{router = Router,
socket = Sock,
unused_channels = lists:seq(0, 255),
used_channels = #{}},
NewConns = maps:put(Route, NewConn, Conns),
State#s{connections = NewConns}
end.
do_add_route(Sock, Peer, Side, State = #s{connections = Conns}) ->
{ok, OurPort} = inet:port(Sock),
Route = #route{from = OurPort, to = Peer},
% Check that this route isn't registered already...
% TODO: Maybe detect this case before we transfer the socket? It's all
% annoying though.
false = maps:is_key(Route, Conns),
{ok, Router} = msp_router:start_link(),
ok = msp_router:begin_routing(Router, Sock, Peer, Side),
NewConn = #route_info{router = Router,
socket = Sock,
unused_channels = lists:seq(0, 255),
used_channels = #{}},
NewConns = maps:put(Route, NewConn, Conns),
State#s{connections = NewConns}.
do_create_channel(State = #s{connections = Conns}, Route) ->
case maps:find(Route, Conns) of

View File

@ -9,7 +9,7 @@
-copyright("Jarvis Carroll <spiveehere@gmail.com>").
-license("MIT").
-export([begin_msp/5]).
-export([begin_routing/4]).
%% gen_server
-export([start_link/0]).
@ -38,13 +38,13 @@
% msp does not negotiate new connections, but once you have a
% socket, and a host that knows you will be talking MSP, then a
% new msp_router can be initialized.
begin_msp(Connection, OurSock, TheirIP, TheirPort, OurSide) ->
begin_routing(Router, OurSock, {TheirIP, TheirPort}, OurSide) ->
% Transfer the socket to the gen_server. If it is
% active then a bunch of messages will be received
% at once, but that is fine.
case gen_udp:controlling_process(OurSock, Connection) of
case gen_udp:controlling_process(OurSock, Router) of
ok ->
gen_server:cast(Connection, {begin_msp, OurSock, {TheirIP, TheirPort}, OurSide});
gen_server:cast(Router, {begin_routing, OurSock, {TheirIP, TheirPort}, OurSide});
{error, Reason} ->
{error, Reason}
end.
@ -69,8 +69,8 @@ handle_call(Unexpected, From, State) ->
{reply, undef, State}.
handle_cast({begin_msp, Sock, Peer, Side}, none) ->
State = do_begin_msp(Sock, Peer, Side),
handle_cast({begin_routing, Sock, Peer, Side}, none) ->
State = do_begin_routing(Sock, Peer, Side),
{noreply, State};
handle_cast(Unexpected, State) ->
ok = log(warning, "Unexpected cast: ~tp", [Unexpected]),
@ -104,7 +104,7 @@ terminate(_, _) ->
%%% Doer Functions
do_begin_msp(Sock, Peer, Side) ->
do_begin_routing(Sock, Peer, Side) ->
ok = inet:setopts(Sock, [{active, once}, {mode, binary}]),
State = #s{socket = Sock,
peer = Peer,

View File

@ -23,17 +23,14 @@ run_tests() ->
make_connection(OurPort, TheirIP, TheirPort, Side) ->
{ok, Sock} = gen_udp:open(OurPort),
{ok, Pid} = msp_connection:start_link(),
ok = msp_connection:begin_msp(Pid, Sock, TheirIP, TheirPort, Side),
ok = msp_channel_man:update_router(OurPort, TheirIP, TheirPort, Pid, Sock),
{Pid, Sock}.
ok = msp_channel_man:add_route(Sock, TheirIP, TheirPort, Side).
send_test() ->
IP = {127, 0, 0, 1},
PortA = 5555,
PortB = 6666,
{A, SockA} = make_connection(PortA, IP, PortB, 0),
{B, SockB} = make_connection(PortB, IP, PortA, 1),
ok = make_connection(PortA, IP, PortB, 0),
ok = make_connection(PortB, IP, PortA, 1),
{ok, {ChanA, IndexA}} = msp_channel_man:create_channel(PortA, IP, PortB),
msp_channel:send_and_close(ChanA, <<"message sent from A to B">>),