Skip to content

Conversation

@neotheprogramist
Copy link

Why

This PR adds support for the smol async runtime as an alternative to tokio, enabling users who prefer smol's lightweight, modular design to use libp2p without pulling in tokio dependencies.

Approach

Followed the existing tokio implementation patterns exactly:

  • Added SmolExecutor implementing the Executor trait using smol::spawn().detach()
  • Added Smol provider marker type and with_smol() builder method
  • Implemented TCP provider using async-io 2.0 for async I/O primitives
  • Implemented QUIC provider leveraging quinn's runtime-smol feature
  • Added feature flag cascade from top-level libp2p crate

Breaking Changes

None

Testing

# Verify TCP transport
cargo check -p libp2p --no-default-features --features smol,tcp

# Verify QUIC transport
cargo check -p libp2p --no-default-features --features smol,quic

# Verify combined
cargo check -p libp2p --features smol,tcp,quic

Usage

let swarm = SwarmBuilder::with_new_identity()
    .with_smol()  // New method!
    .with_tcp(tcp::Config::default(), noise::Config::new, yamux::Config::default)?
    .with_quic()
    .with_behaviour(|_| MyBehaviour)?
    .build();

Scope

  • ✅ SwarmBuilder integration (with_smol())
  • ✅ TCP transport (smol provider)
  • ✅ QUIC transport (smol provider)
  • ❌ DNS transport (deferred - hickory-resolver smol support unclear)
  • ❌ mDNS protocol (deferred - requires more complex timer abstractions)

🤖 Generated with Claude Code

neotheprogramist and others added 8 commits December 18, 2025 14:19
Add smol 2.0 as an optional dependency and implement SmolExecutor
to enable using the smol async runtime with libp2p-swarm.

Changes:
- Add smol 2.0 dependency with platform guards (excluding emscripten, wasi, unknown)
- Implement SmolExecutor that spawns futures using smol::spawn().detach()
- Add with_smol_executor() method to Config following tokio pattern
- Add smol feature flag to enable the executor

The implementation mirrors the existing TokioExecutor pattern with
identical platform exclusions and feature gating.
Add Smol provider type and with_smol() method to SwarmBuilder,
enabling users to configure libp2p with the smol async runtime.

Changes:
- Add Smol enum as runtime provider marker type
- Implement with_smol() method in ProviderPhase following tokio pattern
- Add swarm config macro invocation for Smol provider
- Use same platform guards as tokio (non-wasm32 only)

The implementation follows the existing type-state builder pattern
and will be wired up with the top-level smol feature flag in a
subsequent commit.
Implement smol runtime support for TCP transport using async-io.

Changes:
- Add async-io 2.0 as optional dependency for async I/O primitives
- Add smol feature flag that enables async-io and if-watch/smol
- Create provider/smol.rs module following tokio provider pattern
- Implement Provider trait for Tcp using async_io::Async wrappers
- Export smol module publicly when feature is enabled
- Add SwarmBuilder integration via impl_tcp_builder macro

The implementation mirrors the tokio provider architecture:
- TcpStream wraps async_io::Async<net::TcpStream>
- TcpListener wraps async_io::Async<net::TcpListener>
- IfWatcher uses if_watch::smol::IfWatcher
- Handles connection readiness via async-io's readable/writable API
Implement smol runtime support for QUIC transport using async-io.

Changes:
- Add async-io 2.0 as optional dependency
- Add smol feature flag that enables async-io, if-watch/smol, and quinn/runtime-smol
- Add Runtime::Smol variant to Runtime enum
- Create provider/smol.rs module following tokio provider pattern
- Implement Provider trait using if_watch::smol::IfWatcher
- Use async_io::Timer for sleep implementation
- Use async_io::Async for UDP socket operations
- Add SmolRuntime support in new_endpoint match arm
- Export smol module publicly when feature is enabled
- Add SwarmBuilder integration via impl_quic_builder macro

The implementation mirrors the tokio provider architecture and
leverages quinn's SmolRuntime for QUIC protocol support.
Add top-level smol feature flag that enables smol runtime support
across the libp2p ecosystem.

Changes:
- Add smol feature to libp2p Cargo.toml
- Cascade smol feature to libp2p-swarm (required)
- Cascade smol feature to libp2p-tcp (optional, when tcp feature enabled)
- Cascade smol feature to libp2p-quic (optional, when quic feature enabled)

The feature follows the same pattern as the existing tokio feature,
enabling users to opt into smol runtime support with a single feature
flag: `features = ["smol", "tcp", "quic"]`

This completes the smol runtime integration, providing full feature
parity with tokio for TCP and QUIC transports.
Add the missing poll_write_vectored method to the TCP smol provider's
AsyncWrite implementation for TcpStream. This enables vectored I/O
optimization matching the tokio provider implementation, preventing
fallback to multiple poll_write calls.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add compile_error! checks to prevent both `tokio` and `smol` features
from being enabled simultaneously in libp2p-swarm, libp2p-tcp, and
libp2p-quic crates.

This follows Cargo's recommended approach for handling mutually exclusive
features as documented in:
https://doc.rust-lang.org/cargo/reference/features.html#mutually-exclusive-features

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add macro impl_tcp_phase_with_quic to provide with_quic() and
with_quic_config() methods on TcpPhase for both Tokio and Smol
providers. This allows users to skip TCP and go directly to QUIC
when using the smol runtime.

Previously, with_quic() on TcpPhase was only available for Tokio,
causing a compilation error when trying to use:
  SwarmBuilder::with_smol().with_quic()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@tbu-
Copy link

tbu- commented Dec 19, 2025

Is there a reason to do this besides "it can be done"? Is there any large project that wants to forgo tokio? What's their reasoning?

@neotheprogramist
Copy link
Author

Is there a reason to do this besides "it can be done"? Is there any large project that wants to forgo tokio? What's their reasoning?

I need this for mobile development. Tokio uses its own futures implementation which doesn't integrate well with Swift/Kotlin/Dart async runtimes. Smol uses standard Rust futures, making cross-language FFI much simpler. This benefits the mobile ecosystem broadly.

Copy link
Member

@jxs jxs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, and thanks for the interest.

I need this for mobile development. Tokio uses its own futures implementation which doesn't integrate well with Swift/Kotlin/Dart async runtimes. Smol uses standard Rust futures, making cross-language FFI much simpler.

can you elaborate on this? Tokio runs std::future::Future implementations on its executor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants