Skip to content
minip2p
Esc
navigateopen⌘Jpreview
On this page

Drive events

Choose between poll, next_event, and focused waits without losing unrelated application events.

minip2p remains caller-driven at every layer. The application chooses when to poll, how long to wait, and which event queue to consume. Read where events go first. If you are working with custom streams, also read Register a protocol.

Pick a driving method

Method Behavior Good fit
poll() Performs one non-blocking drive and returns all currently available application events. Existing game, UI, or reactor loops
next_event(deadline) Drives until one ordinary application event or the deadline. Simple synchronous command loops
next_wake(deadline) Drives until an application event, optional-capability progress, interruption, or the deadline. One loop handling ordinary and capability-specific events
wait_peer_ready Drives until one peer completes Identify. Connection setup
wait_ping_rtt Drives until one peer’s Ping finishes. Health checks and measurements
Feature-focused waits Drive until a NAT, pubsub, signed-discovery, or mDNS result. Feature-specific workflows

Deadline forms

Every endpoint wait accepts impl Into<Deadline>:

use std::time::{Duration, Instant};

use minip2p::Deadline;

let relative = Duration::from_secs(5);
let absolute = Instant::now() + Duration::from_secs(5);
let forever = Deadline::NEVER;

An expired deadline still permits the endpoint to inspect events that are already synchronously available, but it will not sleep or poll repeatedly past the deadline.

Build a straightforward event loop

use minip2p::{Deadline, Endpoint, Event};

fn run(mut node: Endpoint) -> Result<(), minip2p::Error> {
    while let Some(event) = node.next_event(Deadline::NEVER)? {
        match event {
            Event::PeerReady { peer_id, .. } => {
                println!("ready={peer_id}");
            }
            Event::Error(error) => {
                eprintln!("runtime error: {error:?}");
            }
            _ => {}
        }
    }
    Ok(())
}

Synchronous method failures are returned as minip2p::Error. Non-fatal problems that occur later while driving appear as Event::Error or a feature-specific failure event.

Focused waits preserve other events

Suppose wait_path is waiting for a NAT result while an application stream receives data. The stream event is retained and becomes available through a later next_event; it is not discarded by the NAT wait.

That retained backlog is bounded by RUN_UNTIL_SKIP_LIMIT (currently 1024 events). If a focused wait cannot find its result while unrelated events keep arriving, it returns:

minip2p::Error::EventBacklogExceeded { limit: RUN_UNTIL_SKIP_LIMIT }

Drain ordinary application events with next_event or poll, handle the high-volume source, and then retry the focused wait.

Stream and connection shutdown

  • Call close_stream_write after the final byte for a graceful half-close.
  • Call reset_stream when the peer should observe abrupt termination and the application still wants terminal events.
  • Call abandon_stream when no matching buffered or future events should be delivered.
  • Call disconnect to close the active peer connection.

Endpoint does not run a background shutdown sequence after it is dropped. Drive any graceful application-level close exchanges before leaving scope, then call endpoint.close()?. close consumes the endpoint, disconnects peers, and briefly drains the resulting events. Dropping also disconnects, but ignores errors.

With the mdns feature, endpoint.shutdown() has a narrower meaning: it sends mDNS goodbyes and stops mDNS while leaving QUIC and TCP usable.

Wait for every event family

next_event waits for ordinary endpoint events. Use next_wake when one loop also needs prompt access to NAT, pubsub, discovery, or relay-server events:

use minip2p::{Deadline, EndpointWake};

loop {
    match node.next_wake(Deadline::NEVER)? {
        EndpointWake::Event(event) => handle_event(event),
        EndpointWake::DriverProgress => {
            for event in node.take_pubsub_events() {
                handle_pubsub_event(event);
            }
        }
        EndpointWake::Deadline | EndpointWake::Interrupted => {}
    }
}

DriverProgress remains ready while any enabled capability queue contains events. Drain every enabled queue before calling next_wake again. Leaving one non-empty causes the next call to return immediately.

See Rust troubleshooting for common event symptoms and responses.

Last updated on September 7, 2026

Was this page helpful?