From 47612c2775df38050577d32837352006ba499a55 Mon Sep 17 00:00:00 2001 From: Jarvis Carroll Date: Sun, 26 Oct 2025 11:14:09 +0000 Subject: [PATCH] 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. --- src/msp_channel_man.erl | 48 +++++++++++++++++++++++++---------------- src/msp_router.erl | 14 ++++++------ src/msp_tests.erl | 9 +++----- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/msp_channel_man.erl b/src/msp_channel_man.erl index 7cd82ee..43b968d 100644 --- a/src/msp_channel_man.erl +++ b/src/msp_channel_man.erl @@ -9,7 +9,7 @@ -copyright("Jarvis Carroll "). -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 diff --git a/src/msp_router.erl b/src/msp_router.erl index d6e044d..4dc2773 100644 --- a/src/msp_router.erl +++ b/src/msp_router.erl @@ -9,7 +9,7 @@ -copyright("Jarvis Carroll "). -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, diff --git a/src/msp_tests.erl b/src/msp_tests.erl index ded193b..17bc03d 100644 --- a/src/msp_tests.erl +++ b/src/msp_tests.erl @@ -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">>),