Basic question about swarm polling

Hello,

I am just getting started with libp2p, rust-libp2p specifically, and I am trying to figure out how to use the Kademlia distributed hash table for arbitrary key-value store, not for peer discovery. I am very closely following the example here and it is working very nicely for me. I understand how Transports are built and how custom Behaviours are implemented. I was also able to create a Swarm just like in the example, and I understand that. However, there is one aspect of all of this that I am slightly confused about…

Starting on line 153 is where I start to get confused. My main point of confusion is: Why does this code need to be in a future, and what is the point of the second loop.

  1. Why does this code need to be run in a future?
    So, I tried removing the future entirely and just making a loop that handles each line from stdin, but that didn’t work at all. It appears that the handle_input_line wasn’t working at all, and no data was stored or retrieved from the swarm’s Kad DHT. Why didn’t that work? Isn’t it the same thing as reading a line from stdin and then handling that line? I don’t get why the same code magically works when it’s being run inside a future. Also,

  2. I don’t see the point of the second loop
    It doesn’t look like the second loop does anything besides just print out the listening address… am I correct? Does this second loop block actually do anything crucial?

I’ve done a lot of experimenting, like removing the second loop and changing around how data is read from stdin with a hope of trying to understand the last part of this (otherwise really helpful) example code. But I still don’t get why the code works in a future.

So, are there any resources that you can point me to to help me understand this problem of mine?

Thanks

TL;DR: Why does reading from stdin need to be in a future (example code in link above)

Hey,

I think having a read over the async book: https://rust-lang.github.io/async-book/ and understanding rust futures may help out a bit here.

Let me start with “why does these need to be in a future?”

In rust, futures need to be poll’d. This means run on an executor or something that can drive the future to completion. You can make a future out of sub-future and build like a tree of futures, but the root future eventually needs to be driven by an executor. This is the same for stdin.

What is happening in the example, is that you are making a big future, composed of two smaller futures (reading from the stdin and driving the swarm).

stdin is a future. You can poll it via the try_poll_next_unpin() which will return Poll::Pending when there is no input. The loop reads all lines that are ready to be read and when there are none left and the future has nothing to return, the loop is exited.

Note again that we need an executor to poll this future. This is why it is run inside the task::block_on().

The future::poll_fn() is creating a big future of the smaller two futures (stdin and swarm) which will only ever complete when the swarm has finished (see line 146).

As for your second question, 2. - This is driving the entire libp2p stack. The swarm implements Stream and by polling it, you drive it to do work until it outputs events.
Notice that on line 145, it will print an event that gets emitted. Polling this stream will drive all the protocols and connections and when ready output swarm events.

When there is nothing to process, the task will idle and swarm will output Poll::Pending. In this instance, we take the opportunity to print out any listening addresses if we have not already done so, before returning Poll::Pending to the big future which puts the task “to sleep” until it is awoken again and needs to be processed.

The swarm creates a big future. It doesn’t do anything until you drive it with an executor.