diff --git a/radicle-node/src/client.rs b/radicle-node/src/client.rs index fd6b0da8..050fbcc2 100644 --- a/radicle-node/src/client.rs +++ b/radicle-node/src/client.rs @@ -11,7 +11,7 @@ use thiserror::Error; use crate::address; use crate::control; -use crate::crypto::Signature; +use crate::crypto::{Signature, Signer}; use crate::node::NodeId; use crate::service::{routing, tracking}; use crate::wire; @@ -55,26 +55,26 @@ pub enum Error { } /// Holds join handles to the client threads, as well as a client handle. -pub struct Runtime { +pub struct Runtime { pub id: NodeId, - pub handle: Handle, + pub handle: Handle, pub control: thread::JoinHandle>, - pub reactor: Reactor, + pub reactor: Reactor>, pub pool: WorkerPool, pub local_addrs: Vec, } -impl Runtime { +impl Runtime { /// Run the client. /// /// This function spawns threads. - pub fn with( + pub fn with( home: Home, config: service::Config, listen: Vec, proxy: net::SocketAddr, signer: G, - ) -> Result + ) -> Result, Error> where G: crypto::Signer + EcSign + Clone + 'static, { diff --git a/radicle-node/src/client/handle.rs b/radicle-node/src/client/handle.rs index 65cf953d..fba2c243 100644 --- a/radicle-node/src/client/handle.rs +++ b/radicle-node/src/client/handle.rs @@ -1,16 +1,19 @@ -use std::io::Write; +use std::io::{self, Write}; use std::os::unix::net::UnixStream; use std::sync::Arc; use crossbeam_channel as chan; +use cyphernet::EcSign; use thiserror::Error; +use crate::crypto::Signer; use crate::identity::Id; use crate::profile::Home; use crate::service; use crate::service::{CommandError, FetchLookup, QueryState}; use crate::service::{NodeId, Sessions}; use crate::wire; +use crate::worker::WorkerResp; /// An error resulting from a handle method. #[derive(Error, Debug)] @@ -50,12 +53,12 @@ impl From> for Error { } } -pub struct Handle { +pub struct Handle { pub(crate) home: Home, - pub(crate) controller: reactor::Controller, + pub(crate) controller: reactor::Controller>, } -impl Clone for Handle { +impl Clone for Handle { fn clone(&self) -> Self { Self { home: self.home.clone(), @@ -64,25 +67,27 @@ impl Clone for Handle { } } -impl Handle { - pub fn new(home: Home, controller: reactor::Controller) -> Self { +impl Handle { + pub fn new(home: Home, controller: reactor::Controller>) -> Self { Self { home, controller } } - pub fn wakeup(&mut self) -> Result<(), Error> { - // TODO: Handle channel disconnect error correctly. - // This just returns `BrokenPipe`. - self.controller.cmd(wire::Control::Wakeup)?; + pub fn worker_result(&mut self, resp: WorkerResp) -> Result<(), Error> { + match self.controller.cmd(wire::Control::Worker(resp)) { + Ok(()) => {} + Err(err) if err.kind() == io::ErrorKind::BrokenPipe => return Err(Error::NotConnected), + Err(err) => return Err(err.into()), + } Ok(()) } fn command(&self, cmd: service::Command) -> Result<(), Error> { - self.controller.cmd(wire::Control::Command(cmd))?; + self.controller.cmd(wire::Control::User(cmd))?; Ok(()) } } -impl radicle::node::Handle for Handle { +impl radicle::node::Handle for Handle { type Sessions = Sessions; type FetchLookup = FetchLookup; type Error = Error; diff --git a/radicle-node/src/service.rs b/radicle-node/src/service.rs index 43ac076a..76e6992d 100644 --- a/radicle-node/src/service.rs +++ b/radicle-node/src/service.rs @@ -526,7 +526,7 @@ where } } - pub fn fetch_complete(&mut self, result: FetchResult) { + pub fn repo_fetched(&mut self, result: FetchResult) { // TODO(cloudhead): handle completed job with service business logic // TODO: Downgrade session to gossip protocol. if let Some(session) = self.sessions.get_mut(result.remote()) { diff --git a/radicle-node/src/tests/e2e.rs b/radicle-node/src/tests/e2e.rs index 0c852bcd..aea1cbf6 100644 --- a/radicle-node/src/tests/e2e.rs +++ b/radicle-node/src/tests/e2e.rs @@ -36,7 +36,7 @@ struct NodeHandle { storage: Storage, addr: net::SocketAddr, thread: ManuallyDrop>>, - handle: ManuallyDrop, + handle: ManuallyDrop>, } impl Drop for NodeHandle { diff --git a/radicle-node/src/wire/protocol.rs b/radicle-node/src/wire/protocol.rs index 49d8ff91..ad638207 100644 --- a/radicle-node/src/wire/protocol.rs +++ b/radicle-node/src/wire/protocol.rs @@ -7,7 +7,7 @@ use std::os::unix::io::AsRawFd; use std::os::unix::prelude::RawFd; use std::sync::Arc; use std::time::{Duration, SystemTime}; -use std::{io, net}; +use std::{fmt, io, net}; use amplify::Wrapper as _; use crossbeam_channel as chan; @@ -31,10 +31,22 @@ use crate::worker::{WorkerReq, WorkerResp}; use crate::Link; use crate::{address, service}; -#[derive(Debug)] -pub enum Control { - Command(service::Command), - Wakeup, +#[allow(clippy::large_enum_variant)] +/// Control message used internally between workers, users, and the service. +pub enum Control { + /// Message from the user to the service. + User(service::Command), + /// Message from a worker to the service. + Worker(WorkerResp), +} + +impl fmt::Debug for Control { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::User(cmd) => cmd.fmt(f), + Self::Worker(resp) => resp.result.fmt(f), + } + } } /// Peer session type. @@ -48,7 +60,7 @@ pub type WireWriter = CypherWriter; type Action = reactor::Action>, NetTransport>>; /// Peer connection state machine. -enum Peer { +enum Peer { /// The initial state before handshake is completed. Connecting { link: Link }, /// The state after handshake is completed. @@ -68,14 +80,10 @@ enum Peer { id: NodeId, }, /// The peer is now upgraded and we are in control of the socket. - Upgraded { - link: Link, - id: NodeId, - response: chan::Receiver>, - }, + Upgraded { link: Link, id: NodeId }, } -impl std::fmt::Debug for Peer { +impl std::fmt::Debug for Peer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Connecting { link } => write!(f, "Connecting({:?})", link), @@ -91,7 +99,7 @@ impl std::fmt::Debug for Peer { } } -impl Peer { +impl Peer { /// Return the peer's id, if any. fn id(&self) -> Option<&NodeId> { match self { @@ -145,7 +153,7 @@ impl Peer { } /// Switch to upgraded state. - fn upgraded(&mut self, listener: chan::Receiver>) -> Fetch { + fn upgraded(&mut self) -> Fetch { if let Self::Upgrading { fetch, id, link } = self { let fetch = fetch.clone(); log::debug!(target: "wire", "Peer {id} upgraded for fetch {}", fetch.repo); @@ -153,7 +161,6 @@ impl Peer { *self = Self::Upgraded { id: *id, link: *link, - response: listener, }; fetch } else { @@ -187,7 +194,7 @@ pub struct Wire { /// Internal queue of actions to send to the reactor. actions: VecDeque>, /// Peer sessions. - peers: HashMap>, + peers: HashMap, /// SOCKS5 proxy address. proxy: net::SocketAddr, /// Buffer for incoming peer data. @@ -229,14 +236,14 @@ where self.actions.push_back(Action::RegisterListener(socket)); } - fn peer_mut_by_fd(&mut self, fd: RawFd) -> &mut Peer { + fn peer_mut_by_fd(&mut self, fd: RawFd) -> &mut Peer { self.peers.get_mut(&fd).unwrap_or_else(|| { log::error!(target: "wire", "Peer with fd {fd} was not found"); panic!("Peer with fd {fd} is not known"); }) } - fn fd_by_id(&self, node_id: &NodeId) -> (RawFd, &Peer) { + fn fd_by_id(&self, node_id: &NodeId) -> (RawFd, &Peer) { self.peers .iter() .find(|(_, peer)| peer.id() == Some(node_id)) @@ -292,8 +299,7 @@ where fn upgraded(&mut self, transport: NetTransport>) { let fd = transport.as_raw_fd(); let peer = self.peer_mut_by_fd(fd); - let (send, recv) = chan::bounded::>(1); - let fetch = peer.upgraded(recv); + let fetch = peer.upgraded(); let session = match transport.into_session() { Ok(session) => session, Err(_) => panic!("Transport::upgraded: peer write buffer not empty on upgrade"), @@ -305,7 +311,6 @@ where fetch, session, drain: self.read_queue.drain(..).collect(), - channel: send, }) .is_err() { @@ -313,21 +318,7 @@ where } } - fn wakeup(&mut self) { - let mut completed = Vec::new(); - for peer in self.peers.values() { - if let Peer::Upgraded { response, .. } = peer { - if let Ok(resp) = response.try_recv() { - completed.push(resp); - } - } - } - for resp in completed { - self.fetch_complete(resp); - } - } - - fn fetch_complete(&mut self, resp: WorkerResp) { + fn worker_result(&mut self, resp: WorkerResp) { log::debug!(target: "wire", "Fetch completed: {:?}", resp.result); let session = resp.session; @@ -351,7 +342,7 @@ where peer.downgrade(); self.actions.push_back(Action::RegisterTransport(session)); - self.service.fetch_complete(resp.result); + self.service.repo_fetched(resp.result); } } @@ -364,7 +355,7 @@ where { type Listener = NetAccept>; type Transport = NetTransport>; - type Command = Control; + type Command = Control; fn tick(&mut self, _time: Duration) { // FIXME: Change this once a proper timestamp is passed into the function. @@ -496,8 +487,8 @@ where fn handle_command(&mut self, cmd: Self::Command) { match cmd { - Control::Command(cmd) => self.service.command(cmd), - Control::Wakeup => self.wakeup(), + Control::User(cmd) => self.service.command(cmd), + Control::Worker(resp) => self.worker_result(resp), } } diff --git a/radicle-node/src/worker.rs b/radicle-node/src/worker.rs index 9c51803e..f7c03ba2 100644 --- a/radicle-node/src/worker.rs +++ b/radicle-node/src/worker.rs @@ -23,7 +23,6 @@ pub struct WorkerReq { pub fetch: Fetch, pub session: WireSession, pub drain: Vec, - pub channel: chan::Sender>, } /// Worker response. @@ -37,10 +36,10 @@ struct Worker { storage: Storage, tasks: chan::Receiver>, timeout: time::Duration, - handle: Handle, + handle: Handle, } -impl Worker { +impl Worker { /// Waits for tasks and runs them. Blocks indefinitely unless there is an error receiving /// the next task. fn run(mut self) -> Result<(), chan::RecvError> { @@ -55,7 +54,6 @@ impl Worker { fetch, session, drain, - channel, } = task; let (session, result) = self._process(&fetch, drain, session); @@ -71,10 +69,12 @@ impl Worker { }; log::debug!(target: "worker", "Sending response back to service.."); - if channel.send(WorkerResp { result, session }).is_err() { + if self + .handle + .worker_result(WorkerResp { result, session }) + .is_err() + { log::error!("Unable to report fetch result: worker channel disconnected"); - } else { - self.handle.wakeup().unwrap(); } } @@ -253,7 +253,7 @@ impl WorkerPool { timeout: time::Duration, storage: Storage, tasks: chan::Receiver>, - handle: Handle, + handle: Handle, name: String, ) -> Self { let mut pool = Vec::with_capacity(capacity);