jamesmunns / bbqueue

A SPSC, lockless, no_std, thread safe, queue, based on BipBuffers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

split_read() does not return new grant if release is not explicitly called

andresv opened this issue · comments

// queue has U50 elements
fn idle() {

    if let Ok(grant) = ctx.resources.uart_in_c.split_read() {
        // underlying circular buffer might have wrapped but we like to get continous buffer
        // so lets get both slices (second one is empty if no wrapping has occured)
        let (first_half, second_half) = grant.bufs();
        defmt::info!("f:{:usize} s:{:usize}", first_half.len(), second_half.len());

        // lets say we only consume data if there is '\r' somewhere
        if let Some(cr_pos) = first_half.iter().position(|n| *n == b'\r') {
            let len = grant.combined_len();
            grant.release(len);
        }
    }
}

#[task(binds = USART3_USART4_LPUART1,resources = [uart, uart_in_p, uart_timeout])]
fn usart_3_and_4_isr(mut ctx: usart_3_and_4_isr::Context) {

    if isr.rxne().bit_is_set() {
        let byte = uart4_ptr.rdr.read().bits() as u8;
        if let Ok(mut grant) = ctx.resources.uart_in_p.grant_exact(1) {
            defmt::warn!("r: {:u8}", byte);
            grant[0] = byte;
            grant.commit(1);
        }
        *ctx.resources.uart_timeout = Some(Instant::now() + 2.millis());
    }
}

Notice that consumer releases data only if it finds \r and does nothing if not. It is assumed that grant goes out of scope and everything is OK. However next calls to split_read() always fail and never give out new grant.

It can be fixed if grant.release(0) is explicitly called:

if let Some(cr_pos) = first_half.iter().position(|n| *n == b'\r') {
    let len = grant.combined_len();
    grant.release(len);
} else {
    grant.release(0);
}

I remember that normal read did not have such requirement, so it must be a bug in fairly new split_read API.

CC @Sympatron, who implemented split_read, I'll try to look at this as soon as possible.

@andresv if you have time, would you mind adding this case as a (failing) unit test? That way it would be easier to demonstrate that we've resolved this. I think you should be able to replicate this in a single thread if it is a logic error (if not we have bigger problems :) )

#[test]
fn split_read_sanity_check() {
    let bb: BBBuffer<U6> = BBBuffer::new();
    let (mut prod, mut cons) = bb.try_split().unwrap();

    const ITERS: usize = 100000;

    for i in 0..ITERS {
        let j = (i & 255) as u8;

        #[cfg(feature = "extra-verbose")]
        println!("===========================");
        #[cfg(feature = "extra-verbose")]
        println!("INDEX: {:?}", j);
        #[cfg(feature = "extra-verbose")]
        println!("===========================");

        #[cfg(feature = "extra-verbose")]
        println!("START: {:?}", bb);

        let mut wgr = prod.grant_exact(1).unwrap();

        #[cfg(feature = "extra-verbose")]
        println!("GRANT: {:?}", bb);

        wgr[0] = j;

        #[cfg(feature = "extra-verbose")]
        println!("WRITE: {:?}", bb);

        wgr.commit(1);

        #[cfg(feature = "extra-verbose")]
        println!("COMIT: {:?}", bb);

        // THIS CAUSES CRASH WHEN split_read() IS CALLED AGAIN
        // panicked at 'called `Result::unwrap()` on an `Err` value: GrantInProgress'
        let rgr = cons.split_read().unwrap();
        drop(rgr);

        #[cfg(feature = "extra-verbose")]
        println!("READ : {:?}", bb);

        let rgr = cons.split_read().unwrap();
        let (first, second) = rgr.bufs();
        if first.len() == 1 {
            assert_eq!(first[0], j);
        }else if second.len() == 1 {
            assert_eq!(second[0], j);
        } else {
            assert!(false, "wrong len");
        }

        #[cfg(feature = "extra-verbose")]
        println!("RELSE: {:?}", bb);

        rgr.release(1);

        #[cfg(feature = "extra-verbose")]
        println!("FINSH: {:?}", bb);
    }
}

Drop is not implemented for SplitGrantR this is probably the issue here.