embassy icon indicating copy to clipboard operation
embassy copied to clipboard

Features `time` and `executor-agnostic` don't work together

Open diondokter opened this issue 3 years ago • 3 comments
trafficstars

Timing data is stored in the task header, so in the function register_timer it tries to get the task header from the waker.

But this waker is not made by embassy and a panic is generated.

Is this a bug? Is this intentional or a "won't fix"? (If so, maybe it shouldn't compile or at least that should be documented)

What I'm running into is that I have an async library that can be used in embassy. It needs some timer things. Then I also want to write some tests using futures-test which then get the aforementioned panic.

diondokter avatar Jun 24 '22 09:06 diondokter

I can kinda do it if I make an embassy executor:

    #[test]
    fn main() {
        let res = std::panic::catch_unwind(|| {
            static EXECUTOR: Forever<Executor> = Forever::new();
            let executor = EXECUTOR.put(Executor::new());
            executor.run(|spawner| {
                spawner.spawn(main_loop()).unwrap();
            });
        });

        match res {
            Ok(_) => {}
            Err(e) => match e.downcast::<&str>() {
                Ok(message) if *message == "Done" => {},
                Ok(e) => std::panic::resume_unwind(e),
                Err(e) => std::panic::resume_unwind(e),
            },
        }
    }

But the executor will still run even if all tasks have returned, so the only way to stop the test is with a panic that you then catch which is not optimal...

diondokter avatar Jun 24 '22 10:06 diondokter

Fyi, for a similar use case of running embassy in a test, I made this: https://github.com/drogue-iot/ector/blob/main/ector/src/testutil.rs#L269-L361

in addition I created a custom macro so you could run a test being handed a context that would signal the runner to stop on drop.

If ppl are interested I could create some 'embassy-test' crate that would allow writing unit tests the same way, but I think ideally making this embassy-agnostic in the tests would be even better so one could reuse existing test frameworks from tokio etc.

lulf avatar Jun 24 '22 12:06 lulf

It is intentional that the current Timer implementation is tied to the executor. It keeps track of expirations per task, not per timer, which allows it to reuse the task queue as a timer queue, which means it's faster/smaller etc.

It's not "wontfix", I do believe in interoperability so I think it'd be great to get it working on any executor. It'd require a new from-scratch implementation, with a real "timer queue" where creating a Timer adds an entry, dropping it removes it, and something (perhaps a time driver alarm?) that watches for expirations and wakes the wakers. It will be slower/bigger than the current one, and more annoying to use (you'll have to specify the size of the timer queue upfront) so it should be opt-in, enabled by the feature executor-agnostic (just like the current AtomicWaker/WakerRegistration).

I won't work on it, but will happily mentor+review anyone who wants to try!

Dirbaio avatar Jun 24 '22 21:06 Dirbaio

embassy-time is now executor-agnostic by default. You can enable an executor agnostic queue with the embassy-time/generic-queue feature, or you can enable the embassy-executor-integrated one with embassy-executor/integrated-timers.

Dirbaio avatar Apr 09 '23 21:04 Dirbaio