Cross platform tests and examples for async network code now without having to TCP!

I just released a new crate:

It implements AsyncRead and AsyncWrite for a ring buffer, allowing to demonstrate and test network utilities like codecs without having to instantiate a TCP connection to localhost. This should avoid port binding issues and improve performance since no syscalls are needed.

When not using TCP people often use std::Cursor, but that cannot ever really represent both reading and writing ends of a stream with just one Seek impl. The nice thing about a ring buffer is now we can block readers when the buffer is empty and writes when the buffer is full.

By playing with the buffer size, you can properly test how your code handles the back-pressure.

It also works on WASM.

If you have data of a type that is Copy to communicate (in a producer/consumer fashion) between async tasks, you can also use this as a lock free communication channel.

Unfortunately docs.rs currently doesn't build the futures library, so there is no online API docs until they update to a more recent rustc version.

This is a basic example:

//! Frame a RingBuf with futures_codec. This example shows how the sending task will block when the buffer is full.
//! When a reader consumes the buffer, the sender is woken up.
//
#![feature(async_await)]

use
{
	futures_ringbuf :: { *                                            } ,
	futures         :: { SinkExt, StreamExt, executor::block_on, join } ,
	futures_codec   :: { Framed, LinesCodec                           } ,
};

fn main()
{
	let program = async
	{
		let mock = RingBuffer::new( 13 );
		let (mut writer, mut reader) = Framed::new( mock, LinesCodec{} ).split();


		let send_task = async move
		{
			writer.send( "Hello World\n".to_string() ).await.expect( "send" );
			println!( "sent first line" );

			writer.send( "Second line\n".to_string() ).await.expect( "send" );
			println!( "sent second line" );

			writer.close().await.expect( "close sender" );
			println!( "sink closed" );
		};


		let receive_task = async move
		{
			// If we would return here, the second line will never get sent because the buffer is full.
			//
			// return;

			while let Some(msg) = reader.next().await.transpose().expect( "receive message" )
			{
				println!( "Received: {:#?}", msg );
			}
		};


		// Poll them in concurrently
		//
		join!( send_task, receive_task );
	};

	block_on( program );
}
4 Likes