Skip to content

feat: Async 2.0

Feodor Alexandrov requested to merge non-blocking-select into master

Async resolving

struct CAresAddrInfo is a rust mirror of the C struct ares_getaddrinfo from ares.h and need to implement an automatic drop logic of the result after it converted to the Rust SocketAddr. For this conversion we use the additional type struct SocketAddress.

Memory which using as context of the ares_getaddrinfo request will also be dropped automatically when GetAddrInfo future completed.

pub async fn connect() {
...
	let sockaddr = SocketAddress::resolve(url, port).await?;    ---------.
	let stream = std::net::TcpStream::connect(sockaddr)                  |
					.map_err(Error::Connect)?;           |
...                                                                          |
}                                                                            |
                                                                             |
struct SocketAddress(*mut CAresAddrInfo, u16);                               |
impl ToSocketAddrs for SocketAddress {...}                                   |
                                                                             |
impl Drop for SocketAddress {                                                |
	...                                                                  |
	crate::ffi::tarantool::coio_ares_freeaddrinfo(self.0);               |
}                                                                            |
                                                                             |
impl SocketAddress {                                                         |
	pub async fn resolve() {     <---------------------------------------'  
		...
		let addrinfo = GetAddrInfo::from_url(&host).await?;  ------.
		...                                                        |
	}                                                                  |
}                                                                          |
                                                                           |
impl Future for GetAddrInfo {                                              | (calling the future)
	fn poll() {                                                        |
		...                                                        |
		addrinfo.memory = unsafe {                                 |
			crate::ffi::tarantool::coio_ares_getaddrinfo(      |
				addrinfo.host.as_ptr(),                    |
				&mut addrinfo.result as *mut _,            |
				addrinfo.err.as_ptr(),                     |
			)                                                  |
		};                                                         |
		...                                                        |
		return Poll::Ready(Ok(addrinfo.result));  <----------------'
	}
}

impl Drop for GetAddrInfo {
    fn drop(&mut self) {
        unsafe {
            crate::ffi::tarantool::coio_ares_request_drop(self.memory);
        }
    }
}

Async waiting

Wait events installs when the future processed by block_on calls the set_coio_wait which push new wait event to event vector stored in ContextExt. This vector stores the pointer to event memory inside. Then block_on initialize this pointer by the event memory allocation with coio_wait_event_alloc. When some future is ready in block_on the memory freed with coio_wait_event_free before Poll::Ready returns.

Timeout

Now deadline of the timeout wrapper for the block_on futures handles via libev timer which links to the fiber it was installed by and awake this fiber in its own callback on the C side.

Known problems

  1. connect still not async
  2. seems that timeout implementation can't broke current tests, but can work with only one fiber
  3. better it would be to implement coio events waiting without using ContextExt at all anyhow
Edited by Feodor Alexandrov

Merge request reports