Clarify whether non-blocking or stateless is the more important aspect of nb

Open
#13 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
25/100
Issue type
Documentation
Clarity
Needs clarification
Activity status
Stale
Tech stack
rust
Domain
embedded-iot

Research direction

Start by reading the linked comment on pull request #12 and compare the two embedded_hal::serial::Write implementations shown for the nRF51. Determine whether nb::Result prioritizes non-blocking behavior or avoiding program-held state, then document the agreed semantics and examples once the design question is resolved.

Written by the indexing model from the issue text.

Description

discussion

In reference to @japaric's comment here.

In my mind operations that return nb::Result signal that they can't be started and completed right
now via WouldBlock but once you get an Ok you know that the operation started and completed in one step, without blocking. This also means that operations that return nb::Result carry no program
state
-- there may be some state in the hardware registers or in the kernel but it's not on the
program stack / heap.

Unfortunately this is not always possible, as an example consider the embedded_hal::serial::Write implementation from the nrf51_hal crate:

pub struct Tx<UART> {
    _uart: PhantomData<UART>,
}

impl hal::serial::Write<u8> for Tx<UART0> {
    type Error = !;

    fn flush(&mut self) -> nb::Result<(), !> {
        Ok(())
    }

    fn write(&mut self, byte: u8) -> nb::Result<(), !> {
        /* Write one 8bit value */
        unsafe { (*UART0::ptr()).txd.write(|w| w.bits(u32::from(byte))) }

        /* Wait until written ... */
        while unsafe { (*UART0::ptr()).events_txdrdy.read().bits() } == 0 {}

        /* ... and clear read bit, there's no other way this will work */
        unsafe { (*UART0::ptr()).events_txdrdy.write(|w| w.bits(0)) };
        Ok(())
    }
}

This is currently a blocking implementation. To convert it to a non-blocking implementation we need to introduce some state of whether we're currently transmitting a byte (as far as I can tell, there is no way to derive this from the microcontroller registers):

pub struct Tx<UART> {
    _uart: PhantomData<UART>,
    busy: bool,
}

impl hal::serial::Write<u8> for Tx<UART0> {
    type Error = !;

    fn flush(&mut self) -> nb::Result<(), !> {
        let uart = unsafe { &*UART0::ptr() };
        if self.busy {
            if uart.events_txdrdy.read().bits() == 1 {
                uart.events_txdrdy.reset();
                self.busy = false;
                Ok(())
            } else {
                Err(nb::Error::WouldBlock)
            }
        } else {
            Ok(())
        }
    }

    fn write(&mut self, byte: u8) -> nb::Result<(), !> {
        let uart = unsafe { &*UART0::ptr() };
        self.flush()?;
        uart.txd.write(|w| unsafe { w.bits(u32::from(byte)) });
        self.busy = true;
        Ok(())
    }
}

In my mind the whole purpose of nb is to support implementing non-blocking IO primitives suitable for integrating into something like a futures event loop (although, I'm still trying to work out how to support interrupt-driven task notifications without having to have event loop specific implementations anyway...). If implementations are going to be forced to block to not have a tiny bit of important state, that undermines the entire utility of this crate.

Once this is clarified I will try and open a PR documenting this along with other stuff mentioned in that comment.

cc @therealprof (in case you have some other ideas about non-blocking uart support on the nrf51)

Dominant language
Rust
Stars
109
Forks
18
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from rust-embedded/nb

All issues in rust-embedded/nb

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.