Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libp2p-swarm] Permit more controlled backpressure. #1586

Closed
wants to merge 14 commits into from
3 changes: 2 additions & 1 deletion protocols/noise/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ snow = { version = "0.6.1", features = ["ring-resolver"], default-features = fal
snow = { version = "0.6.1", features = ["default-resolver"], default-features = false }

[dev-dependencies]
async-std = "~1.5"
env_logger = "0.7.1"
libp2p-tcp = { version = "0.19.0", path = "../../transports/tcp" }
libp2p-tcp = { version = "0.19.0", path = "../../transports/tcp", features = ["async-std"] }
quickcheck = "0.9.0"
sodiumoxide = "^0.2.5"

Expand Down
75 changes: 69 additions & 6 deletions swarm/src/behaviour.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,57 @@ pub trait NetworkBehaviour: Send + 'static {
fn inject_connection_closed(&mut self, _: &PeerId, _: &ConnectionId, _: &ConnectedPoint)
{}

/// Informs the behaviour about an event generated by the handler dedicated to the peer identified by `peer_id`.
/// for the behaviour.
/// Informs the behaviour that one or more connections to a peer are
/// currently busy and unable to receive the given event which was
/// previously emitted by `NetworkBehaviour::poll`.
///
/// By calling this method, the `Swarm` tries to propagate backpressure
/// from the underlying `Network` to the `NetworkBehaviour`. If the
/// `NetworkBehaviour` is unable to re-schedule the given event to be
/// emitted on a later invocation of `NetworkBehaviour::poll`, it must
/// return an `Err` with the given event, in which case the `Swarm` will
/// buffer the event until it can be delivered or the connection(s) close.
/// Note that in this case, the `Swarm` will not call
/// `NetworkBehaviour::poll` again before the buffered event has been
/// consumed, resulting in backpressure on all network connections.
/// Implementing this method thus amounts to implementing peer and
/// connection-specific backpressure in a `NetworkBehaviour`.
///
/// By default, this method always returns an `Err`, indicating that
/// the behaviour cannot handle backpressure and leaves it to the
/// `Swarm`.
///
/// > **Note**: A given event should only be re-emitted by the behaviour
/// > after `NetworkBehaviour::poll` returned `Poll::Pending` at least once,
/// > to allow the underlying `Network` to make progress. Failure to do
/// > so is considered programmer error and may put the `Swarm` in a
/// > busy-loop or an unrecoverable infinite loop. Returning `Poll::Pending`
/// > at least once before trying to re-emit an event that could not be
/// > delivered to the `Network` previously is the essence of what it
/// > means to "try again later".
///
/// > **Note**: If the given event has been previously emitted with
/// > `NotifyHandler::All` and there are multiple connections to the peer,
/// > it may already have been delivered to some of these connections,
/// > in which case emitting the given event later with `NotifyHandler::All`
/// > again will result in duplicate event delivery as well as potentially
/// > sending the event to new connections. If that is undesirable,
/// > the behaviour can make sure to only re-emit the event for the
/// > connection ID(s) given to this method.
fn inject_connections_busy(
&mut self,
_: &PeerId,
_: impl Iterator<Item = ConnectionId>,
e: <<Self::ProtocolsHandler as IntoProtocolsHandler>::Handler as ProtocolsHandler>::InEvent
) -> Result<(), <<Self::ProtocolsHandler as IntoProtocolsHandler>::Handler as ProtocolsHandler>::InEvent>
{
Err(e)
}

/// Informs the behaviour about a network event on a connecton with a peer.
///
/// The `peer_id` is guaranteed to be in a connected state. In other words, `inject_connected`
/// has previously been called with this `PeerId`.
/// has previously been called with the same `PeerId`.
fn inject_event(
&mut self,
peer_id: PeerId,
Expand Down Expand Up @@ -149,10 +195,27 @@ pub trait NetworkBehaviour: Send + 'static {
fn inject_listener_closed(&mut self, _id: ListenerId, _reason: Result<(), &std::io::Error>) {
}

/// Polls for things that swarm should do.
/// Polls for behaviour to ask for the next instruction for the `Swarm`.
///
/// The behaviour is `poll`ed by the `Swarm` until it returns `Poll::Pending`,
/// which signals the `Swarm` that the `NetworkBehaviour` is waiting for
/// progress (and more events, see `inject_event`) from the underlying
/// `Network` before it may be `poll`ed again.
///
/// > **Note**: A `NetworkBehaviour` must eventually and repeatedly emit
/// > `Poll::Pending` to allow the `Network` to make progress. A
/// > `NetworkBehaviour` that never again emits `Poll::Pending` after a
/// > certain point is analogous to a `NetworkBehaviour` that no longer
/// > needs any input from the `Network` from that point onwards and is
/// > thus typically a programmer error.
///
/// This API mimics the API of the `Stream` trait. The method may register the current task in
/// order to wake it up at a later point in time.
/// > **Note**: When `poll`ed, a `NetworkBehaviour` may register the
/// > current task to be woken up with the given `Context`, if it wishes
/// > to be `poll`ed again even in the absence of any network activity.
/// > When `poll` returns `Poll::Pending` without registering the current
/// > task to be woken up, the behaviour is nevertheless always `poll`ed
/// > again when there is activity on the underlying `Network`, but not
/// > earlier.
fn poll(&mut self, cx: &mut Context, params: &mut impl PollParameters)
-> Poll<NetworkBehaviourAction<<<Self::ProtocolsHandler as IntoProtocolsHandler>::Handler as ProtocolsHandler>::InEvent, Self::OutEvent>>;
}
Expand Down
Loading