When should I implement AsyncRead::poll_read_vectored and AsyncWrite::poll_write_vectored

I have a few public types that implement AsyncRead/Write, but I haven't looked at vectored IO yet. I read sometimes that it's bad that people forget to implement these methods because default impls are provided.

Unfortunately, the docs on futures lib don't tell you that and even less when it's appropriate to implement them.

As an example. I put AsyncRead/AsyncWrite on top of websockets.

In my WsStream, I use an algorithm taken from futures::IntoAsyncRead. Note that futures::IntoAsyncRead doesn't implement poll_read_vectored.

I buffer up to one websocket message. In poll_read, I try to get a message out of the underlying stream if I don't have one. Then I try to copy as much of it as I can into the buffer I got. I keep track of how far we read into the message in case it didn't fit entirely, then next time we will fit the rest.

However if the buffer is bigger than the message, currently I just don't fill it entirely (same as futures::IntoAsyncRead). So the first question is, should I poll the stream for the next message, knowing that it might just return pending, and that we cannot return pending here (as we already filled part of the buffer), so it will wake up the task even though we didn't return pending in poll_read.

Is such a spurious wakup a problem? Maybe just call with a dummy waker when we already have data to return?

Secondly then should I implement poll_read_vectored for this? I suppose I could fill several buffers, instead of just filling the first one like the default impl does, but that means either:

  • only do that when the ws message is to big to fit in the first buffer
  • solving the poll_read question above so that we can fill the buffers with data from several websocket messages? If it is fine to have spurious wakeups, why does the default impl not call poll_read several times in order to fill up all the buffers until poll_read returns pending? It could also do something like the dummy waker.

For poll_write_vector, the implementation itself is simpler. The websocket api just wants a Vec<u8> for the websocket message, so I could just allocate one big enough for the total of all vectors passed in and copy all the data in there.

So is it appropriate for such a layer on websockets to implement poll_write_vectored even though the actual networking layer doesn't really do vectored IO and I will copy all buffers into one Vec?

I just thought of another problem. If I poll the underlying stream twice, I might also get back an io::Error instead of Pending... So what to do with that. Buffer it until the next call to poll_read?

If the underlying IO primitive does not have special support for vectored read operations, you should probably just use the provided standard implementation that uses your ordinary read operation.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.