Skip to content
This repository has been archived by the owner on Jul 6, 2019. It is now read-only.

RFC: static vs dynamic dispatch #297

Open
farcaller opened this issue Jun 1, 2015 · 1 comment
Open

RFC: static vs dynamic dispatch #297

farcaller opened this issue Jun 1, 2015 · 1 comment
Labels

Comments

@farcaller
Copy link
Member

I'd like to bring up the discussion on traits once again. Most of RTOSes I'm aware of that have any kind of HAL tend to use dynamic dispatch via function pointers to make the abstraction work with one notable exclusion being TinyOS, that uses a specific superset of C — nesC to make compile-time components binding possible.

In reality, it makes very good sense to use static dispatch as chances are that if you're bringing in a wifi hw and mac stack, you will have exactly one hardware for it and there's no need to do dynamic dispatch, you basically hard-code the appropriate hw layer calls in mac layer.

Problem is that static dispatch in rust is not exactly trivial. Here's the snippet from dht demo:

...

    single_task {
      loop = "run";
      args {
        timer = &timer;
        uart = &uart;
        dht = &dht;
      }
    }

...

#[zinc_task]
fn run(args: &pt::run_args) {

...

DHT is trait-bound over timer and GPIO:

/// Basic DHT22 driver ported over from Arduino example.
pub struct DHT22<'a, T:'a, P:'a> {
  gpio: &'a P,
  timer: &'a T,
}

Which makes PT args struct bound over them as well and makes run() function declaration filled with horrible implementation details, the problem that #[zinc_task] is solving by saving appropriate types in crate context and then modifying actual function arguments.

Problem is that it doesn't scale at all. You can't pass args.dht down the stack as you have absolutely no way to figure out its type, that is well hidden inside PT guts.

A way to "fix" it is to basically say: screw static dispatch. Makes code simpler to read and use, the only bounding requirements would be lifetimes, the added cost is indirect calls (up to two more instructions on arm?) and .data bloated with vtables.

@farcaller farcaller added the RFC label Jun 1, 2015
@mcoffin
Copy link
Contributor

mcoffin commented Jun 1, 2015

Ok, so I see a couple of problems here that could be causing all this complexity:

Drivers borrowing components instead of owning them

Drivers are borrowing their components, not owning them (unsafe? right?). This means that they're parameterized on lifetimes. If drivers own their components (as I think they should... as then instantiating 2 drivers with the same gpio pin will result in a compile time error without platformtree having to check it manually).

Drivers not implementing generic traits

Next, the reason that it's currently problematic to pass the DHT22 down the stack is that it should be. The driver should implement some trait (generic temperature sensor trait or or maybe even Readable). Then, if you wanted to pass it down the stack, you could do it like so:

fn do_something<R: Readable>(sensor: R) {
    // ...
}

This is doable because the end-user (app implementer) knows intuitively that his DHT22 sensor is Readable in the same way the he knows that led4 is a Gpio in the blink example. The above looks a lot nicer than

fn do_something<'a, T: Timer, P: UART>(sensor: DHT22<'a, T, P>) {
    // ...
}

If we start defining a lot more traits, I think the static dispatch will start coming much more naturally.

My opinion is that we should stick with static dispatch wherever possible, because that is the rust way. Zero cost feels awesome and I don't see the point in re-building the wheel when we have this powerful a generics system at our fingertips with which to create something new, exciting, and possibly more performant.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants