Gentian: a proc macro that transforms generators to state machines

doc: gentian - Rust
repo: GitHub - darsvador/generator: This crate provides a proof-of-concept proc macro attribute that allows transforming generators to state machines.
This crate provides a proof-of-concept proc macro attribute that allows transforming generators to state machines.

Motivation

Rust's async and await are cool. But when you manually implement Future/Stream, the problem comes. Either give up a certain performance to extend the lifetime of Future and poll it or manually maintain the state machine. When the logic of the poll is gradually complicated, the correctness of the state machine becomes more difficult to guarantee. Unstable Rust's standard library provides generator but it is not suitable for solving the above problems. In summary, this crate is to support using yield/yield return in a subset of rust's control flow. And, same as async and await, it will be compiled into state machines.

Example

    #[gentian]
    #[gentian_attr(ret_val=Err(ErrorKind::UnexpectedEof.into()).into())]
    pub fn poll_write_encrypted<W>(
        &mut self,
        ctx: &mut Context<'_>,
        w: &mut W,
        data: &[u8],
    ) -> Poll<io::Result<usize>>
    where
        W: AsyncWrite + Unpin,
    {
        loop {
            if data.len() == 0 {
                return Poll::Ready(Ok(0));
            }
            let mut minimal_data_to_write =
                cmp::min(CHUNK_SIZE - self.security.overhead_len(), data.len());
            let data = &data[..minimal_data_to_write];
            self.encrypted_buffer(data);
            loop {
                self.write_res = self.write_data(w, ctx);
                if self.write_res.is_ready() {
                    break;
                }
                co_yield(Poll::Pending);
            }
            self.buffer.clear();
            co_yield(std::mem::replace(&mut self.write_res, Poll::Pending));
        }
    }   

The above code would be expanded as:

// Recursive expansion of gentian! macro
// ======================================

pub fn poll_write_encrypted<W>(&mut self,ctx: &mut Context< '_> ,w: &mut W,data: &[u8],) -> Poll<io::Result<usize> >where W:AsyncWrite+Unpin,{
  'genloop:loop {
    match self.state {
      23 => {
        break 'genloop;
        
      }
      0 => {
        self.state = 4;
        if data.len()==0 {
          self.state = 2;
          continue 'genloop;
          
        }
      }
      2 => {
        self.state = 3;
        return Poll::Ready(Ok(0));
        
      }
      3 => {
        break 'genloop;
        
      }
      4 => {
        let mut minimal_data_to_write = cmp::min(CHUNK_SIZE-self.security.overhead_len(),data.len());
        let data =  &data[..minimal_data_to_write];
        self.encrypted_buffer(data);
        self.state = 5;
        
      }
      5 => {
        self.write_res = self.write_data(w,ctx);
        self.state = 8;
        if self.write_res.is_ready(){
          self.state = 6;
          continue 'genloop;
          
        }
      }
      6 => {
        self.buffer.clear();
        self.state = 0;
        return std::mem::replace(&mut self.write_res,Poll::Pending);
        
      }
      8 => {
        self.state = 5;
        return Poll::Pending;
        
      }
      _ => {
        break 'genloop;
        
      }
    
      }
  }return Err(ErrorKind::UnexpectedEof.into()).into();
  
}

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.