From a0f1b0ee01d461607660d2d56b5b1bdc57e0d3fb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 29 Jul 2022 21:58:35 +0200 Subject: Split embassy crate into embassy-executor, embassy-util. --- .github/workflows/rust.yml | 2 +- .vscode/settings.json | 5 + README.md | 12 +- ci.sh | 50 +- ci_stable.sh | 74 +-- docs/modules/ROOT/examples/basic/Cargo.toml | 2 +- docs/modules/ROOT/examples/basic/src/main.rs | 8 +- .../ROOT/examples/layer-by-layer/Cargo.toml | 2 +- .../layer-by-layer/blinky-async/Cargo.toml | 2 +- .../layer-by-layer/blinky-async/src/main.rs | 4 +- embassy-boot/boot/Cargo.toml | 2 +- embassy-boot/nrf/Cargo.toml | 2 +- embassy-boot/stm32/Cargo.toml | 2 +- embassy-cortex-m/Cargo.toml | 3 +- embassy-cortex-m/src/executor.rs | 8 +- embassy-embedded-hal/Cargo.toml | 2 +- embassy-embedded-hal/src/shared_bus/asynch/i2c.rs | 8 +- embassy-embedded-hal/src/shared_bus/asynch/spi.rs | 8 +- .../src/shared_bus/blocking/i2c.rs | 6 +- .../src/shared_bus/blocking/spi.rs | 6 +- embassy-executor/Cargo.toml | 70 +++ embassy-executor/build.rs | 29 + embassy-executor/src/executor/arch/cortex_m.rs | 59 ++ embassy-executor/src/executor/arch/riscv32.rs | 74 +++ embassy-executor/src/executor/arch/std.rs | 84 +++ embassy-executor/src/executor/arch/wasm.rs | 74 +++ embassy-executor/src/executor/arch/xtensa.rs | 75 +++ embassy-executor/src/executor/mod.rs | 44 ++ embassy-executor/src/executor/raw/mod.rs | 433 +++++++++++++++ embassy-executor/src/executor/raw/run_queue.rs | 74 +++ embassy-executor/src/executor/raw/timer_queue.rs | 85 +++ embassy-executor/src/executor/raw/util.rs | 33 ++ embassy-executor/src/executor/raw/waker.rs | 53 ++ embassy-executor/src/executor/spawner.rs | 202 +++++++ embassy-executor/src/fmt.rs | 228 ++++++++ embassy-executor/src/lib.rs | 22 + embassy-executor/src/time/delay.rs | 98 ++++ embassy-executor/src/time/driver.rs | 170 ++++++ embassy-executor/src/time/driver_std.rs | 208 +++++++ embassy-executor/src/time/driver_wasm.rs | 134 +++++ embassy-executor/src/time/duration.rs | 184 +++++++ embassy-executor/src/time/instant.rs | 159 ++++++ embassy-executor/src/time/mod.rs | 91 +++ embassy-executor/src/time/timer.rs | 151 +++++ embassy-hal-common/Cargo.toml | 5 +- embassy-hal-common/src/lib.rs | 10 - embassy-lora/Cargo.toml | 7 +- embassy-lora/src/lib.rs | 2 +- embassy-lora/src/stm32wl/mod.rs | 2 +- embassy-lora/src/sx127x/sx127x_lora/mod.rs | 2 +- .../src/macros/cortex_m_interrupt_take.rs | 8 +- embassy-macros/src/macros/main.rs | 4 +- embassy-macros/src/macros/task.rs | 2 +- embassy-net/Cargo.toml | 5 +- embassy-net/src/stack.rs | 4 +- embassy-nrf/Cargo.toml | 11 +- embassy-nrf/src/buffered_uarte.rs | 14 +- embassy-nrf/src/gpiote.rs | 2 +- embassy-nrf/src/qdec.rs | 2 +- embassy-nrf/src/qspi.rs | 2 +- embassy-nrf/src/rng.rs | 2 +- embassy-nrf/src/saadc.rs | 2 +- embassy-nrf/src/spim.rs | 2 +- embassy-nrf/src/temp.rs | 2 +- embassy-nrf/src/time_driver.rs | 8 +- embassy-nrf/src/timer.rs | 6 +- embassy-nrf/src/twim.rs | 6 +- embassy-nrf/src/uarte.rs | 2 +- embassy-nrf/src/usb.rs | 2 +- embassy-rp/Cargo.toml | 5 +- embassy-rp/src/gpio.rs | 2 +- embassy-rp/src/timer.rs | 8 +- embassy-stm32/Cargo.toml | 11 +- embassy-stm32/src/dcmi.rs | 2 +- embassy-stm32/src/dma/bdma.rs | 2 +- embassy-stm32/src/dma/dma.rs | 2 +- embassy-stm32/src/dma/gpdma.rs | 2 +- embassy-stm32/src/eth/v1/mod.rs | 2 +- embassy-stm32/src/eth/v2/mod.rs | 2 +- embassy-stm32/src/exti.rs | 2 +- embassy-stm32/src/i2c/v2.rs | 2 +- embassy-stm32/src/rng.rs | 2 +- embassy-stm32/src/sdmmc/mod.rs | 6 +- embassy-stm32/src/subghz/mod.rs | 2 +- embassy-stm32/src/subghz/timeout.rs | 4 +- embassy-stm32/src/subghz/tx_params.rs | 18 +- embassy-stm32/src/time_driver.rs | 10 +- embassy-stm32/src/usart/buffered.rs | 2 +- embassy-stm32/src/usb/usb.rs | 4 +- embassy-usb-hid/Cargo.toml | 4 +- embassy-usb-hid/src/lib.rs | 23 +- embassy-usb-ncm/Cargo.toml | 2 +- embassy-usb-serial/Cargo.toml | 2 +- embassy-usb-serial/src/lib.rs | 2 +- embassy-usb/Cargo.toml | 2 +- embassy-usb/src/lib.rs | 2 +- embassy-util/Cargo.toml | 28 + embassy-util/build.rs | 29 + embassy-util/src/blocking_mutex/mod.rs | 189 +++++++ embassy-util/src/blocking_mutex/raw.rs | 149 +++++ embassy-util/src/channel/mod.rs | 5 + embassy-util/src/channel/mpmc.rs | 596 ++++++++++++++++++++ embassy-util/src/channel/pubsub/mod.rs | 542 ++++++++++++++++++ embassy-util/src/channel/pubsub/publisher.rs | 182 ++++++ embassy-util/src/channel/pubsub/subscriber.rs | 152 +++++ embassy-util/src/channel/signal.rs | 99 ++++ embassy-util/src/fmt.rs | 228 ++++++++ embassy-util/src/forever.rs | 95 ++++ embassy-util/src/lib.rs | 22 + embassy-util/src/mutex.rs | 167 ++++++ embassy-util/src/select.rs | 230 ++++++++ embassy-util/src/waitqueue/mod.rs | 7 + embassy-util/src/waitqueue/multi_waker.rs | 33 ++ embassy-util/src/waitqueue/waker.rs | 92 ++++ embassy-util/src/yield_now.rs | 25 + embassy/Cargo.toml | 95 ---- embassy/build.rs | 29 - embassy/src/blocking_mutex/mod.rs | 200 ------- embassy/src/blocking_mutex/raw.rs | 148 ----- embassy/src/channel/mod.rs | 5 - embassy/src/channel/mpmc.rs | 613 --------------------- embassy/src/channel/pubsub/mod.rs | 542 ------------------ embassy/src/channel/pubsub/publisher.rs | 183 ------ embassy/src/channel/pubsub/subscriber.rs | 153 ----- embassy/src/channel/signal.rs | 99 ---- embassy/src/executor/arch/cortex_m.rs | 56 -- embassy/src/executor/arch/riscv32.rs | 74 --- embassy/src/executor/arch/std.rs | 84 --- embassy/src/executor/arch/wasm.rs | 74 --- embassy/src/executor/arch/xtensa.rs | 75 --- embassy/src/executor/mod.rs | 46 -- embassy/src/executor/raw/mod.rs | 433 --------------- embassy/src/executor/raw/run_queue.rs | 74 --- embassy/src/executor/raw/timer_queue.rs | 85 --- embassy/src/executor/raw/util.rs | 33 -- embassy/src/executor/raw/waker.rs | 53 -- embassy/src/executor/spawner.rs | 202 ------- embassy/src/fmt.rs | 228 -------- embassy/src/lib.rs | 27 - embassy/src/mutex.rs | 180 ------ embassy/src/time/delay.rs | 98 ---- embassy/src/time/driver.rs | 170 ------ embassy/src/time/driver_std.rs | 208 ------- embassy/src/time/driver_wasm.rs | 134 ----- embassy/src/time/duration.rs | 184 ------- embassy/src/time/instant.rs | 159 ------ embassy/src/time/mod.rs | 91 --- embassy/src/time/timer.rs | 151 ----- embassy/src/util/forever.rs | 95 ---- embassy/src/util/mod.rs | 9 - embassy/src/util/select.rs | 230 -------- embassy/src/util/yield_now.rs | 25 - embassy/src/waitqueue/mod.rs | 8 - embassy/src/waitqueue/multi_waker.rs | 33 -- embassy/src/waitqueue/waker.rs | 97 ---- embassy/src/waitqueue/waker_agnostic.rs | 92 ---- examples/boot/application/nrf/Cargo.toml | 3 +- examples/boot/application/nrf/src/bin/a.rs | 4 +- examples/boot/application/nrf/src/bin/b.rs | 6 +- examples/boot/application/stm32f3/Cargo.toml | 3 +- examples/boot/application/stm32f3/src/bin/a.rs | 4 +- examples/boot/application/stm32f3/src/bin/b.rs | 6 +- examples/boot/application/stm32f7/Cargo.toml | 3 +- examples/boot/application/stm32f7/src/bin/a.rs | 4 +- examples/boot/application/stm32f7/src/bin/b.rs | 6 +- examples/boot/application/stm32h7/Cargo.toml | 3 +- examples/boot/application/stm32h7/src/bin/a.rs | 4 +- examples/boot/application/stm32h7/src/bin/b.rs | 6 +- examples/boot/application/stm32l0/Cargo.toml | 3 +- examples/boot/application/stm32l0/src/bin/a.rs | 6 +- examples/boot/application/stm32l0/src/bin/b.rs | 6 +- examples/boot/application/stm32l1/Cargo.toml | 3 +- examples/boot/application/stm32l1/src/bin/a.rs | 6 +- examples/boot/application/stm32l1/src/bin/b.rs | 6 +- examples/boot/application/stm32l4/Cargo.toml | 3 +- examples/boot/application/stm32l4/src/bin/a.rs | 4 +- examples/boot/application/stm32l4/src/bin/b.rs | 6 +- examples/boot/application/stm32wl/Cargo.toml | 3 +- examples/boot/application/stm32wl/src/bin/a.rs | 4 +- examples/boot/application/stm32wl/src/bin/b.rs | 6 +- examples/boot/bootloader/nrf/Cargo.toml | 1 - examples/boot/bootloader/stm32/Cargo.toml | 1 - examples/nrf/Cargo.toml | 5 +- examples/nrf/src/bin/awaitable_timer.rs | 4 +- examples/nrf/src/bin/blinky.rs | 6 +- examples/nrf/src/bin/buffered_uart.rs | 4 +- examples/nrf/src/bin/channel.rs | 12 +- examples/nrf/src/bin/channel_sender_receiver.rs | 16 +- examples/nrf/src/bin/executor_fairness_test.rs | 12 +- examples/nrf/src/bin/gpiote_channel.rs | 4 +- examples/nrf/src/bin/gpiote_port.rs | 6 +- examples/nrf/src/bin/multiprio.rs | 10 +- examples/nrf/src/bin/mutex.rs | 12 +- examples/nrf/src/bin/nvmc.rs | 6 +- examples/nrf/src/bin/ppi.rs | 4 +- examples/nrf/src/bin/pubsub.rs | 16 +- examples/nrf/src/bin/pwm.rs | 6 +- examples/nrf/src/bin/pwm_double_sequence.rs | 6 +- examples/nrf/src/bin/pwm_sequence.rs | 6 +- examples/nrf/src/bin/pwm_sequence_ppi.rs | 4 +- examples/nrf/src/bin/pwm_sequence_ws2812b.rs | 6 +- examples/nrf/src/bin/pwm_servo.rs | 6 +- examples/nrf/src/bin/qdec.rs | 4 +- examples/nrf/src/bin/qspi.rs | 4 +- examples/nrf/src/bin/qspi_lowpower.rs | 6 +- examples/nrf/src/bin/raw_spawn.rs | 8 +- examples/nrf/src/bin/rng.rs | 4 +- examples/nrf/src/bin/saadc.rs | 6 +- examples/nrf/src/bin/saadc_continuous.rs | 8 +- examples/nrf/src/bin/self_spawn.rs | 8 +- .../nrf/src/bin/self_spawn_current_executor.rs | 8 +- examples/nrf/src/bin/spim.rs | 4 +- examples/nrf/src/bin/temp.rs | 6 +- examples/nrf/src/bin/timer.rs | 10 +- examples/nrf/src/bin/twim.rs | 4 +- examples/nrf/src/bin/twim_lowpower.rs | 6 +- examples/nrf/src/bin/uart.rs | 4 +- examples/nrf/src/bin/uart_idle.rs | 4 +- examples/nrf/src/bin/uart_split.rs | 10 +- examples/nrf/src/bin/usb_ethernet.rs | 18 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 13 +- examples/nrf/src/bin/usb_hid_mouse.rs | 10 +- examples/nrf/src/bin/usb_serial.rs | 4 +- examples/nrf/src/bin/usb_serial_multitask.rs | 10 +- examples/nrf/src/bin/wdt.rs | 4 +- examples/rp/Cargo.toml | 3 +- examples/rp/src/bin/blinky.rs | 6 +- examples/rp/src/bin/button.rs | 4 +- examples/rp/src/bin/gpio_async.rs | 6 +- examples/rp/src/bin/spi.rs | 4 +- examples/rp/src/bin/spi_display.rs | 6 +- examples/rp/src/bin/uart.rs | 4 +- examples/std/Cargo.toml | 3 +- examples/std/src/bin/net.rs | 8 +- examples/std/src/bin/serial.rs | 6 +- examples/std/src/bin/tick.rs | 8 +- examples/stm32f0/Cargo.toml | 3 +- examples/stm32f0/src/bin/hello.rs | 6 +- examples/stm32f1/Cargo.toml | 3 +- examples/stm32f1/src/bin/adc.rs | 6 +- examples/stm32f1/src/bin/blinky.rs | 6 +- examples/stm32f1/src/bin/hello.rs | 6 +- examples/stm32f1/src/bin/usb_serial.rs | 6 +- examples/stm32f2/Cargo.toml | 3 +- examples/stm32f2/src/bin/blinky.rs | 6 +- examples/stm32f2/src/bin/pll.rs | 6 +- examples/stm32f3/Cargo.toml | 3 +- examples/stm32f3/src/bin/blinky.rs | 6 +- examples/stm32f3/src/bin/button_events.rs | 14 +- examples/stm32f3/src/bin/button_exti.rs | 4 +- examples/stm32f3/src/bin/flash.rs | 4 +- examples/stm32f3/src/bin/hello.rs | 6 +- examples/stm32f3/src/bin/multiprio.rs | 10 +- examples/stm32f3/src/bin/spi_dma.rs | 4 +- examples/stm32f3/src/bin/usart_dma.rs | 4 +- examples/stm32f3/src/bin/usb_serial.rs | 6 +- examples/stm32f4/Cargo.toml | 3 +- examples/stm32f4/src/bin/adc.rs | 6 +- examples/stm32f4/src/bin/blinky.rs | 6 +- examples/stm32f4/src/bin/button_exti.rs | 4 +- examples/stm32f4/src/bin/flash.rs | 4 +- examples/stm32f4/src/bin/hello.rs | 6 +- examples/stm32f4/src/bin/multiprio.rs | 10 +- examples/stm32f4/src/bin/pwm.rs | 6 +- examples/stm32f4/src/bin/sdmmc.rs | 4 +- examples/stm32f4/src/bin/spi_dma.rs | 4 +- examples/stm32f4/src/bin/usart_buffered.rs | 4 +- examples/stm32f4/src/bin/usart_dma.rs | 4 +- examples/stm32f4/src/bin/wdt.rs | 6 +- examples/stm32f7/Cargo.toml | 3 +- examples/stm32f7/src/bin/adc.rs | 6 +- examples/stm32f7/src/bin/blinky.rs | 6 +- examples/stm32f7/src/bin/button_exti.rs | 4 +- examples/stm32f7/src/bin/eth.rs | 10 +- examples/stm32f7/src/bin/flash.rs | 6 +- examples/stm32f7/src/bin/hello.rs | 6 +- examples/stm32f7/src/bin/sdmmc.rs | 4 +- examples/stm32f7/src/bin/usart_dma.rs | 4 +- examples/stm32g0/Cargo.toml | 3 +- examples/stm32g0/src/bin/blinky.rs | 6 +- examples/stm32g0/src/bin/button_exti.rs | 4 +- examples/stm32g4/Cargo.toml | 3 +- examples/stm32g4/src/bin/blinky.rs | 6 +- examples/stm32g4/src/bin/button_exti.rs | 4 +- examples/stm32g4/src/bin/pwm.rs | 6 +- examples/stm32h7/Cargo.toml | 3 +- examples/stm32h7/src/bin/adc.rs | 6 +- examples/stm32h7/src/bin/blinky.rs | 6 +- examples/stm32h7/src/bin/button_exti.rs | 4 +- examples/stm32h7/src/bin/camera.rs | 8 +- examples/stm32h7/src/bin/eth.rs | 10 +- examples/stm32h7/src/bin/flash.rs | 6 +- examples/stm32h7/src/bin/fmc.rs | 6 +- examples/stm32h7/src/bin/low_level_timer_api.rs | 6 +- examples/stm32h7/src/bin/mco.rs | 6 +- examples/stm32h7/src/bin/pwm.rs | 6 +- examples/stm32h7/src/bin/rng.rs | 4 +- examples/stm32h7/src/bin/sdmmc.rs | 4 +- examples/stm32h7/src/bin/signal.rs | 10 +- examples/stm32h7/src/bin/spi.rs | 6 +- examples/stm32h7/src/bin/spi_dma.rs | 6 +- examples/stm32h7/src/bin/usart.rs | 6 +- examples/stm32h7/src/bin/usart_dma.rs | 6 +- examples/stm32h7/src/bin/usart_split.rs | 12 +- examples/stm32l0/Cargo.toml | 3 +- examples/stm32l0/src/bin/blinky.rs | 6 +- examples/stm32l0/src/bin/button.rs | 4 +- examples/stm32l0/src/bin/button_exti.rs | 4 +- examples/stm32l0/src/bin/flash.rs | 4 +- examples/stm32l0/src/bin/lorawan.rs | 4 +- examples/stm32l0/src/bin/raw_spawn.rs | 8 +- examples/stm32l0/src/bin/spi.rs | 4 +- examples/stm32l0/src/bin/usart_dma.rs | 4 +- examples/stm32l0/src/bin/usart_irq.rs | 4 +- examples/stm32l1/Cargo.toml | 3 +- examples/stm32l1/src/bin/blinky.rs | 6 +- examples/stm32l1/src/bin/flash.rs | 4 +- examples/stm32l1/src/bin/spi.rs | 4 +- examples/stm32l4/Cargo.toml | 3 +- examples/stm32l4/src/bin/adc.rs | 2 +- examples/stm32l4/src/bin/blinky.rs | 6 +- examples/stm32l4/src/bin/button_exti.rs | 4 +- examples/stm32l4/src/bin/i2c.rs | 4 +- examples/stm32l4/src/bin/i2c_blocking_async.rs | 4 +- examples/stm32l4/src/bin/i2c_dma.rs | 4 +- examples/stm32l4/src/bin/rng.rs | 4 +- examples/stm32l4/src/bin/spi_blocking_async.rs | 4 +- examples/stm32l4/src/bin/spi_dma.rs | 4 +- examples/stm32l4/src/bin/usart_dma.rs | 4 +- examples/stm32l5/Cargo.toml | 3 +- examples/stm32l5/src/bin/button_exti.rs | 4 +- examples/stm32l5/src/bin/rng.rs | 4 +- examples/stm32l5/src/bin/usb_ethernet.rs | 18 +- examples/stm32l5/src/bin/usb_hid_mouse.rs | 6 +- examples/stm32l5/src/bin/usb_serial.rs | 4 +- examples/stm32u5/Cargo.toml | 3 +- examples/stm32u5/src/bin/blinky.rs | 6 +- examples/stm32wb/Cargo.toml | 3 +- examples/stm32wb/src/bin/blinky.rs | 6 +- examples/stm32wb/src/bin/button_exti.rs | 4 +- examples/stm32wl/Cargo.toml | 3 +- examples/stm32wl/src/bin/blinky.rs | 6 +- examples/stm32wl/src/bin/button_exti.rs | 4 +- examples/stm32wl/src/bin/flash.rs | 4 +- examples/stm32wl/src/bin/lorawan.rs | 4 +- examples/stm32wl/src/bin/subghz.rs | 6 +- examples/wasm/Cargo.toml | 3 +- examples/wasm/src/lib.rs | 8 +- tests/rp/Cargo.toml | 3 +- tests/rp/src/bin/gpio.rs | 4 +- tests/rp/src/bin/gpio_async.rs | 6 +- tests/stm32/Cargo.toml | 3 +- tests/stm32/src/bin/gpio.rs | 4 +- tests/stm32/src/bin/spi.rs | 4 +- tests/stm32/src/bin/spi_dma.rs | 4 +- tests/stm32/src/bin/timer.rs | 6 +- tests/stm32/src/bin/usart.rs | 4 +- tests/stm32/src/bin/usart_dma.rs | 4 +- 358 files changed, 6499 insertions(+), 6338 deletions(-) create mode 100644 embassy-executor/Cargo.toml create mode 100644 embassy-executor/build.rs create mode 100644 embassy-executor/src/executor/arch/cortex_m.rs create mode 100644 embassy-executor/src/executor/arch/riscv32.rs create mode 100644 embassy-executor/src/executor/arch/std.rs create mode 100644 embassy-executor/src/executor/arch/wasm.rs create mode 100644 embassy-executor/src/executor/arch/xtensa.rs create mode 100644 embassy-executor/src/executor/mod.rs create mode 100644 embassy-executor/src/executor/raw/mod.rs create mode 100644 embassy-executor/src/executor/raw/run_queue.rs create mode 100644 embassy-executor/src/executor/raw/timer_queue.rs create mode 100644 embassy-executor/src/executor/raw/util.rs create mode 100644 embassy-executor/src/executor/raw/waker.rs create mode 100644 embassy-executor/src/executor/spawner.rs create mode 100644 embassy-executor/src/fmt.rs create mode 100644 embassy-executor/src/lib.rs create mode 100644 embassy-executor/src/time/delay.rs create mode 100644 embassy-executor/src/time/driver.rs create mode 100644 embassy-executor/src/time/driver_std.rs create mode 100644 embassy-executor/src/time/driver_wasm.rs create mode 100644 embassy-executor/src/time/duration.rs create mode 100644 embassy-executor/src/time/instant.rs create mode 100644 embassy-executor/src/time/mod.rs create mode 100644 embassy-executor/src/time/timer.rs create mode 100644 embassy-util/Cargo.toml create mode 100644 embassy-util/build.rs create mode 100644 embassy-util/src/blocking_mutex/mod.rs create mode 100644 embassy-util/src/blocking_mutex/raw.rs create mode 100644 embassy-util/src/channel/mod.rs create mode 100644 embassy-util/src/channel/mpmc.rs create mode 100644 embassy-util/src/channel/pubsub/mod.rs create mode 100644 embassy-util/src/channel/pubsub/publisher.rs create mode 100644 embassy-util/src/channel/pubsub/subscriber.rs create mode 100644 embassy-util/src/channel/signal.rs create mode 100644 embassy-util/src/fmt.rs create mode 100644 embassy-util/src/forever.rs create mode 100644 embassy-util/src/lib.rs create mode 100644 embassy-util/src/mutex.rs create mode 100644 embassy-util/src/select.rs create mode 100644 embassy-util/src/waitqueue/mod.rs create mode 100644 embassy-util/src/waitqueue/multi_waker.rs create mode 100644 embassy-util/src/waitqueue/waker.rs create mode 100644 embassy-util/src/yield_now.rs delete mode 100644 embassy/Cargo.toml delete mode 100644 embassy/build.rs delete mode 100644 embassy/src/blocking_mutex/mod.rs delete mode 100644 embassy/src/blocking_mutex/raw.rs delete mode 100644 embassy/src/channel/mod.rs delete mode 100644 embassy/src/channel/mpmc.rs delete mode 100644 embassy/src/channel/pubsub/mod.rs delete mode 100644 embassy/src/channel/pubsub/publisher.rs delete mode 100644 embassy/src/channel/pubsub/subscriber.rs delete mode 100644 embassy/src/channel/signal.rs delete mode 100644 embassy/src/executor/arch/cortex_m.rs delete mode 100644 embassy/src/executor/arch/riscv32.rs delete mode 100644 embassy/src/executor/arch/std.rs delete mode 100644 embassy/src/executor/arch/wasm.rs delete mode 100644 embassy/src/executor/arch/xtensa.rs delete mode 100644 embassy/src/executor/mod.rs delete mode 100644 embassy/src/executor/raw/mod.rs delete mode 100644 embassy/src/executor/raw/run_queue.rs delete mode 100644 embassy/src/executor/raw/timer_queue.rs delete mode 100644 embassy/src/executor/raw/util.rs delete mode 100644 embassy/src/executor/raw/waker.rs delete mode 100644 embassy/src/executor/spawner.rs delete mode 100644 embassy/src/fmt.rs delete mode 100644 embassy/src/lib.rs delete mode 100644 embassy/src/mutex.rs delete mode 100644 embassy/src/time/delay.rs delete mode 100644 embassy/src/time/driver.rs delete mode 100644 embassy/src/time/driver_std.rs delete mode 100644 embassy/src/time/driver_wasm.rs delete mode 100644 embassy/src/time/duration.rs delete mode 100644 embassy/src/time/instant.rs delete mode 100644 embassy/src/time/mod.rs delete mode 100644 embassy/src/time/timer.rs delete mode 100644 embassy/src/util/forever.rs delete mode 100644 embassy/src/util/mod.rs delete mode 100644 embassy/src/util/select.rs delete mode 100644 embassy/src/util/yield_now.rs delete mode 100644 embassy/src/waitqueue/mod.rs delete mode 100644 embassy/src/waitqueue/multi_waker.rs delete mode 100644 embassy/src/waitqueue/waker.rs delete mode 100644 embassy/src/waitqueue/waker_agnostic.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6115e618d..d76e5ced4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -69,4 +69,4 @@ jobs: steps: - uses: actions/checkout@v2 - name: Test - run: cd embassy && cargo test + run: cd embassy-util && cargo test diff --git a/.vscode/settings.json b/.vscode/settings.json index 5ce8e4e7d..1ac3fc513 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,10 +12,15 @@ //"embassy-net/medium-ethernet", //"embassy-net/tcp", //"embassy-net/pool-16", + //"time-tick-16mhz", + //"defmt-timestamp-uptime", "nightly", + //"unstable-traits", ], "rust-analyzer.linkedProjects": [ // Declare for the target you wish to develop + //"embassy-executor/Cargo.toml", + //"embassy-util/Cargo.toml", "examples/nrf/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", diff --git a/README.md b/README.md index a7a7ccd54..423740674 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Rust's async/await allows - embassy-nrf, for the Nordic Semiconductor nRF52, nRF53, nRF91 series. - **Time that Just Works** - -No more messing with hardware timers. embassy::time provides Instant, Duration and Timer types that are globally available and never overflow. +No more messing with hardware timers. embassy_executor::time provides Instant, Duration and Timer types that are globally available and never overflow. - **Real-time ready** - Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities, so that higher priority tasks preempt lower priority ones. See the example. @@ -44,13 +44,13 @@ The nrf-softdevice cr ```rust,ignore use defmt::info; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; use embassy_nrf::Peripherals; // Declare async tasks -#[embassy::task] +#[embassy_executor::task] async fn blink(pin: AnyPin) { let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); @@ -64,7 +64,7 @@ async fn blink(pin: AnyPin) { } // Main is itself an async task as well. -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { // Spawned tasks run in the background, concurrently. spawner.spawn(blink(p.P0_13.degrade())).unwrap(); @@ -132,7 +132,7 @@ Embassy is guaranteed to compile on the latest stable Rust version at the time o Several features require nightly: -- The `#[embassy::main]` and `#[embassy::task]` attribute macros. +- The `#[embassy_executor::main]` and `#[embassy_executor::task]` attribute macros. - Async traits These are enabled by activating the `nightly` Cargo feature. If you do so, Embassy is guaranteed to compile on the exact nightly version specified in `rust-toolchain.toml`. It might compile with older or newer nightly versions, but that may change in any new patch release. diff --git a/ci.sh b/ci.sh index e34f1dfe3..6ec2410f7 100755 --- a/ci.sh +++ b/ci.sh @@ -32,10 +32,10 @@ rm -rf stm32-metapac mv stm32-metapac-gen/out stm32-metapac cargo batch \ - --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features nightly \ - --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features nightly,log,executor-agnostic \ - --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ - --- build --release --manifest-path embassy/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ @@ -54,27 +54,27 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,log \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ - --- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4,embassy/time-tick-32768hz \ + --- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ --- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ @@ -106,7 +106,7 @@ cargo batch \ --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32l4 --bin b \ --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/boot/stm32wl --bin b \ --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ - --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4,embassy/time-tick-32768hz \ + --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/bluepill-stm32f103c8 \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/nucleo-stm32f429zi \ diff --git a/ci_stable.sh b/ci_stable.sh index 2723936e7..7521827d8 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -9,10 +9,10 @@ export DEFMT_LOG=trace sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml cargo batch \ - --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features log,executor-agnostic \ - --- build --release --manifest-path embassy/Cargo.toml --target thumbv7em-none-eabi --features defmt \ - --- build --release --manifest-path embassy/Cargo.toml --target thumbv6m-none-eabi --features defmt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ @@ -30,38 +30,38 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,log \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy/time-tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy-executor/time-tick-32768hz,unstable-traits \ --- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf --bin raw_spawn \ --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \ diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index ed1c3cb1c..59e1a437a 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -5,7 +5,7 @@ name = "embassy-basic-example" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../../../../embassy", features = ["defmt", "nightly"] } +embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } defmt = "0.3" diff --git a/docs/modules/ROOT/examples/basic/src/main.rs b/docs/modules/ROOT/examples/basic/src/main.rs index 461741fd7..cec39fd91 100644 --- a/docs/modules/ROOT/examples/basic/src/main.rs +++ b/docs/modules/ROOT/examples/basic/src/main.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::peripherals::P0_13; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; // global logger -#[embassy::task] +#[embassy_executor::task] async fn blinker(mut led: Output<'static, P0_13>, interval: Duration) { loop { led.set_high(); @@ -20,7 +20,7 @@ async fn blinker(mut led: Output<'static, P0_13>, interval: Duration) { } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { let led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); unwrap!(spawner.spawn(blinker(led, Duration::from_millis(300)))); diff --git a/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml index 2dca3cc8d..9048d9302 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [patch.crates-io] -embassy = { path = "../../../../../embassy" } +embassy-executor = { path = "../../../../../embassy-executor" } embassy-stm32 = { path = "../../../../../embassy-stm32" } stm32-metapac = { path = "../../../../../stm32-metapac" } diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index e0c63251e..e2933076f 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" cortex-m = "0.7" cortex-m-rt = "0.7" embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } -embassy = { version = "0.1.0", default-features = false, features = ["nightly"] } +embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly"] } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs index 56bc698da..b944a7994 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs @@ -2,13 +2,13 @@ #![no_main] #![feature(type_alias_impl_trait)] -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_s: Spawner, p: Peripherals) { let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh); let mut button = ExtiInput::new(Input::new(p.PC13, Pull::Up), p.EXTI13); diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 5bb2c34b3..abb7bb5c2 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -9,7 +9,7 @@ description = "Bootloader using Embassy" [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4", optional = true } -embassy = { path = "../../embassy", default-features = false } +embassy-util = { version = "0.1.0", path = "../../embassy-util" } embedded-storage = "0.3.0" embedded-storage-async = "0.3.0" diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index ea5794836..5dc3ce52b 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml @@ -9,7 +9,7 @@ description = "Bootloader lib for nRF chips" [dependencies] defmt = { version = "0.3", optional = true } -embassy = { path = "../../embassy", default-features = false } +embassy-util = { path = "../../embassy-util" } embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] } embassy-boot = { path = "../boot", default-features = false } cortex-m = { version = "0.7" } diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml index 13ae54b31..eab8d160a 100644 --- a/embassy-boot/stm32/Cargo.toml +++ b/embassy-boot/stm32/Cargo.toml @@ -11,7 +11,7 @@ defmt = { version = "0.3", optional = true } defmt-rtt = { version = "0.3", optional = true } log = { version = "0.4", optional = true } -embassy = { path = "../../embassy", default-features = false } +embassy-util = { path = "../../embassy-util" } embassy-stm32 = { path = "../../embassy-stm32", default-features = false, features = ["nightly"] } embassy-boot = { path = "../boot", default-features = false } cortex-m = { version = "0.7" } diff --git a/embassy-cortex-m/Cargo.toml b/embassy-cortex-m/Cargo.toml index 9dbec0462..454f34e0b 100644 --- a/embassy-cortex-m/Cargo.toml +++ b/embassy-cortex-m/Cargo.toml @@ -35,7 +35,8 @@ prio-bits-8 = [] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy = { version = "0.1.0", path = "../embassy"} +embassy-util = { version = "0.1.0", path = "../embassy-util" } +embassy-executor = { version = "0.1.0", path = "../embassy-executor"} embassy-macros = { version = "0.1.0", path = "../embassy-macros"} embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common"} atomic-polyfill = "0.1.5" diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs index 17ccf0e81..4a3fa9903 100644 --- a/embassy-cortex-m/src/executor.rs +++ b/embassy-cortex-m/src/executor.rs @@ -1,7 +1,7 @@ //! Executor specific to cortex-m devices. use core::marker::PhantomData; -pub use embassy::executor::*; +pub use embassy_executor::executor::*; use crate::interrupt::{Interrupt, InterruptExt}; @@ -60,18 +60,18 @@ impl InterruptExecutor { /// The executor keeps running in the background through the interrupt. /// /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] - /// is returned instead of a [`Spawner`](embassy::executor::Spawner) because the executor effectively runs in a + /// is returned instead of a [`Spawner`](embassy_executor::executor::Spawner) because the executor effectively runs in a /// different "thread" (the interrupt), so spawning tasks on it is effectively /// sending them. /// - /// To obtain a [`Spawner`](embassy::executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy::executor::Spawner::for_current_executor()) from + /// To obtain a [`Spawner`](embassy_executor::executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::executor::Spawner::for_current_executor()) from /// a task running in it. /// /// This function requires `&'static mut self`. This means you have to store the /// Executor instance in a place where it'll live forever and grants you mutable /// access. There's a few ways to do this: /// - /// - a [Forever](embassy::util::Forever) (safe) + /// - a [Forever](embassy_util::Forever) (safe) /// - a `static mut` (unsafe) /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) pub fn start(&'static mut self) -> SendSpawner { diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 455786be9..f245783cf 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -9,7 +9,7 @@ std = [] nightly = ["embedded-hal-async", "embedded-storage-async"] [dependencies] -embassy = { version = "0.1.0", path = "../embassy" } +embassy-util = { version = "0.1.0", path = "../embassy-util" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1", optional = true } diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs index fa77a06d3..bd023fb6a 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs @@ -4,8 +4,8 @@ //! //! ```rust //! use embassy_embedded_hal::shared_bus::i2c::I2cDevice; -//! use embassy::mutex::Mutex; -//! use embassy::blocking_mutex::raw::ThreadModeRawMutex; +//! use embassy_util::mutex::Mutex; +//! use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; //! //! static I2C_BUS: Forever>> = Forever::new(); //! let config = twim::Config::default(); @@ -24,8 +24,8 @@ //! ``` use core::future::Future; -use embassy::blocking_mutex::raw::RawMutex; -use embassy::mutex::Mutex; +use embassy_util::blocking_mutex::raw::RawMutex; +use embassy_util::mutex::Mutex; use embedded_hal_async::i2c; use crate::shared_bus::I2cDeviceError; diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index a08eaa82d..caa37f6f3 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -4,8 +4,8 @@ //! //! ```rust //! use embassy_embedded_hal::shared_bus::spi::SpiDevice; -//! use embassy::mutex::Mutex; -//! use embassy::blocking_mutex::raw::ThreadModeRawMutex; +//! use embassy_util::mutex::Mutex; +//! use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; //! //! static SPI_BUS: Forever>> = Forever::new(); //! let mut config = spim::Config::default(); @@ -27,8 +27,8 @@ //! ``` use core::future::Future; -use embassy::blocking_mutex::raw::RawMutex; -use embassy::mutex::Mutex; +use embassy_util::blocking_mutex::raw::RawMutex; +use embassy_util::mutex::Mutex; use embedded_hal_1::digital::blocking::OutputPin; use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi; diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs index c8b5e30f6..1fc343d15 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs @@ -4,7 +4,7 @@ //! //! ```rust //! use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; -//! use embassy::blocking_mutex::{NoopMutex, raw::NoopRawMutex}; +//! use embassy_util::blocking_mutex::{NoopMutex, raw::NoopRawMutex}; //! //! static I2C_BUS: Forever>>> = Forever::new(); //! let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); @@ -18,8 +18,8 @@ use core::cell::RefCell; -use embassy::blocking_mutex::raw::RawMutex; -use embassy::blocking_mutex::Mutex; +use embassy_util::blocking_mutex::raw::RawMutex; +use embassy_util::blocking_mutex::Mutex; use embedded_hal_1::i2c::blocking::{I2c, Operation}; use embedded_hal_1::i2c::ErrorType; diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index d0648f59a..a61326594 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -4,7 +4,7 @@ //! //! ```rust //! use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; -//! use embassy::blocking_mutex::{NoopMutex, raw::NoopRawMutex}; +//! use embassy_util::blocking_mutex::{NoopMutex, raw::NoopRawMutex}; //! //! static SPI_BUS: Forever>>> = Forever::new(); //! let irq = interrupt::take!(SPIM3); @@ -20,8 +20,8 @@ use core::cell::RefCell; -use embassy::blocking_mutex::raw::RawMutex; -use embassy::blocking_mutex::Mutex; +use embassy_util::blocking_mutex::raw::RawMutex; +use embassy_util::blocking_mutex::Mutex; use embedded_hal_1::digital::blocking::OutputPin; use embedded_hal_1::spi; use embedded_hal_1::spi::blocking::SpiBusFlush; diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml new file mode 100644 index 000000000..d8ac4ac00 --- /dev/null +++ b/embassy-executor/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "embassy-executor" +version = "0.1.0" +edition = "2021" + + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" +features = ["nightly", "defmt", "unstable-traits", "time", "time-tick-1mhz"] +flavors = [ + { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] }, + { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] }, + { name = "thumbv6m-none-eabi", target = "thumbv6m-none-eabi", features = [] }, + { name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] }, + { name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] }, + { name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] }, + { name = "thumbv8m.base-none-eabi", target = "thumbv8m.base-none-eabi", features = [] }, + { name = "thumbv8m.main-none-eabi", target = "thumbv8m.main-none-eabi", features = [] }, + { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, +] + +[features] +default = [] +std = ["time", "time-tick-1mhz", "embassy-macros/std"] +wasm = ["wasm-bindgen", "js-sys", "embassy-macros/wasm", "wasm-timer", "time", "time-tick-1mhz"] + +# Enable nightly-only features +nightly = ["embedded-hal-async"] + +# Implement embedded-hal 1.0 alpha and embedded-hal-async traits. +# Implement embedded-hal-async traits if `nightly` is set as well. +unstable-traits = ["embedded-hal-1"] + +# Display a timestamp of the number of seconds since startup next to defmt log messages +# To use this you must have a time driver provided. +defmt-timestamp-uptime = ["defmt"] + +# Enable `embassy_executor::time` module. +# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. +# Enabling it directly without supplying a time driver will fail to link. +time = [] + +# Set the `embassy_executor::time` tick rate. +# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. +# If you're not writing your own driver, check the driver documentation to customize the tick rate. +# If you're writing a driver and your tick rate is not listed here, please add it and send a PR! +time-tick-32768hz = ["time"] +time-tick-1000hz = ["time"] +time-tick-1mhz = ["time"] +time-tick-16mhz = ["time"] + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} +embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} + +futures-util = { version = "0.3.17", default-features = false } +embassy-macros = { version = "0.1.0", path = "../embassy-macros"} +atomic-polyfill = "0.1.5" +critical-section = "0.2.5" +cfg-if = "1.0.0" + +# WASM dependencies +wasm-bindgen = { version = "0.2.76", features = ["nightly"], optional = true } +js-sys = { version = "0.3", optional = true } +wasm-timer = { version = "0.2.5", optional = true } \ No newline at end of file diff --git a/embassy-executor/build.rs b/embassy-executor/build.rs new file mode 100644 index 000000000..6fe82b44f --- /dev/null +++ b/embassy-executor/build.rs @@ -0,0 +1,29 @@ +use std::env; + +fn main() { + let target = env::var("TARGET").unwrap(); + + if target.starts_with("thumbv6m-") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv6m"); + } else if target.starts_with("thumbv7m-") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv7m"); + } else if target.starts_with("thumbv7em-") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv7m"); + println!("cargo:rustc-cfg=armv7em"); // (not currently used) + } else if target.starts_with("thumbv8m.base") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv8m"); + println!("cargo:rustc-cfg=armv8m_base"); + } else if target.starts_with("thumbv8m.main") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv8m"); + println!("cargo:rustc-cfg=armv8m_main"); + } + + if target.ends_with("-eabihf") { + println!("cargo:rustc-cfg=has_fpu"); + } +} diff --git a/embassy-executor/src/executor/arch/cortex_m.rs b/embassy-executor/src/executor/arch/cortex_m.rs new file mode 100644 index 000000000..d6e758dfb --- /dev/null +++ b/embassy-executor/src/executor/arch/cortex_m.rs @@ -0,0 +1,59 @@ +use core::arch::asm; +use core::marker::PhantomData; +use core::ptr; + +use super::{raw, Spawner}; + +/// Thread mode executor, using WFE/SEV. +/// +/// This is the simplest and most common kind of executor. It runs on +/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction +/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction +/// is executed, to make the `WFE` exit from sleep and poll the task. +/// +/// This executor allows for ultra low power consumption for chips where `WFE` +/// triggers low-power sleep without extra steps. If your chip requires extra steps, +/// you may use [`raw::Executor`] directly to program custom behavior. +pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(|_| unsafe { asm!("sev") }, ptr::null_mut()), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [Forever](crate::util::Forever) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + asm!("wfe"); + }; + } + } +} diff --git a/embassy-executor/src/executor/arch/riscv32.rs b/embassy-executor/src/executor/arch/riscv32.rs new file mode 100644 index 000000000..7a7d5698c --- /dev/null +++ b/embassy-executor/src/executor/arch/riscv32.rs @@ -0,0 +1,74 @@ +use core::marker::PhantomData; +use core::ptr; + +use atomic_polyfill::{AtomicBool, Ordering}; + +use super::{raw, Spawner}; + +/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV +/// +static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + +/// RISCV32 Executor +pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + // use Signal_Work_Thread_Mode as substitute for local interrupt register + inner: raw::Executor::new( + |_| { + SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); + }, + ptr::null_mut(), + ), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [Forever](crate::util::Forever) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + //will only set this value to true. + critical_section::with(|_| { + // if there is work to do, loop back to polling + // TODO can we relax this? + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } + // if not, wait for interrupt + else { + core::arch::asm!("wfi"); + } + }); + // if an interrupt occurred while waiting, it will be serviced here + } + } + } +} diff --git a/embassy-executor/src/executor/arch/std.rs b/embassy-executor/src/executor/arch/std.rs new file mode 100644 index 000000000..b93ab8a79 --- /dev/null +++ b/embassy-executor/src/executor/arch/std.rs @@ -0,0 +1,84 @@ +use std::marker::PhantomData; +use std::sync::{Condvar, Mutex}; + +use super::{raw, Spawner}; + +/// Single-threaded std-based executor. +pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + signaler: &'static Signaler, +} + +impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + let signaler = &*Box::leak(Box::new(Signaler::new())); + Self { + inner: raw::Executor::new( + |p| unsafe { + let s = &*(p as *const () as *const Signaler); + s.signal() + }, + signaler as *const _ as _, + ), + not_send: PhantomData, + signaler, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [Forever](crate::util::Forever) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { self.inner.poll() }; + self.signaler.wait() + } + } +} + +struct Signaler { + mutex: Mutex, + condvar: Condvar, +} + +impl Signaler { + fn new() -> Self { + Self { + mutex: Mutex::new(false), + condvar: Condvar::new(), + } + } + + fn wait(&self) { + let mut signaled = self.mutex.lock().unwrap(); + while !*signaled { + signaled = self.condvar.wait(signaled).unwrap(); + } + *signaled = false; + } + + fn signal(&self) { + let mut signaled = self.mutex.lock().unwrap(); + *signaled = true; + self.condvar.notify_one(); + } +} diff --git a/embassy-executor/src/executor/arch/wasm.rs b/embassy-executor/src/executor/arch/wasm.rs new file mode 100644 index 000000000..9d5aa31ed --- /dev/null +++ b/embassy-executor/src/executor/arch/wasm.rs @@ -0,0 +1,74 @@ +use core::marker::PhantomData; + +use js_sys::Promise; +use wasm_bindgen::prelude::*; + +use super::raw::util::UninitCell; +use super::raw::{self}; +use super::Spawner; + +/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. +pub struct Executor { + inner: raw::Executor, + ctx: &'static WasmContext, + not_send: PhantomData<*mut ()>, +} + +pub(crate) struct WasmContext { + promise: Promise, + closure: UninitCell>, +} + +impl WasmContext { + pub fn new() -> Self { + Self { + promise: Promise::resolve(&JsValue::undefined()), + closure: UninitCell::uninit(), + } + } +} + +impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + let ctx = &*Box::leak(Box::new(WasmContext::new())); + let inner = raw::Executor::new( + |p| unsafe { + let ctx = &*(p as *const () as *const WasmContext); + let _ = ctx.promise.then(ctx.closure.as_mut()); + }, + ctx as *const _ as _, + ); + Self { + inner, + not_send: PhantomData, + ctx, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [Forever](crate::util::Forever) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { + unsafe { + let executor = &self.inner; + self.ctx.closure.write(Closure::new(move |_| { + executor.poll(); + })); + init(self.inner.spawner()); + } + } +} diff --git a/embassy-executor/src/executor/arch/xtensa.rs b/embassy-executor/src/executor/arch/xtensa.rs new file mode 100644 index 000000000..20bd7b8a5 --- /dev/null +++ b/embassy-executor/src/executor/arch/xtensa.rs @@ -0,0 +1,75 @@ +use core::marker::PhantomData; +use core::ptr; + +use atomic_polyfill::{AtomicBool, Ordering}; + +use super::{raw, Spawner}; + +/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa +/// +static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + +/// Xtensa Executor +pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + // use Signal_Work_Thread_Mode as substitute for local interrupt register + inner: raw::Executor::new( + |_| { + SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); + }, + ptr::null_mut(), + ), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [Forever](crate::util::Forever) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + // will only set this value to true. + // if there is work to do, loop back to polling + // TODO can we relax this? + critical_section::with(|_| { + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } else { + // waiti sets the PS.INTLEVEL when slipping into sleep + // because critical sections in Xtensa are implemented via increasing + // PS.INTLEVEL the critical section ends here + // take care not add code after `waiti` if it needs to be inside the CS + core::arch::asm!("waiti 0"); // critical section ends here + } + }); + } + } + } +} diff --git a/embassy-executor/src/executor/mod.rs b/embassy-executor/src/executor/mod.rs new file mode 100644 index 000000000..45d00c568 --- /dev/null +++ b/embassy-executor/src/executor/mod.rs @@ -0,0 +1,44 @@ +//! Async task executor. +//! +//! This module provides an async/await executor designed for embedded usage. +//! +//! - No `alloc`, no heap needed. Task futures are statically allocated. +//! - No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning. +//! - Integrated timer queue: sleeping is easy, just do `Timer::after(Duration::from_secs(1)).await;`. +//! - No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`. +//! - Efficient polling: a wake will only poll the woken task, not all of them. +//! - Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time. +//! - Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks. + +cfg_if::cfg_if! { + if #[cfg(cortex_m)] { + #[path="arch/cortex_m.rs"] + mod arch; + pub use arch::*; + } + else if #[cfg(target_arch="riscv32")] { + #[path="arch/riscv32.rs"] + mod arch; + pub use arch::*; + } + else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] { + #[path="arch/xtensa.rs"] + mod arch; + pub use arch::*; + } + else if #[cfg(feature="wasm")] { + #[path="arch/wasm.rs"] + mod arch; + pub use arch::*; + } + else if #[cfg(feature="std")] { + #[path="arch/std.rs"] + mod arch; + pub use arch::*; + } +} + +pub mod raw; + +mod spawner; +pub use spawner::*; diff --git a/embassy-executor/src/executor/raw/mod.rs b/embassy-executor/src/executor/raw/mod.rs new file mode 100644 index 000000000..87317bc02 --- /dev/null +++ b/embassy-executor/src/executor/raw/mod.rs @@ -0,0 +1,433 @@ +//! Raw executor. +//! +//! This module exposes "raw" Executor and Task structs for more low level control. +//! +//! ## WARNING: here be dragons! +//! +//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe +//! executor wrappers in [`executor`](crate::executor) and the [`embassy_executor::task`](embassy_macros::task) macro, which are fully safe. + +mod run_queue; +#[cfg(feature = "time")] +mod timer_queue; +pub(crate) mod util; +mod waker; + +use core::cell::Cell; +use core::future::Future; +use core::pin::Pin; +use core::ptr::NonNull; +use core::task::{Context, Poll}; +use core::{mem, ptr}; + +use atomic_polyfill::{AtomicU32, Ordering}; +use critical_section::CriticalSection; + +use self::run_queue::{RunQueue, RunQueueItem}; +use self::util::UninitCell; +pub use self::waker::task_from_waker; +use super::SpawnToken; +#[cfg(feature = "time")] +use crate::time::driver::{self, AlarmHandle}; +#[cfg(feature = "time")] +use crate::time::Instant; + +/// Task is spawned (has a future) +pub(crate) const STATE_SPAWNED: u32 = 1 << 0; +/// Task is in the executor run queue +pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; +/// Task is in the executor timer queue +#[cfg(feature = "time")] +pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; + +/// Raw task header for use in task pointers. +/// +/// This is an opaque struct, used for raw pointers to tasks, for use +/// with funtions like [`wake_task`] and [`task_from_waker`]. +pub struct TaskHeader { + pub(crate) state: AtomicU32, + pub(crate) run_queue_item: RunQueueItem, + pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 + pub(crate) poll_fn: UninitCell)>, // Valid if STATE_SPAWNED + + #[cfg(feature = "time")] + pub(crate) expires_at: Cell, + #[cfg(feature = "time")] + pub(crate) timer_queue_item: timer_queue::TimerQueueItem, +} + +impl TaskHeader { + pub(crate) const fn new() -> Self { + Self { + state: AtomicU32::new(0), + run_queue_item: RunQueueItem::new(), + executor: Cell::new(ptr::null()), + poll_fn: UninitCell::uninit(), + + #[cfg(feature = "time")] + expires_at: Cell::new(Instant::from_ticks(0)), + #[cfg(feature = "time")] + timer_queue_item: timer_queue::TimerQueueItem::new(), + } + } + + pub(crate) unsafe fn enqueue(&self) { + critical_section::with(|cs| { + let state = self.state.load(Ordering::Relaxed); + + // If already scheduled, or if not started, + if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { + return; + } + + // Mark it as scheduled + self.state.store(state | STATE_RUN_QUEUED, Ordering::Relaxed); + + // We have just marked the task as scheduled, so enqueue it. + let executor = &*self.executor.get(); + executor.enqueue(cs, self as *const TaskHeader as *mut TaskHeader); + }) + } +} + +/// Raw storage in which a task can be spawned. +/// +/// This struct holds the necessary memory to spawn one task whose future is `F`. +/// At a given time, the `TaskStorage` may be in spawned or not-spawned state. You +/// may spawn it with [`TaskStorage::spawn()`], which will fail if it is already spawned. +/// +/// A `TaskStorage` must live forever, it may not be deallocated even after the task has finished +/// running. Hence the relevant methods require `&'static self`. It may be reused, however. +/// +/// Internally, the [embassy_executor::task](embassy_macros::task) macro allocates an array of `TaskStorage`s +/// in a `static`. The most common reason to use the raw `Task` is to have control of where +/// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc. + +// repr(C) is needed to guarantee that the Task is located at offset 0 +// This makes it safe to cast between TaskHeader and TaskStorage pointers. +#[repr(C)] +pub struct TaskStorage { + raw: TaskHeader, + future: UninitCell, // Valid if STATE_SPAWNED +} + +impl TaskStorage { + const NEW: Self = Self::new(); + + /// Create a new TaskStorage, in not-spawned state. + pub const fn new() -> Self { + Self { + raw: TaskHeader::new(), + future: UninitCell::uninit(), + } + } + + /// Try to spawn the task. + /// + /// The `future` closure constructs the future. It's only called if spawning is + /// actually possible. It is a closure instead of a simple `future: F` param to ensure + /// the future is constructed in-place, avoiding a temporary copy in the stack thanks to + /// NRVO optimizations. + /// + /// This function will fail if the task is already spawned and has not finished running. + /// In this case, the error is delayed: a "poisoned" SpawnToken is returned, which will + /// cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. + /// + /// Once the task has finished running, you may spawn it again. It is allowed to spawn it + /// on a different executor. + pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + if self.spawn_mark_used() { + return unsafe { SpawnToken::::new(self.spawn_initialize(future)) }; + } + + SpawnToken::::new_failed() + } + + fn spawn_mark_used(&'static self) -> bool { + let state = STATE_SPAWNED | STATE_RUN_QUEUED; + self.raw + .state + .compare_exchange(0, state, Ordering::AcqRel, Ordering::Acquire) + .is_ok() + } + + unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> NonNull { + // Initialize the task + self.raw.poll_fn.write(Self::poll); + self.future.write(future()); + NonNull::new_unchecked(&self.raw as *const TaskHeader as *mut TaskHeader) + } + + unsafe fn poll(p: NonNull) { + let this = &*(p.as_ptr() as *const TaskStorage); + + let future = Pin::new_unchecked(this.future.as_mut()); + let waker = waker::from_task(p); + let mut cx = Context::from_waker(&waker); + match future.poll(&mut cx) { + Poll::Ready(_) => { + this.future.drop_in_place(); + this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel); + } + Poll::Pending => {} + } + + // the compiler is emitting a virtual call for waker drop, but we know + // it's a noop for our waker. + mem::forget(waker); + } +} + +unsafe impl Sync for TaskStorage {} + +/// Raw storage that can hold up to N tasks of the same type. +/// +/// This is essentially a `[TaskStorage; N]`. +pub struct TaskPool { + pool: [TaskStorage; N], +} + +impl TaskPool { + /// Create a new TaskPool, with all tasks in non-spawned state. + pub const fn new() -> Self { + Self { + pool: [TaskStorage::NEW; N], + } + } + + /// Try to spawn a task in the pool. + /// + /// See [`TaskStorage::spawn()`] for details. + /// + /// This will loop over the pool and spawn the task in the first storage that + /// is currently free. If none is free, a "poisoned" SpawnToken is returned, + /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. + pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + for task in &self.pool { + if task.spawn_mark_used() { + return unsafe { SpawnToken::::new(task.spawn_initialize(future)) }; + } + } + + SpawnToken::::new_failed() + } + + /// Like spawn(), but allows the task to be send-spawned if the args are Send even if + /// the future is !Send. + /// + /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used + /// by the Embassy macros ONLY. + /// + /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` + /// is an `async fn`, NOT a hand-written `Future`. + #[doc(hidden)] + pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken + where + FutFn: FnOnce() -> F, + { + // When send-spawning a task, we construct the future in this thread, and effectively + // "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory, + // send-spawning should require the future `F` to be `Send`. + // + // The problem is this is more restrictive than needed. Once the future is executing, + // it is never sent to another thread. It is only sent when spawning. It should be + // enough for the task's arguments to be Send. (and in practice it's super easy to + // accidentally make your futures !Send, for example by holding an `Rc` or a `&RefCell` across an `.await`.) + // + // We can do it by sending the task args and constructing the future in the executor thread + // on first poll. However, this cannot be done in-place, so it'll waste stack space for a copy + // of the args. + // + // Luckily, an `async fn` future contains just the args when freshly constructed. So, if the + // args are Send, it's OK to send a !Send future, as long as we do it before first polling it. + // + // (Note: this is how the generators are implemented today, it's not officially guaranteed yet, + // but it's possible it'll be guaranteed in the future. See zulip thread: + // https://rust-lang.zulipchat.com/#narrow/stream/187312-wg-async/topic/.22only.20before.20poll.22.20Send.20futures ) + // + // The `FutFn` captures all the args, so if it's Send, the task can be send-spawned. + // This is why we return `SpawnToken` below. + // + // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly + // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken`. + + for task in &self.pool { + if task.spawn_mark_used() { + return SpawnToken::::new(task.spawn_initialize(future)); + } + } + + SpawnToken::::new_failed() + } +} + +/// Raw executor. +/// +/// This is the core of the Embassy executor. It is low-level, requiring manual +/// handling of wakeups and task polling. If you can, prefer using one of the +/// higher level executors in [`crate::executor`]. +/// +/// The raw executor leaves it up to you to handle wakeups and scheduling: +/// +/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks +/// that "want to run"). +/// - You must supply a `signal_fn`. The executor will call it to notify you it has work +/// to do. You must arrange for `poll()` to be called as soon as possible. +/// +/// `signal_fn` can be called from *any* context: any thread, any interrupt priority +/// level, etc. It may be called synchronously from any `Executor` method call as well. +/// You must deal with this correctly. +/// +/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates +/// the requirement for `poll` to not be called reentrantly. +pub struct Executor { + run_queue: RunQueue, + signal_fn: fn(*mut ()), + signal_ctx: *mut (), + + #[cfg(feature = "time")] + pub(crate) timer_queue: timer_queue::TimerQueue, + #[cfg(feature = "time")] + alarm: AlarmHandle, +} + +impl Executor { + /// Create a new executor. + /// + /// When the executor has work to do, it will call `signal_fn` with + /// `signal_ctx` as argument. + /// + /// See [`Executor`] docs for details on `signal_fn`. + pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { + #[cfg(feature = "time")] + let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; + #[cfg(feature = "time")] + driver::set_alarm_callback(alarm, signal_fn, signal_ctx); + + Self { + run_queue: RunQueue::new(), + signal_fn, + signal_ctx, + + #[cfg(feature = "time")] + timer_queue: timer_queue::TimerQueue::new(), + #[cfg(feature = "time")] + alarm, + } + } + + /// Enqueue a task in the task queue + /// + /// # Safety + /// - `task` must be a valid pointer to a spawned task. + /// - `task` must be set up to run in this executor. + /// - `task` must NOT be already enqueued (in this executor or another one). + #[inline(always)] + unsafe fn enqueue(&self, cs: CriticalSection, task: *mut TaskHeader) { + if self.run_queue.enqueue(cs, task) { + (self.signal_fn)(self.signal_ctx) + } + } + + /// Spawn a task in this executor. + /// + /// # Safety + /// + /// `task` must be a valid pointer to an initialized but not-already-spawned task. + /// + /// It is OK to use `unsafe` to call this from a thread that's not the executor thread. + /// In this case, the task's Future must be Send. This is because this is effectively + /// sending the task to the executor thread. + pub(super) unsafe fn spawn(&'static self, task: NonNull) { + let task = task.as_ref(); + task.executor.set(self); + + critical_section::with(|cs| { + self.enqueue(cs, task as *const _ as _); + }) + } + + /// Poll all queued tasks in this executor. + /// + /// This loops over all tasks that are queued to be polled (i.e. they're + /// freshly spawned or they've been woken). Other tasks are not polled. + /// + /// You must call `poll` after receiving a call to `signal_fn`. It is OK + /// to call `poll` even when not requested by `signal_fn`, but it wastes + /// energy. + /// + /// # Safety + /// + /// You must NOT call `poll` reentrantly on the same executor. + /// + /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you + /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to + /// somehow schedule for `poll()` to be called later, at a time you know for sure there's + /// no `poll()` already running. + pub unsafe fn poll(&'static self) { + #[cfg(feature = "time")] + self.timer_queue.dequeue_expired(Instant::now(), |p| { + p.as_ref().enqueue(); + }); + + self.run_queue.dequeue_all(|p| { + let task = p.as_ref(); + + #[cfg(feature = "time")] + task.expires_at.set(Instant::MAX); + + let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); + if state & STATE_SPAWNED == 0 { + // If task is not running, ignore it. This can happen in the following scenario: + // - Task gets dequeued, poll starts + // - While task is being polled, it gets woken. It gets placed in the queue. + // - Task poll finishes, returning done=true + // - RUNNING bit is cleared, but the task is already in the queue. + return; + } + + // Run the task + task.poll_fn.read()(p as _); + + // Enqueue or update into timer_queue + #[cfg(feature = "time")] + self.timer_queue.update(p); + }); + + #[cfg(feature = "time")] + { + // If this is already in the past, set_alarm will immediately trigger the alarm. + // This will cause `signal_fn` to be called, which will cause `poll()` to be called again, + // so we immediately do another poll loop iteration. + let next_expiration = self.timer_queue.next_expiration(); + driver::set_alarm(self.alarm, next_expiration.as_ticks()); + } + } + + /// Get a spawner that spawns tasks in this executor. + /// + /// It is OK to call this method multiple times to obtain multiple + /// `Spawner`s. You may also copy `Spawner`s. + pub fn spawner(&'static self) -> super::Spawner { + super::Spawner::new(self) + } +} + +/// Wake a task by raw pointer. +/// +/// You can obtain task pointers from `Waker`s using [`task_from_waker`]. +/// +/// # Safety +/// +/// `task` must be a valid task pointer obtained from [`task_from_waker`]. +pub unsafe fn wake_task(task: NonNull) { + task.as_ref().enqueue(); +} + +#[cfg(feature = "time")] +pub(crate) unsafe fn register_timer(at: Instant, waker: &core::task::Waker) { + let task = waker::task_from_waker(waker); + let task = task.as_ref(); + let expires_at = task.expires_at.get(); + task.expires_at.set(expires_at.min(at)); +} diff --git a/embassy-executor/src/executor/raw/run_queue.rs b/embassy-executor/src/executor/raw/run_queue.rs new file mode 100644 index 000000000..31615da7e --- /dev/null +++ b/embassy-executor/src/executor/raw/run_queue.rs @@ -0,0 +1,74 @@ +use core::ptr; +use core::ptr::NonNull; + +use atomic_polyfill::{AtomicPtr, Ordering}; +use critical_section::CriticalSection; + +use super::TaskHeader; + +pub(crate) struct RunQueueItem { + next: AtomicPtr, +} + +impl RunQueueItem { + pub const fn new() -> Self { + Self { + next: AtomicPtr::new(ptr::null_mut()), + } + } +} + +/// Atomic task queue using a very, very simple lock-free linked-list queue: +/// +/// To enqueue a task, task.next is set to the old head, and head is atomically set to task. +/// +/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with +/// null. Then the batch is iterated following the next pointers until null is reached. +/// +/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK +/// for our purposes: it can't create fairness problems since the next batch won't run until the +/// current batch is completely processed, so even if a task enqueues itself instantly (for example +/// by waking its own waker) can't prevent other tasks from running. +pub(crate) struct RunQueue { + head: AtomicPtr, +} + +impl RunQueue { + pub const fn new() -> Self { + Self { + head: AtomicPtr::new(ptr::null_mut()), + } + } + + /// Enqueues an item. Returns true if the queue was empty. + /// + /// # Safety + /// + /// `item` must NOT be already enqueued in any queue. + #[inline(always)] + pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: *mut TaskHeader) -> bool { + let prev = self.head.load(Ordering::Relaxed); + (*task).run_queue_item.next.store(prev, Ordering::Relaxed); + self.head.store(task, Ordering::Relaxed); + prev.is_null() + } + + /// Empty the queue, then call `on_task` for each task that was in the queue. + /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue + /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. + pub(crate) fn dequeue_all(&self, on_task: impl Fn(NonNull)) { + // Atomically empty the queue. + let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); + + // Iterate the linked list of tasks that were previously in the queue. + while let Some(task) = NonNull::new(ptr) { + // If the task re-enqueues itself, the `next` pointer will get overwritten. + // Therefore, first read the next pointer, and only then process the task. + let next = unsafe { task.as_ref() }.run_queue_item.next.load(Ordering::Relaxed); + + on_task(task); + + ptr = next + } + } +} diff --git a/embassy-executor/src/executor/raw/timer_queue.rs b/embassy-executor/src/executor/raw/timer_queue.rs new file mode 100644 index 000000000..62fcfc531 --- /dev/null +++ b/embassy-executor/src/executor/raw/timer_queue.rs @@ -0,0 +1,85 @@ +use core::cell::Cell; +use core::cmp::min; +use core::ptr; +use core::ptr::NonNull; + +use atomic_polyfill::Ordering; + +use super::{TaskHeader, STATE_TIMER_QUEUED}; +use crate::time::Instant; + +pub(crate) struct TimerQueueItem { + next: Cell<*mut TaskHeader>, +} + +impl TimerQueueItem { + pub const fn new() -> Self { + Self { + next: Cell::new(ptr::null_mut()), + } + } +} + +pub(crate) struct TimerQueue { + head: Cell<*mut TaskHeader>, +} + +impl TimerQueue { + pub const fn new() -> Self { + Self { + head: Cell::new(ptr::null_mut()), + } + } + + pub(crate) unsafe fn update(&self, p: NonNull) { + let task = p.as_ref(); + if task.expires_at.get() != Instant::MAX { + let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel); + let is_new = old_state & STATE_TIMER_QUEUED == 0; + + if is_new { + task.timer_queue_item.next.set(self.head.get()); + self.head.set(p.as_ptr()); + } + } + } + + pub(crate) unsafe fn next_expiration(&self) -> Instant { + let mut res = Instant::MAX; + self.retain(|p| { + let task = p.as_ref(); + let expires = task.expires_at.get(); + res = min(res, expires); + expires != Instant::MAX + }); + res + } + + pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(NonNull)) { + self.retain(|p| { + let task = p.as_ref(); + if task.expires_at.get() <= now { + on_task(p); + false + } else { + true + } + }); + } + + pub(crate) unsafe fn retain(&self, mut f: impl FnMut(NonNull) -> bool) { + let mut prev = &self.head; + while !prev.get().is_null() { + let p = NonNull::new_unchecked(prev.get()); + let task = &*p.as_ptr(); + if f(p) { + // Skip to next + prev = &task.timer_queue_item.next; + } else { + // Remove it + prev.set(task.timer_queue_item.next.get()); + task.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel); + } + } + } +} diff --git a/embassy-executor/src/executor/raw/util.rs b/embassy-executor/src/executor/raw/util.rs new file mode 100644 index 000000000..ed5822188 --- /dev/null +++ b/embassy-executor/src/executor/raw/util.rs @@ -0,0 +1,33 @@ +use core::cell::UnsafeCell; +use core::mem::MaybeUninit; +use core::ptr; + +pub(crate) struct UninitCell(MaybeUninit>); +impl UninitCell { + pub const fn uninit() -> Self { + Self(MaybeUninit::uninit()) + } + + pub unsafe fn as_mut_ptr(&self) -> *mut T { + (*self.0.as_ptr()).get() + } + + #[allow(clippy::mut_from_ref)] + pub unsafe fn as_mut(&self) -> &mut T { + &mut *self.as_mut_ptr() + } + + pub unsafe fn write(&self, val: T) { + ptr::write(self.as_mut_ptr(), val) + } + + pub unsafe fn drop_in_place(&self) { + ptr::drop_in_place(self.as_mut_ptr()) + } +} + +impl UninitCell { + pub unsafe fn read(&self) -> T { + ptr::read(self.as_mut_ptr()) + } +} diff --git a/embassy-executor/src/executor/raw/waker.rs b/embassy-executor/src/executor/raw/waker.rs new file mode 100644 index 000000000..f6ae332fa --- /dev/null +++ b/embassy-executor/src/executor/raw/waker.rs @@ -0,0 +1,53 @@ +use core::mem; +use core::ptr::NonNull; +use core::task::{RawWaker, RawWakerVTable, Waker}; + +use super::TaskHeader; + +const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); + +unsafe fn clone(p: *const ()) -> RawWaker { + RawWaker::new(p, &VTABLE) +} + +unsafe fn wake(p: *const ()) { + (*(p as *mut TaskHeader)).enqueue() +} + +unsafe fn drop(_: *const ()) { + // nop +} + +pub(crate) unsafe fn from_task(p: NonNull) -> Waker { + Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE)) +} + +/// Get a task pointer from a waker. +/// +/// This can be used as an optimization in wait queues to store task pointers +/// (1 word) instead of full Wakers (2 words). This saves a bit of RAM and helps +/// avoid dynamic dispatch. +/// +/// You can use the returned task pointer to wake the task with [`wake_task`](super::wake_task). +/// +/// # Panics +/// +/// Panics if the waker is not created by the Embassy executor. +pub fn task_from_waker(waker: &Waker) -> NonNull { + // safety: OK because WakerHack has the same layout as Waker. + // This is not really guaranteed because the structs are `repr(Rust)`, it is + // indeed the case in the current implementation. + // TODO use waker_getters when stable. https://github.com/rust-lang/rust/issues/96992 + let hack: &WakerHack = unsafe { mem::transmute(waker) }; + if hack.vtable != &VTABLE { + panic!("Found waker not created by the Embassy executor. `embassy_executor::time::Timer` only works with the Embassy executor.") + } + + // safety: we never create a waker with a null data pointer. + unsafe { NonNull::new_unchecked(hack.data as *mut TaskHeader) } +} + +struct WakerHack { + data: *const (), + vtable: &'static RawWakerVTable, +} diff --git a/embassy-executor/src/executor/spawner.rs b/embassy-executor/src/executor/spawner.rs new file mode 100644 index 000000000..25a0d7dbb --- /dev/null +++ b/embassy-executor/src/executor/spawner.rs @@ -0,0 +1,202 @@ +use core::marker::PhantomData; +use core::mem; +use core::ptr::NonNull; +use core::task::Poll; + +use futures_util::future::poll_fn; + +use super::raw; + +/// Token to spawn a newly-created task in an executor. +/// +/// When calling a task function (like `#[embassy_executor::task] async fn my_task() { ... }`), the returned +/// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must +/// then spawn it into an executor, typically with [`Spawner::spawn()`]. +/// +/// The generic parameter `S` determines whether the task can be spawned in executors +/// in other threads or not. If `S: Send`, it can, which allows spawning it into a [`SendSpawner`]. +/// If not, it can't, so it can only be spawned into the current thread's executor, with [`Spawner`]. +/// +/// # Panics +/// +/// Dropping a SpawnToken instance panics. You may not "abort" spawning a task in this way. +/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. +#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] +pub struct SpawnToken { + raw_task: Option>, + phantom: PhantomData<*mut S>, +} + +impl SpawnToken { + pub(crate) unsafe fn new(raw_task: NonNull) -> Self { + Self { + raw_task: Some(raw_task), + phantom: PhantomData, + } + } + + pub(crate) fn new_failed() -> Self { + Self { + raw_task: None, + phantom: PhantomData, + } + } +} + +impl Drop for SpawnToken { + fn drop(&mut self) { + // TODO deallocate the task instead. + panic!("SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()") + } +} + +/// Error returned when spawning a task. +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SpawnError { + /// Too many instances of this task are already running. + /// + /// By default, a task marked with `#[embassy_executor::task]` can only have one instance + /// running at a time. You may allow multiple instances to run in parallel with + /// `#[embassy_executor::task(pool_size = 4)]`, at the cost of higher RAM usage. + Busy, +} + +/// Handle to spawn tasks into an executor. +/// +/// This Spawner can spawn any task (Send and non-Send ones), but it can +/// only be used in the executor thread (it is not Send itself). +/// +/// If you want to spawn tasks from another thread, use [SendSpawner]. +#[derive(Copy, Clone)] +pub struct Spawner { + executor: &'static raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl Spawner { + pub(crate) fn new(executor: &'static raw::Executor) -> Self { + Self { + executor, + not_send: PhantomData, + } + } + + /// Get a Spawner for the current executor. + /// + /// This function is `async` just to get access to the current async + /// context. It returns instantly, it does not block/yield. + /// + /// # Panics + /// + /// Panics if the current executor is not an Embassy executor. + pub async fn for_current_executor() -> Self { + poll_fn(|cx| unsafe { + let task = raw::task_from_waker(cx.waker()); + let executor = (*task.as_ptr()).executor.get(); + Poll::Ready(Self::new(&*executor)) + }) + .await + } + + /// Spawn a task into an executor. + /// + /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). + pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + let task = token.raw_task; + mem::forget(token); + + match task { + Some(task) => { + unsafe { self.executor.spawn(task) }; + Ok(()) + } + None => Err(SpawnError::Busy), + } + } + + // Used by the `embassy_macros::main!` macro to throw an error when spawn + // fails. This is here to allow conditional use of `defmt::unwrap!` + // without introducing a `defmt` feature in the `embassy_macros` package, + // which would require use of `-Z namespaced-features`. + /// Spawn a task into an executor, panicking on failure. + /// + /// # Panics + /// + /// Panics if the spawning fails. + pub fn must_spawn(&self, token: SpawnToken) { + unwrap!(self.spawn(token)); + } + + /// Convert this Spawner to a SendSpawner. This allows you to send the + /// spawner to other threads, but the spawner loses the ability to spawn + /// non-Send tasks. + pub fn make_send(&self) -> SendSpawner { + SendSpawner { + executor: self.executor, + } + } +} + +/// Handle to spawn tasks into an executor from any thread. +/// +/// This Spawner can be used from any thread (it is Send), but it can +/// only spawn Send tasks. The reason for this is spawning is effectively +/// "sending" the tasks to the executor thread. +/// +/// If you want to spawn non-Send tasks, use [Spawner]. +#[derive(Copy, Clone)] +pub struct SendSpawner { + executor: &'static raw::Executor, +} + +unsafe impl Send for SendSpawner {} +unsafe impl Sync for SendSpawner {} + +impl SendSpawner { + pub(crate) fn new(executor: &'static raw::Executor) -> Self { + Self { executor } + } + + /// Get a Spawner for the current executor. + /// + /// This function is `async` just to get access to the current async + /// context. It returns instantly, it does not block/yield. + /// + /// # Panics + /// + /// Panics if the current executor is not an Embassy executor. + pub async fn for_current_executor() -> Self { + poll_fn(|cx| unsafe { + let task = raw::task_from_waker(cx.waker()); + let executor = (*task.as_ptr()).executor.get(); + Poll::Ready(Self::new(&*executor)) + }) + .await + } + + /// Spawn a task into an executor. + /// + /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). + pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + let header = token.raw_task; + mem::forget(token); + + match header { + Some(header) => { + unsafe { self.executor.spawn(header) }; + Ok(()) + } + None => Err(SpawnError::Busy), + } + } + + /// Spawn a task into an executor, panicking on failure. + /// + /// # Panics + /// + /// Panics if the spawning fails. + pub fn must_spawn(&self, token: SpawnToken) { + unwrap!(self.spawn(token)); + } +} diff --git a/embassy-executor/src/fmt.rs b/embassy-executor/src/fmt.rs new file mode 100644 index 000000000..f8bb0a035 --- /dev/null +++ b/embassy-executor/src/fmt.rs @@ -0,0 +1,228 @@ +#![macro_use] +#![allow(unused_macros)] + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[cfg(feature = "defmt-timestamp-uptime")] +defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs new file mode 100644 index 000000000..69e4aeb4b --- /dev/null +++ b/embassy-executor/src/lib.rs @@ -0,0 +1,22 @@ +#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] +#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(all(feature = "nightly", target_arch = "xtensa"), feature(asm_experimental_arch))] +#![allow(clippy::new_without_default)] +#![doc = include_str!("../../README.md")] +#![warn(missing_docs)] + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +pub mod executor; +#[cfg(feature = "time")] +pub mod time; + +#[cfg(feature = "nightly")] +pub use embassy_macros::{main, task}; + +#[doc(hidden)] +/// Implementation details for embassy macros. DO NOT USE. +pub mod export { + pub use atomic_polyfill as atomic; +} diff --git a/embassy-executor/src/time/delay.rs b/embassy-executor/src/time/delay.rs new file mode 100644 index 000000000..d76ed32eb --- /dev/null +++ b/embassy-executor/src/time/delay.rs @@ -0,0 +1,98 @@ +use super::{Duration, Instant}; + +/// Blocks for at least `duration`. +pub fn block_for(duration: Duration) { + let expires_at = Instant::now() + duration; + while Instant::now() < expires_at {} +} + +/// Type implementing async delays and blocking `embedded-hal` delays. +/// +/// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least +/// the amount provided, but accuracy can be affected by many factors, including interrupt usage. +/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently +/// active driver. +pub struct Delay; + +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl embedded_hal_1::delay::blocking::DelayUs for Delay { + type Error = core::convert::Infallible; + + fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + Ok(block_for(Duration::from_micros(us as u64))) + } + + fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + Ok(block_for(Duration::from_millis(ms as u64))) + } + } +} + +cfg_if::cfg_if! { + if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { + use crate::time::Timer; + use core::future::Future; + use futures_util::FutureExt; + + impl embedded_hal_async::delay::DelayUs for Delay { + type Error = core::convert::Infallible; + + type DelayUsFuture<'a> = impl Future> + 'a where Self: 'a; + + fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> { + Timer::after(Duration::from_micros(micros as _)).map(Ok) + } + + type DelayMsFuture<'a> = impl Future> + 'a where Self: 'a; + + fn delay_ms(&mut self, millis: u32) -> Self::DelayMsFuture<'_> { + Timer::after(Duration::from_millis(millis as _)).map(Ok) + } + } + } +} + +mod eh02 { + use embedded_hal_02::blocking::delay::{DelayMs, DelayUs}; + + use super::*; + + impl DelayMs for Delay { + fn delay_ms(&mut self, ms: u8) { + block_for(Duration::from_millis(ms as u64)) + } + } + + impl DelayMs for Delay { + fn delay_ms(&mut self, ms: u16) { + block_for(Duration::from_millis(ms as u64)) + } + } + + impl DelayMs for Delay { + fn delay_ms(&mut self, ms: u32) { + block_for(Duration::from_millis(ms as u64)) + } + } + + impl DelayUs for Delay { + fn delay_us(&mut self, us: u8) { + block_for(Duration::from_micros(us as u64)) + } + } + + impl DelayUs for Delay { + fn delay_us(&mut self, us: u16) { + block_for(Duration::from_micros(us as u64)) + } + } + + impl DelayUs for Delay { + fn delay_us(&mut self, us: u32) { + block_for(Duration::from_micros(us as u64)) + } + } +} diff --git a/embassy-executor/src/time/driver.rs b/embassy-executor/src/time/driver.rs new file mode 100644 index 000000000..48e2f1c7d --- /dev/null +++ b/embassy-executor/src/time/driver.rs @@ -0,0 +1,170 @@ +//! Time driver interface +//! +//! This module defines the interface a driver needs to implement to power the `embassy_executor::time` module. +//! +//! # Implementing a driver +//! +//! - Define a struct `MyDriver` +//! - Implement [`Driver`] for it +//! - Register it as the global driver with [`time_driver_impl`]. +//! - Enable the Cargo features `embassy-executor/time` and one of `embassy-executor/time-tick-*` corresponding to the +//! tick rate of your driver. +//! +//! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own +//! Cargo features and having each enable the corresponding `embassy-executor/time-tick-*`. +//! +//! # Linkage details +//! +//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. +//! +//! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. +//! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the +//! calls from the `embassy` crate to call into the driver crate. +//! +//! If there is none or multiple drivers in the crate tree, linking will fail. +//! +//! This method has a few key advantages for something as foundational as timekeeping: +//! +//! - The time driver is available everywhere easily, without having to thread the implementation +//! through generic parameters. This is especially helpful for libraries. +//! - It means comparing `Instant`s will always make sense: if there were multiple drivers +//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which +//! would yield incorrect results. +//! +//! # Example +//! +//! ``` +//! use embassy_executor::time::driver::{Driver, AlarmHandle}; +//! +//! struct MyDriver{}; // not public! +//! embassy_executor::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); +//! +//! impl Driver for MyDriver { +//! fn now(&self) -> u64 { +//! todo!() +//! } +//! unsafe fn allocate_alarm(&self) -> Option { +//! todo!() +//! } +//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { +//! todo!() +//! } +//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { +//! todo!() +//! } +//! } +//! ``` + +/// Alarm handle, assigned by the driver. +#[derive(Clone, Copy)] +pub struct AlarmHandle { + id: u8, +} + +impl AlarmHandle { + /// Create an AlarmHandle + /// + /// Safety: May only be called by the current global Driver impl. + /// The impl is allowed to rely on the fact that all `AlarmHandle` instances + /// are created by itself in unsafe code (e.g. indexing operations) + pub unsafe fn new(id: u8) -> Self { + Self { id } + } + + /// Get the ID of the AlarmHandle. + pub fn id(&self) -> u8 { + self.id + } +} + +/// Time driver +pub trait Driver: Send + Sync + 'static { + /// Return the current timestamp in ticks. + /// + /// Implementations MUST ensure that: + /// - This is guaranteed to be monotonic, i.e. a call to now() will always return + /// a greater or equal value than earler calls. Time can't "roll backwards". + /// - It "never" overflows. It must not overflow in a sufficiently long time frame, say + /// in 10_000 years (Human civilization is likely to already have self-destructed + /// 10_000 years from now.). This means if your hardware only has 16bit/32bit timers + /// you MUST extend them to 64-bit, for example by counting overflows in software, + /// or chaining multiple timers together. + fn now(&self) -> u64; + + /// Try allocating an alarm handle. Returns None if no alarms left. + /// Initially the alarm has no callback set, and a null `ctx` pointer. + /// + /// # Safety + /// It is UB to make the alarm fire before setting a callback. + unsafe fn allocate_alarm(&self) -> Option; + + /// Sets the callback function to be called when the alarm triggers. + /// The callback may be called from any context (interrupt or thread mode). + fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); + + /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm + /// timestamp, the provided callback function will be called. + /// + /// If `timestamp` is already in the past, the alarm callback must be immediately fired. + /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`. + /// + /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. + /// + /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); +} + +extern "Rust" { + fn _embassy_time_now() -> u64; + fn _embassy_time_allocate_alarm() -> Option; + fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); + fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); +} + +pub(crate) fn now() -> u64 { + unsafe { _embassy_time_now() } +} +/// Safety: it is UB to make the alarm fire before setting a callback. +pub(crate) unsafe fn allocate_alarm() -> Option { + _embassy_time_allocate_alarm() +} +pub(crate) fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) } +} +pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) { + unsafe { _embassy_time_set_alarm(alarm, timestamp) } +} + +/// Set the time Driver implementation. +/// +/// See the module documentation for an example. +#[macro_export] +macro_rules! time_driver_impl { + (static $name:ident: $t: ty = $val:expr) => { + static $name: $t = $val; + + #[no_mangle] + fn _embassy_time_now() -> u64 { + <$t as $crate::time::driver::Driver>::now(&$name) + } + + #[no_mangle] + unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::time::driver::AlarmHandle> { + <$t as $crate::time::driver::Driver>::allocate_alarm(&$name) + } + + #[no_mangle] + fn _embassy_time_set_alarm_callback( + alarm: $crate::time::driver::AlarmHandle, + callback: fn(*mut ()), + ctx: *mut (), + ) { + <$t as $crate::time::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx) + } + + #[no_mangle] + fn _embassy_time_set_alarm(alarm: $crate::time::driver::AlarmHandle, timestamp: u64) { + <$t as $crate::time::driver::Driver>::set_alarm(&$name, alarm, timestamp) + } + }; +} diff --git a/embassy-executor/src/time/driver_std.rs b/embassy-executor/src/time/driver_std.rs new file mode 100644 index 000000000..cb66f7c19 --- /dev/null +++ b/embassy-executor/src/time/driver_std.rs @@ -0,0 +1,208 @@ +use std::cell::UnsafeCell; +use std::mem::MaybeUninit; +use std::sync::{Condvar, Mutex, Once}; +use std::time::{Duration as StdDuration, Instant as StdInstant}; +use std::{mem, ptr, thread}; + +use atomic_polyfill::{AtomicU8, Ordering}; + +use crate::time::driver::{AlarmHandle, Driver}; + +const ALARM_COUNT: usize = 4; + +struct AlarmState { + timestamp: u64, + + // This is really a Option<(fn(*mut ()), *mut ())> + // but fn pointers aren't allowed in const yet + callback: *const (), + ctx: *mut (), +} + +unsafe impl Send for AlarmState {} + +impl AlarmState { + const fn new() -> Self { + Self { + timestamp: u64::MAX, + callback: ptr::null(), + ctx: ptr::null_mut(), + } + } +} + +struct TimeDriver { + alarm_count: AtomicU8, + + once: Once, + alarms: UninitCell>, + zero_instant: UninitCell, + signaler: UninitCell, +} + +const ALARM_NEW: AlarmState = AlarmState::new(); +crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { + alarm_count: AtomicU8::new(0), + + once: Once::new(), + alarms: UninitCell::uninit(), + zero_instant: UninitCell::uninit(), + signaler: UninitCell::uninit(), +}); + +impl TimeDriver { + fn init(&self) { + self.once.call_once(|| unsafe { + self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); + self.zero_instant.write(StdInstant::now()); + self.signaler.write(Signaler::new()); + + thread::spawn(Self::alarm_thread); + }); + } + + fn alarm_thread() { + let zero = unsafe { DRIVER.zero_instant.read() }; + loop { + let now = DRIVER.now(); + + let mut next_alarm = u64::MAX; + { + let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap(); + for alarm in alarms { + if alarm.timestamp <= now { + alarm.timestamp = u64::MAX; + + // Call after clearing alarm, so the callback can set another alarm. + + // safety: + // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. + // - other than that we only store valid function pointers into alarm.callback + let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) }; + f(alarm.ctx); + } else { + next_alarm = next_alarm.min(alarm.timestamp); + } + } + } + + // Ensure we don't overflow + let until = zero + .checked_add(StdDuration::from_micros(next_alarm)) + .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1)); + + unsafe { DRIVER.signaler.as_ref() }.wait_until(until); + } + } +} + +impl Driver for TimeDriver { + fn now(&self) -> u64 { + self.init(); + + let zero = unsafe { self.zero_instant.read() }; + StdInstant::now().duration_since(zero).as_micros() as u64 + } + + unsafe fn allocate_alarm(&self) -> Option { + let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { + if x < ALARM_COUNT as u8 { + Some(x + 1) + } else { + None + } + }); + + match id { + Ok(id) => Some(AlarmHandle::new(id)), + Err(_) => None, + } + } + + fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + self.init(); + let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); + let alarm = &mut alarms[alarm.id() as usize]; + alarm.callback = callback as *const (); + alarm.ctx = ctx; + } + + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + self.init(); + let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); + let alarm = &mut alarms[alarm.id() as usize]; + alarm.timestamp = timestamp; + unsafe { self.signaler.as_ref() }.signal(); + } +} + +struct Signaler { + mutex: Mutex, + condvar: Condvar, +} + +impl Signaler { + fn new() -> Self { + Self { + mutex: Mutex::new(false), + condvar: Condvar::new(), + } + } + + fn wait_until(&self, until: StdInstant) { + let mut signaled = self.mutex.lock().unwrap(); + while !*signaled { + let now = StdInstant::now(); + + if now >= until { + break; + } + + let dur = until - now; + let (signaled2, timeout) = self.condvar.wait_timeout(signaled, dur).unwrap(); + signaled = signaled2; + if timeout.timed_out() { + break; + } + } + *signaled = false; + } + + fn signal(&self) { + let mut signaled = self.mutex.lock().unwrap(); + *signaled = true; + self.condvar.notify_one(); + } +} + +pub(crate) struct UninitCell(MaybeUninit>); +unsafe impl Send for UninitCell {} +unsafe impl Sync for UninitCell {} + +impl UninitCell { + pub const fn uninit() -> Self { + Self(MaybeUninit::uninit()) + } + + pub unsafe fn as_ptr(&self) -> *const T { + (*self.0.as_ptr()).get() + } + + pub unsafe fn as_mut_ptr(&self) -> *mut T { + (*self.0.as_ptr()).get() + } + + pub unsafe fn as_ref(&self) -> &T { + &*self.as_ptr() + } + + pub unsafe fn write(&self, val: T) { + ptr::write(self.as_mut_ptr(), val) + } +} + +impl UninitCell { + pub unsafe fn read(&self) -> T { + ptr::read(self.as_mut_ptr()) + } +} diff --git a/embassy-executor/src/time/driver_wasm.rs b/embassy-executor/src/time/driver_wasm.rs new file mode 100644 index 000000000..5f585a19a --- /dev/null +++ b/embassy-executor/src/time/driver_wasm.rs @@ -0,0 +1,134 @@ +use std::cell::UnsafeCell; +use std::mem::MaybeUninit; +use std::ptr; +use std::sync::{Mutex, Once}; + +use atomic_polyfill::{AtomicU8, Ordering}; +use wasm_bindgen::prelude::*; +use wasm_timer::Instant as StdInstant; + +use crate::time::driver::{AlarmHandle, Driver}; + +const ALARM_COUNT: usize = 4; + +struct AlarmState { + token: Option, + closure: Option>, +} + +unsafe impl Send for AlarmState {} + +impl AlarmState { + const fn new() -> Self { + Self { + token: None, + closure: None, + } + } +} + +#[wasm_bindgen] +extern "C" { + fn setTimeout(closure: &Closure, millis: u32) -> f64; + fn clearTimeout(token: f64); +} + +struct TimeDriver { + alarm_count: AtomicU8, + + once: Once, + alarms: UninitCell>, + zero_instant: UninitCell, +} + +const ALARM_NEW: AlarmState = AlarmState::new(); +crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { + alarm_count: AtomicU8::new(0), + once: Once::new(), + alarms: UninitCell::uninit(), + zero_instant: UninitCell::uninit(), +}); + +impl TimeDriver { + fn init(&self) { + self.once.call_once(|| unsafe { + self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); + self.zero_instant.write(StdInstant::now()); + }); + } +} + +impl Driver for TimeDriver { + fn now(&self) -> u64 { + self.init(); + + let zero = unsafe { self.zero_instant.read() }; + StdInstant::now().duration_since(zero).as_micros() as u64 + } + + unsafe fn allocate_alarm(&self) -> Option { + let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { + if x < ALARM_COUNT as u8 { + Some(x + 1) + } else { + None + } + }); + + match id { + Ok(id) => Some(AlarmHandle::new(id)), + Err(_) => None, + } + } + + fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + self.init(); + let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); + let alarm = &mut alarms[alarm.id() as usize]; + alarm.closure.replace(Closure::new(move || { + callback(ctx); + })); + } + + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + self.init(); + let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); + let alarm = &mut alarms[alarm.id() as usize]; + let timeout = (timestamp - self.now()) as u32; + if let Some(token) = alarm.token { + clearTimeout(token); + } + alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); + } +} + +pub(crate) struct UninitCell(MaybeUninit>); +unsafe impl Send for UninitCell {} +unsafe impl Sync for UninitCell {} + +impl UninitCell { + pub const fn uninit() -> Self { + Self(MaybeUninit::uninit()) + } + unsafe fn as_ptr(&self) -> *const T { + (*self.0.as_ptr()).get() + } + + pub unsafe fn as_mut_ptr(&self) -> *mut T { + (*self.0.as_ptr()).get() + } + + pub unsafe fn as_ref(&self) -> &T { + &*self.as_ptr() + } + + pub unsafe fn write(&self, val: T) { + ptr::write(self.as_mut_ptr(), val) + } +} + +impl UninitCell { + pub unsafe fn read(&self) -> T { + ptr::read(self.as_mut_ptr()) + } +} diff --git a/embassy-executor/src/time/duration.rs b/embassy-executor/src/time/duration.rs new file mode 100644 index 000000000..dc4f16bd4 --- /dev/null +++ b/embassy-executor/src/time/duration.rs @@ -0,0 +1,184 @@ +use core::fmt; +use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; + +use super::{GCD_1K, GCD_1M, TICKS_PER_SECOND}; + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Represents the difference between two [Instant](struct.Instant.html)s +pub struct Duration { + pub(crate) ticks: u64, +} + +impl Duration { + /// The smallest value that can be represented by the `Duration` type. + pub const MIN: Duration = Duration { ticks: u64::MIN }; + /// The largest value that can be represented by the `Duration` type. + pub const MAX: Duration = Duration { ticks: u64::MAX }; + + /// Tick count of the `Duration`. + pub const fn as_ticks(&self) -> u64 { + self.ticks + } + + /// Convert the `Duration` to seconds, rounding down. + pub const fn as_secs(&self) -> u64 { + self.ticks / TICKS_PER_SECOND + } + + /// Convert the `Duration` to milliseconds, rounding down. + pub const fn as_millis(&self) -> u64 { + self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) + } + + /// Convert the `Duration` to microseconds, rounding down. + pub const fn as_micros(&self) -> u64 { + self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) + } + + /// Creates a duration from the specified number of clock ticks + pub const fn from_ticks(ticks: u64) -> Duration { + Duration { ticks } + } + + /// Creates a duration from the specified number of seconds, rounding up. + pub const fn from_secs(secs: u64) -> Duration { + Duration { + ticks: secs * TICKS_PER_SECOND, + } + } + + /// Creates a duration from the specified number of milliseconds, rounding up. + pub const fn from_millis(millis: u64) -> Duration { + Duration { + ticks: div_ceil(millis * (TICKS_PER_SECOND / GCD_1K), 1000 / GCD_1K), + } + } + + /// Creates a duration from the specified number of microseconds, rounding up. + /// NOTE: Delays this small may be inaccurate. + pub const fn from_micros(micros: u64) -> Duration { + Duration { + ticks: div_ceil(micros * (TICKS_PER_SECOND / GCD_1M), 1_000_000 / GCD_1M), + } + } + + /// Creates a duration from the specified number of seconds, rounding down. + pub const fn from_secs_floor(secs: u64) -> Duration { + Duration { + ticks: secs * TICKS_PER_SECOND, + } + } + + /// Creates a duration from the specified number of milliseconds, rounding down. + pub const fn from_millis_floor(millis: u64) -> Duration { + Duration { + ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), + } + } + + /// Creates a duration from the specified number of microseconds, rounding down. + /// NOTE: Delays this small may be inaccurate. + pub const fn from_micros_floor(micros: u64) -> Duration { + Duration { + ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), + } + } + + /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. + pub fn checked_add(self, rhs: Duration) -> Option { + self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks }) + } + + /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow. + pub fn checked_sub(self, rhs: Duration) -> Option { + self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks }) + } + + /// Multiplies one Duration by a scalar u32, returning a new Duration or None in the event of an overflow. + pub fn checked_mul(self, rhs: u32) -> Option { + self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks }) + } + + /// Divides one Duration a scalar u32, returning a new Duration or None in the event of an overflow. + pub fn checked_div(self, rhs: u32) -> Option { + self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks }) + } +} + +impl Add for Duration { + type Output = Duration; + + fn add(self, rhs: Duration) -> Duration { + self.checked_add(rhs).expect("overflow when adding durations") + } +} + +impl AddAssign for Duration { + fn add_assign(&mut self, rhs: Duration) { + *self = *self + rhs; + } +} + +impl Sub for Duration { + type Output = Duration; + + fn sub(self, rhs: Duration) -> Duration { + self.checked_sub(rhs).expect("overflow when subtracting durations") + } +} + +impl SubAssign for Duration { + fn sub_assign(&mut self, rhs: Duration) { + *self = *self - rhs; + } +} + +impl Mul for Duration { + type Output = Duration; + + fn mul(self, rhs: u32) -> Duration { + self.checked_mul(rhs) + .expect("overflow when multiplying duration by scalar") + } +} + +impl Mul for u32 { + type Output = Duration; + + fn mul(self, rhs: Duration) -> Duration { + rhs * self + } +} + +impl MulAssign for Duration { + fn mul_assign(&mut self, rhs: u32) { + *self = *self * rhs; + } +} + +impl Div for Duration { + type Output = Duration; + + fn div(self, rhs: u32) -> Duration { + self.checked_div(rhs) + .expect("divide by zero error when dividing duration by scalar") + } +} + +impl DivAssign for Duration { + fn div_assign(&mut self, rhs: u32) { + *self = *self / rhs; + } +} + +impl<'a> fmt::Display for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} ticks", self.ticks) + } +} + +#[inline] +const fn div_ceil(num: u64, den: u64) -> u64 { + (num + den - 1) / den +} diff --git a/embassy-executor/src/time/instant.rs b/embassy-executor/src/time/instant.rs new file mode 100644 index 000000000..6a4925f47 --- /dev/null +++ b/embassy-executor/src/time/instant.rs @@ -0,0 +1,159 @@ +use core::fmt; +use core::ops::{Add, AddAssign, Sub, SubAssign}; + +use super::{driver, Duration, GCD_1K, GCD_1M, TICKS_PER_SECOND}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// An Instant in time, based on the MCU's clock ticks since startup. +pub struct Instant { + ticks: u64, +} + +impl Instant { + /// The smallest (earliest) value that can be represented by the `Instant` type. + pub const MIN: Instant = Instant { ticks: u64::MIN }; + /// The largest (latest) value that can be represented by the `Instant` type. + pub const MAX: Instant = Instant { ticks: u64::MAX }; + + /// Returns an Instant representing the current time. + pub fn now() -> Instant { + Instant { ticks: driver::now() } + } + + /// Create an Instant from a tick count since system boot. + pub const fn from_ticks(ticks: u64) -> Self { + Self { ticks } + } + + /// Create an Instant from a microsecond count since system boot. + pub const fn from_micros(micros: u64) -> Self { + Self { + ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), + } + } + + /// Create an Instant from a millisecond count since system boot. + pub const fn from_millis(millis: u64) -> Self { + Self { + ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), + } + } + + /// Create an Instant from a second count since system boot. + pub const fn from_secs(seconds: u64) -> Self { + Self { + ticks: seconds * TICKS_PER_SECOND, + } + } + + /// Tick count since system boot. + pub const fn as_ticks(&self) -> u64 { + self.ticks + } + + /// Seconds since system boot. + pub const fn as_secs(&self) -> u64 { + self.ticks / TICKS_PER_SECOND + } + + /// Milliseconds since system boot. + pub const fn as_millis(&self) -> u64 { + self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) + } + + /// Microseconds since system boot. + pub const fn as_micros(&self) -> u64 { + self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) + } + + /// Duration between this Instant and another Instant + /// Panics on over/underflow. + pub fn duration_since(&self, earlier: Instant) -> Duration { + Duration { + ticks: self.ticks.checked_sub(earlier.ticks).unwrap(), + } + } + + /// Duration between this Instant and another Instant + pub fn checked_duration_since(&self, earlier: Instant) -> Option { + if self.ticks < earlier.ticks { + None + } else { + Some(Duration { + ticks: self.ticks - earlier.ticks, + }) + } + } + + /// Returns the duration since the "earlier" Instant. + /// If the "earlier" instant is in the future, the duration is set to zero. + pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { + Duration { + ticks: if self.ticks < earlier.ticks { + 0 + } else { + self.ticks - earlier.ticks + }, + } + } + + /// Duration elapsed since this Instant. + pub fn elapsed(&self) -> Duration { + Instant::now() - *self + } + + /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow. + pub fn checked_add(&self, duration: Duration) -> Option { + self.ticks.checked_add(duration.ticks).map(|ticks| Instant { ticks }) + } + + /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow. + pub fn checked_sub(&self, duration: Duration) -> Option { + self.ticks.checked_sub(duration.ticks).map(|ticks| Instant { ticks }) + } +} + +impl Add for Instant { + type Output = Instant; + + fn add(self, other: Duration) -> Instant { + self.checked_add(other) + .expect("overflow when adding duration to instant") + } +} + +impl AddAssign for Instant { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + +impl Sub for Instant { + type Output = Instant; + + fn sub(self, other: Duration) -> Instant { + self.checked_sub(other) + .expect("overflow when subtracting duration from instant") + } +} + +impl SubAssign for Instant { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + +impl Sub for Instant { + type Output = Duration; + + fn sub(self, other: Instant) -> Duration { + self.duration_since(other) + } +} + +impl<'a> fmt::Display for Instant { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} ticks", self.ticks) + } +} diff --git a/embassy-executor/src/time/mod.rs b/embassy-executor/src/time/mod.rs new file mode 100644 index 000000000..b787a5cf2 --- /dev/null +++ b/embassy-executor/src/time/mod.rs @@ -0,0 +1,91 @@ +//! Timekeeping, delays and timeouts. +//! +//! Timekeeping is done with elapsed time since system boot. Time is represented in +//! ticks, where the tick rate is defined by the current driver, usually to match +//! the tick rate of the hardware. +//! +//! Tick counts are 64 bits. At the highest supported tick rate of 1Mhz this supports +//! representing time spans of up to ~584558 years, which is big enough for all practical +//! purposes and allows not having to worry about overflows. +//! +//! [`Instant`] represents a given instant of time (relative to system boot), and [`Duration`] +//! represents the duration of a span of time. They implement the math operations you'd expect, +//! like addition and substraction. +//! +//! # Delays and timeouts +//! +//! [`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time. +//! +//! An implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility +//! with libraries from the ecosystem. +//! +//! # Wall-clock time +//! +//! The `time` module deals exclusively with a monotonically increasing tick count. +//! Therefore it has no direct support for wall-clock time ("real life" datetimes +//! like `2021-08-24 13:33:21`). +//! +//! If persistence across reboots is not needed, support can be built on top of +//! `embassy_executor::time` by storing the offset between "seconds elapsed since boot" +//! and "seconds since unix epoch". +//! +//! # Time driver +//! +//! The `time` module is backed by a global "time driver" specified at build time. +//! Only one driver can be active in a program. +//! +//! All methods and structs transparently call into the active driver. This makes it +//! possible for libraries to use `embassy_executor::time` in a driver-agnostic way without +//! requiring generic parameters. +//! +//! For more details, check the [`driver`] module. + +#![deny(missing_docs)] + +mod delay; +pub mod driver; +mod duration; +mod instant; +mod timer; + +#[cfg(feature = "std")] +mod driver_std; + +#[cfg(feature = "wasm")] +mod driver_wasm; + +pub use delay::{block_for, Delay}; +pub use duration::Duration; +pub use instant::Instant; +pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; + +#[cfg(feature = "time-tick-1000hz")] +const TPS: u64 = 1_000; + +#[cfg(feature = "time-tick-32768hz")] +const TPS: u64 = 32_768; + +#[cfg(feature = "time-tick-1mhz")] +const TPS: u64 = 1_000_000; + +#[cfg(feature = "time-tick-16mhz")] +const TPS: u64 = 16_000_000; + +/// Ticks per second of the global timebase. +/// +/// This value is specified by the `time-tick-*` Cargo features, which +/// should be set by the time driver. Some drivers support a fixed tick rate, others +/// allow you to choose a tick rate with Cargo features of their own. You should not +/// set the `time-tick-*` features for embassy yourself as an end user. +pub const TICKS_PER_SECOND: u64 = TPS; + +const fn gcd(a: u64, b: u64) -> u64 { + if b == 0 { + a + } else { + gcd(b, a % b) + } +} + +pub(crate) const GCD_1K: u64 = gcd(TICKS_PER_SECOND, 1_000); +pub(crate) const GCD_1M: u64 = gcd(TICKS_PER_SECOND, 1_000_000); diff --git a/embassy-executor/src/time/timer.rs b/embassy-executor/src/time/timer.rs new file mode 100644 index 000000000..b9cdb1be5 --- /dev/null +++ b/embassy-executor/src/time/timer.rs @@ -0,0 +1,151 @@ +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use futures_util::future::{select, Either}; +use futures_util::{pin_mut, Stream}; + +use crate::executor::raw; +use crate::time::{Duration, Instant}; + +/// Error returned by [`with_timeout`] on timeout. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct TimeoutError; + +/// Runs a given future with a timeout. +/// +/// If the future completes before the timeout, its output is returned. Otherwise, on timeout, +/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. +pub async fn with_timeout(timeout: Duration, fut: F) -> Result { + let timeout_fut = Timer::after(timeout); + pin_mut!(fut); + match select(fut, timeout_fut).await { + Either::Left((r, _)) => Ok(r), + Either::Right(_) => Err(TimeoutError), + } +} + +/// A future that completes at a specified [Instant](struct.Instant.html). +pub struct Timer { + expires_at: Instant, + yielded_once: bool, +} + +impl Timer { + /// Expire at specified [Instant](struct.Instant.html) + pub fn at(expires_at: Instant) -> Self { + Self { + expires_at, + yielded_once: false, + } + } + + /// Expire after specified [Duration](struct.Duration.html). + /// This can be used as a `sleep` abstraction. + /// + /// Example: + /// ``` no_run + /// # #![feature(type_alias_impl_trait)] + /// # + /// # fn foo() {} + /// use embassy_executor::time::{Duration, Timer}; + /// + /// #[embassy_executor::task] + /// async fn demo_sleep_seconds() { + /// // suspend this task for one second. + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// ``` + pub fn after(duration: Duration) -> Self { + Self { + expires_at: Instant::now() + duration, + yielded_once: false, + } + } +} + +impl Unpin for Timer {} + +impl Future for Timer { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.yielded_once && self.expires_at <= Instant::now() { + Poll::Ready(()) + } else { + unsafe { raw::register_timer(self.expires_at, cx.waker()) }; + self.yielded_once = true; + Poll::Pending + } + } +} + +/// Asynchronous stream that yields every Duration, indefinitely. +/// +/// This stream will tick at uniform intervals, even if blocking work is performed between ticks. +/// +/// For instance, consider the following code fragment. +/// ``` no_run +/// # #![feature(type_alias_impl_trait)] +/// # +/// use embassy_executor::time::{Duration, Timer}; +/// # fn foo() {} +/// +/// #[embassy_executor::task] +/// async fn ticker_example_0() { +/// loop { +/// foo(); +/// Timer::after(Duration::from_secs(1)).await; +/// } +/// } +/// ``` +/// +/// This fragment will not call `foo` every second. +/// Instead, it will call it every second + the time it took to previously call `foo`. +/// +/// Example using ticker, which will consistently call `foo` once a second. +/// +/// ``` no_run +/// # #![feature(type_alias_impl_trait)] +/// # +/// use embassy_executor::time::{Duration, Ticker}; +/// use futures::StreamExt; +/// # fn foo(){} +/// +/// #[embassy_executor::task] +/// async fn ticker_example_1() { +/// let mut ticker = Ticker::every(Duration::from_secs(1)); +/// loop { +/// foo(); +/// ticker.next().await; +/// } +/// } +/// ``` +pub struct Ticker { + expires_at: Instant, + duration: Duration, +} + +impl Ticker { + /// Creates a new ticker that ticks at the specified duration interval. + pub fn every(duration: Duration) -> Self { + let expires_at = Instant::now() + duration; + Self { expires_at, duration } + } +} + +impl Unpin for Ticker {} + +impl Stream for Ticker { + type Item = (); + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.expires_at <= Instant::now() { + let dur = self.duration; + self.expires_at += dur; + Poll::Ready(Some(())) + } else { + unsafe { raw::register_timer(self.expires_at, cx.waker()) }; + Poll::Pending + } + } +} diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-common/Cargo.toml index f7ebcc21e..4a6a61003 100644 --- a/embassy-hal-common/Cargo.toml +++ b/embassy-hal-common/Cargo.toml @@ -6,9 +6,8 @@ edition = "2021" [features] [dependencies] -embassy = { version = "0.1.0", path = "../embassy" } - defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -cortex-m = "0.7.3" + +embassy-util = { version = "0.1.0", path = "../embassy-util" } num-traits = { version = "0.2.14", default-features = false } diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index d3d9e0a84..5d2649d02 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs @@ -10,13 +10,3 @@ mod peripheral; pub mod ratio; pub mod ring_buffer; pub use peripheral::{Peripheral, PeripheralRef}; - -/// Low power blocking wait loop using WFE/SEV. -pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { - while !condition() { - // WFE might "eat" an event that would have otherwise woken the executor. - cortex_m::asm::wfe(); - } - // Retrigger an event to be transparent to the executor. - cortex_m::asm::sev(); -} diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 9b6b2c652..6c1b01e67 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -8,8 +8,8 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ - { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy/time-tick-32768hz"] }, - { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy/time-tick-32768hz"] }, + { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy-executor/time-tick-32768hz"] }, + { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy-executor/time-tick-32768hz"] }, ] [lib] @@ -24,7 +24,8 @@ time = [] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy = { version = "0.1.0", path = "../embassy", default-features = false } +embassy-executor = { version = "0.1.0", path = "../embassy-executor" } +embassy-util = { version = "0.1.0", path = "../embassy-util" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index b2da22090..29ea45863 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -18,6 +18,6 @@ pub struct LoraTimer; impl lorawan_device::async_device::radio::Timer for LoraTimer { type DelayFuture<'m> = impl core::future::Future + 'm; fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> { - embassy::time::Timer::after(embassy::time::Duration::from_millis(millis)) + embassy_executor::time::Timer::after(embassy_executor::time::Duration::from_millis(millis)) } } diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index b0d101b77..4a4c5cfb7 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -2,7 +2,6 @@ use core::future::Future; use core::mem::MaybeUninit; -use embassy::channel::signal::Signal; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{AnyPin, Output}; @@ -13,6 +12,7 @@ use embassy_stm32::subghz::{ Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; use embassy_stm32::Peripheral; +use embassy_util::channel::signal::Signal; use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; use lorawan_device::async_device::Timings; diff --git a/embassy-lora/src/sx127x/sx127x_lora/mod.rs b/embassy-lora/src/sx127x/sx127x_lora/mod.rs index 8b937ec2f..b3636d097 100644 --- a/embassy-lora/src/sx127x/sx127x_lora/mod.rs +++ b/embassy-lora/src/sx127x/sx127x_lora/mod.rs @@ -6,7 +6,7 @@ #![allow(dead_code)] use bit_field::BitField; -use embassy::time::{Duration, Timer}; +use embassy_executor::time::{Duration, Timer}; use embedded_hal::digital::v2::OutputPin; use embedded_hal_async::spi::SpiBus; diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs index 29dca12fd..133eb5c26 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_take.rs @@ -16,15 +16,15 @@ pub fn run(name: syn::Ident) -> Result { static HANDLER: interrupt::Handler; } - let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed); - let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed); + let func = HANDLER.func.load(::embassy_executor::export::atomic::Ordering::Relaxed); + let ctx = HANDLER.ctx.load(::embassy_executor::export::atomic::Ordering::Relaxed); let func: fn(*mut ()) = ::core::mem::transmute(func); func(ctx) } - static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false); + static TAKEN: ::embassy_executor::export::atomic::AtomicBool = ::embassy_executor::export::atomic::AtomicBool::new(false); - if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() { + if TAKEN.compare_exchange(false, true, ::embassy_executor::export::atomic::Ordering::AcqRel, ::embassy_executor::export::atomic::Ordering::Acquire).is_err() { core::panic!("IRQ Already taken"); } diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs index 5638276b2..a8c8bb0d7 100644 --- a/embassy-macros/src/macros/main.rs +++ b/embassy-macros/src/macros/main.rs @@ -49,14 +49,14 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result Result<(), wasm_bindgen::JsValue> { - static EXECUTOR: #embassy_path::util::Forever<#embassy_path::executor::Executor> = #embassy_path::util::Forever::new(); + static EXECUTOR: ::embassy_util::Forever<#embassy_path::executor::Executor> = ::embassy_util::Forever::new(); let executor = EXECUTOR.put(#embassy_path::executor::Executor::new()); executor.start(|spawner| { diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs index 53c9af546..57087c81c 100644 --- a/embassy-macros/src/macros/task.rs +++ b/embassy-macros/src/macros/task.rs @@ -16,7 +16,7 @@ struct Args { pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result { let args = Args::from_list(&args).map_err(|e| e.write_errors())?; - let embassy_prefix = args.embassy_prefix.append("embassy"); + let embassy_prefix = args.embassy_prefix.append("embassy_executor"); let embassy_path = embassy_prefix.path(); let pool_size: usize = args.pool_size.unwrap_or(1); diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 70acc7e4e..64cb5bd8f 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = [ "pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "embassy/time", "embassy/time-tick-1mhz"] +features = [ "pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "embassy-executor/time", "embassy-executor/time-tick-1mhz"] flavors = [ { name = "default", target = "thumbv7em-none-eabihf" }, ] @@ -37,7 +37,8 @@ pool-128 = [] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy = { version = "0.1.0", path = "../embassy" } +embassy-executor = { version = "0.1.0", path = "../embassy-executor" } +embassy-util = { version = "0.1.0", path = "../embassy-util" } embedded-io = { version = "0.3.0", features = [ "async" ] } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index f3b1ff9d4..06bb732ff 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -2,8 +2,8 @@ use core::cell::UnsafeCell; use core::future::Future; use core::task::{Context, Poll}; -use embassy::time::{Instant, Timer}; -use embassy::waitqueue::WakerRegistration; +use embassy_executor::time::{Instant, Timer}; +use embassy_util::waitqueue::WakerRegistration; use futures::future::poll_fn; use futures::pin_mut; use heapless::Vec; diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index b2b6db1cd..75780d417 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -16,12 +16,12 @@ flavors = [ [features] -time = ["embassy/time"] +time = ["embassy-executor/time"] -defmt = ["dep:defmt", "embassy/defmt", "embassy-usb?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] +defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-util/defmt", "embassy-usb?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] # Enable nightly-only features -nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] +nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] # Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. @@ -57,14 +57,15 @@ _nrf5340-net = ["_nrf5340", "nrf5340-net-pac"] _nrf5340 = ["_gpio-p1", "_dppi"] _nrf9160 = ["nrf9160-pac", "_dppi"] -_time-driver = ["embassy/time-tick-32768hz", "time"] +_time-driver = ["embassy-executor/time-tick-32768hz", "time"] _ppi = [] _dppi = [] _gpio-p1 = [] [dependencies] -embassy = { version = "0.1.0", path = "../embassy" } +embassy-executor = { version = "0.1.0", path = "../embassy-executor", optional = true } +embassy-util = { version = "0.1.0", path = "../embassy-util" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["nrf"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 89c1ba908..21ff1d73b 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -18,10 +18,10 @@ use core::future::Future; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy::waitqueue::WakerRegistration; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy_hal_common::ring_buffer::RingBuffer; -use embassy_hal_common::{into_ref, low_power_wait_until, PeripheralRef}; +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::WakerRegistration; use futures::future::poll_fn; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; @@ -450,3 +450,13 @@ impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a, trace!("irq: end"); } } + +/// Low power blocking wait loop using WFE/SEV. +fn low_power_wait_until(mut condition: impl FnMut() -> bool) { + while !condition() { + // WFE might "eat" an event that would have otherwise woken the executor. + cortex_m::asm::wfe(); + } + // Retrigger an event to be transparent to the executor. + cortex_m::asm::sev(); +} diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index cef80ae0a..cf49b0db0 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -2,8 +2,8 @@ use core::convert::Infallible; use core::future::Future; use core::task::{Context, Poll}; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index f6daec252..83f2916b9 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -2,8 +2,8 @@ use core::task::Poll; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 67634b5b7..cedf88de5 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -512,7 +512,7 @@ cfg_if::cfg_if! { } pub(crate) mod sealed { - use embassy::waitqueue::AtomicWaker; + use embassy_util::waitqueue::AtomicWaker; use super::*; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 9bebd6fa3..7aad561b6 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -2,9 +2,9 @@ use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::interrupt::InterruptExt; diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 6ddc70e52..f2ef46d8d 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -3,8 +3,8 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use pac::{saadc, SAADC}; use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A}; diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index a512b4813..57c0c14c7 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -363,7 +363,7 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { } pub(crate) mod sealed { - use embassy::waitqueue::AtomicWaker; + use embassy_util::waitqueue::AtomicWaker; use super::*; diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index a3b25ce05..1491e4268 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -2,9 +2,9 @@ use core::task::Poll; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use fixed::types::I30F2; use futures::future::poll_fn; diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index f7b3345b2..05fa0aea8 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -3,9 +3,9 @@ use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; use core::{mem, ptr}; use critical_section::CriticalSection; -use embassy::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy::blocking_mutex::CriticalSectionMutex as Mutex; -use embassy::time::driver::{AlarmHandle, Driver}; +use embassy_executor::time::driver::{AlarmHandle, Driver}; +use embassy_util::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_util::blocking_mutex::CriticalSectionMutex as Mutex; use crate::interrupt::{Interrupt, InterruptExt}; use crate::{interrupt, pac}; @@ -119,7 +119,7 @@ struct RtcDriver { } const ALARM_STATE_NEW: AlarmState = AlarmState::new(); -embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { +embassy_executor::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), alarm_count: AtomicU8::new(0), alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 8deecdc03..b3b613db2 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -3,9 +3,9 @@ use core::marker::PhantomData; use core::task::Poll; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::interrupt::{Interrupt, InterruptExt}; @@ -40,8 +40,8 @@ macro_rules! impl_timer { fn regs() -> &'static pac::timer0::RegisterBlock { unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) } } - fn waker(n: usize) -> &'static ::embassy::waitqueue::AtomicWaker { - use ::embassy::waitqueue::AtomicWaker; + fn waker(n: usize) -> &'static ::embassy_util::waitqueue::AtomicWaker { + use ::embassy_util::waitqueue::AtomicWaker; const NEW_AW: AtomicWaker = AtomicWaker::new(); static WAKERS: [AtomicWaker; $ccs] = [NEW_AW; $ccs]; &WAKERS[n] diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 6d6eb84e7..494abe0cc 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -11,11 +11,11 @@ use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; -#[cfg(feature = "time")] -use embassy::time::{Duration, Instant}; -use embassy::waitqueue::AtomicWaker; use embassy_embedded_hal::SetConfig; +#[cfg(feature = "time")] +use embassy_executor::time::{Duration, Instant}; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 792b8ecca..0d24cf65f 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -932,7 +932,7 @@ impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> { pub(crate) mod sealed { use core::sync::atomic::AtomicU8; - use embassy::waitqueue::AtomicWaker; + use embassy_util::waitqueue::AtomicWaker; use super::*; diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 378492859..ee99a9a6a 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -6,11 +6,11 @@ use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; use core::task::Poll; use cortex_m::peripheral::NVIC; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embassy_usb; use embassy_usb::driver::{self, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use futures::Future; use pac::usbd::RegisterBlock; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 6529e1a37..303617ffc 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -20,14 +20,15 @@ flavors = [ unstable-pac = [] # Enable nightly-only features -nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. unstable-traits = ["embedded-hal-1"] [dependencies] -embassy = { version = "0.1.0", path = "../embassy", features = [ "time-tick-1mhz" ] } +embassy-util = { version = "0.1.0", path = "../embassy-util" } +embassy-executor = { version = "0.1.0", path = "../embassy-executor", features = [ "time-tick-1mhz" ] } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 5db1a690b..9779f1378 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -3,9 +3,9 @@ use core::future::Future; use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; -use embassy::waitqueue::AtomicWaker; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use crate::pac::common::{Reg, RW}; use crate::pac::SIO; diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 6c4c258b1..142fd410d 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -2,9 +2,9 @@ use core::cell::Cell; use atomic_polyfill::{AtomicU8, Ordering}; use critical_section::CriticalSection; -use embassy::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy::blocking_mutex::Mutex; -use embassy::time::driver::{AlarmHandle, Driver}; +use embassy_executor::time::driver::{AlarmHandle, Driver}; +use embassy_util::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_util::blocking_mutex::Mutex; use crate::interrupt::{Interrupt, InterruptExt}; use crate::{interrupt, pac}; @@ -26,7 +26,7 @@ struct TimerDriver { next_alarm: AtomicU8, } -embassy::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ +embassy_executor::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [DUMMY_ALARM; ALARM_COUNT]), next_alarm: AtomicU8::new(0), }); diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index e9c3fdf45..ff228cc85 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -10,7 +10,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32 # TODO: sdmmc # TODO: net # TODO: subghz -features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "embassy/time-tick-32768hz"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "embassy-executor/time-tick-32768hz"] flavors = [ { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, @@ -31,7 +31,8 @@ flavors = [ ] [dependencies] -embassy = { version = "0.1.0", path = "../embassy" } +embassy-util = { version = "0.1.0", path = "../embassy-util" } +embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } @@ -72,7 +73,7 @@ quote = "1.0.15" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} [features] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-util/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb?/defmt"] sdmmc-rs = ["embedded-sdmmc"] net = ["embassy-net" ] memory-x = ["stm32-metapac/memory-x"] @@ -81,7 +82,7 @@ exti = [] # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. -_time-driver = ["embassy/time"] +_time-driver = ["embassy-executor/time"] time-driver-any = ["_time-driver"] time-driver-tim2 = ["_time-driver"] time-driver-tim3 = ["_time-driver"] @@ -91,7 +92,7 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb", "embassy-embedded-hal/nightly"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb", "embassy-embedded-hal/nightly"] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index e28225426..bbb9a12cb 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -1,7 +1,7 @@ use core::task::Poll; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::gpio::sealed::AFType; diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index c87f3ac49..bd2cd5b57 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -3,7 +3,7 @@ use core::sync::atomic::{fence, Ordering}; use core::task::Waker; -use embassy::waitqueue::AtomicWaker; +use embassy_util::waitqueue::AtomicWaker; use super::{TransferOptions, Word, WordSize}; use crate::_generated::BDMA_CHANNEL_COUNT; diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index e8e589de8..0c66005c7 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,7 +1,7 @@ use core::sync::atomic::{fence, Ordering}; use core::task::Waker; -use embassy::waitqueue::AtomicWaker; +use embassy_util::waitqueue::AtomicWaker; use super::{Burst, FlowControl, Request, TransferOptions, Word, WordSize}; use crate::_generated::DMA_CHANNEL_COUNT; diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 8e901d725..1aea6c65d 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -1,7 +1,7 @@ use core::sync::atomic::{fence, Ordering}; use core::task::Waker; -use embassy::waitqueue::AtomicWaker; +use embassy_util::waitqueue::AtomicWaker; use super::{Request, TransferOptions, Word, WordSize}; use crate::_generated::GPDMA_CHANNEL_COUNT; diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 5e31c32b5..37593914f 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -4,10 +4,10 @@ use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; use core::task::Waker; -use embassy::waitqueue::AtomicWaker; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU}; +use embassy_util::waitqueue::AtomicWaker; use crate::gpio::sealed::{AFType, Pin as __GpioPin}; use crate::gpio::{AnyPin, Speed}; diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 2b4a9367b..1bc1fb72e 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -2,10 +2,10 @@ use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; use core::task::Waker; -use embassy::waitqueue::AtomicWaker; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU}; +use embassy_util::waitqueue::AtomicWaker; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Speed}; diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 3b8d9390f..ecb180bbe 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -3,8 +3,8 @@ use core::marker::PhantomData; use core::pin::Pin; use core::task::{Context, Poll}; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::impl_peripheral; +use embassy_util::waitqueue::AtomicWaker; use crate::gpio::{AnyPin, Input, Pin as GpioPin}; use crate::pac::exti::regs::Lines; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index dec92cc88..f8067e8b3 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -2,10 +2,10 @@ use core::cmp; use core::task::Poll; use atomic_polyfill::{AtomicUsize, Ordering}; -use embassy::waitqueue::AtomicWaker; use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::dma::NoDma; diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 2b0ee7131..81e28f355 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -2,8 +2,8 @@ use core::task::Poll; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use rand_core::{CryptoRng, RngCore}; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index b9dff8faf..1de4b2aa6 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -3,9 +3,9 @@ use core::default::Default; use core::task::Poll; -use embassy::waitqueue::AtomicWaker; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; @@ -1507,8 +1507,8 @@ foreach_peripheral!( INNER } - fn state() -> &'static ::embassy::waitqueue::AtomicWaker { - static WAKER: ::embassy::waitqueue::AtomicWaker = ::embassy::waitqueue::AtomicWaker::new(); + fn state() -> &'static ::embassy_util::waitqueue::AtomicWaker { + static WAKER: ::embassy_util::waitqueue::AtomicWaker = ::embassy_util::waitqueue::AtomicWaker::new(); &WAKER } } diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs index f02fc140f..4e53efed5 100644 --- a/embassy-stm32/src/subghz/mod.rs +++ b/embassy-stm32/src/subghz/mod.rs @@ -504,7 +504,7 @@ impl<'d> SubGhz<'d, NoDma, NoDma> { /// /// sg.set_standby(StandbyClk::Rc)?; /// unsafe { sg.set_sleep(SleepCfg::default())? }; - /// embassy::time::Timer::after(embassy::time::Duration::from_micros(500)).await; + /// embassy_executor::time::Timer::after(embassy_executor::time::Duration::from_micros(500)).await; /// unsafe { wakeup() }; /// # Ok::<(), embassy_stm32::subghz::Error>(()) /// ``` diff --git a/embassy-stm32/src/subghz/timeout.rs b/embassy-stm32/src/subghz/timeout.rs index 9dbdc6374..b8d6ad8f9 100644 --- a/embassy-stm32/src/subghz/timeout.rs +++ b/embassy-stm32/src/subghz/timeout.rs @@ -439,9 +439,9 @@ impl From for [u8; 3] { } } -impl From for embassy::time::Duration { +impl From for embassy_executor::time::Duration { fn from(to: Timeout) -> Self { - embassy::time::Duration::from_micros(to.as_micros().into()) + embassy_executor::time::Duration::from_micros(to.as_micros().into()) } } diff --git a/embassy-stm32/src/subghz/tx_params.rs b/embassy-stm32/src/subghz/tx_params.rs index 5194a8379..a72c060f3 100644 --- a/embassy-stm32/src/subghz/tx_params.rs +++ b/embassy-stm32/src/subghz/tx_params.rs @@ -44,17 +44,17 @@ impl From for core::time::Duration { } } -impl From for embassy::time::Duration { +impl From for embassy_executor::time::Duration { fn from(rt: RampTime) -> Self { match rt { - RampTime::Micros10 => embassy::time::Duration::from_micros(10), - RampTime::Micros20 => embassy::time::Duration::from_micros(20), - RampTime::Micros40 => embassy::time::Duration::from_micros(40), - RampTime::Micros80 => embassy::time::Duration::from_micros(80), - RampTime::Micros200 => embassy::time::Duration::from_micros(200), - RampTime::Micros800 => embassy::time::Duration::from_micros(800), - RampTime::Micros1700 => embassy::time::Duration::from_micros(1700), - RampTime::Micros3400 => embassy::time::Duration::from_micros(3400), + RampTime::Micros10 => embassy_executor::time::Duration::from_micros(10), + RampTime::Micros20 => embassy_executor::time::Duration::from_micros(20), + RampTime::Micros40 => embassy_executor::time::Duration::from_micros(40), + RampTime::Micros80 => embassy_executor::time::Duration::from_micros(80), + RampTime::Micros200 => embassy_executor::time::Duration::from_micros(200), + RampTime::Micros800 => embassy_executor::time::Duration::from_micros(800), + RampTime::Micros1700 => embassy_executor::time::Duration::from_micros(1700), + RampTime::Micros3400 => embassy_executor::time::Duration::from_micros(3400), } } } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 38a4adc1d..6cea43f18 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -4,10 +4,10 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::{mem, ptr}; use atomic_polyfill::{AtomicU32, AtomicU8}; -use embassy::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy::blocking_mutex::Mutex; -use embassy::time::driver::{AlarmHandle, Driver}; -use embassy::time::TICKS_PER_SECOND; +use embassy_executor::time::driver::{AlarmHandle, Driver}; +use embassy_executor::time::TICKS_PER_SECOND; +use embassy_util::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_util::blocking_mutex::Mutex; use stm32_metapac::timer::regs; use crate::interrupt::{CriticalSection, InterruptExt}; @@ -133,7 +133,7 @@ struct RtcDriver { const ALARM_STATE_NEW: AlarmState = AlarmState::new(); -embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { +embassy_executor::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), alarm_count: AtomicU8::new(0), alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 4e47ef73d..0e8d0d682 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -2,9 +2,9 @@ use core::future::Future; use core::task::Poll; use atomic_polyfill::{compiler_fence, Ordering}; -use embassy::waitqueue::WakerRegistration; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy_hal_common::ring_buffer::RingBuffer; +use embassy_util::waitqueue::WakerRegistration; use futures::future::poll_fn; use super::*; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 11ef75954..764b21461 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -5,11 +5,11 @@ use core::sync::atomic::Ordering; use core::task::Poll; use atomic_polyfill::{AtomicBool, AtomicU8}; -use embassy::time::{block_for, Duration}; -use embassy::waitqueue::AtomicWaker; +use embassy_executor::time::{block_for, Duration}; use embassy_hal_common::into_ref; use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use futures::Future; use pac::common::{Reg, RW}; diff --git a/embassy-usb-hid/Cargo.toml b/embassy-usb-hid/Cargo.toml index b8185fa5a..53b6db3da 100644 --- a/embassy-usb-hid/Cargo.toml +++ b/embassy-usb-hid/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-hid-v$VERSION/embassy-usb-hid/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-hid/src/" -features = ["defmt", "embassy/time-tick-1mhz"] +features = ["defmt", "embassy-executor/time-tick-1mhz"] flavors = [ { name = "default", target = "thumbv7em-none-eabihf" }, ] @@ -16,7 +16,7 @@ default = ["usbd-hid"] usbd-hid = ["dep:usbd-hid", "ssmarshal"] [dependencies] -embassy = { version = "0.1.0", path = "../embassy" } +embassy-util = { version = "0.1.0", path = "../embassy-util" } embassy-usb = { version = "0.1.0", path = "../embassy-usb" } defmt = { version = "0.3", optional = true } diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb-hid/src/lib.rs index b87f57481..5fee60bbc 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb-hid/src/lib.rs @@ -11,7 +11,6 @@ use core::mem::MaybeUninit; use core::ops::Range; use core::sync::atomic::{AtomicUsize, Ordering}; -use embassy::time::Duration; use embassy_usb::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use embassy_usb::Builder; @@ -373,7 +372,7 @@ pub trait RequestHandler { /// If `id` is `None`, get the idle rate for all reports. Returning `None` /// will reject the control request. Any duration at or above 1.024 seconds /// or below 4ms will be returned as an indefinite idle rate. - fn get_idle(&self, id: Option) -> Option { + fn get_idle_ms(&self, id: Option) -> Option { let _ = id; None } @@ -381,9 +380,9 @@ pub trait RequestHandler { /// Set the idle rate for `id` to `dur`. /// /// If `id` is `None`, set the idle rate of all input reports to `dur`. If - /// an indefinite duration is requested, `dur` will be set to `Duration::MAX`. - fn set_idle(&self, id: Option, dur: Duration) { - let _ = (id, dur); + /// an indefinite duration is requested, `dur` will be set to `u32::MAX`. + fn set_idle_ms(&self, id: Option, duration_ms: u32) { + let _ = (id, duration_ms); } } @@ -447,13 +446,9 @@ impl<'d> ControlHandler for Control<'d> { if let Some(handler) = self.request_handler { let id = req.value as u8; let id = (id != 0).then(|| ReportId::In(id)); - let dur = u64::from(req.value >> 8); - let dur = if dur == 0 { - Duration::MAX - } else { - Duration::from_millis(4 * dur) - }; - handler.set_idle(id, dur); + let dur = u32::from(req.value >> 8); + let dur = if dur == 0 { u32::MAX } else { 4 * dur }; + handler.set_idle_ms(id, dur); } OutResponse::Accepted } @@ -495,8 +490,8 @@ impl<'d> ControlHandler for Control<'d> { if let Some(handler) = self.request_handler { let id = req.value as u8; let id = (id != 0).then(|| ReportId::In(id)); - if let Some(dur) = handler.get_idle(id) { - let dur = u8::try_from(dur.as_millis() / 4).unwrap_or(0); + if let Some(dur) = handler.get_idle_ms(id) { + let dur = u8::try_from(dur / 4).unwrap_or(0); buf[0] = dur; InResponse::Accepted(&buf[0..1]) } else { diff --git a/embassy-usb-ncm/Cargo.toml b/embassy-usb-ncm/Cargo.toml index 636a81c2c..fa6fa0176 100644 --- a/embassy-usb-ncm/Cargo.toml +++ b/embassy-usb-ncm/Cargo.toml @@ -12,7 +12,7 @@ flavors = [ ] [dependencies] -embassy = { version = "0.1.0", path = "../embassy" } +embassy-util = { version = "0.1.0", path = "../embassy-util" } embassy-usb = { version = "0.1.0", path = "../embassy-usb" } defmt = { version = "0.3", optional = true } diff --git a/embassy-usb-serial/Cargo.toml b/embassy-usb-serial/Cargo.toml index 682707afe..495dd9fea 100644 --- a/embassy-usb-serial/Cargo.toml +++ b/embassy-usb-serial/Cargo.toml @@ -12,7 +12,7 @@ flavors = [ ] [dependencies] -embassy = { version = "0.1.0", path = "../embassy" } +embassy-util = { version = "0.1.0", path = "../embassy-util" } embassy-usb = { version = "0.1.0", path = "../embassy-usb" } defmt = { version = "0.3", optional = true } diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs index a23d62e65..e561be9df 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb-serial/src/lib.rs @@ -9,11 +9,11 @@ use core::cell::Cell; use core::mem::{self, MaybeUninit}; use core::sync::atomic::{AtomicBool, Ordering}; -use embassy::blocking_mutex::CriticalSectionMutex; use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request}; use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use embassy_usb::types::*; use embassy_usb::Builder; +use embassy_util::blocking_mutex::CriticalSectionMutex; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 513dc8c1f..98734e98f 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -12,7 +12,7 @@ flavors = [ ] [dependencies] -embassy = { version = "0.1.0", path = "../embassy" } +embassy-util = { version = "0.1.0", path = "../embassy-util" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 82ff4e32f..3f6e13472 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -12,7 +12,7 @@ mod descriptor_reader; pub mod driver; pub mod types; -use embassy::util::{select, Either}; +use embassy_util::{select, Either}; use heapless::Vec; pub use self::builder::{Builder, Config}; diff --git a/embassy-util/Cargo.toml b/embassy-util/Cargo.toml new file mode 100644 index 000000000..32b796c0a --- /dev/null +++ b/embassy-util/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "embassy-util" +version = "0.1.0" +edition = "2021" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-util-v$VERSION/embassy-util/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-util/src/" +features = ["nightly", "defmt", "unstable-traits", "time", "time-tick-1mhz"] +flavors = [ + { name = "default", target = "x86_64-unknown-linux-gnu" }, +] + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +futures-util = { version = "0.3.17", default-features = false } +atomic-polyfill = "0.1.5" +critical-section = "0.2.5" +heapless = "0.7.5" +cfg-if = "1.0.0" + +[dev-dependencies] +futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } +futures-test = "0.3.17" +futures-timer = "3.0.2" +futures-util = { version = "0.3.17", features = [ "channel" ] } diff --git a/embassy-util/build.rs b/embassy-util/build.rs new file mode 100644 index 000000000..6fe82b44f --- /dev/null +++ b/embassy-util/build.rs @@ -0,0 +1,29 @@ +use std::env; + +fn main() { + let target = env::var("TARGET").unwrap(); + + if target.starts_with("thumbv6m-") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv6m"); + } else if target.starts_with("thumbv7m-") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv7m"); + } else if target.starts_with("thumbv7em-") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv7m"); + println!("cargo:rustc-cfg=armv7em"); // (not currently used) + } else if target.starts_with("thumbv8m.base") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv8m"); + println!("cargo:rustc-cfg=armv8m_base"); + } else if target.starts_with("thumbv8m.main") { + println!("cargo:rustc-cfg=cortex_m"); + println!("cargo:rustc-cfg=armv8m"); + println!("cargo:rustc-cfg=armv8m_main"); + } + + if target.ends_with("-eabihf") { + println!("cargo:rustc-cfg=has_fpu"); + } +} diff --git a/embassy-util/src/blocking_mutex/mod.rs b/embassy-util/src/blocking_mutex/mod.rs new file mode 100644 index 000000000..8a4a4c642 --- /dev/null +++ b/embassy-util/src/blocking_mutex/mod.rs @@ -0,0 +1,189 @@ +//! Blocking mutex. +//! +//! This module provides a blocking mutex that can be used to synchronize data. +pub mod raw; + +use core::cell::UnsafeCell; + +use self::raw::RawMutex; + +/// Blocking mutex (not async) +/// +/// Provides a blocking mutual exclusion primitive backed by an implementation of [`raw::RawMutex`]. +/// +/// Which implementation you select depends on the context in which you're using the mutex, and you can choose which kind +/// of interior mutability fits your use case. +/// +/// Use [`CriticalSectionMutex`] when data can be shared between threads and interrupts. +/// +/// Use [`NoopMutex`] when data is only shared between tasks running on the same executor. +/// +/// Use [`ThreadModeMutex`] when data is shared between tasks running on the same executor but you want a global singleton. +/// +/// In all cases, the blocking mutex is intended to be short lived and not held across await points. +/// Use the async [`Mutex`](crate::mutex::Mutex) if you need a lock that is held across await points. +pub struct Mutex { + // NOTE: `raw` must be FIRST, so when using ThreadModeMutex the "can't drop in non-thread-mode" gets + // to run BEFORE dropping `data`. + raw: R, + data: UnsafeCell, +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +impl Mutex { + /// Creates a new mutex in an unlocked state ready for use. + #[inline] + pub const fn new(val: T) -> Mutex { + Mutex { + raw: R::INIT, + data: UnsafeCell::new(val), + } + } + + /// Creates a critical section and grants temporary access to the protected data. + pub fn lock(&self, f: impl FnOnce(&T) -> U) -> U { + self.raw.lock(|| { + let ptr = self.data.get() as *const T; + let inner = unsafe { &*ptr }; + f(inner) + }) + } +} + +impl Mutex { + /// Creates a new mutex based on a pre-existing raw mutex. + /// + /// This allows creating a mutex in a constant context on stable Rust. + #[inline] + pub const fn const_new(raw_mutex: R, val: T) -> Mutex { + Mutex { + raw: raw_mutex, + data: UnsafeCell::new(val), + } + } + + /// Consumes this mutex, returning the underlying data. + #[inline] + pub fn into_inner(self) -> T { + self.data.into_inner() + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the `Mutex` mutably, no actual locking needs to + /// take place---the mutable borrow statically guarantees no locks exist. + #[inline] + pub fn get_mut(&mut self) -> &mut T { + unsafe { &mut *self.data.get() } + } +} + +/// A mutex that allows borrowing data across executors and interrupts. +/// +/// # Safety +/// +/// This mutex is safe to share between different executors and interrupts. +pub type CriticalSectionMutex = Mutex; + +/// A mutex that allows borrowing data in the context of a single executor. +/// +/// # Safety +/// +/// **This Mutex is only safe within a single executor.** +pub type NoopMutex = Mutex; + +impl Mutex { + /// Borrows the data for the duration of the critical section + pub fn borrow<'cs>(&'cs self, _cs: critical_section::CriticalSection<'cs>) -> &'cs T { + let ptr = self.data.get() as *const T; + unsafe { &*ptr } + } +} + +impl Mutex { + /// Borrows the data + pub fn borrow(&self) -> &T { + let ptr = self.data.get() as *const T; + unsafe { &*ptr } + } +} + +// ThreadModeMutex does NOT use the generic mutex from above because it's special: +// it's Send+Sync even if T: !Send. There's no way to do that without specialization (I think?). +// +// There's still a ThreadModeRawMutex for use with the generic Mutex (handy with Channel, for example), +// but that will require T: Send even though it shouldn't be needed. + +#[cfg(any(cortex_m, feature = "std"))] +pub use thread_mode_mutex::*; +#[cfg(any(cortex_m, feature = "std"))] +mod thread_mode_mutex { + use super::*; + + /// A "mutex" that only allows borrowing from thread mode. + /// + /// # Safety + /// + /// **This Mutex is only safe on single-core systems.** + /// + /// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access. + pub struct ThreadModeMutex { + inner: UnsafeCell, + } + + // NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode. + // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can + // be Send+Sync even if T is not Send (unlike CriticalSectionMutex) + unsafe impl Sync for ThreadModeMutex {} + unsafe impl Send for ThreadModeMutex {} + + impl ThreadModeMutex { + /// Creates a new mutex + pub const fn new(value: T) -> Self { + ThreadModeMutex { + inner: UnsafeCell::new(value), + } + } + } + + impl ThreadModeMutex { + /// Lock the `ThreadModeMutex`, granting access to the data. + /// + /// # Panics + /// + /// This will panic if not currently running in thread mode. + pub fn lock(&self, f: impl FnOnce(&T) -> R) -> R { + f(self.borrow()) + } + + /// Borrows the data + /// + /// # Panics + /// + /// This will panic if not currently running in thread mode. + pub fn borrow(&self) -> &T { + assert!( + raw::in_thread_mode(), + "ThreadModeMutex can only be borrowed from thread mode." + ); + unsafe { &*self.inner.get() } + } + } + + impl Drop for ThreadModeMutex { + fn drop(&mut self) { + // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so + // `drop` needs the same guarantees as `lock`. `ThreadModeMutex` is Send even if + // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, + // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. + assert!( + raw::in_thread_mode(), + "ThreadModeMutex can only be dropped from thread mode." + ); + + // Drop of the inner `T` happens after this. + } + } +} diff --git a/embassy-util/src/blocking_mutex/raw.rs b/embassy-util/src/blocking_mutex/raw.rs new file mode 100644 index 000000000..15796f1b2 --- /dev/null +++ b/embassy-util/src/blocking_mutex/raw.rs @@ -0,0 +1,149 @@ +//! Mutex primitives. +//! +//! This module provides a trait for mutexes that can be used in different contexts. +use core::marker::PhantomData; + +/// Raw mutex trait. +/// +/// This mutex is "raw", which means it does not actually contain the protected data, it +/// just implements the mutex mechanism. For most uses you should use [`super::Mutex`] instead, +/// which is generic over a RawMutex and contains the protected data. +/// +/// Note that, unlike other mutexes, implementations only guarantee no +/// concurrent access from other threads: concurrent access from the current +/// thread is allwed. For example, it's possible to lock the same mutex multiple times reentrantly. +/// +/// Therefore, locking a `RawMutex` is only enough to guarantee safe shared (`&`) access +/// to the data, it is not enough to guarantee exclusive (`&mut`) access. +/// +/// # Safety +/// +/// RawMutex implementations must ensure that, while locked, no other thread can lock +/// the RawMutex concurrently. +/// +/// Unsafe code is allowed to rely on this fact, so incorrect implementations will cause undefined behavior. +pub unsafe trait RawMutex { + /// Create a new `RawMutex` instance. + /// + /// This is a const instead of a method to allow creating instances in const context. + const INIT: Self; + + /// Lock this `RawMutex`. + fn lock(&self, f: impl FnOnce() -> R) -> R; +} + +/// A mutex that allows borrowing data across executors and interrupts. +/// +/// # Safety +/// +/// This mutex is safe to share between different executors and interrupts. +pub struct CriticalSectionRawMutex { + _phantom: PhantomData<()>, +} +unsafe impl Send for CriticalSectionRawMutex {} +unsafe impl Sync for CriticalSectionRawMutex {} + +impl CriticalSectionRawMutex { + /// Create a new `CriticalSectionRawMutex`. + pub const fn new() -> Self { + Self { _phantom: PhantomData } + } +} + +unsafe impl RawMutex for CriticalSectionRawMutex { + const INIT: Self = Self::new(); + + fn lock(&self, f: impl FnOnce() -> R) -> R { + critical_section::with(|_| f()) + } +} + +// ================ + +/// A mutex that allows borrowing data in the context of a single executor. +/// +/// # Safety +/// +/// **This Mutex is only safe within a single executor.** +pub struct NoopRawMutex { + _phantom: PhantomData<*mut ()>, +} + +unsafe impl Send for NoopRawMutex {} + +impl NoopRawMutex { + /// Create a new `NoopRawMutex`. + pub const fn new() -> Self { + Self { _phantom: PhantomData } + } +} + +unsafe impl RawMutex for NoopRawMutex { + const INIT: Self = Self::new(); + fn lock(&self, f: impl FnOnce() -> R) -> R { + f() + } +} + +// ================ + +#[cfg(any(cortex_m, feature = "std"))] +mod thread_mode { + use super::*; + + /// A "mutex" that only allows borrowing from thread mode. + /// + /// # Safety + /// + /// **This Mutex is only safe on single-core systems.** + /// + /// On multi-core systems, a `ThreadModeRawMutex` **is not sufficient** to ensure exclusive access. + pub struct ThreadModeRawMutex { + _phantom: PhantomData<()>, + } + + unsafe impl Send for ThreadModeRawMutex {} + unsafe impl Sync for ThreadModeRawMutex {} + + impl ThreadModeRawMutex { + /// Create a new `ThreadModeRawMutex`. + pub const fn new() -> Self { + Self { _phantom: PhantomData } + } + } + + unsafe impl RawMutex for ThreadModeRawMutex { + const INIT: Self = Self::new(); + fn lock(&self, f: impl FnOnce() -> R) -> R { + assert!(in_thread_mode(), "ThreadModeMutex can only be locked from thread mode."); + + f() + } + } + + impl Drop for ThreadModeRawMutex { + fn drop(&mut self) { + // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so + // `drop` needs the same guarantees as `lock`. `ThreadModeMutex` is Send even if + // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, + // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. + assert!( + in_thread_mode(), + "ThreadModeMutex can only be dropped from thread mode." + ); + + // Drop of the inner `T` happens after this. + } + } + + pub(crate) fn in_thread_mode() -> bool { + #[cfg(feature = "std")] + return Some("main") == std::thread::current().name(); + + #[cfg(not(feature = "std"))] + // ICSR.VECTACTIVE == 0 + return unsafe { (0xE000ED04 as *const u32).read_volatile() } & 0x1FF == 0; + } +} +#[cfg(any(cortex_m, feature = "std"))] +pub use thread_mode::*; diff --git a/embassy-util/src/channel/mod.rs b/embassy-util/src/channel/mod.rs new file mode 100644 index 000000000..5df1f5c5c --- /dev/null +++ b/embassy-util/src/channel/mod.rs @@ -0,0 +1,5 @@ +//! Async channels + +pub mod mpmc; +pub mod pubsub; +pub mod signal; diff --git a/embassy-util/src/channel/mpmc.rs b/embassy-util/src/channel/mpmc.rs new file mode 100644 index 000000000..535f77e6f --- /dev/null +++ b/embassy-util/src/channel/mpmc.rs @@ -0,0 +1,596 @@ +//! A queue for sending values between asynchronous tasks. +//! +//! It can be used concurrently by multiple producers (senders) and multiple +//! consumers (receivers), i.e. it is an "MPMC channel". +//! +//! Receivers are competing for messages. So a message that is received by +//! one receiver is not received by any other. +//! +//! This queue takes a Mutex type so that various +//! targets can be attained. For example, a ThreadModeMutex can be used +//! for single-core Cortex-M targets where messages are only passed +//! between tasks running in thread mode. Similarly, a CriticalSectionMutex +//! can also be used for single-core targets where messages are to be +//! passed from exception mode e.g. out of an interrupt handler. +//! +//! This module provides a bounded channel that has a limit on the number of +//! messages that it can store, and if this limit is reached, trying to send +//! another message will result in an error being returned. +//! + +use core::cell::RefCell; +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use heapless::Deque; + +use crate::blocking_mutex::raw::RawMutex; +use crate::blocking_mutex::Mutex; +use crate::waitqueue::WakerRegistration; + +/// Send-only access to a [`Channel`]. +#[derive(Copy)] +pub struct Sender<'ch, M, T, const N: usize> +where + M: RawMutex, +{ + channel: &'ch Channel, +} + +impl<'ch, M, T, const N: usize> Clone for Sender<'ch, M, T, N> +where + M: RawMutex, +{ + fn clone(&self) -> Self { + Sender { channel: self.channel } + } +} + +impl<'ch, M, T, const N: usize> Sender<'ch, M, T, N> +where + M: RawMutex, +{ + /// Sends a value. + /// + /// See [`Channel::send()`] + pub fn send(&self, message: T) -> SendFuture<'ch, M, T, N> { + self.channel.send(message) + } + + /// Attempt to immediately send a message. + /// + /// See [`Channel::send()`] + pub fn try_send(&self, message: T) -> Result<(), TrySendError> { + self.channel.try_send(message) + } +} + +/// Send-only access to a [`Channel`] without knowing channel size. +#[derive(Copy)] +pub struct DynamicSender<'ch, T> { + channel: &'ch dyn DynamicChannel, +} + +impl<'ch, T> Clone for DynamicSender<'ch, T> { + fn clone(&self) -> Self { + DynamicSender { channel: self.channel } + } +} + +impl<'ch, M, T, const N: usize> From> for DynamicSender<'ch, T> +where + M: RawMutex, +{ + fn from(s: Sender<'ch, M, T, N>) -> Self { + Self { channel: s.channel } + } +} + +impl<'ch, T> DynamicSender<'ch, T> { + /// Sends a value. + /// + /// See [`Channel::send()`] + pub fn send(&self, message: T) -> DynamicSendFuture<'ch, T> { + DynamicSendFuture { + channel: self.channel, + message: Some(message), + } + } + + /// Attempt to immediately send a message. + /// + /// See [`Channel::send()`] + pub fn try_send(&self, message: T) -> Result<(), TrySendError> { + self.channel.try_send_with_context(message, None) + } +} + +/// Receive-only access to a [`Channel`]. +#[derive(Copy)] +pub struct Receiver<'ch, M, T, const N: usize> +where + M: RawMutex, +{ + channel: &'ch Channel, +} + +impl<'ch, M, T, const N: usize> Clone for Receiver<'ch, M, T, N> +where + M: RawMutex, +{ + fn clone(&self) -> Self { + Receiver { channel: self.channel } + } +} + +impl<'ch, M, T, const N: usize> Receiver<'ch, M, T, N> +where + M: RawMutex, +{ + /// Receive the next value. + /// + /// See [`Channel::recv()`]. + pub fn recv(&self) -> RecvFuture<'_, M, T, N> { + self.channel.recv() + } + + /// Attempt to immediately receive the next value. + /// + /// See [`Channel::try_recv()`] + pub fn try_recv(&self) -> Result { + self.channel.try_recv() + } +} + +/// Receive-only access to a [`Channel`] without knowing channel size. +#[derive(Copy)] +pub struct DynamicReceiver<'ch, T> { + channel: &'ch dyn DynamicChannel, +} + +impl<'ch, T> Clone for DynamicReceiver<'ch, T> { + fn clone(&self) -> Self { + DynamicReceiver { channel: self.channel } + } +} + +impl<'ch, T> DynamicReceiver<'ch, T> { + /// Receive the next value. + /// + /// See [`Channel::recv()`]. + pub fn recv(&self) -> DynamicRecvFuture<'_, T> { + DynamicRecvFuture { channel: self.channel } + } + + /// Attempt to immediately receive the next value. + /// + /// See [`Channel::try_recv()`] + pub fn try_recv(&self) -> Result { + self.channel.try_recv_with_context(None) + } +} + +impl<'ch, M, T, const N: usize> From> for DynamicReceiver<'ch, T> +where + M: RawMutex, +{ + fn from(s: Receiver<'ch, M, T, N>) -> Self { + Self { channel: s.channel } + } +} + +/// Future returned by [`Channel::recv`] and [`Receiver::recv`]. +pub struct RecvFuture<'ch, M, T, const N: usize> +where + M: RawMutex, +{ + channel: &'ch Channel, +} + +impl<'ch, M, T, const N: usize> Future for RecvFuture<'ch, M, T, N> +where + M: RawMutex, +{ + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.channel.try_recv_with_context(Some(cx)) { + Ok(v) => Poll::Ready(v), + Err(TryRecvError::Empty) => Poll::Pending, + } + } +} + +/// Future returned by [`DynamicReceiver::recv`]. +pub struct DynamicRecvFuture<'ch, T> { + channel: &'ch dyn DynamicChannel, +} + +impl<'ch, T> Future for DynamicRecvFuture<'ch, T> { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.channel.try_recv_with_context(Some(cx)) { + Ok(v) => Poll::Ready(v), + Err(TryRecvError::Empty) => Poll::Pending, + } + } +} + +/// Future returned by [`Channel::send`] and [`Sender::send`]. +pub struct SendFuture<'ch, M, T, const N: usize> +where + M: RawMutex, +{ + channel: &'ch Channel, + message: Option, +} + +impl<'ch, M, T, const N: usize> Future for SendFuture<'ch, M, T, N> +where + M: RawMutex, +{ + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.message.take() { + Some(m) => match self.channel.try_send_with_context(m, Some(cx)) { + Ok(..) => Poll::Ready(()), + Err(TrySendError::Full(m)) => { + self.message = Some(m); + Poll::Pending + } + }, + None => panic!("Message cannot be None"), + } + } +} + +impl<'ch, M, T, const N: usize> Unpin for SendFuture<'ch, M, T, N> where M: RawMutex {} + +/// Future returned by [`DynamicSender::send`]. +pub struct DynamicSendFuture<'ch, T> { + channel: &'ch dyn DynamicChannel, + message: Option, +} + +impl<'ch, T> Future for DynamicSendFuture<'ch, T> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.message.take() { + Some(m) => match self.channel.try_send_with_context(m, Some(cx)) { + Ok(..) => Poll::Ready(()), + Err(TrySendError::Full(m)) => { + self.message = Some(m); + Poll::Pending + } + }, + None => panic!("Message cannot be None"), + } + } +} + +impl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {} + +trait DynamicChannel { + fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError>; + + fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; +} + +/// Error returned by [`try_recv`](Channel::try_recv). +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TryRecvError { + /// A message could not be received because the channel is empty. + Empty, +} + +/// Error returned by [`try_send`](Channel::try_send). +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TrySendError { + /// The data could not be sent on the channel because the channel is + /// currently full and sending would require blocking. + Full(T), +} + +struct ChannelState { + queue: Deque, + receiver_waker: WakerRegistration, + senders_waker: WakerRegistration, +} + +impl ChannelState { + const fn new() -> Self { + ChannelState { + queue: Deque::new(), + receiver_waker: WakerRegistration::new(), + senders_waker: WakerRegistration::new(), + } + } + + fn try_recv(&mut self) -> Result { + self.try_recv_with_context(None) + } + + fn try_recv_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result { + if self.queue.is_full() { + self.senders_waker.wake(); + } + + if let Some(message) = self.queue.pop_front() { + Ok(message) + } else { + if let Some(cx) = cx { + self.receiver_waker.register(cx.waker()); + } + Err(TryRecvError::Empty) + } + } + + fn try_send(&mut self, message: T) -> Result<(), TrySendError> { + self.try_send_with_context(message, None) + } + + fn try_send_with_context(&mut self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError> { + match self.queue.push_back(message) { + Ok(()) => { + self.receiver_waker.wake(); + Ok(()) + } + Err(message) => { + if let Some(cx) = cx { + self.senders_waker.register(cx.waker()); + } + Err(TrySendError::Full(message)) + } + } + } +} + +/// A bounded channel for communicating between asynchronous tasks +/// with backpressure. +/// +/// The channel will buffer up to the provided number of messages. Once the +/// buffer is full, attempts to `send` new messages will wait until a message is +/// received from the channel. +/// +/// All data sent will become available in the same order as it was sent. +pub struct Channel +where + M: RawMutex, +{ + inner: Mutex>>, +} + +impl Channel +where + M: RawMutex, +{ + /// Establish a new bounded channel. For example, to create one with a NoopMutex: + /// + /// ``` + /// use embassy_util::channel::mpmc::Channel; + /// use embassy_util::blocking_mutex::raw::NoopRawMutex; + /// + /// // Declare a bounded channel of 3 u32s. + /// let mut channel = Channel::::new(); + /// ``` + pub const fn new() -> Self { + Self { + inner: Mutex::new(RefCell::new(ChannelState::new())), + } + } + + fn lock(&self, f: impl FnOnce(&mut ChannelState) -> R) -> R { + self.inner.lock(|rc| f(&mut *rc.borrow_mut())) + } + + fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { + self.lock(|c| c.try_recv_with_context(cx)) + } + + fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError> { + self.lock(|c| c.try_send_with_context(m, cx)) + } + + /// Get a sender for this channel. + pub fn sender(&self) -> Sender<'_, M, T, N> { + Sender { channel: self } + } + + /// Get a receiver for this channel. + pub fn receiver(&self) -> Receiver<'_, M, T, N> { + Receiver { channel: self } + } + + /// Send a value, waiting until there is capacity. + /// + /// Sending completes when the value has been pushed to the channel's queue. + /// This doesn't mean the value has been received yet. + pub fn send(&self, message: T) -> SendFuture<'_, M, T, N> { + SendFuture { + channel: self, + message: Some(message), + } + } + + /// Attempt to immediately send a message. + /// + /// This method differs from [`send`](Channel::send) by returning immediately if the channel's + /// buffer is full, instead of waiting. + /// + /// # Errors + /// + /// If the channel capacity has been reached, i.e., the channel has `n` + /// buffered values where `n` is the argument passed to [`Channel`], then an + /// error is returned. + pub fn try_send(&self, message: T) -> Result<(), TrySendError> { + self.lock(|c| c.try_send(message)) + } + + /// Receive the next value. + /// + /// If there are no messages in the channel's buffer, this method will + /// wait until a message is sent. + pub fn recv(&self) -> RecvFuture<'_, M, T, N> { + RecvFuture { channel: self } + } + + /// Attempt to immediately receive a message. + /// + /// This method will either receive a message from the channel immediately or return an error + /// if the channel is empty. + pub fn try_recv(&self) -> Result { + self.lock(|c| c.try_recv()) + } +} + +/// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the +/// tradeoff cost of dynamic dispatch. +impl DynamicChannel for Channel +where + M: RawMutex, +{ + fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError> { + Channel::try_send_with_context(self, m, cx) + } + + fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { + Channel::try_recv_with_context(self, cx) + } +} + +#[cfg(test)] +mod tests { + use core::time::Duration; + + use futures_executor::ThreadPool; + use futures_timer::Delay; + use futures_util::task::SpawnExt; + + use super::*; + use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; + use crate::Forever; + + fn capacity(c: &ChannelState) -> usize { + c.queue.capacity() - c.queue.len() + } + + #[test] + fn sending_once() { + let mut c = ChannelState::::new(); + assert!(c.try_send(1).is_ok()); + assert_eq!(capacity(&c), 2); + } + + #[test] + fn sending_when_full() { + let mut c = ChannelState::::new(); + let _ = c.try_send(1); + let _ = c.try_send(1); + let _ = c.try_send(1); + match c.try_send(2) { + Err(TrySendError::Full(2)) => assert!(true), + _ => assert!(false), + } + assert_eq!(capacity(&c), 0); + } + + #[test] + fn receiving_once_with_one_send() { + let mut c = ChannelState::::new(); + assert!(c.try_send(1).is_ok()); + assert_eq!(c.try_recv().unwrap(), 1); + assert_eq!(capacity(&c), 3); + } + + #[test] + fn receiving_when_empty() { + let mut c = ChannelState::::new(); + match c.try_recv() { + Err(TryRecvError::Empty) => assert!(true), + _ => assert!(false), + } + assert_eq!(capacity(&c), 3); + } + + #[test] + fn simple_send_and_receive() { + let c = Channel::::new(); + assert!(c.try_send(1).is_ok()); + assert_eq!(c.try_recv().unwrap(), 1); + } + + #[test] + fn cloning() { + let c = Channel::::new(); + let r1 = c.receiver(); + let s1 = c.sender(); + + let _ = r1.clone(); + let _ = s1.clone(); + } + + #[test] + fn dynamic_dispatch() { + let c = Channel::::new(); + let s: DynamicSender<'_, u32> = c.sender().into(); + let r: DynamicReceiver<'_, u32> = c.receiver().into(); + + assert!(s.try_send(1).is_ok()); + assert_eq!(r.try_recv().unwrap(), 1); + } + + #[futures_test::test] + async fn receiver_receives_given_try_send_async() { + let executor = ThreadPool::new().unwrap(); + + static CHANNEL: Forever> = Forever::new(); + let c = &*CHANNEL.put(Channel::new()); + let c2 = c; + assert!(executor + .spawn(async move { + assert!(c2.try_send(1).is_ok()); + }) + .is_ok()); + assert_eq!(c.recv().await, 1); + } + + #[futures_test::test] + async fn sender_send_completes_if_capacity() { + let c = Channel::::new(); + c.send(1).await; + assert_eq!(c.recv().await, 1); + } + + #[futures_test::test] + async fn senders_sends_wait_until_capacity() { + let executor = ThreadPool::new().unwrap(); + + static CHANNEL: Forever> = Forever::new(); + let c = &*CHANNEL.put(Channel::new()); + assert!(c.try_send(1).is_ok()); + + let c2 = c; + let send_task_1 = executor.spawn_with_handle(async move { c2.send(2).await }); + let c2 = c; + let send_task_2 = executor.spawn_with_handle(async move { c2.send(3).await }); + // Wish I could think of a means of determining that the async send is waiting instead. + // However, I've used the debugger to observe that the send does indeed wait. + Delay::new(Duration::from_millis(500)).await; + assert_eq!(c.recv().await, 1); + assert!(executor + .spawn(async move { + loop { + c.recv().await; + } + }) + .is_ok()); + send_task_1.unwrap().await; + send_task_2.unwrap().await; + } +} diff --git a/embassy-util/src/channel/pubsub/mod.rs b/embassy-util/src/channel/pubsub/mod.rs new file mode 100644 index 000000000..ecc8fbd8f --- /dev/null +++ b/embassy-util/src/channel/pubsub/mod.rs @@ -0,0 +1,542 @@ +//! Implementation of [PubSubChannel], a queue where published messages get received by all subscribers. + +#![deny(missing_docs)] + +use core::cell::RefCell; +use core::fmt::Debug; +use core::task::{Context, Poll, Waker}; + +use heapless::Deque; + +use self::publisher::{ImmediatePub, Pub}; +use self::subscriber::Sub; +use crate::blocking_mutex::raw::RawMutex; +use crate::blocking_mutex::Mutex; +use crate::waitqueue::MultiWakerRegistration; + +pub mod publisher; +pub mod subscriber; + +pub use publisher::{DynImmediatePublisher, DynPublisher, ImmediatePublisher, Publisher}; +pub use subscriber::{DynSubscriber, Subscriber}; + +/// A broadcast channel implementation where multiple publishers can send messages to multiple subscribers +/// +/// Any published message can be read by all subscribers. +/// A publisher can choose how it sends its message. +/// +/// - With [Pub::publish()] the publisher has to wait until there is space in the internal message queue. +/// - With [Pub::publish_immediate()] the publisher doesn't await and instead lets the oldest message +/// in the queue drop if necessary. This will cause any [Subscriber] that missed the message to receive +/// an error to indicate that it has lagged. +/// +/// ## Example +/// +/// ``` +/// # use embassy_util::blocking_mutex::raw::NoopRawMutex; +/// # use embassy_util::channel::pubsub::WaitResult; +/// # use embassy_util::channel::pubsub::PubSubChannel; +/// # use futures_executor::block_on; +/// # let test = async { +/// // Create the channel. This can be static as well +/// let channel = PubSubChannel::::new(); +/// +/// // This is a generic subscriber with a direct reference to the channel +/// let mut sub0 = channel.subscriber().unwrap(); +/// // This is a dynamic subscriber with a dynamic (trait object) reference to the channel +/// let mut sub1 = channel.dyn_subscriber().unwrap(); +/// +/// let pub0 = channel.publisher().unwrap(); +/// +/// // Publish a message, but wait if the queue is full +/// pub0.publish(42).await; +/// +/// // Publish a message, but if the queue is full, just kick out the oldest message. +/// // This may cause some subscribers to miss a message +/// pub0.publish_immediate(43); +/// +/// // Wait for a new message. If the subscriber missed a message, the WaitResult will be a Lag result +/// assert_eq!(sub0.next_message().await, WaitResult::Message(42)); +/// assert_eq!(sub1.next_message().await, WaitResult::Message(42)); +/// +/// // Wait again, but this time ignore any Lag results +/// assert_eq!(sub0.next_message_pure().await, 43); +/// assert_eq!(sub1.next_message_pure().await, 43); +/// +/// // There's also a polling interface +/// assert_eq!(sub0.try_next_message(), None); +/// assert_eq!(sub1.try_next_message(), None); +/// # }; +/// # +/// # block_on(test); +/// ``` +/// +pub struct PubSubChannel { + inner: Mutex>>, +} + +impl + PubSubChannel +{ + /// Create a new channel + pub const fn new() -> Self { + Self { + inner: Mutex::const_new(M::INIT, RefCell::new(PubSubState::new())), + } + } + + /// Create a new subscriber. It will only receive messages that are published after its creation. + /// + /// If there are no subscriber slots left, an error will be returned. + pub fn subscriber(&self) -> Result, Error> { + self.inner.lock(|inner| { + let mut s = inner.borrow_mut(); + + if s.subscriber_count >= SUBS { + Err(Error::MaximumSubscribersReached) + } else { + s.subscriber_count += 1; + Ok(Subscriber(Sub::new(s.next_message_id, self))) + } + }) + } + + /// Create a new subscriber. It will only receive messages that are published after its creation. + /// + /// If there are no subscriber slots left, an error will be returned. + pub fn dyn_subscriber(&self) -> Result, Error> { + self.inner.lock(|inner| { + let mut s = inner.borrow_mut(); + + if s.subscriber_count >= SUBS { + Err(Error::MaximumSubscribersReached) + } else { + s.subscriber_count += 1; + Ok(DynSubscriber(Sub::new(s.next_message_id, self))) + } + }) + } + + /// Create a new publisher + /// + /// If there are no publisher slots left, an error will be returned. + pub fn publisher(&self) -> Result, Error> { + self.inner.lock(|inner| { + let mut s = inner.borrow_mut(); + + if s.publisher_count >= PUBS { + Err(Error::MaximumPublishersReached) + } else { + s.publisher_count += 1; + Ok(Publisher(Pub::new(self))) + } + }) + } + + /// Create a new publisher + /// + /// If there are no publisher slots left, an error will be returned. + pub fn dyn_publisher(&self) -> Result, Error> { + self.inner.lock(|inner| { + let mut s = inner.borrow_mut(); + + if s.publisher_count >= PUBS { + Err(Error::MaximumPublishersReached) + } else { + s.publisher_count += 1; + Ok(DynPublisher(Pub::new(self))) + } + }) + } + + /// Create a new publisher that can only send immediate messages. + /// This kind of publisher does not take up a publisher slot. + pub fn immediate_publisher(&self) -> ImmediatePublisher { + ImmediatePublisher(ImmediatePub::new(self)) + } + + /// Create a new publisher that can only send immediate messages. + /// This kind of publisher does not take up a publisher slot. + pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher { + DynImmediatePublisher(ImmediatePub::new(self)) + } +} + +impl PubSubBehavior + for PubSubChannel +{ + fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll> { + self.inner.lock(|s| { + let mut s = s.borrow_mut(); + + // Check if we can read a message + match s.get_message(*next_message_id) { + // Yes, so we are done polling + Some(WaitResult::Message(message)) => { + *next_message_id += 1; + Poll::Ready(WaitResult::Message(message)) + } + // No, so we need to reregister our waker and sleep again + None => { + if let Some(cx) = cx { + s.register_subscriber_waker(cx.waker()); + } + Poll::Pending + } + // We missed a couple of messages. We must do our internal bookkeeping and return that we lagged + Some(WaitResult::Lagged(amount)) => { + *next_message_id += amount; + Poll::Ready(WaitResult::Lagged(amount)) + } + } + }) + } + + fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T> { + self.inner.lock(|s| { + let mut s = s.borrow_mut(); + // Try to publish the message + match s.try_publish(message) { + // We did it, we are ready + Ok(()) => Ok(()), + // The queue is full, so we need to reregister our waker and go to sleep + Err(message) => { + if let Some(cx) = cx { + s.register_publisher_waker(cx.waker()); + } + Err(message) + } + } + }) + } + + fn publish_immediate(&self, message: T) { + self.inner.lock(|s| { + let mut s = s.borrow_mut(); + s.publish_immediate(message) + }) + } + + fn unregister_subscriber(&self, subscriber_next_message_id: u64) { + self.inner.lock(|s| { + let mut s = s.borrow_mut(); + s.unregister_subscriber(subscriber_next_message_id) + }) + } + + fn unregister_publisher(&self) { + self.inner.lock(|s| { + let mut s = s.borrow_mut(); + s.unregister_publisher() + }) + } +} + +/// Internal state for the PubSub channel +struct PubSubState { + /// The queue contains the last messages that have been published and a countdown of how many subscribers are yet to read it + queue: Deque<(T, usize), CAP>, + /// Every message has an id. + /// Don't worry, we won't run out. + /// If a million messages were published every second, then the ID's would run out in about 584942 years. + next_message_id: u64, + /// Collection of wakers for Subscribers that are waiting. + subscriber_wakers: MultiWakerRegistration, + /// Collection of wakers for Publishers that are waiting. + publisher_wakers: MultiWakerRegistration, + /// The amount of subscribers that are active + subscriber_count: usize, + /// The amount of publishers that are active + publisher_count: usize, +} + +impl PubSubState { + /// Create a new internal channel state + const fn new() -> Self { + Self { + queue: Deque::new(), + next_message_id: 0, + subscriber_wakers: MultiWakerRegistration::new(), + publisher_wakers: MultiWakerRegistration::new(), + subscriber_count: 0, + publisher_count: 0, + } + } + + fn try_publish(&mut self, message: T) -> Result<(), T> { + if self.subscriber_count == 0 { + // We don't need to publish anything because there is no one to receive it + return Ok(()); + } + + if self.queue.is_full() { + return Err(message); + } + // We just did a check for this + self.queue.push_back((message, self.subscriber_count)).ok().unwrap(); + + self.next_message_id += 1; + + // Wake all of the subscribers + self.subscriber_wakers.wake(); + + Ok(()) + } + + fn publish_immediate(&mut self, message: T) { + // Make space in the queue if required + if self.queue.is_full() { + self.queue.pop_front(); + } + + // This will succeed because we made sure there is space + self.try_publish(message).ok().unwrap(); + } + + fn get_message(&mut self, message_id: u64) -> Option> { + let start_id = self.next_message_id - self.queue.len() as u64; + + if message_id < start_id { + return Some(WaitResult::Lagged(start_id - message_id)); + } + + let current_message_index = (message_id - start_id) as usize; + + if current_message_index >= self.queue.len() { + return None; + } + + // We've checked that the index is valid + let queue_item = self.queue.iter_mut().nth(current_message_index).unwrap(); + + // We're reading this item, so decrement the counter + queue_item.1 -= 1; + let message = queue_item.0.clone(); + + if current_message_index == 0 && queue_item.1 == 0 { + self.queue.pop_front(); + self.publisher_wakers.wake(); + } + + Some(WaitResult::Message(message)) + } + + fn register_subscriber_waker(&mut self, waker: &Waker) { + match self.subscriber_wakers.register(waker) { + Ok(()) => {} + Err(_) => { + // All waker slots were full. This can only happen when there was a subscriber that now has dropped. + // We need to throw it away. It's a bit inefficient, but we can wake everything. + // Any future that is still active will simply reregister. + // This won't happen a lot, so it's ok. + self.subscriber_wakers.wake(); + self.subscriber_wakers.register(waker).unwrap(); + } + } + } + + fn register_publisher_waker(&mut self, waker: &Waker) { + match self.publisher_wakers.register(waker) { + Ok(()) => {} + Err(_) => { + // All waker slots were full. This can only happen when there was a publisher that now has dropped. + // We need to throw it away. It's a bit inefficient, but we can wake everything. + // Any future that is still active will simply reregister. + // This won't happen a lot, so it's ok. + self.publisher_wakers.wake(); + self.publisher_wakers.register(waker).unwrap(); + } + } + } + + fn unregister_subscriber(&mut self, subscriber_next_message_id: u64) { + self.subscriber_count -= 1; + + // All messages that haven't been read yet by this subscriber must have their counter decremented + let start_id = self.next_message_id - self.queue.len() as u64; + if subscriber_next_message_id >= start_id { + let current_message_index = (subscriber_next_message_id - start_id) as usize; + self.queue + .iter_mut() + .skip(current_message_index) + .for_each(|(_, counter)| *counter -= 1); + } + } + + fn unregister_publisher(&mut self) { + self.publisher_count -= 1; + } +} + +/// Error type for the [PubSubChannel] +#[derive(Debug, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// All subscriber slots are used. To add another subscriber, first another subscriber must be dropped or + /// the capacity of the channels must be increased. + MaximumSubscribersReached, + /// All publisher slots are used. To add another publisher, first another publisher must be dropped or + /// the capacity of the channels must be increased. + MaximumPublishersReached, +} + +/// 'Middle level' behaviour of the pubsub channel. +/// This trait is used so that Sub and Pub can be generic over the channel. +pub trait PubSubBehavior { + /// Try to get a message from the queue with the given message id. + /// + /// If the message is not yet present and a context is given, then its waker is registered in the subsriber wakers. + fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll>; + + /// Try to publish a message to the queue. + /// + /// If the queue is full and a context is given, then its waker is registered in the publisher wakers. + fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T>; + + /// Publish a message immediately + fn publish_immediate(&self, message: T); + + /// Let the channel know that a subscriber has dropped + fn unregister_subscriber(&self, subscriber_next_message_id: u64); + + /// Let the channel know that a publisher has dropped + fn unregister_publisher(&self); +} + +/// The result of the subscriber wait procedure +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum WaitResult { + /// The subscriber did not receive all messages and lagged by the given amount of messages. + /// (This is the amount of messages that were missed) + Lagged(u64), + /// A message was received + Message(T), +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::blocking_mutex::raw::NoopRawMutex; + + #[futures_test::test] + async fn dyn_pub_sub_works() { + let channel = PubSubChannel::::new(); + + let mut sub0 = channel.dyn_subscriber().unwrap(); + let mut sub1 = channel.dyn_subscriber().unwrap(); + let pub0 = channel.dyn_publisher().unwrap(); + + pub0.publish(42).await; + + assert_eq!(sub0.next_message().await, WaitResult::Message(42)); + assert_eq!(sub1.next_message().await, WaitResult::Message(42)); + + assert_eq!(sub0.try_next_message(), None); + assert_eq!(sub1.try_next_message(), None); + } + + #[futures_test::test] + async fn all_subscribers_receive() { + let channel = PubSubChannel::::new(); + + let mut sub0 = channel.subscriber().unwrap(); + let mut sub1 = channel.subscriber().unwrap(); + let pub0 = channel.publisher().unwrap(); + + pub0.publish(42).await; + + assert_eq!(sub0.next_message().await, WaitResult::Message(42)); + assert_eq!(sub1.next_message().await, WaitResult::Message(42)); + + assert_eq!(sub0.try_next_message(), None); + assert_eq!(sub1.try_next_message(), None); + } + + #[futures_test::test] + async fn lag_when_queue_full_on_immediate_publish() { + let channel = PubSubChannel::::new(); + + let mut sub0 = channel.subscriber().unwrap(); + let pub0 = channel.publisher().unwrap(); + + pub0.publish_immediate(42); + pub0.publish_immediate(43); + pub0.publish_immediate(44); + pub0.publish_immediate(45); + pub0.publish_immediate(46); + pub0.publish_immediate(47); + + assert_eq!(sub0.try_next_message(), Some(WaitResult::Lagged(2))); + assert_eq!(sub0.next_message().await, WaitResult::Message(44)); + assert_eq!(sub0.next_message().await, WaitResult::Message(45)); + assert_eq!(sub0.next_message().await, WaitResult::Message(46)); + assert_eq!(sub0.next_message().await, WaitResult::Message(47)); + assert_eq!(sub0.try_next_message(), None); + } + + #[test] + fn limited_subs_and_pubs() { + let channel = PubSubChannel::::new(); + + let sub0 = channel.subscriber(); + let sub1 = channel.subscriber(); + let sub2 = channel.subscriber(); + let sub3 = channel.subscriber(); + let sub4 = channel.subscriber(); + + assert!(sub0.is_ok()); + assert!(sub1.is_ok()); + assert!(sub2.is_ok()); + assert!(sub3.is_ok()); + assert_eq!(sub4.err().unwrap(), Error::MaximumSubscribersReached); + + drop(sub0); + + let sub5 = channel.subscriber(); + assert!(sub5.is_ok()); + + // publishers + + let pub0 = channel.publisher(); + let pub1 = channel.publisher(); + let pub2 = channel.publisher(); + let pub3 = channel.publisher(); + let pub4 = channel.publisher(); + + assert!(pub0.is_ok()); + assert!(pub1.is_ok()); + assert!(pub2.is_ok()); + assert!(pub3.is_ok()); + assert_eq!(pub4.err().unwrap(), Error::MaximumPublishersReached); + + drop(pub0); + + let pub5 = channel.publisher(); + assert!(pub5.is_ok()); + } + + #[test] + fn publisher_wait_on_full_queue() { + let channel = PubSubChannel::::new(); + + let pub0 = channel.publisher().unwrap(); + + // There are no subscribers, so the queue will never be full + assert_eq!(pub0.try_publish(0), Ok(())); + assert_eq!(pub0.try_publish(0), Ok(())); + assert_eq!(pub0.try_publish(0), Ok(())); + assert_eq!(pub0.try_publish(0), Ok(())); + assert_eq!(pub0.try_publish(0), Ok(())); + + let sub0 = channel.subscriber().unwrap(); + + assert_eq!(pub0.try_publish(0), Ok(())); + assert_eq!(pub0.try_publish(0), Ok(())); + assert_eq!(pub0.try_publish(0), Ok(())); + assert_eq!(pub0.try_publish(0), Ok(())); + assert_eq!(pub0.try_publish(0), Err(0)); + + drop(sub0); + } +} diff --git a/embassy-util/src/channel/pubsub/publisher.rs b/embassy-util/src/channel/pubsub/publisher.rs new file mode 100644 index 000000000..705797f60 --- /dev/null +++ b/embassy-util/src/channel/pubsub/publisher.rs @@ -0,0 +1,182 @@ +//! Implementation of anything directly publisher related + +use core::future::Future; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use super::{PubSubBehavior, PubSubChannel}; +use crate::blocking_mutex::raw::RawMutex; + +/// A publisher to a channel +pub struct Pub<'a, PSB: PubSubBehavior + ?Sized, T: Clone> { + /// The channel we are a publisher for + channel: &'a PSB, + _phantom: PhantomData, +} + +impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { + pub(super) fn new(channel: &'a PSB) -> Self { + Self { + channel, + _phantom: Default::default(), + } + } + + /// Publish a message right now even when the queue is full. + /// This may cause a subscriber to miss an older message. + pub fn publish_immediate(&self, message: T) { + self.channel.publish_immediate(message) + } + + /// Publish a message. But if the message queue is full, wait for all subscribers to have read the last message + pub fn publish<'s>(&'s self, message: T) -> PublisherWaitFuture<'s, 'a, PSB, T> { + PublisherWaitFuture { + message: Some(message), + publisher: self, + } + } + + /// Publish a message if there is space in the message queue + pub fn try_publish(&self, message: T) -> Result<(), T> { + self.channel.publish_with_context(message, None) + } +} + +impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Pub<'a, PSB, T> { + fn drop(&mut self) { + self.channel.unregister_publisher() + } +} + +/// A publisher that holds a dynamic reference to the channel +pub struct DynPublisher<'a, T: Clone>(pub(super) Pub<'a, dyn PubSubBehavior + 'a, T>); + +impl<'a, T: Clone> Deref for DynPublisher<'a, T> { + type Target = Pub<'a, dyn PubSubBehavior + 'a, T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, T: Clone> DerefMut for DynPublisher<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// A publisher that holds a generic reference to the channel +pub struct Publisher<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>( + pub(super) Pub<'a, PubSubChannel, T>, +); + +impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> Deref + for Publisher<'a, M, T, CAP, SUBS, PUBS> +{ + type Target = Pub<'a, PubSubChannel, T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> DerefMut + for Publisher<'a, M, T, CAP, SUBS, PUBS> +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// A publisher that can only use the `publish_immediate` function, but it doesn't have to be registered with the channel. +/// (So an infinite amount is possible) +pub struct ImmediatePub<'a, PSB: PubSubBehavior + ?Sized, T: Clone> { + /// The channel we are a publisher for + channel: &'a PSB, + _phantom: PhantomData, +} + +impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> ImmediatePub<'a, PSB, T> { + pub(super) fn new(channel: &'a PSB) -> Self { + Self { + channel, + _phantom: Default::default(), + } + } + /// Publish the message right now even when the queue is full. + /// This may cause a subscriber to miss an older message. + pub fn publish_immediate(&self, message: T) { + self.channel.publish_immediate(message) + } + + /// Publish a message if there is space in the message queue + pub fn try_publish(&self, message: T) -> Result<(), T> { + self.channel.publish_with_context(message, None) + } +} + +/// An immediate publisher that holds a dynamic reference to the channel +pub struct DynImmediatePublisher<'a, T: Clone>(pub(super) ImmediatePub<'a, dyn PubSubBehavior + 'a, T>); + +impl<'a, T: Clone> Deref for DynImmediatePublisher<'a, T> { + type Target = ImmediatePub<'a, dyn PubSubBehavior + 'a, T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, T: Clone> DerefMut for DynImmediatePublisher<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// An immediate publisher that holds a generic reference to the channel +pub struct ImmediatePublisher<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>( + pub(super) ImmediatePub<'a, PubSubChannel, T>, +); + +impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> Deref + for ImmediatePublisher<'a, M, T, CAP, SUBS, PUBS> +{ + type Target = ImmediatePub<'a, PubSubChannel, T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> DerefMut + for ImmediatePublisher<'a, M, T, CAP, SUBS, PUBS> +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Future for the publisher wait action +pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { + /// The message we need to publish + message: Option, + publisher: &'s Pub<'a, PSB, T>, +} + +impl<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> Future for PublisherWaitFuture<'s, 'a, PSB, T> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let message = self.message.take().unwrap(); + match self.publisher.channel.publish_with_context(message, Some(cx)) { + Ok(()) => Poll::Ready(()), + Err(message) => { + self.message = Some(message); + Poll::Pending + } + } + } +} + +impl<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> Unpin for PublisherWaitFuture<'s, 'a, PSB, T> {} diff --git a/embassy-util/src/channel/pubsub/subscriber.rs b/embassy-util/src/channel/pubsub/subscriber.rs new file mode 100644 index 000000000..b9a2cbe18 --- /dev/null +++ b/embassy-util/src/channel/pubsub/subscriber.rs @@ -0,0 +1,152 @@ +//! Implementation of anything directly subscriber related + +use core::future::Future; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use super::{PubSubBehavior, PubSubChannel, WaitResult}; +use crate::blocking_mutex::raw::RawMutex; + +/// A subscriber to a channel +pub struct Sub<'a, PSB: PubSubBehavior + ?Sized, T: Clone> { + /// The message id of the next message we are yet to receive + next_message_id: u64, + /// The channel we are a subscriber to + channel: &'a PSB, + _phantom: PhantomData, +} + +impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Sub<'a, PSB, T> { + pub(super) fn new(next_message_id: u64, channel: &'a PSB) -> Self { + Self { + next_message_id, + channel, + _phantom: Default::default(), + } + } + + /// Wait for a published message + pub fn next_message<'s>(&'s mut self) -> SubscriberWaitFuture<'s, 'a, PSB, T> { + SubscriberWaitFuture { subscriber: self } + } + + /// Wait for a published message (ignoring lag results) + pub async fn next_message_pure(&mut self) -> T { + loop { + match self.next_message().await { + WaitResult::Lagged(_) => continue, + WaitResult::Message(message) => break message, + } + } + } + + /// Try to see if there's a published message we haven't received yet. + /// + /// This function does not peek. The message is received if there is one. + pub fn try_next_message(&mut self) -> Option> { + match self.channel.get_message_with_context(&mut self.next_message_id, None) { + Poll::Ready(result) => Some(result), + Poll::Pending => None, + } + } + + /// Try to see if there's a published message we haven't received yet (ignoring lag results). + /// + /// This function does not peek. The message is received if there is one. + pub fn try_next_message_pure(&mut self) -> Option { + loop { + match self.try_next_message() { + Some(WaitResult::Lagged(_)) => continue, + Some(WaitResult::Message(message)) => break Some(message), + None => break None, + } + } + } +} + +impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Sub<'a, PSB, T> { + fn drop(&mut self) { + self.channel.unregister_subscriber(self.next_message_id) + } +} + +impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Unpin for Sub<'a, PSB, T> {} + +/// Warning: The stream implementation ignores lag results and returns all messages. +/// This might miss some messages without you knowing it. +impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> futures_util::Stream for Sub<'a, PSB, T> { + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self + .channel + .get_message_with_context(&mut self.next_message_id, Some(cx)) + { + Poll::Ready(WaitResult::Message(message)) => Poll::Ready(Some(message)), + Poll::Ready(WaitResult::Lagged(_)) => { + cx.waker().wake_by_ref(); + Poll::Pending + } + Poll::Pending => Poll::Pending, + } + } +} + +/// A subscriber that holds a dynamic reference to the channel +pub struct DynSubscriber<'a, T: Clone>(pub(super) Sub<'a, dyn PubSubBehavior + 'a, T>); + +impl<'a, T: Clone> Deref for DynSubscriber<'a, T> { + type Target = Sub<'a, dyn PubSubBehavior + 'a, T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, T: Clone> DerefMut for DynSubscriber<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// A subscriber that holds a generic reference to the channel +pub struct Subscriber<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>( + pub(super) Sub<'a, PubSubChannel, T>, +); + +impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> Deref + for Subscriber<'a, M, T, CAP, SUBS, PUBS> +{ + type Target = Sub<'a, PubSubChannel, T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> DerefMut + for Subscriber<'a, M, T, CAP, SUBS, PUBS> +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Future for the subscriber wait action +pub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { + subscriber: &'s mut Sub<'a, PSB, T>, +} + +impl<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> Future for SubscriberWaitFuture<'s, 'a, PSB, T> { + type Output = WaitResult; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.subscriber + .channel + .get_message_with_context(&mut self.subscriber.next_message_id, Some(cx)) + } +} + +impl<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> Unpin for SubscriberWaitFuture<'s, 'a, PSB, T> {} diff --git a/embassy-util/src/channel/signal.rs b/embassy-util/src/channel/signal.rs new file mode 100644 index 000000000..a58469c4f --- /dev/null +++ b/embassy-util/src/channel/signal.rs @@ -0,0 +1,99 @@ +//! A synchronization primitive for passing the latest value to a task. +use core::cell::UnsafeCell; +use core::future::Future; +use core::mem; +use core::task::{Context, Poll, Waker}; + +/// Single-slot signaling primitive. +/// +/// This is similar to a [`Channel`](crate::channel::mpmc::Channel) with a buffer size of 1, except +/// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead +/// of waiting for the receiver to pop the previous value. +/// +/// It is useful for sending data between tasks when the receiver only cares about +/// the latest data, and therefore it's fine to "lose" messages. This is often the case for "state" +/// updates. +/// +/// For more advanced use cases, you might want to use [`Channel`](crate::channel::mpmc::Channel) instead. +/// +/// Signals are generally declared as `static`s and then borrowed as required. +/// +/// ``` +/// use embassy_util::channel::signal::Signal; +/// +/// enum SomeCommand { +/// On, +/// Off, +/// } +/// +/// static SOME_SIGNAL: Signal = Signal::new(); +/// ``` +pub struct Signal { + state: UnsafeCell>, +} + +enum State { + None, + Waiting(Waker), + Signaled(T), +} + +unsafe impl Send for Signal {} +unsafe impl Sync for Signal {} + +impl Signal { + /// Create a new `Signal`. + pub const fn new() -> Self { + Self { + state: UnsafeCell::new(State::None), + } + } +} + +impl Signal { + /// Mark this Signal as signaled. + pub fn signal(&self, val: T) { + critical_section::with(|_| unsafe { + let state = &mut *self.state.get(); + if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) { + waker.wake(); + } + }) + } + + /// Remove the queued value in this `Signal`, if any. + pub fn reset(&self) { + critical_section::with(|_| unsafe { + let state = &mut *self.state.get(); + *state = State::None + }) + } + + fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { + critical_section::with(|_| unsafe { + let state = &mut *self.state.get(); + match state { + State::None => { + *state = State::Waiting(cx.waker().clone()); + Poll::Pending + } + State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending, + State::Waiting(_) => panic!("waker overflow"), + State::Signaled(_) => match mem::replace(state, State::None) { + State::Signaled(res) => Poll::Ready(res), + _ => unreachable!(), + }, + } + }) + } + + /// Future that completes when this Signal has been signaled. + pub fn wait(&self) -> impl Future + '_ { + futures_util::future::poll_fn(move |cx| self.poll_wait(cx)) + } + + /// non-blocking method to check whether this signal has been signaled. + pub fn signaled(&self) -> bool { + critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_))) + } +} diff --git a/embassy-util/src/fmt.rs b/embassy-util/src/fmt.rs new file mode 100644 index 000000000..f8bb0a035 --- /dev/null +++ b/embassy-util/src/fmt.rs @@ -0,0 +1,228 @@ +#![macro_use] +#![allow(unused_macros)] + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[cfg(feature = "defmt-timestamp-uptime")] +defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-util/src/forever.rs b/embassy-util/src/forever.rs new file mode 100644 index 000000000..4f3698211 --- /dev/null +++ b/embassy-util/src/forever.rs @@ -0,0 +1,95 @@ +use core::cell::UnsafeCell; +use core::mem::MaybeUninit; + +use atomic_polyfill::{AtomicBool, Ordering}; + +/// Type with static lifetime that may be written to once at runtime. +/// +/// This may be used to initialize static objects at runtime, typically in the init routine. +/// This is useful for objects such as Embassy's RTC, which cannot be initialized in a const +/// context. +/// +/// Note: IF a global mutable variable is desired, use a CriticalSectionMutex or ThreadModeMutex instead. +/// +/// ``` +/// use embassy_util::Forever; +/// // Using an integer for the sake of keeping this example self-contained, +/// // see https://github.com/embassy-rs/embassy/wiki/Getting-Started for a more "proper" example. +/// static SOME_INT: Forever =Forever::new(); +/// +/// // put returns a mutable pointer to the object stored in the forever, which may then be passed +/// // around. +/// let mut x = SOME_INT.put(42); +/// assert_eq!(*x, 42); +/// ``` +pub struct Forever { + used: AtomicBool, + t: UnsafeCell>, +} + +unsafe impl Send for Forever {} +unsafe impl Sync for Forever {} + +impl Forever { + /// Create a new `Forever`. + #[inline(always)] + pub const fn new() -> Self { + Self { + used: AtomicBool::new(false), + t: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + /// Store a value in this `Forever`, returning a mutable reference to it. + /// + /// Using this method, the compiler usually constructs `val` in the stack and then moves + /// it into the `Forever`. If `T` is big, this is likely to cause stack overflows. + /// Considering using [`Signal::put_with`] instead, which will construct it in-place inside the `Forever`. + /// + /// # Panics + /// + /// Panics if this `Forever` already has a value stored in it. + #[inline(always)] + #[allow(clippy::mut_from_ref)] + pub fn put(&'static self, val: T) -> &'static mut T { + self.put_with(|| val) + } + + /// Store the closure return value in this `Forever`, returning a mutable reference to it. + /// + /// The advantage over [`Forever::put`] is that this method allows the closure to construct + /// the `T` value in-place directly inside the `Forever`, saving stack space. + /// + /// # Panics + /// + /// Panics if this `Forever` already has a value stored in it. + #[inline(always)] + #[allow(clippy::mut_from_ref)] + pub fn put_with(&'static self, val: impl FnOnce() -> T) -> &'static mut T { + if self + .used + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_err() + { + panic!("Forever.put() called multiple times"); + } + + let p: &'static mut MaybeUninit = unsafe { &mut *self.t.get() }; + p.write(val()) + } + + /// Unsafely get a mutable reference to the contents of this Forever. + /// + /// # Safety + /// + /// This is undefined behavior if: + /// + /// - The `Forever` has not been initialized yet (with `put' or `put_with`), or + /// - A reference to the contents (mutable or not) already exists. + #[inline(always)] + #[allow(clippy::mut_from_ref)] + pub unsafe fn steal(&self) -> &mut T { + let p: &mut MaybeUninit = &mut *self.t.get(); + p.assume_init_mut() + } +} diff --git a/embassy-util/src/lib.rs b/embassy-util/src/lib.rs new file mode 100644 index 000000000..07b1633ea --- /dev/null +++ b/embassy-util/src/lib.rs @@ -0,0 +1,22 @@ +#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] +#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(all(feature = "nightly", target_arch = "xtensa"), feature(asm_experimental_arch))] +#![allow(clippy::new_without_default)] +#![doc = include_str!("../../README.md")] +#![warn(missing_docs)] + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +pub mod blocking_mutex; +pub mod channel; +pub mod mutex; +pub mod waitqueue; + +mod forever; +mod select; +mod yield_now; + +pub use forever::*; +pub use select::*; +pub use yield_now::*; diff --git a/embassy-util/src/mutex.rs b/embassy-util/src/mutex.rs new file mode 100644 index 000000000..75a6e8dd3 --- /dev/null +++ b/embassy-util/src/mutex.rs @@ -0,0 +1,167 @@ +//! Async mutex. +//! +//! This module provides a mutex that can be used to synchronize data between asynchronous tasks. +use core::cell::{RefCell, UnsafeCell}; +use core::ops::{Deref, DerefMut}; +use core::task::Poll; + +use futures_util::future::poll_fn; + +use crate::blocking_mutex::raw::RawMutex; +use crate::blocking_mutex::Mutex as BlockingMutex; +use crate::waitqueue::WakerRegistration; + +/// Error returned by [`Mutex::try_lock`] +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct TryLockError; + +struct State { + locked: bool, + waker: WakerRegistration, +} + +/// Async mutex. +/// +/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex). +/// The raw mutex is used to guard access to the internal "is locked" flag. It +/// is held for very short periods only, while locking and unlocking. It is *not* held +/// for the entire time the async Mutex is locked. +/// +/// Which implementation you select depends on the context in which you're using the mutex. +/// +/// Use [`CriticalSectionRawMutex`](crate::blocking_mutex::raw::CriticalSectionRawMutex) when data can be shared between threads and interrupts. +/// +/// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor. +/// +/// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton. +/// +pub struct Mutex +where + M: RawMutex, + T: ?Sized, +{ + state: BlockingMutex>, + inner: UnsafeCell, +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +/// Async mutex. +impl Mutex +where + M: RawMutex, +{ + /// Create a new mutex with the given value. + pub const fn new(value: T) -> Self { + Self { + inner: UnsafeCell::new(value), + state: BlockingMutex::new(RefCell::new(State { + locked: false, + waker: WakerRegistration::new(), + })), + } + } +} + +impl Mutex +where + M: RawMutex, + T: ?Sized, +{ + /// Lock the mutex. + /// + /// This will wait for the mutex to be unlocked if it's already locked. + pub async fn lock(&self) -> MutexGuard<'_, M, T> { + poll_fn(|cx| { + let ready = self.state.lock(|s| { + let mut s = s.borrow_mut(); + if s.locked { + s.waker.register(cx.waker()); + false + } else { + s.locked = true; + true + } + }); + + if ready { + Poll::Ready(MutexGuard { mutex: self }) + } else { + Poll::Pending + } + }) + .await + } + + /// Attempt to immediately lock the mutex. + /// + /// If the mutex is already locked, this will return an error instead of waiting. + pub fn try_lock(&self) -> Result, TryLockError> { + self.state.lock(|s| { + let mut s = s.borrow_mut(); + if s.locked { + Err(TryLockError) + } else { + s.locked = true; + Ok(()) + } + })?; + + Ok(MutexGuard { mutex: self }) + } +} + +/// Async mutex guard. +/// +/// Owning an instance of this type indicates having +/// successfully locked the mutex, and grants access to the contents. +/// +/// Dropping it unlocks the mutex. +pub struct MutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + mutex: &'a Mutex, +} + +impl<'a, M, T> Drop for MutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + fn drop(&mut self) { + self.mutex.state.lock(|s| { + let mut s = s.borrow_mut(); + s.locked = false; + s.waker.wake(); + }) + } +} + +impl<'a, M, T> Deref for MutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + type Target = T; + fn deref(&self) -> &Self::Target { + // Safety: the MutexGuard represents exclusive access to the contents + // of the mutex, so it's OK to get it. + unsafe { &*(self.mutex.inner.get() as *const T) } + } +} + +impl<'a, M, T> DerefMut for MutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + // Safety: the MutexGuard represents exclusive access to the contents + // of the mutex, so it's OK to get it. + unsafe { &mut *(self.mutex.inner.get()) } + } +} diff --git a/embassy-util/src/select.rs b/embassy-util/src/select.rs new file mode 100644 index 000000000..8cecb7fa0 --- /dev/null +++ b/embassy-util/src/select.rs @@ -0,0 +1,230 @@ +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +/// Result for [`select`]. +#[derive(Debug, Clone)] +pub enum Either { + /// First future finished first. + First(A), + /// Second future finished first. + Second(B), +} + +/// Wait for one of two futures to complete. +/// +/// This function returns a new future which polls all the futures. +/// When one of them completes, it will complete with its result value. +/// +/// The other future is dropped. +pub fn select(a: A, b: B) -> Select +where + A: Future, + B: Future, +{ + Select { a, b } +} + +/// Future for the [`select`] function. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Select { + a: A, + b: B, +} + +impl Unpin for Select {} + +impl Future for Select +where + A: Future, + B: Future, +{ + type Output = Either; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + let a = unsafe { Pin::new_unchecked(&mut this.a) }; + let b = unsafe { Pin::new_unchecked(&mut this.b) }; + if let Poll::Ready(x) = a.poll(cx) { + return Poll::Ready(Either::First(x)); + } + if let Poll::Ready(x) = b.poll(cx) { + return Poll::Ready(Either::Second(x)); + } + Poll::Pending + } +} + +// ==================================================================== + +/// Result for [`select3`]. +#[derive(Debug, Clone)] +pub enum Either3 { + /// First future finished first. + First(A), + /// Second future finished first. + Second(B), + /// Third future finished first. + Third(C), +} + +/// Same as [`select`], but with more futures. +pub fn select3(a: A, b: B, c: C) -> Select3 +where + A: Future, + B: Future, + C: Future, +{ + Select3 { a, b, c } +} + +/// Future for the [`select3`] function. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Select3 { + a: A, + b: B, + c: C, +} + +impl Future for Select3 +where + A: Future, + B: Future, + C: Future, +{ + type Output = Either3; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + let a = unsafe { Pin::new_unchecked(&mut this.a) }; + let b = unsafe { Pin::new_unchecked(&mut this.b) }; + let c = unsafe { Pin::new_unchecked(&mut this.c) }; + if let Poll::Ready(x) = a.poll(cx) { + return Poll::Ready(Either3::First(x)); + } + if let Poll::Ready(x) = b.poll(cx) { + return Poll::Ready(Either3::Second(x)); + } + if let Poll::Ready(x) = c.poll(cx) { + return Poll::Ready(Either3::Third(x)); + } + Poll::Pending + } +} + +// ==================================================================== + +/// Result for [`select4`]. +#[derive(Debug, Clone)] +pub enum Either4 { + /// First future finished first. + First(A), + /// Second future finished first. + Second(B), + /// Third future finished first. + Third(C), + /// Fourth future finished first. + Fourth(D), +} + +/// Same as [`select`], but with more futures. +pub fn select4(a: A, b: B, c: C, d: D) -> Select4 +where + A: Future, + B: Future, + C: Future, + D: Future, +{ + Select4 { a, b, c, d } +} + +/// Future for the [`select4`] function. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Select4 { + a: A, + b: B, + c: C, + d: D, +} + +impl Future for Select4 +where + A: Future, + B: Future, + C: Future, + D: Future, +{ + type Output = Either4; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + let a = unsafe { Pin::new_unchecked(&mut this.a) }; + let b = unsafe { Pin::new_unchecked(&mut this.b) }; + let c = unsafe { Pin::new_unchecked(&mut this.c) }; + let d = unsafe { Pin::new_unchecked(&mut this.d) }; + if let Poll::Ready(x) = a.poll(cx) { + return Poll::Ready(Either4::First(x)); + } + if let Poll::Ready(x) = b.poll(cx) { + return Poll::Ready(Either4::Second(x)); + } + if let Poll::Ready(x) = c.poll(cx) { + return Poll::Ready(Either4::Third(x)); + } + if let Poll::Ready(x) = d.poll(cx) { + return Poll::Ready(Either4::Fourth(x)); + } + Poll::Pending + } +} + +// ==================================================================== + +/// Future for the [`select_all`] function. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct SelectAll { + inner: [Fut; N], +} + +/// Creates a new future which will select over a list of futures. +/// +/// The returned future will wait for any future within `iter` to be ready. Upon +/// completion the item resolved will be returned, along with the index of the +/// future that was ready. +/// +/// # Panics +/// +/// This function will panic if the array specified contains no items. +pub fn select_all(arr: [Fut; N]) -> SelectAll { + assert!(N > 0); + SelectAll { inner: arr } +} + +impl Future for SelectAll { + type Output = (Fut::Output, usize); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move, + // its elements also cannot move. Therefore it is safe to access `inner` and pin + // references to the contained futures. + let item = unsafe { + self.get_unchecked_mut() + .inner + .iter_mut() + .enumerate() + .find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) { + Poll::Pending => None, + Poll::Ready(e) => Some((i, e)), + }) + }; + + match item { + Some((idx, res)) => Poll::Ready((res, idx)), + None => Poll::Pending, + } + } +} diff --git a/embassy-util/src/waitqueue/mod.rs b/embassy-util/src/waitqueue/mod.rs new file mode 100644 index 000000000..6661a6b61 --- /dev/null +++ b/embassy-util/src/waitqueue/mod.rs @@ -0,0 +1,7 @@ +//! Async low-level wait queues + +mod waker; +pub use waker::*; + +mod multi_waker; +pub use multi_waker::*; diff --git a/embassy-util/src/waitqueue/multi_waker.rs b/embassy-util/src/waitqueue/multi_waker.rs new file mode 100644 index 000000000..325d2cb3a --- /dev/null +++ b/embassy-util/src/waitqueue/multi_waker.rs @@ -0,0 +1,33 @@ +use core::task::Waker; + +use super::WakerRegistration; + +/// Utility struct to register and wake multiple wakers. +pub struct MultiWakerRegistration { + wakers: [WakerRegistration; N], +} + +impl MultiWakerRegistration { + /// Create a new empty instance + pub const fn new() -> Self { + const WAKER: WakerRegistration = WakerRegistration::new(); + Self { wakers: [WAKER; N] } + } + + /// Register a waker. If the buffer is full the function returns it in the error + pub fn register<'a>(&mut self, w: &'a Waker) -> Result<(), &'a Waker> { + if let Some(waker_slot) = self.wakers.iter_mut().find(|waker_slot| !waker_slot.occupied()) { + waker_slot.register(w); + Ok(()) + } else { + Err(w) + } + } + + /// Wake all registered wakers. This clears the buffer + pub fn wake(&mut self) { + for waker_slot in self.wakers.iter_mut() { + waker_slot.wake() + } + } +} diff --git a/embassy-util/src/waitqueue/waker.rs b/embassy-util/src/waitqueue/waker.rs new file mode 100644 index 000000000..64e300eb8 --- /dev/null +++ b/embassy-util/src/waitqueue/waker.rs @@ -0,0 +1,92 @@ +use core::cell::Cell; +use core::mem; +use core::task::Waker; + +use crate::blocking_mutex::raw::CriticalSectionRawMutex; +use crate::blocking_mutex::Mutex; + +/// Utility struct to register and wake a waker. +#[derive(Debug)] +pub struct WakerRegistration { + waker: Option, +} + +impl WakerRegistration { + /// Create a new `WakerRegistration`. + pub const fn new() -> Self { + Self { waker: None } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&mut self, w: &Waker) { + match self.waker { + // Optimization: If both the old and new Wakers wake the same task, we can simply + // keep the old waker, skipping the clone. (In most executor implementations, + // cloning a waker is somewhat expensive, comparable to cloning an Arc). + Some(ref w2) if (w2.will_wake(w)) => {} + _ => { + // clone the new waker and store it + if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) { + // We had a waker registered for another task. Wake it, so the other task can + // reregister itself if it's still interested. + // + // If two tasks are waiting on the same thing concurrently, this will cause them + // to wake each other in a loop fighting over this WakerRegistration. This wastes + // CPU but things will still work. + // + // If the user wants to have two tasks waiting on the same thing they should use + // a more appropriate primitive that can store multiple wakers. + old_waker.wake() + } + } + } + } + + /// Wake the registered waker, if any. + pub fn wake(&mut self) { + if let Some(w) = self.waker.take() { + w.wake() + } + } + + /// Returns true if a waker is currently registered + pub fn occupied(&self) -> bool { + self.waker.is_some() + } +} + +/// Utility struct to register and wake a waker. +pub struct AtomicWaker { + waker: Mutex>>, +} + +impl AtomicWaker { + /// Create a new `AtomicWaker`. + pub const fn new() -> Self { + Self { + waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&self, w: &Waker) { + critical_section::with(|cs| { + let cell = self.waker.borrow(cs); + cell.set(match cell.replace(None) { + Some(w2) if (w2.will_wake(w)) => Some(w2), + _ => Some(w.clone()), + }) + }) + } + + /// Wake the registered waker, if any. + pub fn wake(&self) { + critical_section::with(|cs| { + let cell = self.waker.borrow(cs); + if let Some(w) = cell.replace(None) { + w.wake_by_ref(); + cell.set(Some(w)); + } + }) + } +} diff --git a/embassy-util/src/yield_now.rs b/embassy-util/src/yield_now.rs new file mode 100644 index 000000000..1ebecb916 --- /dev/null +++ b/embassy-util/src/yield_now.rs @@ -0,0 +1,25 @@ +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +/// Yield from the current task once, allowing other tasks to run. +pub fn yield_now() -> impl Future { + YieldNowFuture { yielded: false } +} + +struct YieldNowFuture { + yielded: bool, +} + +impl Future for YieldNowFuture { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.yielded { + Poll::Ready(()) + } else { + self.yielded = true; + cx.waker().wake_by_ref(); + Poll::Pending + } + } +} diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml deleted file mode 100644 index a5d36c10c..000000000 --- a/embassy/Cargo.toml +++ /dev/null @@ -1,95 +0,0 @@ -[package] -name = "embassy" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-v$VERSION/embassy/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy/src/" -features = ["nightly", "defmt", "unstable-traits", "time", "time-tick-1mhz"] -flavors = [ - { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] }, - { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] }, - { name = "thumbv6m-none-eabi", target = "thumbv6m-none-eabi", features = [] }, - { name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] }, - { name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] }, - { name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] }, - { name = "thumbv8m.base-none-eabi", target = "thumbv8m.base-none-eabi", features = [] }, - { name = "thumbv8m.main-none-eabi", target = "thumbv8m.main-none-eabi", features = [] }, - { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, -] - -[features] -default = [] -std = ["futures/std", "time", "time-tick-1mhz", "embassy-macros/std"] -wasm = ["wasm-bindgen", "js-sys", "embassy-macros/wasm", "wasm-timer", "time", "time-tick-1mhz"] - -# Enable nightly-only features -nightly = ["embedded-hal-async"] - -# Implement embedded-hal 1.0 alpha and embedded-hal-async traits. -# Implement embedded-hal-async traits if `nightly` is set as well. -unstable-traits = ["embedded-hal-1"] - -# Display a timestamp of the number of seconds since startup next to defmt log messages -# To use this you must have a time driver provided. -defmt-timestamp-uptime = ["defmt"] - -# Enable `embassy::time` module. -# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. -# Enabling it directly without supplying a time driver will fail to link. -time = [] - -# Set the `embassy::time` tick rate. -# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. -# If you're not writing your own driver, check the driver documentation to customize the tick rate. -# If you're writing a driver and your tick rate is not listed here, please add it and send a PR! -time-tick-32768hz = ["time"] -time-tick-1000hz = ["time"] -time-tick-1mhz = ["time"] -time-tick-16mhz = ["time"] - -executor-agnostic = [] - -[dependencies] -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } - -embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} - -futures = { version = "0.3.17", default-features = false, features = [ "cfg-target-has-atomic", "unstable" ] } -pin-project = { version = "1.0.8", default-features = false } -embassy-macros = { version = "0.1.0", path = "../embassy-macros"} -atomic-polyfill = "0.1.5" -critical-section = "0.2.5" -heapless = "0.7.5" -cfg-if = "1.0.0" - -# WASM dependencies -wasm-bindgen = { version = "0.2.76", features = ["nightly"], optional = true } -js-sys = { version = "0.3", optional = true } -wasm-timer = { version = "0.2.5", optional = true } - -[target."thumbv6m-none-eabi".dependencies] -cortex-m = "0.7.3" -[target."thumbv7m-none-eabi".dependencies] -cortex-m = "0.7.3" -[target."thumbv7em-none-eabi".dependencies] -cortex-m = "0.7.3" -[target."thumbv7em-none-eabihf".dependencies] -cortex-m = "0.7.3" -[target."thumbv8m.base-none-eabi".dependencies] -cortex-m = "0.7.3" -[target."thumbv8m.main-none-eabi".dependencies] -cortex-m = "0.7.3" -[target."thumbv8m.main-none-eabihf".dependencies] -cortex-m = "0.7.3" - -[dev-dependencies] -embassy = { path = ".", features = ["executor-agnostic"] } -futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } -futures-test = "0.3.17" -futures-timer = "3.0.2" -futures-util = { version = "0.3.17", features = [ "channel" ] } diff --git a/embassy/build.rs b/embassy/build.rs deleted file mode 100644 index 6fe82b44f..000000000 --- a/embassy/build.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::env; - -fn main() { - let target = env::var("TARGET").unwrap(); - - if target.starts_with("thumbv6m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv6m"); - } else if target.starts_with("thumbv7m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - } else if target.starts_with("thumbv7em-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - println!("cargo:rustc-cfg=armv7em"); // (not currently used) - } else if target.starts_with("thumbv8m.base") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_base"); - } else if target.starts_with("thumbv8m.main") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_main"); - } - - if target.ends_with("-eabihf") { - println!("cargo:rustc-cfg=has_fpu"); - } -} diff --git a/embassy/src/blocking_mutex/mod.rs b/embassy/src/blocking_mutex/mod.rs deleted file mode 100644 index c9db89f01..000000000 --- a/embassy/src/blocking_mutex/mod.rs +++ /dev/null @@ -1,200 +0,0 @@ -//! Blocking mutex. -//! -//! This module provides a blocking mutex that can be used to synchronize data. -pub mod raw; - -use core::cell::UnsafeCell; - -use self::raw::RawMutex; - -/// Blocking mutex (not async) -/// -/// Provides a blocking mutual exclusion primitive backed by an implementation of [`raw::RawMutex`]. -/// -/// Which implementation you select depends on the context in which you're using the mutex, and you can choose which kind -/// of interior mutability fits your use case. -/// -/// Use [`CriticalSectionMutex`] when data can be shared between threads and interrupts. -/// -/// Use [`NoopMutex`] when data is only shared between tasks running on the same executor. -/// -/// Use [`ThreadModeMutex`] when data is shared between tasks running on the same executor but you want a global singleton. -/// -/// In all cases, the blocking mutex is intended to be short lived and not held across await points. -/// Use the async [`Mutex`](crate::mutex::Mutex) if you need a lock that is held across await points. -pub struct Mutex { - // NOTE: `raw` must be FIRST, so when using ThreadModeMutex the "can't drop in non-thread-mode" gets - // to run BEFORE dropping `data`. - raw: R, - data: UnsafeCell, -} - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -impl Mutex { - /// Creates a new mutex in an unlocked state ready for use. - #[cfg(feature = "nightly")] - #[inline] - pub const fn new(val: T) -> Mutex { - Mutex { - raw: R::INIT, - data: UnsafeCell::new(val), - } - } - - /// Creates a new mutex in an unlocked state ready for use. - #[cfg(not(feature = "nightly"))] - #[inline] - pub fn new(val: T) -> Mutex { - Mutex { - raw: R::INIT, - data: UnsafeCell::new(val), - } - } - - /// Creates a critical section and grants temporary access to the protected data. - pub fn lock(&self, f: impl FnOnce(&T) -> U) -> U { - self.raw.lock(|| { - let ptr = self.data.get() as *const T; - let inner = unsafe { &*ptr }; - f(inner) - }) - } -} - -impl Mutex { - /// Creates a new mutex based on a pre-existing raw mutex. - /// - /// This allows creating a mutex in a constant context on stable Rust. - #[inline] - pub const fn const_new(raw_mutex: R, val: T) -> Mutex { - Mutex { - raw: raw_mutex, - data: UnsafeCell::new(val), - } - } - - /// Consumes this mutex, returning the underlying data. - #[inline] - pub fn into_inner(self) -> T { - self.data.into_inner() - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the `Mutex` mutably, no actual locking needs to - /// take place---the mutable borrow statically guarantees no locks exist. - #[inline] - pub fn get_mut(&mut self) -> &mut T { - unsafe { &mut *self.data.get() } - } -} - -/// A mutex that allows borrowing data across executors and interrupts. -/// -/// # Safety -/// -/// This mutex is safe to share between different executors and interrupts. -pub type CriticalSectionMutex = Mutex; - -/// A mutex that allows borrowing data in the context of a single executor. -/// -/// # Safety -/// -/// **This Mutex is only safe within a single executor.** -pub type NoopMutex = Mutex; - -impl Mutex { - /// Borrows the data for the duration of the critical section - pub fn borrow<'cs>(&'cs self, _cs: critical_section::CriticalSection<'cs>) -> &'cs T { - let ptr = self.data.get() as *const T; - unsafe { &*ptr } - } -} - -impl Mutex { - /// Borrows the data - pub fn borrow(&self) -> &T { - let ptr = self.data.get() as *const T; - unsafe { &*ptr } - } -} - -// ThreadModeMutex does NOT use the generic mutex from above because it's special: -// it's Send+Sync even if T: !Send. There's no way to do that without specialization (I think?). -// -// There's still a ThreadModeRawMutex for use with the generic Mutex (handy with Channel, for example), -// but that will require T: Send even though it shouldn't be needed. - -#[cfg(any(cortex_m, feature = "std"))] -pub use thread_mode_mutex::*; -#[cfg(any(cortex_m, feature = "std"))] -mod thread_mode_mutex { - use super::*; - - /// A "mutex" that only allows borrowing from thread mode. - /// - /// # Safety - /// - /// **This Mutex is only safe on single-core systems.** - /// - /// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access. - pub struct ThreadModeMutex { - inner: UnsafeCell, - } - - // NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode. - // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can - // be Send+Sync even if T is not Send (unlike CriticalSectionMutex) - unsafe impl Sync for ThreadModeMutex {} - unsafe impl Send for ThreadModeMutex {} - - impl ThreadModeMutex { - /// Creates a new mutex - pub const fn new(value: T) -> Self { - ThreadModeMutex { - inner: UnsafeCell::new(value), - } - } - } - - impl ThreadModeMutex { - /// Lock the `ThreadModeMutex`, granting access to the data. - /// - /// # Panics - /// - /// This will panic if not currently running in thread mode. - pub fn lock(&self, f: impl FnOnce(&T) -> R) -> R { - f(self.borrow()) - } - - /// Borrows the data - /// - /// # Panics - /// - /// This will panic if not currently running in thread mode. - pub fn borrow(&self) -> &T { - assert!( - raw::in_thread_mode(), - "ThreadModeMutex can only be borrowed from thread mode." - ); - unsafe { &*self.inner.get() } - } - } - - impl Drop for ThreadModeMutex { - fn drop(&mut self) { - // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so - // `drop` needs the same guarantees as `lock`. `ThreadModeMutex` is Send even if - // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, - // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. - assert!( - raw::in_thread_mode(), - "ThreadModeMutex can only be dropped from thread mode." - ); - - // Drop of the inner `T` happens after this. - } - } -} diff --git a/embassy/src/blocking_mutex/raw.rs b/embassy/src/blocking_mutex/raw.rs deleted file mode 100644 index 669a23e31..000000000 --- a/embassy/src/blocking_mutex/raw.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! Mutex primitives. -//! -//! This module provides a trait for mutexes that can be used in different contexts. -use core::marker::PhantomData; - -/// Raw mutex trait. -/// -/// This mutex is "raw", which means it does not actually contain the protected data, it -/// just implements the mutex mechanism. For most uses you should use [`super::Mutex`] instead, -/// which is generic over a RawMutex and contains the protected data. -/// -/// Note that, unlike other mutexes, implementations only guarantee no -/// concurrent access from other threads: concurrent access from the current -/// thread is allwed. For example, it's possible to lock the same mutex multiple times reentrantly. -/// -/// Therefore, locking a `RawMutex` is only enough to guarantee safe shared (`&`) access -/// to the data, it is not enough to guarantee exclusive (`&mut`) access. -/// -/// # Safety -/// -/// RawMutex implementations must ensure that, while locked, no other thread can lock -/// the RawMutex concurrently. -/// -/// Unsafe code is allowed to rely on this fact, so incorrect implementations will cause undefined behavior. -pub unsafe trait RawMutex { - /// Create a new `RawMutex` instance. - /// - /// This is a const instead of a method to allow creating instances in const context. - const INIT: Self; - - /// Lock this `RawMutex`. - fn lock(&self, f: impl FnOnce() -> R) -> R; -} - -/// A mutex that allows borrowing data across executors and interrupts. -/// -/// # Safety -/// -/// This mutex is safe to share between different executors and interrupts. -pub struct CriticalSectionRawMutex { - _phantom: PhantomData<()>, -} -unsafe impl Send for CriticalSectionRawMutex {} -unsafe impl Sync for CriticalSectionRawMutex {} - -impl CriticalSectionRawMutex { - /// Create a new `CriticalSectionRawMutex`. - pub const fn new() -> Self { - Self { _phantom: PhantomData } - } -} - -unsafe impl RawMutex for CriticalSectionRawMutex { - const INIT: Self = Self::new(); - - fn lock(&self, f: impl FnOnce() -> R) -> R { - critical_section::with(|_| f()) - } -} - -// ================ - -/// A mutex that allows borrowing data in the context of a single executor. -/// -/// # Safety -/// -/// **This Mutex is only safe within a single executor.** -pub struct NoopRawMutex { - _phantom: PhantomData<*mut ()>, -} - -unsafe impl Send for NoopRawMutex {} - -impl NoopRawMutex { - /// Create a new `NoopRawMutex`. - pub const fn new() -> Self { - Self { _phantom: PhantomData } - } -} - -unsafe impl RawMutex for NoopRawMutex { - const INIT: Self = Self::new(); - fn lock(&self, f: impl FnOnce() -> R) -> R { - f() - } -} - -// ================ - -#[cfg(any(cortex_m, feature = "std"))] -mod thread_mode { - use super::*; - - /// A "mutex" that only allows borrowing from thread mode. - /// - /// # Safety - /// - /// **This Mutex is only safe on single-core systems.** - /// - /// On multi-core systems, a `ThreadModeRawMutex` **is not sufficient** to ensure exclusive access. - pub struct ThreadModeRawMutex { - _phantom: PhantomData<()>, - } - - unsafe impl Send for ThreadModeRawMutex {} - unsafe impl Sync for ThreadModeRawMutex {} - - impl ThreadModeRawMutex { - /// Create a new `ThreadModeRawMutex`. - pub const fn new() -> Self { - Self { _phantom: PhantomData } - } - } - - unsafe impl RawMutex for ThreadModeRawMutex { - const INIT: Self = Self::new(); - fn lock(&self, f: impl FnOnce() -> R) -> R { - assert!(in_thread_mode(), "ThreadModeMutex can only be locked from thread mode."); - - f() - } - } - - impl Drop for ThreadModeRawMutex { - fn drop(&mut self) { - // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so - // `drop` needs the same guarantees as `lock`. `ThreadModeMutex` is Send even if - // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, - // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. - assert!( - in_thread_mode(), - "ThreadModeMutex can only be dropped from thread mode." - ); - - // Drop of the inner `T` happens after this. - } - } - - pub(crate) fn in_thread_mode() -> bool { - #[cfg(feature = "std")] - return Some("main") == std::thread::current().name(); - - #[cfg(not(feature = "std"))] - return cortex_m::peripheral::SCB::vect_active() == cortex_m::peripheral::scb::VectActive::ThreadMode; - } -} -#[cfg(any(cortex_m, feature = "std"))] -pub use thread_mode::*; diff --git a/embassy/src/channel/mod.rs b/embassy/src/channel/mod.rs deleted file mode 100644 index 5df1f5c5c..000000000 --- a/embassy/src/channel/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Async channels - -pub mod mpmc; -pub mod pubsub; -pub mod signal; diff --git a/embassy/src/channel/mpmc.rs b/embassy/src/channel/mpmc.rs deleted file mode 100644 index ca2214bb5..000000000 --- a/embassy/src/channel/mpmc.rs +++ /dev/null @@ -1,613 +0,0 @@ -//! A queue for sending values between asynchronous tasks. -//! -//! It can be used concurrently by multiple producers (senders) and multiple -//! consumers (receivers), i.e. it is an "MPMC channel". -//! -//! Receivers are competing for messages. So a message that is received by -//! one receiver is not received by any other. -//! -//! This queue takes a Mutex type so that various -//! targets can be attained. For example, a ThreadModeMutex can be used -//! for single-core Cortex-M targets where messages are only passed -//! between tasks running in thread mode. Similarly, a CriticalSectionMutex -//! can also be used for single-core targets where messages are to be -//! passed from exception mode e.g. out of an interrupt handler. -//! -//! This module provides a bounded channel that has a limit on the number of -//! messages that it can store, and if this limit is reached, trying to send -//! another message will result in an error being returned. -//! - -use core::cell::RefCell; -use core::pin::Pin; -use core::task::{Context, Poll}; - -use futures::Future; -use heapless::Deque; - -use crate::blocking_mutex::raw::RawMutex; -use crate::blocking_mutex::Mutex; -use crate::waitqueue::WakerRegistration; - -/// Send-only access to a [`Channel`]. -#[derive(Copy)] -pub struct Sender<'ch, M, T, const N: usize> -where - M: RawMutex, -{ - channel: &'ch Channel, -} - -impl<'ch, M, T, const N: usize> Clone for Sender<'ch, M, T, N> -where - M: RawMutex, -{ - fn clone(&self) -> Self { - Sender { channel: self.channel } - } -} - -impl<'ch, M, T, const N: usize> Sender<'ch, M, T, N> -where - M: RawMutex, -{ - /// Sends a value. - /// - /// See [`Channel::send()`] - pub fn send(&self, message: T) -> SendFuture<'ch, M, T, N> { - self.channel.send(message) - } - - /// Attempt to immediately send a message. - /// - /// See [`Channel::send()`] - pub fn try_send(&self, message: T) -> Result<(), TrySendError> { - self.channel.try_send(message) - } -} - -/// Send-only access to a [`Channel`] without knowing channel size. -#[derive(Copy)] -pub struct DynamicSender<'ch, T> { - channel: &'ch dyn DynamicChannel, -} - -impl<'ch, T> Clone for DynamicSender<'ch, T> { - fn clone(&self) -> Self { - DynamicSender { channel: self.channel } - } -} - -impl<'ch, M, T, const N: usize> From> for DynamicSender<'ch, T> -where - M: RawMutex, -{ - fn from(s: Sender<'ch, M, T, N>) -> Self { - Self { channel: s.channel } - } -} - -impl<'ch, T> DynamicSender<'ch, T> { - /// Sends a value. - /// - /// See [`Channel::send()`] - pub fn send(&self, message: T) -> DynamicSendFuture<'ch, T> { - DynamicSendFuture { - channel: self.channel, - message: Some(message), - } - } - - /// Attempt to immediately send a message. - /// - /// See [`Channel::send()`] - pub fn try_send(&self, message: T) -> Result<(), TrySendError> { - self.channel.try_send_with_context(message, None) - } -} - -/// Receive-only access to a [`Channel`]. -#[derive(Copy)] -pub struct Receiver<'ch, M, T, const N: usize> -where - M: RawMutex, -{ - channel: &'ch Channel, -} - -impl<'ch, M, T, const N: usize> Clone for Receiver<'ch, M, T, N> -where - M: RawMutex, -{ - fn clone(&self) -> Self { - Receiver { channel: self.channel } - } -} - -impl<'ch, M, T, const N: usize> Receiver<'ch, M, T, N> -where - M: RawMutex, -{ - /// Receive the next value. - /// - /// See [`Channel::recv()`]. - pub fn recv(&self) -> RecvFuture<'_, M, T, N> { - self.channel.recv() - } - - /// Attempt to immediately receive the next value. - /// - /// See [`Channel::try_recv()`] - pub fn try_recv(&self) -> Result { - self.channel.try_recv() - } -} - -/// Receive-only access to a [`Channel`] without knowing channel size. -#[derive(Copy)] -pub struct DynamicReceiver<'ch, T> { - channel: &'ch dyn DynamicChannel, -} - -impl<'ch, T> Clone for DynamicReceiver<'ch, T> { - fn clone(&self) -> Self { - DynamicReceiver { channel: self.channel } - } -} - -impl<'ch, T> DynamicReceiver<'ch, T> { - /// Receive the next value. - /// - /// See [`Channel::recv()`]. - pub fn recv(&self) -> DynamicRecvFuture<'_, T> { - DynamicRecvFuture { channel: self.channel } - } - - /// Attempt to immediately receive the next value. - /// - /// See [`Channel::try_recv()`] - pub fn try_recv(&self) -> Result { - self.channel.try_recv_with_context(None) - } -} - -impl<'ch, M, T, const N: usize> From> for DynamicReceiver<'ch, T> -where - M: RawMutex, -{ - fn from(s: Receiver<'ch, M, T, N>) -> Self { - Self { channel: s.channel } - } -} - -/// Future returned by [`Channel::recv`] and [`Receiver::recv`]. -pub struct RecvFuture<'ch, M, T, const N: usize> -where - M: RawMutex, -{ - channel: &'ch Channel, -} - -impl<'ch, M, T, const N: usize> Future for RecvFuture<'ch, M, T, N> -where - M: RawMutex, -{ - type Output = T; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.channel.try_recv_with_context(Some(cx)) { - Ok(v) => Poll::Ready(v), - Err(TryRecvError::Empty) => Poll::Pending, - } - } -} - -/// Future returned by [`DynamicReceiver::recv`]. -pub struct DynamicRecvFuture<'ch, T> { - channel: &'ch dyn DynamicChannel, -} - -impl<'ch, T> Future for DynamicRecvFuture<'ch, T> { - type Output = T; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.channel.try_recv_with_context(Some(cx)) { - Ok(v) => Poll::Ready(v), - Err(TryRecvError::Empty) => Poll::Pending, - } - } -} - -/// Future returned by [`Channel::send`] and [`Sender::send`]. -pub struct SendFuture<'ch, M, T, const N: usize> -where - M: RawMutex, -{ - channel: &'ch Channel, - message: Option, -} - -impl<'ch, M, T, const N: usize> Future for SendFuture<'ch, M, T, N> -where - M: RawMutex, -{ - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.message.take() { - Some(m) => match self.channel.try_send_with_context(m, Some(cx)) { - Ok(..) => Poll::Ready(()), - Err(TrySendError::Full(m)) => { - self.message = Some(m); - Poll::Pending - } - }, - None => panic!("Message cannot be None"), - } - } -} - -impl<'ch, M, T, const N: usize> Unpin for SendFuture<'ch, M, T, N> where M: RawMutex {} - -/// Future returned by [`DynamicSender::send`]. -pub struct DynamicSendFuture<'ch, T> { - channel: &'ch dyn DynamicChannel, - message: Option, -} - -impl<'ch, T> Future for DynamicSendFuture<'ch, T> { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.message.take() { - Some(m) => match self.channel.try_send_with_context(m, Some(cx)) { - Ok(..) => Poll::Ready(()), - Err(TrySendError::Full(m)) => { - self.message = Some(m); - Poll::Pending - } - }, - None => panic!("Message cannot be None"), - } - } -} - -impl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {} - -trait DynamicChannel { - fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError>; - - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; -} - -/// Error returned by [`try_recv`](Channel::try_recv). -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TryRecvError { - /// A message could not be received because the channel is empty. - Empty, -} - -/// Error returned by [`try_send`](Channel::try_send). -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TrySendError { - /// The data could not be sent on the channel because the channel is - /// currently full and sending would require blocking. - Full(T), -} - -struct ChannelState { - queue: Deque, - receiver_waker: WakerRegistration, - senders_waker: WakerRegistration, -} - -impl ChannelState { - const fn new() -> Self { - ChannelState { - queue: Deque::new(), - receiver_waker: WakerRegistration::new(), - senders_waker: WakerRegistration::new(), - } - } - - fn try_recv(&mut self) -> Result { - self.try_recv_with_context(None) - } - - fn try_recv_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result { - if self.queue.is_full() { - self.senders_waker.wake(); - } - - if let Some(message) = self.queue.pop_front() { - Ok(message) - } else { - if let Some(cx) = cx { - self.receiver_waker.register(cx.waker()); - } - Err(TryRecvError::Empty) - } - } - - fn try_send(&mut self, message: T) -> Result<(), TrySendError> { - self.try_send_with_context(message, None) - } - - fn try_send_with_context(&mut self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError> { - match self.queue.push_back(message) { - Ok(()) => { - self.receiver_waker.wake(); - Ok(()) - } - Err(message) => { - if let Some(cx) = cx { - self.senders_waker.register(cx.waker()); - } - Err(TrySendError::Full(message)) - } - } - } -} - -/// A bounded channel for communicating between asynchronous tasks -/// with backpressure. -/// -/// The channel will buffer up to the provided number of messages. Once the -/// buffer is full, attempts to `send` new messages will wait until a message is -/// received from the channel. -/// -/// All data sent will become available in the same order as it was sent. -pub struct Channel -where - M: RawMutex, -{ - inner: Mutex>>, -} - -impl Channel -where - M: RawMutex, -{ - /// Establish a new bounded channel. For example, to create one with a NoopMutex: - /// - /// ``` - /// use embassy::channel::mpmc::Channel; - /// use embassy::blocking_mutex::raw::NoopRawMutex; - /// - /// // Declare a bounded channel of 3 u32s. - /// let mut channel = Channel::::new(); - /// ``` - #[cfg(feature = "nightly")] - pub const fn new() -> Self { - Self { - inner: Mutex::new(RefCell::new(ChannelState::new())), - } - } - - /// Establish a new bounded channel. For example, to create one with a NoopMutex: - /// - /// ``` - /// use embassy::channel::mpmc::Channel; - /// use embassy::blocking_mutex::raw::NoopRawMutex; - /// - /// // Declare a bounded channel of 3 u32s. - /// let mut channel = Channel::::new(); - /// ``` - #[cfg(not(feature = "nightly"))] - pub fn new() -> Self { - Self { - inner: Mutex::new(RefCell::new(ChannelState::new())), - } - } - - fn lock(&self, f: impl FnOnce(&mut ChannelState) -> R) -> R { - self.inner.lock(|rc| f(&mut *rc.borrow_mut())) - } - - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { - self.lock(|c| c.try_recv_with_context(cx)) - } - - fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError> { - self.lock(|c| c.try_send_with_context(m, cx)) - } - - /// Get a sender for this channel. - pub fn sender(&self) -> Sender<'_, M, T, N> { - Sender { channel: self } - } - - /// Get a receiver for this channel. - pub fn receiver(&self) -> Receiver<'_, M, T, N> { - Receiver { channel: self } - } - - /// Send a value, waiting until there is capacity. - /// - /// Sending completes when the value has been pushed to the channel's queue. - /// This doesn't mean the value has been received yet. - pub fn send(&self, message: T) -> SendFuture<'_, M, T, N> { - SendFuture { - channel: self, - message: Some(message), - } - } - - /// Attempt to immediately send a message. - /// - /// This method differs from [`send`](Channel::send) by returning immediately if the channel's - /// buffer is full, instead of waiting. - /// - /// # Errors - /// - /// If the channel capacity has been reached, i.e., the channel has `n` - /// buffered values where `n` is the argument passed to [`Channel`], then an - /// error is returned. - pub fn try_send(&self, message: T) -> Result<(), TrySendError> { - self.lock(|c| c.try_send(message)) - } - - /// Receive the next value. - /// - /// If there are no messages in the channel's buffer, this method will - /// wait until a message is sent. - pub fn recv(&self) -> RecvFuture<'_, M, T, N> { - RecvFuture { channel: self } - } - - /// Attempt to immediately receive a message. - /// - /// This method will either receive a message from the channel immediately or return an error - /// if the channel is empty. - pub fn try_recv(&self) -> Result { - self.lock(|c| c.try_recv()) - } -} - -/// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the -/// tradeoff cost of dynamic dispatch. -impl DynamicChannel for Channel -where - M: RawMutex, -{ - fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError> { - Channel::try_send_with_context(self, m, cx) - } - - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { - Channel::try_recv_with_context(self, cx) - } -} - -#[cfg(test)] -mod tests { - use core::time::Duration; - - use futures::task::SpawnExt; - use futures_executor::ThreadPool; - use futures_timer::Delay; - - use super::*; - use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; - use crate::util::Forever; - - fn capacity(c: &ChannelState) -> usize { - c.queue.capacity() - c.queue.len() - } - - #[test] - fn sending_once() { - let mut c = ChannelState::::new(); - assert!(c.try_send(1).is_ok()); - assert_eq!(capacity(&c), 2); - } - - #[test] - fn sending_when_full() { - let mut c = ChannelState::::new(); - let _ = c.try_send(1); - let _ = c.try_send(1); - let _ = c.try_send(1); - match c.try_send(2) { - Err(TrySendError::Full(2)) => assert!(true), - _ => assert!(false), - } - assert_eq!(capacity(&c), 0); - } - - #[test] - fn receiving_once_with_one_send() { - let mut c = ChannelState::::new(); - assert!(c.try_send(1).is_ok()); - assert_eq!(c.try_recv().unwrap(), 1); - assert_eq!(capacity(&c), 3); - } - - #[test] - fn receiving_when_empty() { - let mut c = ChannelState::::new(); - match c.try_recv() { - Err(TryRecvError::Empty) => assert!(true), - _ => assert!(false), - } - assert_eq!(capacity(&c), 3); - } - - #[test] - fn simple_send_and_receive() { - let c = Channel::::new(); - assert!(c.try_send(1).is_ok()); - assert_eq!(c.try_recv().unwrap(), 1); - } - - #[test] - fn cloning() { - let c = Channel::::new(); - let r1 = c.receiver(); - let s1 = c.sender(); - - let _ = r1.clone(); - let _ = s1.clone(); - } - - #[test] - fn dynamic_dispatch() { - let c = Channel::::new(); - let s: DynamicSender<'_, u32> = c.sender().into(); - let r: DynamicReceiver<'_, u32> = c.receiver().into(); - - assert!(s.try_send(1).is_ok()); - assert_eq!(r.try_recv().unwrap(), 1); - } - - #[futures_test::test] - async fn receiver_receives_given_try_send_async() { - let executor = ThreadPool::new().unwrap(); - - static CHANNEL: Forever> = Forever::new(); - let c = &*CHANNEL.put(Channel::new()); - let c2 = c; - assert!(executor - .spawn(async move { - assert!(c2.try_send(1).is_ok()); - }) - .is_ok()); - assert_eq!(c.recv().await, 1); - } - - #[futures_test::test] - async fn sender_send_completes_if_capacity() { - let c = Channel::::new(); - c.send(1).await; - assert_eq!(c.recv().await, 1); - } - - #[futures_test::test] - async fn senders_sends_wait_until_capacity() { - let executor = ThreadPool::new().unwrap(); - - static CHANNEL: Forever> = Forever::new(); - let c = &*CHANNEL.put(Channel::new()); - assert!(c.try_send(1).is_ok()); - - let c2 = c; - let send_task_1 = executor.spawn_with_handle(async move { c2.send(2).await }); - let c2 = c; - let send_task_2 = executor.spawn_with_handle(async move { c2.send(3).await }); - // Wish I could think of a means of determining that the async send is waiting instead. - // However, I've used the debugger to observe that the send does indeed wait. - Delay::new(Duration::from_millis(500)).await; - assert_eq!(c.recv().await, 1); - assert!(executor - .spawn(async move { - loop { - c.recv().await; - } - }) - .is_ok()); - send_task_1.unwrap().await; - send_task_2.unwrap().await; - } -} diff --git a/embassy/src/channel/pubsub/mod.rs b/embassy/src/channel/pubsub/mod.rs deleted file mode 100644 index 64a72a52c..000000000 --- a/embassy/src/channel/pubsub/mod.rs +++ /dev/null @@ -1,542 +0,0 @@ -//! Implementation of [PubSubChannel], a queue where published messages get received by all subscribers. - -#![deny(missing_docs)] - -use core::cell::RefCell; -use core::fmt::Debug; -use core::task::{Context, Poll, Waker}; - -use heapless::Deque; - -use self::publisher::{ImmediatePub, Pub}; -use self::subscriber::Sub; -use crate::blocking_mutex::raw::RawMutex; -use crate::blocking_mutex::Mutex; -use crate::waitqueue::MultiWakerRegistration; - -pub mod publisher; -pub mod subscriber; - -pub use publisher::{DynImmediatePublisher, DynPublisher, ImmediatePublisher, Publisher}; -pub use subscriber::{DynSubscriber, Subscriber}; - -/// A broadcast channel implementation where multiple publishers can send messages to multiple subscribers -/// -/// Any published message can be read by all subscribers. -/// A publisher can choose how it sends its message. -/// -/// - With [Pub::publish()] the publisher has to wait until there is space in the internal message queue. -/// - With [Pub::publish_immediate()] the publisher doesn't await and instead lets the oldest message -/// in the queue drop if necessary. This will cause any [Subscriber] that missed the message to receive -/// an error to indicate that it has lagged. -/// -/// ## Example -/// -/// ``` -/// # use embassy::blocking_mutex::raw::NoopRawMutex; -/// # use embassy::channel::pubsub::WaitResult; -/// # use embassy::channel::pubsub::PubSubChannel; -/// # use futures_executor::block_on; -/// # let test = async { -/// // Create the channel. This can be static as well -/// let channel = PubSubChannel::::new(); -/// -/// // This is a generic subscriber with a direct reference to the channel -/// let mut sub0 = channel.subscriber().unwrap(); -/// // This is a dynamic subscriber with a dynamic (trait object) reference to the channel -/// let mut sub1 = channel.dyn_subscriber().unwrap(); -/// -/// let pub0 = channel.publisher().unwrap(); -/// -/// // Publish a message, but wait if the queue is full -/// pub0.publish(42).await; -/// -/// // Publish a message, but if the queue is full, just kick out the oldest message. -/// // This may cause some subscribers to miss a message -/// pub0.publish_immediate(43); -/// -/// // Wait for a new message. If the subscriber missed a message, the WaitResult will be a Lag result -/// assert_eq!(sub0.next_message().await, WaitResult::Message(42)); -/// assert_eq!(sub1.next_message().await, WaitResult::Message(42)); -/// -/// // Wait again, but this time ignore any Lag results -/// assert_eq!(sub0.next_message_pure().await, 43); -/// assert_eq!(sub1.next_message_pure().await, 43); -/// -/// // There's also a polling interface -/// assert_eq!(sub0.try_next_message(), None); -/// assert_eq!(sub1.try_next_message(), None); -/// # }; -/// # -/// # block_on(test); -/// ``` -/// -pub struct PubSubChannel { - inner: Mutex>>, -} - -impl - PubSubChannel -{ - /// Create a new channel - pub const fn new() -> Self { - Self { - inner: Mutex::const_new(M::INIT, RefCell::new(PubSubState::new())), - } - } - - /// Create a new subscriber. It will only receive messages that are published after its creation. - /// - /// If there are no subscriber slots left, an error will be returned. - pub fn subscriber(&self) -> Result, Error> { - self.inner.lock(|inner| { - let mut s = inner.borrow_mut(); - - if s.subscriber_count >= SUBS { - Err(Error::MaximumSubscribersReached) - } else { - s.subscriber_count += 1; - Ok(Subscriber(Sub::new(s.next_message_id, self))) - } - }) - } - - /// Create a new subscriber. It will only receive messages that are published after its creation. - /// - /// If there are no subscriber slots left, an error will be returned. - pub fn dyn_subscriber(&self) -> Result, Error> { - self.inner.lock(|inner| { - let mut s = inner.borrow_mut(); - - if s.subscriber_count >= SUBS { - Err(Error::MaximumSubscribersReached) - } else { - s.subscriber_count += 1; - Ok(DynSubscriber(Sub::new(s.next_message_id, self))) - } - }) - } - - /// Create a new publisher - /// - /// If there are no publisher slots left, an error will be returned. - pub fn publisher(&self) -> Result, Error> { - self.inner.lock(|inner| { - let mut s = inner.borrow_mut(); - - if s.publisher_count >= PUBS { - Err(Error::MaximumPublishersReached) - } else { - s.publisher_count += 1; - Ok(Publisher(Pub::new(self))) - } - }) - } - - /// Create a new publisher - /// - /// If there are no publisher slots left, an error will be returned. - pub fn dyn_publisher(&self) -> Result, Error> { - self.inner.lock(|inner| { - let mut s = inner.borrow_mut(); - - if s.publisher_count >= PUBS { - Err(Error::MaximumPublishersReached) - } else { - s.publisher_count += 1; - Ok(DynPublisher(Pub::new(self))) - } - }) - } - - /// Create a new publisher that can only send immediate messages. - /// This kind of publisher does not take up a publisher slot. - pub fn immediate_publisher(&self) -> ImmediatePublisher { - ImmediatePublisher(ImmediatePub::new(self)) - } - - /// Create a new publisher that can only send immediate messages. - /// This kind of publisher does not take up a publisher slot. - pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher { - DynImmediatePublisher(ImmediatePub::new(self)) - } -} - -impl PubSubBehavior - for PubSubChannel -{ - fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll> { - self.inner.lock(|s| { - let mut s = s.borrow_mut(); - - // Check if we can read a message - match s.get_message(*next_message_id) { - // Yes, so we are done polling - Some(WaitResult::Message(message)) => { - *next_message_id += 1; - Poll::Ready(WaitResult::Message(message)) - } - // No, so we need to reregister our waker and sleep again - None => { - if let Some(cx) = cx { - s.register_subscriber_waker(cx.waker()); - } - Poll::Pending - } - // We missed a couple of messages. We must do our internal bookkeeping and return that we lagged - Some(WaitResult::Lagged(amount)) => { - *next_message_id += amount; - Poll::Ready(WaitResult::Lagged(amount)) - } - } - }) - } - - fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T> { - self.inner.lock(|s| { - let mut s = s.borrow_mut(); - // Try to publish the message - match s.try_publish(message) { - // We did it, we are ready - Ok(()) => Ok(()), - // The queue is full, so we need to reregister our waker and go to sleep - Err(message) => { - if let Some(cx) = cx { - s.register_publisher_waker(cx.waker()); - } - Err(message) - } - } - }) - } - - fn publish_immediate(&self, message: T) { - self.inner.lock(|s| { - let mut s = s.borrow_mut(); - s.publish_immediate(message) - }) - } - - fn unregister_subscriber(&self, subscriber_next_message_id: u64) { - self.inner.lock(|s| { - let mut s = s.borrow_mut(); - s.unregister_subscriber(subscriber_next_message_id) - }) - } - - fn unregister_publisher(&self) { - self.inner.lock(|s| { - let mut s = s.borrow_mut(); - s.unregister_publisher() - }) - } -} - -/// Internal state for the PubSub channel -struct PubSubState { - /// The queue contains the last messages that have been published and a countdown of how many subscribers are yet to read it - queue: Deque<(T, usize), CAP>, - /// Every message has an id. - /// Don't worry, we won't run out. - /// If a million messages were published every second, then the ID's would run out in about 584942 years. - next_message_id: u64, - /// Collection of wakers for Subscribers that are waiting. - subscriber_wakers: MultiWakerRegistration, - /// Collection of wakers for Publishers that are waiting. - publisher_wakers: MultiWakerRegistration, - /// The amount of subscribers that are active - subscriber_count: usize, - /// The amount of publishers that are active - publisher_count: usize, -} - -impl PubSubState { - /// Create a new internal channel state - const fn new() -> Self { - Self { - queue: Deque::new(), - next_message_id: 0, - subscriber_wakers: MultiWakerRegistration::new(), - publisher_wakers: MultiWakerRegistration::new(), - subscriber_count: 0, - publisher_count: 0, - } - } - - fn try_publish(&mut self, message: T) -> Result<(), T> { - if self.subscriber_count == 0 { - // We don't need to publish anything because there is no one to receive it - return Ok(()); - } - - if self.queue.is_full() { - return Err(message); - } - // We just did a check for this - self.queue.push_back((message, self.subscriber_count)).ok().unwrap(); - - self.next_message_id += 1; - - // Wake all of the subscribers - self.subscriber_wakers.wake(); - - Ok(()) - } - - fn publish_immediate(&mut self, message: T) { - // Make space in the queue if required - if self.queue.is_full() { - self.queue.pop_front(); - } - - // This will succeed because we made sure there is space - self.try_publish(message).ok().unwrap(); - } - - fn get_message(&mut self, message_id: u64) -> Option> { - let start_id = self.next_message_id - self.queue.len() as u64; - - if message_id < start_id { - return Some(WaitResult::Lagged(start_id - message_id)); - } - - let current_message_index = (message_id - start_id) as usize; - - if current_message_index >= self.queue.len() { - return None; - } - - // We've checked that the index is valid - let queue_item = self.queue.iter_mut().nth(current_message_index).unwrap(); - - // We're reading this item, so decrement the counter - queue_item.1 -= 1; - let message = queue_item.0.clone(); - - if current_message_index == 0 && queue_item.1 == 0 { - self.queue.pop_front(); - self.publisher_wakers.wake(); - } - - Some(WaitResult::Message(message)) - } - - fn register_subscriber_waker(&mut self, waker: &Waker) { - match self.subscriber_wakers.register(waker) { - Ok(()) => {} - Err(_) => { - // All waker slots were full. This can only happen when there was a subscriber that now has dropped. - // We need to throw it away. It's a bit inefficient, but we can wake everything. - // Any future that is still active will simply reregister. - // This won't happen a lot, so it's ok. - self.subscriber_wakers.wake(); - self.subscriber_wakers.register(waker).unwrap(); - } - } - } - - fn register_publisher_waker(&mut self, waker: &Waker) { - match self.publisher_wakers.register(waker) { - Ok(()) => {} - Err(_) => { - // All waker slots were full. This can only happen when there was a publisher that now has dropped. - // We need to throw it away. It's a bit inefficient, but we can wake everything. - // Any future that is still active will simply reregister. - // This won't happen a lot, so it's ok. - self.publisher_wakers.wake(); - self.publisher_wakers.register(waker).unwrap(); - } - } - } - - fn unregister_subscriber(&mut self, subscriber_next_message_id: u64) { - self.subscriber_count -= 1; - - // All messages that haven't been read yet by this subscriber must have their counter decremented - let start_id = self.next_message_id - self.queue.len() as u64; - if subscriber_next_message_id >= start_id { - let current_message_index = (subscriber_next_message_id - start_id) as usize; - self.queue - .iter_mut() - .skip(current_message_index) - .for_each(|(_, counter)| *counter -= 1); - } - } - - fn unregister_publisher(&mut self) { - self.publisher_count -= 1; - } -} - -/// Error type for the [PubSubChannel] -#[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - /// All subscriber slots are used. To add another subscriber, first another subscriber must be dropped or - /// the capacity of the channels must be increased. - MaximumSubscribersReached, - /// All publisher slots are used. To add another publisher, first another publisher must be dropped or - /// the capacity of the channels must be increased. - MaximumPublishersReached, -} - -/// 'Middle level' behaviour of the pubsub channel. -/// This trait is used so that Sub and Pub can be generic over the channel. -pub trait PubSubBehavior { - /// Try to get a message from the queue with the given message id. - /// - /// If the message is not yet present and a context is given, then its waker is registered in the subsriber wakers. - fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll>; - - /// Try to publish a message to the queue. - /// - /// If the queue is full and a context is given, then its waker is registered in the publisher wakers. - fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T>; - - /// Publish a message immediately - fn publish_immediate(&self, message: T); - - /// Let the channel know that a subscriber has dropped - fn unregister_subscriber(&self, subscriber_next_message_id: u64); - - /// Let the channel know that a publisher has dropped - fn unregister_publisher(&self); -} - -/// The result of the subscriber wait procedure -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum WaitResult { - /// The subscriber did not receive all messages and lagged by the given amount of messages. - /// (This is the amount of messages that were missed) - Lagged(u64), - /// A message was received - Message(T), -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::blocking_mutex::raw::NoopRawMutex; - - #[futures_test::test] - async fn dyn_pub_sub_works() { - let channel = PubSubChannel::::new(); - - let mut sub0 = channel.dyn_subscriber().unwrap(); - let mut sub1 = channel.dyn_subscriber().unwrap(); - let pub0 = channel.dyn_publisher().unwrap(); - - pub0.publish(42).await; - - assert_eq!(sub0.next_message().await, WaitResult::Message(42)); - assert_eq!(sub1.next_message().await, WaitResult::Message(42)); - - assert_eq!(sub0.try_next_message(), None); - assert_eq!(sub1.try_next_message(), None); - } - - #[futures_test::test] - async fn all_subscribers_receive() { - let channel = PubSubChannel::::new(); - - let mut sub0 = channel.subscriber().unwrap(); - let mut sub1 = channel.subscriber().unwrap(); - let pub0 = channel.publisher().unwrap(); - - pub0.publish(42).await; - - assert_eq!(sub0.next_message().await, WaitResult::Message(42)); - assert_eq!(sub1.next_message().await, WaitResult::Message(42)); - - assert_eq!(sub0.try_next_message(), None); - assert_eq!(sub1.try_next_message(), None); - } - - #[futures_test::test] - async fn lag_when_queue_full_on_immediate_publish() { - let channel = PubSubChannel::::new(); - - let mut sub0 = channel.subscriber().unwrap(); - let pub0 = channel.publisher().unwrap(); - - pub0.publish_immediate(42); - pub0.publish_immediate(43); - pub0.publish_immediate(44); - pub0.publish_immediate(45); - pub0.publish_immediate(46); - pub0.publish_immediate(47); - - assert_eq!(sub0.try_next_message(), Some(WaitResult::Lagged(2))); - assert_eq!(sub0.next_message().await, WaitResult::Message(44)); - assert_eq!(sub0.next_message().await, WaitResult::Message(45)); - assert_eq!(sub0.next_message().await, WaitResult::Message(46)); - assert_eq!(sub0.next_message().await, WaitResult::Message(47)); - assert_eq!(sub0.try_next_message(), None); - } - - #[test] - fn limited_subs_and_pubs() { - let channel = PubSubChannel::::new(); - - let sub0 = channel.subscriber(); - let sub1 = channel.subscriber(); - let sub2 = channel.subscriber(); - let sub3 = channel.subscriber(); - let sub4 = channel.subscriber(); - - assert!(sub0.is_ok()); - assert!(sub1.is_ok()); - assert!(sub2.is_ok()); - assert!(sub3.is_ok()); - assert_eq!(sub4.err().unwrap(), Error::MaximumSubscribersReached); - - drop(sub0); - - let sub5 = channel.subscriber(); - assert!(sub5.is_ok()); - - // publishers - - let pub0 = channel.publisher(); - let pub1 = channel.publisher(); - let pub2 = channel.publisher(); - let pub3 = channel.publisher(); - let pub4 = channel.publisher(); - - assert!(pub0.is_ok()); - assert!(pub1.is_ok()); - assert!(pub2.is_ok()); - assert!(pub3.is_ok()); - assert_eq!(pub4.err().unwrap(), Error::MaximumPublishersReached); - - drop(pub0); - - let pub5 = channel.publisher(); - assert!(pub5.is_ok()); - } - - #[test] - fn publisher_wait_on_full_queue() { - let channel = PubSubChannel::::new(); - - let pub0 = channel.publisher().unwrap(); - - // There are no subscribers, so the queue will never be full - assert_eq!(pub0.try_publish(0), Ok(())); - assert_eq!(pub0.try_publish(0), Ok(())); - assert_eq!(pub0.try_publish(0), Ok(())); - assert_eq!(pub0.try_publish(0), Ok(())); - assert_eq!(pub0.try_publish(0), Ok(())); - - let sub0 = channel.subscriber().unwrap(); - - assert_eq!(pub0.try_publish(0), Ok(())); - assert_eq!(pub0.try_publish(0), Ok(())); - assert_eq!(pub0.try_publish(0), Ok(())); - assert_eq!(pub0.try_publish(0), Ok(())); - assert_eq!(pub0.try_publish(0), Err(0)); - - drop(sub0); - } -} diff --git a/embassy/src/channel/pubsub/publisher.rs b/embassy/src/channel/pubsub/publisher.rs deleted file mode 100644 index 53b0d43ba..000000000 --- a/embassy/src/channel/pubsub/publisher.rs +++ /dev/null @@ -1,183 +0,0 @@ -//! Implementation of anything directly publisher related - -use core::marker::PhantomData; -use core::ops::{Deref, DerefMut}; -use core::pin::Pin; -use core::task::{Context, Poll}; - -use futures::Future; - -use super::{PubSubBehavior, PubSubChannel}; -use crate::blocking_mutex::raw::RawMutex; - -/// A publisher to a channel -pub struct Pub<'a, PSB: PubSubBehavior + ?Sized, T: Clone> { - /// The channel we are a publisher for - channel: &'a PSB, - _phantom: PhantomData, -} - -impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { - pub(super) fn new(channel: &'a PSB) -> Self { - Self { - channel, - _phantom: Default::default(), - } - } - - /// Publish a message right now even when the queue is full. - /// This may cause a subscriber to miss an older message. - pub fn publish_immediate(&self, message: T) { - self.channel.publish_immediate(message) - } - - /// Publish a message. But if the message queue is full, wait for all subscribers to have read the last message - pub fn publish<'s>(&'s self, message: T) -> PublisherWaitFuture<'s, 'a, PSB, T> { - PublisherWaitFuture { - message: Some(message), - publisher: self, - } - } - - /// Publish a message if there is space in the message queue - pub fn try_publish(&self, message: T) -> Result<(), T> { - self.channel.publish_with_context(message, None) - } -} - -impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Pub<'a, PSB, T> { - fn drop(&mut self) { - self.channel.unregister_publisher() - } -} - -/// A publisher that holds a dynamic reference to the channel -pub struct DynPublisher<'a, T: Clone>(pub(super) Pub<'a, dyn PubSubBehavior + 'a, T>); - -impl<'a, T: Clone> Deref for DynPublisher<'a, T> { - type Target = Pub<'a, dyn PubSubBehavior + 'a, T>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, T: Clone> DerefMut for DynPublisher<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -/// A publisher that holds a generic reference to the channel -pub struct Publisher<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>( - pub(super) Pub<'a, PubSubChannel, T>, -); - -impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> Deref - for Publisher<'a, M, T, CAP, SUBS, PUBS> -{ - type Target = Pub<'a, PubSubChannel, T>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> DerefMut - for Publisher<'a, M, T, CAP, SUBS, PUBS> -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -/// A publisher that can only use the `publish_immediate` function, but it doesn't have to be registered with the channel. -/// (So an infinite amount is possible) -pub struct ImmediatePub<'a, PSB: PubSubBehavior + ?Sized, T: Clone> { - /// The channel we are a publisher for - channel: &'a PSB, - _phantom: PhantomData, -} - -impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> ImmediatePub<'a, PSB, T> { - pub(super) fn new(channel: &'a PSB) -> Self { - Self { - channel, - _phantom: Default::default(), - } - } - /// Publish the message right now even when the queue is full. - /// This may cause a subscriber to miss an older message. - pub fn publish_immediate(&self, message: T) { - self.channel.publish_immediate(message) - } - - /// Publish a message if there is space in the message queue - pub fn try_publish(&self, message: T) -> Result<(), T> { - self.channel.publish_with_context(message, None) - } -} - -/// An immediate publisher that holds a dynamic reference to the channel -pub struct DynImmediatePublisher<'a, T: Clone>(pub(super) ImmediatePub<'a, dyn PubSubBehavior + 'a, T>); - -impl<'a, T: Clone> Deref for DynImmediatePublisher<'a, T> { - type Target = ImmediatePub<'a, dyn PubSubBehavior + 'a, T>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, T: Clone> DerefMut for DynImmediatePublisher<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -/// An immediate publisher that holds a generic reference to the channel -pub struct ImmediatePublisher<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>( - pub(super) ImmediatePub<'a, PubSubChannel, T>, -); - -impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> Deref - for ImmediatePublisher<'a, M, T, CAP, SUBS, PUBS> -{ - type Target = ImmediatePub<'a, PubSubChannel, T>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> DerefMut - for ImmediatePublisher<'a, M, T, CAP, SUBS, PUBS> -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -/// Future for the publisher wait action -pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { - /// The message we need to publish - message: Option, - publisher: &'s Pub<'a, PSB, T>, -} - -impl<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> Future for PublisherWaitFuture<'s, 'a, PSB, T> { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let message = self.message.take().unwrap(); - match self.publisher.channel.publish_with_context(message, Some(cx)) { - Ok(()) => Poll::Ready(()), - Err(message) => { - self.message = Some(message); - Poll::Pending - } - } - } -} - -impl<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> Unpin for PublisherWaitFuture<'s, 'a, PSB, T> {} diff --git a/embassy/src/channel/pubsub/subscriber.rs b/embassy/src/channel/pubsub/subscriber.rs deleted file mode 100644 index 23c4938d9..000000000 --- a/embassy/src/channel/pubsub/subscriber.rs +++ /dev/null @@ -1,153 +0,0 @@ -//! Implementation of anything directly subscriber related - -use core::marker::PhantomData; -use core::ops::{Deref, DerefMut}; -use core::pin::Pin; -use core::task::{Context, Poll}; - -use futures::Future; - -use super::{PubSubBehavior, PubSubChannel, WaitResult}; -use crate::blocking_mutex::raw::RawMutex; - -/// A subscriber to a channel -pub struct Sub<'a, PSB: PubSubBehavior + ?Sized, T: Clone> { - /// The message id of the next message we are yet to receive - next_message_id: u64, - /// The channel we are a subscriber to - channel: &'a PSB, - _phantom: PhantomData, -} - -impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Sub<'a, PSB, T> { - pub(super) fn new(next_message_id: u64, channel: &'a PSB) -> Self { - Self { - next_message_id, - channel, - _phantom: Default::default(), - } - } - - /// Wait for a published message - pub fn next_message<'s>(&'s mut self) -> SubscriberWaitFuture<'s, 'a, PSB, T> { - SubscriberWaitFuture { subscriber: self } - } - - /// Wait for a published message (ignoring lag results) - pub async fn next_message_pure(&mut self) -> T { - loop { - match self.next_message().await { - WaitResult::Lagged(_) => continue, - WaitResult::Message(message) => break message, - } - } - } - - /// Try to see if there's a published message we haven't received yet. - /// - /// This function does not peek. The message is received if there is one. - pub fn try_next_message(&mut self) -> Option> { - match self.channel.get_message_with_context(&mut self.next_message_id, None) { - Poll::Ready(result) => Some(result), - Poll::Pending => None, - } - } - - /// Try to see if there's a published message we haven't received yet (ignoring lag results). - /// - /// This function does not peek. The message is received if there is one. - pub fn try_next_message_pure(&mut self) -> Option { - loop { - match self.try_next_message() { - Some(WaitResult::Lagged(_)) => continue, - Some(WaitResult::Message(message)) => break Some(message), - None => break None, - } - } - } -} - -impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Sub<'a, PSB, T> { - fn drop(&mut self) { - self.channel.unregister_subscriber(self.next_message_id) - } -} - -impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Unpin for Sub<'a, PSB, T> {} - -/// Warning: The stream implementation ignores lag results and returns all messages. -/// This might miss some messages without you knowing it. -impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> futures::Stream for Sub<'a, PSB, T> { - type Item = T; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self - .channel - .get_message_with_context(&mut self.next_message_id, Some(cx)) - { - Poll::Ready(WaitResult::Message(message)) => Poll::Ready(Some(message)), - Poll::Ready(WaitResult::Lagged(_)) => { - cx.waker().wake_by_ref(); - Poll::Pending - } - Poll::Pending => Poll::Pending, - } - } -} - -/// A subscriber that holds a dynamic reference to the channel -pub struct DynSubscriber<'a, T: Clone>(pub(super) Sub<'a, dyn PubSubBehavior + 'a, T>); - -impl<'a, T: Clone> Deref for DynSubscriber<'a, T> { - type Target = Sub<'a, dyn PubSubBehavior + 'a, T>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, T: Clone> DerefMut for DynSubscriber<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -/// A subscriber that holds a generic reference to the channel -pub struct Subscriber<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>( - pub(super) Sub<'a, PubSubChannel, T>, -); - -impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> Deref - for Subscriber<'a, M, T, CAP, SUBS, PUBS> -{ - type Target = Sub<'a, PubSubChannel, T>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> DerefMut - for Subscriber<'a, M, T, CAP, SUBS, PUBS> -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -/// Future for the subscriber wait action -pub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { - subscriber: &'s mut Sub<'a, PSB, T>, -} - -impl<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> Future for SubscriberWaitFuture<'s, 'a, PSB, T> { - type Output = WaitResult; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.subscriber - .channel - .get_message_with_context(&mut self.subscriber.next_message_id, Some(cx)) - } -} - -impl<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> Unpin for SubscriberWaitFuture<'s, 'a, PSB, T> {} diff --git a/embassy/src/channel/signal.rs b/embassy/src/channel/signal.rs deleted file mode 100644 index 3f3c482fb..000000000 --- a/embassy/src/channel/signal.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! A synchronization primitive for passing the latest value to a task. -use core::cell::UnsafeCell; -use core::future::Future; -use core::mem; -use core::task::{Context, Poll, Waker}; - -/// Single-slot signaling primitive. -/// -/// This is similar to a [`Channel`](crate::channel::mpmc::Channel) with a buffer size of 1, except -/// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead -/// of waiting for the receiver to pop the previous value. -/// -/// It is useful for sending data between tasks when the receiver only cares about -/// the latest data, and therefore it's fine to "lose" messages. This is often the case for "state" -/// updates. -/// -/// For more advanced use cases, you might want to use [`Channel`](crate::channel::mpmc::Channel) instead. -/// -/// Signals are generally declared as `static`s and then borrowed as required. -/// -/// ``` -/// use embassy::channel::signal::Signal; -/// -/// enum SomeCommand { -/// On, -/// Off, -/// } -/// -/// static SOME_SIGNAL: Signal = Signal::new(); -/// ``` -pub struct Signal { - state: UnsafeCell>, -} - -enum State { - None, - Waiting(Waker), - Signaled(T), -} - -unsafe impl Send for Signal {} -unsafe impl Sync for Signal {} - -impl Signal { - /// Create a new `Signal`. - pub const fn new() -> Self { - Self { - state: UnsafeCell::new(State::None), - } - } -} - -impl Signal { - /// Mark this Signal as signaled. - pub fn signal(&self, val: T) { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); - if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) { - waker.wake(); - } - }) - } - - /// Remove the queued value in this `Signal`, if any. - pub fn reset(&self) { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); - *state = State::None - }) - } - - fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); - match state { - State::None => { - *state = State::Waiting(cx.waker().clone()); - Poll::Pending - } - State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending, - State::Waiting(_) => panic!("waker overflow"), - State::Signaled(_) => match mem::replace(state, State::None) { - State::Signaled(res) => Poll::Ready(res), - _ => unreachable!(), - }, - } - }) - } - - /// Future that completes when this Signal has been signaled. - pub fn wait(&self) -> impl Future + '_ { - futures::future::poll_fn(move |cx| self.poll_wait(cx)) - } - - /// non-blocking method to check whether this signal has been signaled. - pub fn signaled(&self) -> bool { - critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_))) - } -} diff --git a/embassy/src/executor/arch/cortex_m.rs b/embassy/src/executor/arch/cortex_m.rs deleted file mode 100644 index cf80389b6..000000000 --- a/embassy/src/executor/arch/cortex_m.rs +++ /dev/null @@ -1,56 +0,0 @@ -use core::marker::PhantomData; -use core::ptr; - -use super::{raw, Spawner}; - -/// Thread mode executor, using WFE/SEV. -/// -/// This is the simplest and most common kind of executor. It runs on -/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction -/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction -/// is executed, to make the `WFE` exit from sleep and poll the task. -/// -/// This executor allows for ultra low power consumption for chips where `WFE` -/// triggers low-power sleep without extra steps. If your chip requires extra steps, -/// you may use [`raw::Executor`] directly to program custom behavior. -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} - -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()), - not_send: PhantomData, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [Forever](crate::util::Forever) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { self.inner.poll() }; - cortex_m::asm::wfe(); - } - } -} diff --git a/embassy/src/executor/arch/riscv32.rs b/embassy/src/executor/arch/riscv32.rs deleted file mode 100644 index 7a7d5698c..000000000 --- a/embassy/src/executor/arch/riscv32.rs +++ /dev/null @@ -1,74 +0,0 @@ -use core::marker::PhantomData; -use core::ptr; - -use atomic_polyfill::{AtomicBool, Ordering}; - -use super::{raw, Spawner}; - -/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV -/// -static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - -/// RISCV32 Executor -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} - -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - // use Signal_Work_Thread_Mode as substitute for local interrupt register - inner: raw::Executor::new( - |_| { - SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); - }, - ptr::null_mut(), - ), - not_send: PhantomData, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [Forever](crate::util::Forever) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { - self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - //will only set this value to true. - critical_section::with(|_| { - // if there is work to do, loop back to polling - // TODO can we relax this? - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } - // if not, wait for interrupt - else { - core::arch::asm!("wfi"); - } - }); - // if an interrupt occurred while waiting, it will be serviced here - } - } - } -} diff --git a/embassy/src/executor/arch/std.rs b/embassy/src/executor/arch/std.rs deleted file mode 100644 index b93ab8a79..000000000 --- a/embassy/src/executor/arch/std.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::marker::PhantomData; -use std::sync::{Condvar, Mutex}; - -use super::{raw, Spawner}; - -/// Single-threaded std-based executor. -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, - signaler: &'static Signaler, -} - -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - let signaler = &*Box::leak(Box::new(Signaler::new())); - Self { - inner: raw::Executor::new( - |p| unsafe { - let s = &*(p as *const () as *const Signaler); - s.signal() - }, - signaler as *const _ as _, - ), - not_send: PhantomData, - signaler, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [Forever](crate::util::Forever) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { self.inner.poll() }; - self.signaler.wait() - } - } -} - -struct Signaler { - mutex: Mutex, - condvar: Condvar, -} - -impl Signaler { - fn new() -> Self { - Self { - mutex: Mutex::new(false), - condvar: Condvar::new(), - } - } - - fn wait(&self) { - let mut signaled = self.mutex.lock().unwrap(); - while !*signaled { - signaled = self.condvar.wait(signaled).unwrap(); - } - *signaled = false; - } - - fn signal(&self) { - let mut signaled = self.mutex.lock().unwrap(); - *signaled = true; - self.condvar.notify_one(); - } -} diff --git a/embassy/src/executor/arch/wasm.rs b/embassy/src/executor/arch/wasm.rs deleted file mode 100644 index 9d5aa31ed..000000000 --- a/embassy/src/executor/arch/wasm.rs +++ /dev/null @@ -1,74 +0,0 @@ -use core::marker::PhantomData; - -use js_sys::Promise; -use wasm_bindgen::prelude::*; - -use super::raw::util::UninitCell; -use super::raw::{self}; -use super::Spawner; - -/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. -pub struct Executor { - inner: raw::Executor, - ctx: &'static WasmContext, - not_send: PhantomData<*mut ()>, -} - -pub(crate) struct WasmContext { - promise: Promise, - closure: UninitCell>, -} - -impl WasmContext { - pub fn new() -> Self { - Self { - promise: Promise::resolve(&JsValue::undefined()), - closure: UninitCell::uninit(), - } - } -} - -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - let ctx = &*Box::leak(Box::new(WasmContext::new())); - let inner = raw::Executor::new( - |p| unsafe { - let ctx = &*(p as *const () as *const WasmContext); - let _ = ctx.promise.then(ctx.closure.as_mut()); - }, - ctx as *const _ as _, - ); - Self { - inner, - not_send: PhantomData, - ctx, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [Forever](crate::util::Forever) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { - unsafe { - let executor = &self.inner; - self.ctx.closure.write(Closure::new(move |_| { - executor.poll(); - })); - init(self.inner.spawner()); - } - } -} diff --git a/embassy/src/executor/arch/xtensa.rs b/embassy/src/executor/arch/xtensa.rs deleted file mode 100644 index 20bd7b8a5..000000000 --- a/embassy/src/executor/arch/xtensa.rs +++ /dev/null @@ -1,75 +0,0 @@ -use core::marker::PhantomData; -use core::ptr; - -use atomic_polyfill::{AtomicBool, Ordering}; - -use super::{raw, Spawner}; - -/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa -/// -static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - -/// Xtensa Executor -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} - -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - // use Signal_Work_Thread_Mode as substitute for local interrupt register - inner: raw::Executor::new( - |_| { - SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); - }, - ptr::null_mut(), - ), - not_send: PhantomData, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [Forever](crate::util::Forever) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { - self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - // will only set this value to true. - // if there is work to do, loop back to polling - // TODO can we relax this? - critical_section::with(|_| { - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } else { - // waiti sets the PS.INTLEVEL when slipping into sleep - // because critical sections in Xtensa are implemented via increasing - // PS.INTLEVEL the critical section ends here - // take care not add code after `waiti` if it needs to be inside the CS - core::arch::asm!("waiti 0"); // critical section ends here - } - }); - } - } - } -} diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs deleted file mode 100644 index 758269363..000000000 --- a/embassy/src/executor/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! Async task executor. -//! -//! This module provides an async/await executor designed for embedded usage. -//! -//! - No `alloc`, no heap needed. Task futures are statically allocated. -//! - No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning. -//! - Integrated timer queue: sleeping is easy, just do `Timer::after(Duration::from_secs(1)).await;`. -//! - No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`. -//! - Efficient polling: a wake will only poll the woken task, not all of them. -//! - Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time. -//! - Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks. - -#![deny(missing_docs)] - -cfg_if::cfg_if! { - if #[cfg(cortex_m)] { - #[path="arch/cortex_m.rs"] - mod arch; - pub use arch::*; - } - else if #[cfg(target_arch="riscv32")] { - #[path="arch/riscv32.rs"] - mod arch; - pub use arch::*; - } - else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] { - #[path="arch/xtensa.rs"] - mod arch; - pub use arch::*; - } - else if #[cfg(feature="wasm")] { - #[path="arch/wasm.rs"] - mod arch; - pub use arch::*; - } - else if #[cfg(feature="std")] { - #[path="arch/std.rs"] - mod arch; - pub use arch::*; - } -} - -pub mod raw; - -mod spawner; -pub use spawner::*; diff --git a/embassy/src/executor/raw/mod.rs b/embassy/src/executor/raw/mod.rs deleted file mode 100644 index 0cfe617eb..000000000 --- a/embassy/src/executor/raw/mod.rs +++ /dev/null @@ -1,433 +0,0 @@ -//! Raw executor. -//! -//! This module exposes "raw" Executor and Task structs for more low level control. -//! -//! ## WARNING: here be dragons! -//! -//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe -//! executor wrappers in [`executor`](crate::executor) and the [`embassy::task`](embassy_macros::task) macro, which are fully safe. - -mod run_queue; -#[cfg(feature = "time")] -mod timer_queue; -pub(crate) mod util; -mod waker; - -use core::cell::Cell; -use core::future::Future; -use core::pin::Pin; -use core::ptr::NonNull; -use core::task::{Context, Poll}; -use core::{mem, ptr}; - -use atomic_polyfill::{AtomicU32, Ordering}; -use critical_section::CriticalSection; - -use self::run_queue::{RunQueue, RunQueueItem}; -use self::util::UninitCell; -pub use self::waker::task_from_waker; -use super::SpawnToken; -#[cfg(feature = "time")] -use crate::time::driver::{self, AlarmHandle}; -#[cfg(feature = "time")] -use crate::time::Instant; - -/// Task is spawned (has a future) -pub(crate) const STATE_SPAWNED: u32 = 1 << 0; -/// Task is in the executor run queue -pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; -/// Task is in the executor timer queue -#[cfg(feature = "time")] -pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; - -/// Raw task header for use in task pointers. -/// -/// This is an opaque struct, used for raw pointers to tasks, for use -/// with funtions like [`wake_task`] and [`task_from_waker`]. -pub struct TaskHeader { - pub(crate) state: AtomicU32, - pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 - pub(crate) poll_fn: UninitCell)>, // Valid if STATE_SPAWNED - - #[cfg(feature = "time")] - pub(crate) expires_at: Cell, - #[cfg(feature = "time")] - pub(crate) timer_queue_item: timer_queue::TimerQueueItem, -} - -impl TaskHeader { - pub(crate) const fn new() -> Self { - Self { - state: AtomicU32::new(0), - run_queue_item: RunQueueItem::new(), - executor: Cell::new(ptr::null()), - poll_fn: UninitCell::uninit(), - - #[cfg(feature = "time")] - expires_at: Cell::new(Instant::from_ticks(0)), - #[cfg(feature = "time")] - timer_queue_item: timer_queue::TimerQueueItem::new(), - } - } - - pub(crate) unsafe fn enqueue(&self) { - critical_section::with(|cs| { - let state = self.state.load(Ordering::Relaxed); - - // If already scheduled, or if not started, - if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { - return; - } - - // Mark it as scheduled - self.state.store(state | STATE_RUN_QUEUED, Ordering::Relaxed); - - // We have just marked the task as scheduled, so enqueue it. - let executor = &*self.executor.get(); - executor.enqueue(cs, self as *const TaskHeader as *mut TaskHeader); - }) - } -} - -/// Raw storage in which a task can be spawned. -/// -/// This struct holds the necessary memory to spawn one task whose future is `F`. -/// At a given time, the `TaskStorage` may be in spawned or not-spawned state. You -/// may spawn it with [`TaskStorage::spawn()`], which will fail if it is already spawned. -/// -/// A `TaskStorage` must live forever, it may not be deallocated even after the task has finished -/// running. Hence the relevant methods require `&'static self`. It may be reused, however. -/// -/// Internally, the [embassy::task](embassy_macros::task) macro allocates an array of `TaskStorage`s -/// in a `static`. The most common reason to use the raw `Task` is to have control of where -/// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc. - -// repr(C) is needed to guarantee that the Task is located at offset 0 -// This makes it safe to cast between TaskHeader and TaskStorage pointers. -#[repr(C)] -pub struct TaskStorage { - raw: TaskHeader, - future: UninitCell, // Valid if STATE_SPAWNED -} - -impl TaskStorage { - const NEW: Self = Self::new(); - - /// Create a new TaskStorage, in not-spawned state. - pub const fn new() -> Self { - Self { - raw: TaskHeader::new(), - future: UninitCell::uninit(), - } - } - - /// Try to spawn the task. - /// - /// The `future` closure constructs the future. It's only called if spawning is - /// actually possible. It is a closure instead of a simple `future: F` param to ensure - /// the future is constructed in-place, avoiding a temporary copy in the stack thanks to - /// NRVO optimizations. - /// - /// This function will fail if the task is already spawned and has not finished running. - /// In this case, the error is delayed: a "poisoned" SpawnToken is returned, which will - /// cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. - /// - /// Once the task has finished running, you may spawn it again. It is allowed to spawn it - /// on a different executor. - pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { - if self.spawn_mark_used() { - return unsafe { SpawnToken::::new(self.spawn_initialize(future)) }; - } - - SpawnToken::::new_failed() - } - - fn spawn_mark_used(&'static self) -> bool { - let state = STATE_SPAWNED | STATE_RUN_QUEUED; - self.raw - .state - .compare_exchange(0, state, Ordering::AcqRel, Ordering::Acquire) - .is_ok() - } - - unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> NonNull { - // Initialize the task - self.raw.poll_fn.write(Self::poll); - self.future.write(future()); - NonNull::new_unchecked(&self.raw as *const TaskHeader as *mut TaskHeader) - } - - unsafe fn poll(p: NonNull) { - let this = &*(p.as_ptr() as *const TaskStorage); - - let future = Pin::new_unchecked(this.future.as_mut()); - let waker = waker::from_task(p); - let mut cx = Context::from_waker(&waker); - match future.poll(&mut cx) { - Poll::Ready(_) => { - this.future.drop_in_place(); - this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel); - } - Poll::Pending => {} - } - - // the compiler is emitting a virtual call for waker drop, but we know - // it's a noop for our waker. - mem::forget(waker); - } -} - -unsafe impl Sync for TaskStorage {} - -/// Raw storage that can hold up to N tasks of the same type. -/// -/// This is essentially a `[TaskStorage; N]`. -pub struct TaskPool { - pool: [TaskStorage; N], -} - -impl TaskPool { - /// Create a new TaskPool, with all tasks in non-spawned state. - pub const fn new() -> Self { - Self { - pool: [TaskStorage::NEW; N], - } - } - - /// Try to spawn a task in the pool. - /// - /// See [`TaskStorage::spawn()`] for details. - /// - /// This will loop over the pool and spawn the task in the first storage that - /// is currently free. If none is free, a "poisoned" SpawnToken is returned, - /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. - pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { - for task in &self.pool { - if task.spawn_mark_used() { - return unsafe { SpawnToken::::new(task.spawn_initialize(future)) }; - } - } - - SpawnToken::::new_failed() - } - - /// Like spawn(), but allows the task to be send-spawned if the args are Send even if - /// the future is !Send. - /// - /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used - /// by the Embassy macros ONLY. - /// - /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` - /// is an `async fn`, NOT a hand-written `Future`. - #[doc(hidden)] - pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken - where - FutFn: FnOnce() -> F, - { - // When send-spawning a task, we construct the future in this thread, and effectively - // "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory, - // send-spawning should require the future `F` to be `Send`. - // - // The problem is this is more restrictive than needed. Once the future is executing, - // it is never sent to another thread. It is only sent when spawning. It should be - // enough for the task's arguments to be Send. (and in practice it's super easy to - // accidentally make your futures !Send, for example by holding an `Rc` or a `&RefCell` across an `.await`.) - // - // We can do it by sending the task args and constructing the future in the executor thread - // on first poll. However, this cannot be done in-place, so it'll waste stack space for a copy - // of the args. - // - // Luckily, an `async fn` future contains just the args when freshly constructed. So, if the - // args are Send, it's OK to send a !Send future, as long as we do it before first polling it. - // - // (Note: this is how the generators are implemented today, it's not officially guaranteed yet, - // but it's possible it'll be guaranteed in the future. See zulip thread: - // https://rust-lang.zulipchat.com/#narrow/stream/187312-wg-async/topic/.22only.20before.20poll.22.20Send.20futures ) - // - // The `FutFn` captures all the args, so if it's Send, the task can be send-spawned. - // This is why we return `SpawnToken` below. - // - // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly - // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken`. - - for task in &self.pool { - if task.spawn_mark_used() { - return SpawnToken::::new(task.spawn_initialize(future)); - } - } - - SpawnToken::::new_failed() - } -} - -/// Raw executor. -/// -/// This is the core of the Embassy executor. It is low-level, requiring manual -/// handling of wakeups and task polling. If you can, prefer using one of the -/// higher level executors in [`crate::executor`]. -/// -/// The raw executor leaves it up to you to handle wakeups and scheduling: -/// -/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks -/// that "want to run"). -/// - You must supply a `signal_fn`. The executor will call it to notify you it has work -/// to do. You must arrange for `poll()` to be called as soon as possible. -/// -/// `signal_fn` can be called from *any* context: any thread, any interrupt priority -/// level, etc. It may be called synchronously from any `Executor` method call as well. -/// You must deal with this correctly. -/// -/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates -/// the requirement for `poll` to not be called reentrantly. -pub struct Executor { - run_queue: RunQueue, - signal_fn: fn(*mut ()), - signal_ctx: *mut (), - - #[cfg(feature = "time")] - pub(crate) timer_queue: timer_queue::TimerQueue, - #[cfg(feature = "time")] - alarm: AlarmHandle, -} - -impl Executor { - /// Create a new executor. - /// - /// When the executor has work to do, it will call `signal_fn` with - /// `signal_ctx` as argument. - /// - /// See [`Executor`] docs for details on `signal_fn`. - pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { - #[cfg(feature = "time")] - let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; - #[cfg(feature = "time")] - driver::set_alarm_callback(alarm, signal_fn, signal_ctx); - - Self { - run_queue: RunQueue::new(), - signal_fn, - signal_ctx, - - #[cfg(feature = "time")] - timer_queue: timer_queue::TimerQueue::new(), - #[cfg(feature = "time")] - alarm, - } - } - - /// Enqueue a task in the task queue - /// - /// # Safety - /// - `task` must be a valid pointer to a spawned task. - /// - `task` must be set up to run in this executor. - /// - `task` must NOT be already enqueued (in this executor or another one). - #[inline(always)] - unsafe fn enqueue(&self, cs: CriticalSection, task: *mut TaskHeader) { - if self.run_queue.enqueue(cs, task) { - (self.signal_fn)(self.signal_ctx) - } - } - - /// Spawn a task in this executor. - /// - /// # Safety - /// - /// `task` must be a valid pointer to an initialized but not-already-spawned task. - /// - /// It is OK to use `unsafe` to call this from a thread that's not the executor thread. - /// In this case, the task's Future must be Send. This is because this is effectively - /// sending the task to the executor thread. - pub(super) unsafe fn spawn(&'static self, task: NonNull) { - let task = task.as_ref(); - task.executor.set(self); - - critical_section::with(|cs| { - self.enqueue(cs, task as *const _ as _); - }) - } - - /// Poll all queued tasks in this executor. - /// - /// This loops over all tasks that are queued to be polled (i.e. they're - /// freshly spawned or they've been woken). Other tasks are not polled. - /// - /// You must call `poll` after receiving a call to `signal_fn`. It is OK - /// to call `poll` even when not requested by `signal_fn`, but it wastes - /// energy. - /// - /// # Safety - /// - /// You must NOT call `poll` reentrantly on the same executor. - /// - /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you - /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to - /// somehow schedule for `poll()` to be called later, at a time you know for sure there's - /// no `poll()` already running. - pub unsafe fn poll(&'static self) { - #[cfg(feature = "time")] - self.timer_queue.dequeue_expired(Instant::now(), |p| { - p.as_ref().enqueue(); - }); - - self.run_queue.dequeue_all(|p| { - let task = p.as_ref(); - - #[cfg(feature = "time")] - task.expires_at.set(Instant::MAX); - - let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); - if state & STATE_SPAWNED == 0 { - // If task is not running, ignore it. This can happen in the following scenario: - // - Task gets dequeued, poll starts - // - While task is being polled, it gets woken. It gets placed in the queue. - // - Task poll finishes, returning done=true - // - RUNNING bit is cleared, but the task is already in the queue. - return; - } - - // Run the task - task.poll_fn.read()(p as _); - - // Enqueue or update into timer_queue - #[cfg(feature = "time")] - self.timer_queue.update(p); - }); - - #[cfg(feature = "time")] - { - // If this is already in the past, set_alarm will immediately trigger the alarm. - // This will cause `signal_fn` to be called, which will cause `poll()` to be called again, - // so we immediately do another poll loop iteration. - let next_expiration = self.timer_queue.next_expiration(); - driver::set_alarm(self.alarm, next_expiration.as_ticks()); - } - } - - /// Get a spawner that spawns tasks in this executor. - /// - /// It is OK to call this method multiple times to obtain multiple - /// `Spawner`s. You may also copy `Spawner`s. - pub fn spawner(&'static self) -> super::Spawner { - super::Spawner::new(self) - } -} - -/// Wake a task by raw pointer. -/// -/// You can obtain task pointers from `Waker`s using [`task_from_waker`]. -/// -/// # Safety -/// -/// `task` must be a valid task pointer obtained from [`task_from_waker`]. -pub unsafe fn wake_task(task: NonNull) { - task.as_ref().enqueue(); -} - -#[cfg(feature = "time")] -pub(crate) unsafe fn register_timer(at: Instant, waker: &core::task::Waker) { - let task = waker::task_from_waker(waker); - let task = task.as_ref(); - let expires_at = task.expires_at.get(); - task.expires_at.set(expires_at.min(at)); -} diff --git a/embassy/src/executor/raw/run_queue.rs b/embassy/src/executor/raw/run_queue.rs deleted file mode 100644 index 31615da7e..000000000 --- a/embassy/src/executor/raw/run_queue.rs +++ /dev/null @@ -1,74 +0,0 @@ -use core::ptr; -use core::ptr::NonNull; - -use atomic_polyfill::{AtomicPtr, Ordering}; -use critical_section::CriticalSection; - -use super::TaskHeader; - -pub(crate) struct RunQueueItem { - next: AtomicPtr, -} - -impl RunQueueItem { - pub const fn new() -> Self { - Self { - next: AtomicPtr::new(ptr::null_mut()), - } - } -} - -/// Atomic task queue using a very, very simple lock-free linked-list queue: -/// -/// To enqueue a task, task.next is set to the old head, and head is atomically set to task. -/// -/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with -/// null. Then the batch is iterated following the next pointers until null is reached. -/// -/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK -/// for our purposes: it can't create fairness problems since the next batch won't run until the -/// current batch is completely processed, so even if a task enqueues itself instantly (for example -/// by waking its own waker) can't prevent other tasks from running. -pub(crate) struct RunQueue { - head: AtomicPtr, -} - -impl RunQueue { - pub const fn new() -> Self { - Self { - head: AtomicPtr::new(ptr::null_mut()), - } - } - - /// Enqueues an item. Returns true if the queue was empty. - /// - /// # Safety - /// - /// `item` must NOT be already enqueued in any queue. - #[inline(always)] - pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: *mut TaskHeader) -> bool { - let prev = self.head.load(Ordering::Relaxed); - (*task).run_queue_item.next.store(prev, Ordering::Relaxed); - self.head.store(task, Ordering::Relaxed); - prev.is_null() - } - - /// Empty the queue, then call `on_task` for each task that was in the queue. - /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue - /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. - pub(crate) fn dequeue_all(&self, on_task: impl Fn(NonNull)) { - // Atomically empty the queue. - let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); - - // Iterate the linked list of tasks that were previously in the queue. - while let Some(task) = NonNull::new(ptr) { - // If the task re-enqueues itself, the `next` pointer will get overwritten. - // Therefore, first read the next pointer, and only then process the task. - let next = unsafe { task.as_ref() }.run_queue_item.next.load(Ordering::Relaxed); - - on_task(task); - - ptr = next - } - } -} diff --git a/embassy/src/executor/raw/timer_queue.rs b/embassy/src/executor/raw/timer_queue.rs deleted file mode 100644 index 62fcfc531..000000000 --- a/embassy/src/executor/raw/timer_queue.rs +++ /dev/null @@ -1,85 +0,0 @@ -use core::cell::Cell; -use core::cmp::min; -use core::ptr; -use core::ptr::NonNull; - -use atomic_polyfill::Ordering; - -use super::{TaskHeader, STATE_TIMER_QUEUED}; -use crate::time::Instant; - -pub(crate) struct TimerQueueItem { - next: Cell<*mut TaskHeader>, -} - -impl TimerQueueItem { - pub const fn new() -> Self { - Self { - next: Cell::new(ptr::null_mut()), - } - } -} - -pub(crate) struct TimerQueue { - head: Cell<*mut TaskHeader>, -} - -impl TimerQueue { - pub const fn new() -> Self { - Self { - head: Cell::new(ptr::null_mut()), - } - } - - pub(crate) unsafe fn update(&self, p: NonNull) { - let task = p.as_ref(); - if task.expires_at.get() != Instant::MAX { - let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel); - let is_new = old_state & STATE_TIMER_QUEUED == 0; - - if is_new { - task.timer_queue_item.next.set(self.head.get()); - self.head.set(p.as_ptr()); - } - } - } - - pub(crate) unsafe fn next_expiration(&self) -> Instant { - let mut res = Instant::MAX; - self.retain(|p| { - let task = p.as_ref(); - let expires = task.expires_at.get(); - res = min(res, expires); - expires != Instant::MAX - }); - res - } - - pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(NonNull)) { - self.retain(|p| { - let task = p.as_ref(); - if task.expires_at.get() <= now { - on_task(p); - false - } else { - true - } - }); - } - - pub(crate) unsafe fn retain(&self, mut f: impl FnMut(NonNull) -> bool) { - let mut prev = &self.head; - while !prev.get().is_null() { - let p = NonNull::new_unchecked(prev.get()); - let task = &*p.as_ptr(); - if f(p) { - // Skip to next - prev = &task.timer_queue_item.next; - } else { - // Remove it - prev.set(task.timer_queue_item.next.get()); - task.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel); - } - } - } -} diff --git a/embassy/src/executor/raw/util.rs b/embassy/src/executor/raw/util.rs deleted file mode 100644 index ed5822188..000000000 --- a/embassy/src/executor/raw/util.rs +++ /dev/null @@ -1,33 +0,0 @@ -use core::cell::UnsafeCell; -use core::mem::MaybeUninit; -use core::ptr; - -pub(crate) struct UninitCell(MaybeUninit>); -impl UninitCell { - pub const fn uninit() -> Self { - Self(MaybeUninit::uninit()) - } - - pub unsafe fn as_mut_ptr(&self) -> *mut T { - (*self.0.as_ptr()).get() - } - - #[allow(clippy::mut_from_ref)] - pub unsafe fn as_mut(&self) -> &mut T { - &mut *self.as_mut_ptr() - } - - pub unsafe fn write(&self, val: T) { - ptr::write(self.as_mut_ptr(), val) - } - - pub unsafe fn drop_in_place(&self) { - ptr::drop_in_place(self.as_mut_ptr()) - } -} - -impl UninitCell { - pub unsafe fn read(&self) -> T { - ptr::read(self.as_mut_ptr()) - } -} diff --git a/embassy/src/executor/raw/waker.rs b/embassy/src/executor/raw/waker.rs deleted file mode 100644 index 605cda4ca..000000000 --- a/embassy/src/executor/raw/waker.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::mem; -use core::ptr::NonNull; -use core::task::{RawWaker, RawWakerVTable, Waker}; - -use super::TaskHeader; - -const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); - -unsafe fn clone(p: *const ()) -> RawWaker { - RawWaker::new(p, &VTABLE) -} - -unsafe fn wake(p: *const ()) { - (*(p as *mut TaskHeader)).enqueue() -} - -unsafe fn drop(_: *const ()) { - // nop -} - -pub(crate) unsafe fn from_task(p: NonNull) -> Waker { - Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE)) -} - -/// Get a task pointer from a waker. -/// -/// This can be used as an optimization in wait queues to store task pointers -/// (1 word) instead of full Wakers (2 words). This saves a bit of RAM and helps -/// avoid dynamic dispatch. -/// -/// You can use the returned task pointer to wake the task with [`wake_task`](super::wake_task). -/// -/// # Panics -/// -/// Panics if the waker is not created by the Embassy executor. -pub fn task_from_waker(waker: &Waker) -> NonNull { - // safety: OK because WakerHack has the same layout as Waker. - // This is not really guaranteed because the structs are `repr(Rust)`, it is - // indeed the case in the current implementation. - // TODO use waker_getters when stable. https://github.com/rust-lang/rust/issues/96992 - let hack: &WakerHack = unsafe { mem::transmute(waker) }; - if hack.vtable != &VTABLE { - panic!("Found waker not created by the embassy executor. Consider enabling the `executor-agnostic` feature on the `embassy` crate.") - } - - // safety: we never create a waker with a null data pointer. - unsafe { NonNull::new_unchecked(hack.data as *mut TaskHeader) } -} - -struct WakerHack { - data: *const (), - vtable: &'static RawWakerVTable, -} diff --git a/embassy/src/executor/spawner.rs b/embassy/src/executor/spawner.rs deleted file mode 100644 index c8d036eb8..000000000 --- a/embassy/src/executor/spawner.rs +++ /dev/null @@ -1,202 +0,0 @@ -use core::marker::PhantomData; -use core::mem; -use core::ptr::NonNull; -use core::task::Poll; - -use futures::future::poll_fn; - -use super::raw; - -/// Token to spawn a newly-created task in an executor. -/// -/// When calling a task function (like `#[embassy::task] async fn my_task() { ... }`), the returned -/// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must -/// then spawn it into an executor, typically with [`Spawner::spawn()`]. -/// -/// The generic parameter `S` determines whether the task can be spawned in executors -/// in other threads or not. If `S: Send`, it can, which allows spawning it into a [`SendSpawner`]. -/// If not, it can't, so it can only be spawned into the current thread's executor, with [`Spawner`]. -/// -/// # Panics -/// -/// Dropping a SpawnToken instance panics. You may not "abort" spawning a task in this way. -/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. -#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] -pub struct SpawnToken { - raw_task: Option>, - phantom: PhantomData<*mut S>, -} - -impl SpawnToken { - pub(crate) unsafe fn new(raw_task: NonNull) -> Self { - Self { - raw_task: Some(raw_task), - phantom: PhantomData, - } - } - - pub(crate) fn new_failed() -> Self { - Self { - raw_task: None, - phantom: PhantomData, - } - } -} - -impl Drop for SpawnToken { - fn drop(&mut self) { - // TODO deallocate the task instead. - panic!("SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()") - } -} - -/// Error returned when spawning a task. -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum SpawnError { - /// Too many instances of this task are already running. - /// - /// By default, a task marked with `#[embassy::task]` can only have one instance - /// running at a time. You may allow multiple instances to run in parallel with - /// `#[embassy::task(pool_size = 4)]`, at the cost of higher RAM usage. - Busy, -} - -/// Handle to spawn tasks into an executor. -/// -/// This Spawner can spawn any task (Send and non-Send ones), but it can -/// only be used in the executor thread (it is not Send itself). -/// -/// If you want to spawn tasks from another thread, use [SendSpawner]. -#[derive(Copy, Clone)] -pub struct Spawner { - executor: &'static raw::Executor, - not_send: PhantomData<*mut ()>, -} - -impl Spawner { - pub(crate) fn new(executor: &'static raw::Executor) -> Self { - Self { - executor, - not_send: PhantomData, - } - } - - /// Get a Spawner for the current executor. - /// - /// This function is `async` just to get access to the current async - /// context. It returns instantly, it does not block/yield. - /// - /// # Panics - /// - /// Panics if the current executor is not an Embassy executor. - pub async fn for_current_executor() -> Self { - poll_fn(|cx| unsafe { - let task = raw::task_from_waker(cx.waker()); - let executor = (*task.as_ptr()).executor.get(); - Poll::Ready(Self::new(&*executor)) - }) - .await - } - - /// Spawn a task into an executor. - /// - /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`). - pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { - let task = token.raw_task; - mem::forget(token); - - match task { - Some(task) => { - unsafe { self.executor.spawn(task) }; - Ok(()) - } - None => Err(SpawnError::Busy), - } - } - - // Used by the `embassy_macros::main!` macro to throw an error when spawn - // fails. This is here to allow conditional use of `defmt::unwrap!` - // without introducing a `defmt` feature in the `embassy_macros` package, - // which would require use of `-Z namespaced-features`. - /// Spawn a task into an executor, panicking on failure. - /// - /// # Panics - /// - /// Panics if the spawning fails. - pub fn must_spawn(&self, token: SpawnToken) { - unwrap!(self.spawn(token)); - } - - /// Convert this Spawner to a SendSpawner. This allows you to send the - /// spawner to other threads, but the spawner loses the ability to spawn - /// non-Send tasks. - pub fn make_send(&self) -> SendSpawner { - SendSpawner { - executor: self.executor, - } - } -} - -/// Handle to spawn tasks into an executor from any thread. -/// -/// This Spawner can be used from any thread (it is Send), but it can -/// only spawn Send tasks. The reason for this is spawning is effectively -/// "sending" the tasks to the executor thread. -/// -/// If you want to spawn non-Send tasks, use [Spawner]. -#[derive(Copy, Clone)] -pub struct SendSpawner { - executor: &'static raw::Executor, -} - -unsafe impl Send for SendSpawner {} -unsafe impl Sync for SendSpawner {} - -impl SendSpawner { - pub(crate) fn new(executor: &'static raw::Executor) -> Self { - Self { executor } - } - - /// Get a Spawner for the current executor. - /// - /// This function is `async` just to get access to the current async - /// context. It returns instantly, it does not block/yield. - /// - /// # Panics - /// - /// Panics if the current executor is not an Embassy executor. - pub async fn for_current_executor() -> Self { - poll_fn(|cx| unsafe { - let task = raw::task_from_waker(cx.waker()); - let executor = (*task.as_ptr()).executor.get(); - Poll::Ready(Self::new(&*executor)) - }) - .await - } - - /// Spawn a task into an executor. - /// - /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`). - pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { - let header = token.raw_task; - mem::forget(token); - - match header { - Some(header) => { - unsafe { self.executor.spawn(header) }; - Ok(()) - } - None => Err(SpawnError::Busy), - } - } - - /// Spawn a task into an executor, panicking on failure. - /// - /// # Panics - /// - /// Panics if the spawning fails. - pub fn must_spawn(&self, token: SpawnToken) { - unwrap!(self.spawn(token)); - } -} diff --git a/embassy/src/fmt.rs b/embassy/src/fmt.rs deleted file mode 100644 index f8bb0a035..000000000 --- a/embassy/src/fmt.rs +++ /dev/null @@ -1,228 +0,0 @@ -#![macro_use] -#![allow(unused_macros)] - -#[cfg(all(feature = "defmt", feature = "log"))] -compile_error!("You may not enable both `defmt` and `log` features."); - -macro_rules! assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert!($($x)*); - } - }; -} - -macro_rules! assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_eq!($($x)*); - } - }; -} - -macro_rules! assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_ne!($($x)*); - } - }; -} - -macro_rules! debug_assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert!($($x)*); - } - }; -} - -macro_rules! debug_assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_eq!($($x)*); - } - }; -} - -macro_rules! debug_assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_ne!($($x)*); - } - }; -} - -macro_rules! todo { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::todo!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::todo!($($x)*); - } - }; -} - -macro_rules! unreachable { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } - }; -} - -macro_rules! panic { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::panic!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::panic!($($x)*); - } - }; -} - -macro_rules! trace { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::trace!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::trace!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! debug { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::debug!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::debug!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! info { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::info!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::info!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! warn { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::warn!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::warn!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! error { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::error!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::error!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -#[cfg(feature = "defmt")] -macro_rules! unwrap { - ($($x:tt)*) => { - ::defmt::unwrap!($($x)*) - }; -} - -#[cfg(not(feature = "defmt"))] -macro_rules! unwrap { - ($arg:expr) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); - } - } - }; - ($arg:expr, $($msg:expr),+ $(,)? ) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); - } - } - } -} - -#[cfg(feature = "defmt-timestamp-uptime")] -defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct NoneError; - -pub trait Try { - type Ok; - type Error; - fn into_result(self) -> Result; -} - -impl Try for Option { - type Ok = T; - type Error = NoneError; - - #[inline] - fn into_result(self) -> Result { - self.ok_or(NoneError) - } -} - -impl Try for Result { - type Ok = T; - type Error = E; - - #[inline] - fn into_result(self) -> Self { - self - } -} diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs deleted file mode 100644 index b7be8b34c..000000000 --- a/embassy/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] -#![cfg_attr(all(feature = "nightly", target_arch = "xtensa"), feature(asm_experimental_arch))] -#![allow(clippy::new_without_default)] -#![doc = include_str!("../../README.md")] -#![warn(missing_docs)] - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - -pub mod blocking_mutex; -pub mod channel; -pub mod executor; -pub mod mutex; -#[cfg(feature = "time")] -pub mod time; -pub mod util; -pub mod waitqueue; - -#[cfg(feature = "nightly")] -pub use embassy_macros::{main, task}; - -#[doc(hidden)] -/// Implementation details for embassy macros. DO NOT USE. -pub mod export { - pub use atomic_polyfill as atomic; -} diff --git a/embassy/src/mutex.rs b/embassy/src/mutex.rs deleted file mode 100644 index 9cfaaa845..000000000 --- a/embassy/src/mutex.rs +++ /dev/null @@ -1,180 +0,0 @@ -//! Async mutex. -//! -//! This module provides a mutex that can be used to synchronize data between asynchronous tasks. -use core::cell::{RefCell, UnsafeCell}; -use core::ops::{Deref, DerefMut}; -use core::task::Poll; - -use futures::future::poll_fn; - -use crate::blocking_mutex::raw::RawMutex; -use crate::blocking_mutex::Mutex as BlockingMutex; -use crate::waitqueue::WakerRegistration; - -/// Error returned by [`Mutex::try_lock`] -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TryLockError; - -struct State { - locked: bool, - waker: WakerRegistration, -} - -/// Async mutex. -/// -/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex). -/// The raw mutex is used to guard access to the internal "is locked" flag. It -/// is held for very short periods only, while locking and unlocking. It is *not* held -/// for the entire time the async Mutex is locked. -/// -/// Which implementation you select depends on the context in which you're using the mutex. -/// -/// Use [`CriticalSectionRawMutex`](crate::blocking_mutex::raw::CriticalSectionRawMutex) when data can be shared between threads and interrupts. -/// -/// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor. -/// -/// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton. -/// -pub struct Mutex -where - M: RawMutex, - T: ?Sized, -{ - state: BlockingMutex>, - inner: UnsafeCell, -} - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -/// Async mutex. -impl Mutex -where - M: RawMutex, -{ - /// Create a new mutex with the given value. - #[cfg(feature = "nightly")] - pub const fn new(value: T) -> Self { - Self { - inner: UnsafeCell::new(value), - state: BlockingMutex::new(RefCell::new(State { - locked: false, - waker: WakerRegistration::new(), - })), - } - } - - /// Create a new mutex with the given value. - #[cfg(not(feature = "nightly"))] - pub fn new(value: T) -> Self { - Self { - inner: UnsafeCell::new(value), - state: BlockingMutex::new(RefCell::new(State { - locked: false, - waker: WakerRegistration::new(), - })), - } - } -} - -impl Mutex -where - M: RawMutex, - T: ?Sized, -{ - /// Lock the mutex. - /// - /// This will wait for the mutex to be unlocked if it's already locked. - pub async fn lock(&self) -> MutexGuard<'_, M, T> { - poll_fn(|cx| { - let ready = self.state.lock(|s| { - let mut s = s.borrow_mut(); - if s.locked { - s.waker.register(cx.waker()); - false - } else { - s.locked = true; - true - } - }); - - if ready { - Poll::Ready(MutexGuard { mutex: self }) - } else { - Poll::Pending - } - }) - .await - } - - /// Attempt to immediately lock the mutex. - /// - /// If the mutex is already locked, this will return an error instead of waiting. - pub fn try_lock(&self) -> Result, TryLockError> { - self.state.lock(|s| { - let mut s = s.borrow_mut(); - if s.locked { - Err(TryLockError) - } else { - s.locked = true; - Ok(()) - } - })?; - - Ok(MutexGuard { mutex: self }) - } -} - -/// Async mutex guard. -/// -/// Owning an instance of this type indicates having -/// successfully locked the mutex, and grants access to the contents. -/// -/// Dropping it unlocks the mutex. -pub struct MutexGuard<'a, M, T> -where - M: RawMutex, - T: ?Sized, -{ - mutex: &'a Mutex, -} - -impl<'a, M, T> Drop for MutexGuard<'a, M, T> -where - M: RawMutex, - T: ?Sized, -{ - fn drop(&mut self) { - self.mutex.state.lock(|s| { - let mut s = s.borrow_mut(); - s.locked = false; - s.waker.wake(); - }) - } -} - -impl<'a, M, T> Deref for MutexGuard<'a, M, T> -where - M: RawMutex, - T: ?Sized, -{ - type Target = T; - fn deref(&self) -> &Self::Target { - // Safety: the MutexGuard represents exclusive access to the contents - // of the mutex, so it's OK to get it. - unsafe { &*(self.mutex.inner.get() as *const T) } - } -} - -impl<'a, M, T> DerefMut for MutexGuard<'a, M, T> -where - M: RawMutex, - T: ?Sized, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - // Safety: the MutexGuard represents exclusive access to the contents - // of the mutex, so it's OK to get it. - unsafe { &mut *(self.mutex.inner.get()) } - } -} diff --git a/embassy/src/time/delay.rs b/embassy/src/time/delay.rs deleted file mode 100644 index 83a895e93..000000000 --- a/embassy/src/time/delay.rs +++ /dev/null @@ -1,98 +0,0 @@ -use super::{Duration, Instant}; - -/// Blocks for at least `duration`. -pub fn block_for(duration: Duration) { - let expires_at = Instant::now() + duration; - while Instant::now() < expires_at {} -} - -/// Type implementing async delays and blocking `embedded-hal` delays. -/// -/// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least -/// the amount provided, but accuracy can be affected by many factors, including interrupt usage. -/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently -/// active driver. -pub struct Delay; - -#[cfg(feature = "unstable-traits")] -mod eh1 { - use super::*; - - impl embedded_hal_1::delay::blocking::DelayUs for Delay { - type Error = core::convert::Infallible; - - fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { - Ok(block_for(Duration::from_micros(us as u64))) - } - - fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { - Ok(block_for(Duration::from_millis(ms as u64))) - } - } -} - -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - use crate::time::Timer; - use core::future::Future; - use futures::FutureExt; - - impl embedded_hal_async::delay::DelayUs for Delay { - type Error = core::convert::Infallible; - - type DelayUsFuture<'a> = impl Future> + 'a where Self: 'a; - - fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> { - Timer::after(Duration::from_micros(micros as _)).map(Ok) - } - - type DelayMsFuture<'a> = impl Future> + 'a where Self: 'a; - - fn delay_ms(&mut self, millis: u32) -> Self::DelayMsFuture<'_> { - Timer::after(Duration::from_millis(millis as _)).map(Ok) - } - } - } -} - -mod eh02 { - use embedded_hal_02::blocking::delay::{DelayMs, DelayUs}; - - use super::*; - - impl DelayMs for Delay { - fn delay_ms(&mut self, ms: u8) { - block_for(Duration::from_millis(ms as u64)) - } - } - - impl DelayMs for Delay { - fn delay_ms(&mut self, ms: u16) { - block_for(Duration::from_millis(ms as u64)) - } - } - - impl DelayMs for Delay { - fn delay_ms(&mut self, ms: u32) { - block_for(Duration::from_millis(ms as u64)) - } - } - - impl DelayUs for Delay { - fn delay_us(&mut self, us: u8) { - block_for(Duration::from_micros(us as u64)) - } - } - - impl DelayUs for Delay { - fn delay_us(&mut self, us: u16) { - block_for(Duration::from_micros(us as u64)) - } - } - - impl DelayUs for Delay { - fn delay_us(&mut self, us: u32) { - block_for(Duration::from_micros(us as u64)) - } - } -} diff --git a/embassy/src/time/driver.rs b/embassy/src/time/driver.rs deleted file mode 100644 index 760a828b3..000000000 --- a/embassy/src/time/driver.rs +++ /dev/null @@ -1,170 +0,0 @@ -//! Time driver interface -//! -//! This module defines the interface a driver needs to implement to power the `embassy::time` module. -//! -//! # Implementing a driver -//! -//! - Define a struct `MyDriver` -//! - Implement [`Driver`] for it -//! - Register it as the global driver with [`time_driver_impl`]. -//! - Enable the Cargo features `embassy/time` and one of `embassy/time-tick-*` corresponding to the -//! tick rate of your driver. -//! -//! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own -//! Cargo features and having each enable the corresponding `embassy/time-tick-*`. -//! -//! # Linkage details -//! -//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. -//! -//! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. -//! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the -//! calls from the `embassy` crate to call into the driver crate. -//! -//! If there is none or multiple drivers in the crate tree, linking will fail. -//! -//! This method has a few key advantages for something as foundational as timekeeping: -//! -//! - The time driver is available everywhere easily, without having to thread the implementation -//! through generic parameters. This is especially helpful for libraries. -//! - It means comparing `Instant`s will always make sense: if there were multiple drivers -//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which -//! would yield incorrect results. -//! -//! # Example -//! -//! ``` -//! use embassy::time::driver::{Driver, AlarmHandle}; -//! -//! struct MyDriver{}; // not public! -//! embassy::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); -//! -//! impl Driver for MyDriver { -//! fn now(&self) -> u64 { -//! todo!() -//! } -//! unsafe fn allocate_alarm(&self) -> Option { -//! todo!() -//! } -//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { -//! todo!() -//! } -//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { -//! todo!() -//! } -//! } -//! ``` - -/// Alarm handle, assigned by the driver. -#[derive(Clone, Copy)] -pub struct AlarmHandle { - id: u8, -} - -impl AlarmHandle { - /// Create an AlarmHandle - /// - /// Safety: May only be called by the current global Driver impl. - /// The impl is allowed to rely on the fact that all `AlarmHandle` instances - /// are created by itself in unsafe code (e.g. indexing operations) - pub unsafe fn new(id: u8) -> Self { - Self { id } - } - - /// Get the ID of the AlarmHandle. - pub fn id(&self) -> u8 { - self.id - } -} - -/// Time driver -pub trait Driver: Send + Sync + 'static { - /// Return the current timestamp in ticks. - /// - /// Implementations MUST ensure that: - /// - This is guaranteed to be monotonic, i.e. a call to now() will always return - /// a greater or equal value than earler calls. Time can't "roll backwards". - /// - It "never" overflows. It must not overflow in a sufficiently long time frame, say - /// in 10_000 years (Human civilization is likely to already have self-destructed - /// 10_000 years from now.). This means if your hardware only has 16bit/32bit timers - /// you MUST extend them to 64-bit, for example by counting overflows in software, - /// or chaining multiple timers together. - fn now(&self) -> u64; - - /// Try allocating an alarm handle. Returns None if no alarms left. - /// Initially the alarm has no callback set, and a null `ctx` pointer. - /// - /// # Safety - /// It is UB to make the alarm fire before setting a callback. - unsafe fn allocate_alarm(&self) -> Option; - - /// Sets the callback function to be called when the alarm triggers. - /// The callback may be called from any context (interrupt or thread mode). - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); - - /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm - /// timestamp, the provided callback function will be called. - /// - /// If `timestamp` is already in the past, the alarm callback must be immediately fired. - /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`. - /// - /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. - /// - /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); -} - -extern "Rust" { - fn _embassy_time_now() -> u64; - fn _embassy_time_allocate_alarm() -> Option; - fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); - fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); -} - -pub(crate) fn now() -> u64 { - unsafe { _embassy_time_now() } -} -/// Safety: it is UB to make the alarm fire before setting a callback. -pub(crate) unsafe fn allocate_alarm() -> Option { - _embassy_time_allocate_alarm() -} -pub(crate) fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) } -} -pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) { - unsafe { _embassy_time_set_alarm(alarm, timestamp) } -} - -/// Set the time Driver implementation. -/// -/// See the module documentation for an example. -#[macro_export] -macro_rules! time_driver_impl { - (static $name:ident: $t: ty = $val:expr) => { - static $name: $t = $val; - - #[no_mangle] - fn _embassy_time_now() -> u64 { - <$t as $crate::time::driver::Driver>::now(&$name) - } - - #[no_mangle] - unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::time::driver::AlarmHandle> { - <$t as $crate::time::driver::Driver>::allocate_alarm(&$name) - } - - #[no_mangle] - fn _embassy_time_set_alarm_callback( - alarm: $crate::time::driver::AlarmHandle, - callback: fn(*mut ()), - ctx: *mut (), - ) { - <$t as $crate::time::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx) - } - - #[no_mangle] - fn _embassy_time_set_alarm(alarm: $crate::time::driver::AlarmHandle, timestamp: u64) { - <$t as $crate::time::driver::Driver>::set_alarm(&$name, alarm, timestamp) - } - }; -} diff --git a/embassy/src/time/driver_std.rs b/embassy/src/time/driver_std.rs deleted file mode 100644 index cb66f7c19..000000000 --- a/embassy/src/time/driver_std.rs +++ /dev/null @@ -1,208 +0,0 @@ -use std::cell::UnsafeCell; -use std::mem::MaybeUninit; -use std::sync::{Condvar, Mutex, Once}; -use std::time::{Duration as StdDuration, Instant as StdInstant}; -use std::{mem, ptr, thread}; - -use atomic_polyfill::{AtomicU8, Ordering}; - -use crate::time::driver::{AlarmHandle, Driver}; - -const ALARM_COUNT: usize = 4; - -struct AlarmState { - timestamp: u64, - - // This is really a Option<(fn(*mut ()), *mut ())> - // but fn pointers aren't allowed in const yet - callback: *const (), - ctx: *mut (), -} - -unsafe impl Send for AlarmState {} - -impl AlarmState { - const fn new() -> Self { - Self { - timestamp: u64::MAX, - callback: ptr::null(), - ctx: ptr::null_mut(), - } - } -} - -struct TimeDriver { - alarm_count: AtomicU8, - - once: Once, - alarms: UninitCell>, - zero_instant: UninitCell, - signaler: UninitCell, -} - -const ALARM_NEW: AlarmState = AlarmState::new(); -crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { - alarm_count: AtomicU8::new(0), - - once: Once::new(), - alarms: UninitCell::uninit(), - zero_instant: UninitCell::uninit(), - signaler: UninitCell::uninit(), -}); - -impl TimeDriver { - fn init(&self) { - self.once.call_once(|| unsafe { - self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); - self.zero_instant.write(StdInstant::now()); - self.signaler.write(Signaler::new()); - - thread::spawn(Self::alarm_thread); - }); - } - - fn alarm_thread() { - let zero = unsafe { DRIVER.zero_instant.read() }; - loop { - let now = DRIVER.now(); - - let mut next_alarm = u64::MAX; - { - let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap(); - for alarm in alarms { - if alarm.timestamp <= now { - alarm.timestamp = u64::MAX; - - // Call after clearing alarm, so the callback can set another alarm. - - // safety: - // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. - // - other than that we only store valid function pointers into alarm.callback - let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) }; - f(alarm.ctx); - } else { - next_alarm = next_alarm.min(alarm.timestamp); - } - } - } - - // Ensure we don't overflow - let until = zero - .checked_add(StdDuration::from_micros(next_alarm)) - .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1)); - - unsafe { DRIVER.signaler.as_ref() }.wait_until(until); - } - } -} - -impl Driver for TimeDriver { - fn now(&self) -> u64 { - self.init(); - - let zero = unsafe { self.zero_instant.read() }; - StdInstant::now().duration_since(zero).as_micros() as u64 - } - - unsafe fn allocate_alarm(&self) -> Option { - let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { - if x < ALARM_COUNT as u8 { - Some(x + 1) - } else { - None - } - }); - - match id { - Ok(id) => Some(AlarmHandle::new(id)), - Err(_) => None, - } - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.callback = callback as *const (); - alarm.ctx = ctx; - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.timestamp = timestamp; - unsafe { self.signaler.as_ref() }.signal(); - } -} - -struct Signaler { - mutex: Mutex, - condvar: Condvar, -} - -impl Signaler { - fn new() -> Self { - Self { - mutex: Mutex::new(false), - condvar: Condvar::new(), - } - } - - fn wait_until(&self, until: StdInstant) { - let mut signaled = self.mutex.lock().unwrap(); - while !*signaled { - let now = StdInstant::now(); - - if now >= until { - break; - } - - let dur = until - now; - let (signaled2, timeout) = self.condvar.wait_timeout(signaled, dur).unwrap(); - signaled = signaled2; - if timeout.timed_out() { - break; - } - } - *signaled = false; - } - - fn signal(&self) { - let mut signaled = self.mutex.lock().unwrap(); - *signaled = true; - self.condvar.notify_one(); - } -} - -pub(crate) struct UninitCell(MaybeUninit>); -unsafe impl Send for UninitCell {} -unsafe impl Sync for UninitCell {} - -impl UninitCell { - pub const fn uninit() -> Self { - Self(MaybeUninit::uninit()) - } - - pub unsafe fn as_ptr(&self) -> *const T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_mut_ptr(&self) -> *mut T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_ref(&self) -> &T { - &*self.as_ptr() - } - - pub unsafe fn write(&self, val: T) { - ptr::write(self.as_mut_ptr(), val) - } -} - -impl UninitCell { - pub unsafe fn read(&self) -> T { - ptr::read(self.as_mut_ptr()) - } -} diff --git a/embassy/src/time/driver_wasm.rs b/embassy/src/time/driver_wasm.rs deleted file mode 100644 index 5f585a19a..000000000 --- a/embassy/src/time/driver_wasm.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::cell::UnsafeCell; -use std::mem::MaybeUninit; -use std::ptr; -use std::sync::{Mutex, Once}; - -use atomic_polyfill::{AtomicU8, Ordering}; -use wasm_bindgen::prelude::*; -use wasm_timer::Instant as StdInstant; - -use crate::time::driver::{AlarmHandle, Driver}; - -const ALARM_COUNT: usize = 4; - -struct AlarmState { - token: Option, - closure: Option>, -} - -unsafe impl Send for AlarmState {} - -impl AlarmState { - const fn new() -> Self { - Self { - token: None, - closure: None, - } - } -} - -#[wasm_bindgen] -extern "C" { - fn setTimeout(closure: &Closure, millis: u32) -> f64; - fn clearTimeout(token: f64); -} - -struct TimeDriver { - alarm_count: AtomicU8, - - once: Once, - alarms: UninitCell>, - zero_instant: UninitCell, -} - -const ALARM_NEW: AlarmState = AlarmState::new(); -crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { - alarm_count: AtomicU8::new(0), - once: Once::new(), - alarms: UninitCell::uninit(), - zero_instant: UninitCell::uninit(), -}); - -impl TimeDriver { - fn init(&self) { - self.once.call_once(|| unsafe { - self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); - self.zero_instant.write(StdInstant::now()); - }); - } -} - -impl Driver for TimeDriver { - fn now(&self) -> u64 { - self.init(); - - let zero = unsafe { self.zero_instant.read() }; - StdInstant::now().duration_since(zero).as_micros() as u64 - } - - unsafe fn allocate_alarm(&self) -> Option { - let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { - if x < ALARM_COUNT as u8 { - Some(x + 1) - } else { - None - } - }); - - match id { - Ok(id) => Some(AlarmHandle::new(id)), - Err(_) => None, - } - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.closure.replace(Closure::new(move || { - callback(ctx); - })); - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - let timeout = (timestamp - self.now()) as u32; - if let Some(token) = alarm.token { - clearTimeout(token); - } - alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); - } -} - -pub(crate) struct UninitCell(MaybeUninit>); -unsafe impl Send for UninitCell {} -unsafe impl Sync for UninitCell {} - -impl UninitCell { - pub const fn uninit() -> Self { - Self(MaybeUninit::uninit()) - } - unsafe fn as_ptr(&self) -> *const T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_mut_ptr(&self) -> *mut T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_ref(&self) -> &T { - &*self.as_ptr() - } - - pub unsafe fn write(&self, val: T) { - ptr::write(self.as_mut_ptr(), val) - } -} - -impl UninitCell { - pub unsafe fn read(&self) -> T { - ptr::read(self.as_mut_ptr()) - } -} diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs deleted file mode 100644 index dc4f16bd4..000000000 --- a/embassy/src/time/duration.rs +++ /dev/null @@ -1,184 +0,0 @@ -use core::fmt; -use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; - -use super::{GCD_1K, GCD_1M, TICKS_PER_SECOND}; - -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Represents the difference between two [Instant](struct.Instant.html)s -pub struct Duration { - pub(crate) ticks: u64, -} - -impl Duration { - /// The smallest value that can be represented by the `Duration` type. - pub const MIN: Duration = Duration { ticks: u64::MIN }; - /// The largest value that can be represented by the `Duration` type. - pub const MAX: Duration = Duration { ticks: u64::MAX }; - - /// Tick count of the `Duration`. - pub const fn as_ticks(&self) -> u64 { - self.ticks - } - - /// Convert the `Duration` to seconds, rounding down. - pub const fn as_secs(&self) -> u64 { - self.ticks / TICKS_PER_SECOND - } - - /// Convert the `Duration` to milliseconds, rounding down. - pub const fn as_millis(&self) -> u64 { - self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) - } - - /// Convert the `Duration` to microseconds, rounding down. - pub const fn as_micros(&self) -> u64 { - self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) - } - - /// Creates a duration from the specified number of clock ticks - pub const fn from_ticks(ticks: u64) -> Duration { - Duration { ticks } - } - - /// Creates a duration from the specified number of seconds, rounding up. - pub const fn from_secs(secs: u64) -> Duration { - Duration { - ticks: secs * TICKS_PER_SECOND, - } - } - - /// Creates a duration from the specified number of milliseconds, rounding up. - pub const fn from_millis(millis: u64) -> Duration { - Duration { - ticks: div_ceil(millis * (TICKS_PER_SECOND / GCD_1K), 1000 / GCD_1K), - } - } - - /// Creates a duration from the specified number of microseconds, rounding up. - /// NOTE: Delays this small may be inaccurate. - pub const fn from_micros(micros: u64) -> Duration { - Duration { - ticks: div_ceil(micros * (TICKS_PER_SECOND / GCD_1M), 1_000_000 / GCD_1M), - } - } - - /// Creates a duration from the specified number of seconds, rounding down. - pub const fn from_secs_floor(secs: u64) -> Duration { - Duration { - ticks: secs * TICKS_PER_SECOND, - } - } - - /// Creates a duration from the specified number of milliseconds, rounding down. - pub const fn from_millis_floor(millis: u64) -> Duration { - Duration { - ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), - } - } - - /// Creates a duration from the specified number of microseconds, rounding down. - /// NOTE: Delays this small may be inaccurate. - pub const fn from_micros_floor(micros: u64) -> Duration { - Duration { - ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), - } - } - - /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. - pub fn checked_add(self, rhs: Duration) -> Option { - self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks }) - } - - /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow. - pub fn checked_sub(self, rhs: Duration) -> Option { - self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks }) - } - - /// Multiplies one Duration by a scalar u32, returning a new Duration or None in the event of an overflow. - pub fn checked_mul(self, rhs: u32) -> Option { - self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks }) - } - - /// Divides one Duration a scalar u32, returning a new Duration or None in the event of an overflow. - pub fn checked_div(self, rhs: u32) -> Option { - self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks }) - } -} - -impl Add for Duration { - type Output = Duration; - - fn add(self, rhs: Duration) -> Duration { - self.checked_add(rhs).expect("overflow when adding durations") - } -} - -impl AddAssign for Duration { - fn add_assign(&mut self, rhs: Duration) { - *self = *self + rhs; - } -} - -impl Sub for Duration { - type Output = Duration; - - fn sub(self, rhs: Duration) -> Duration { - self.checked_sub(rhs).expect("overflow when subtracting durations") - } -} - -impl SubAssign for Duration { - fn sub_assign(&mut self, rhs: Duration) { - *self = *self - rhs; - } -} - -impl Mul for Duration { - type Output = Duration; - - fn mul(self, rhs: u32) -> Duration { - self.checked_mul(rhs) - .expect("overflow when multiplying duration by scalar") - } -} - -impl Mul for u32 { - type Output = Duration; - - fn mul(self, rhs: Duration) -> Duration { - rhs * self - } -} - -impl MulAssign for Duration { - fn mul_assign(&mut self, rhs: u32) { - *self = *self * rhs; - } -} - -impl Div for Duration { - type Output = Duration; - - fn div(self, rhs: u32) -> Duration { - self.checked_div(rhs) - .expect("divide by zero error when dividing duration by scalar") - } -} - -impl DivAssign for Duration { - fn div_assign(&mut self, rhs: u32) { - *self = *self / rhs; - } -} - -impl<'a> fmt::Display for Duration { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} ticks", self.ticks) - } -} - -#[inline] -const fn div_ceil(num: u64, den: u64) -> u64 { - (num + den - 1) / den -} diff --git a/embassy/src/time/instant.rs b/embassy/src/time/instant.rs deleted file mode 100644 index 6a4925f47..000000000 --- a/embassy/src/time/instant.rs +++ /dev/null @@ -1,159 +0,0 @@ -use core::fmt; -use core::ops::{Add, AddAssign, Sub, SubAssign}; - -use super::{driver, Duration, GCD_1K, GCD_1M, TICKS_PER_SECOND}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// An Instant in time, based on the MCU's clock ticks since startup. -pub struct Instant { - ticks: u64, -} - -impl Instant { - /// The smallest (earliest) value that can be represented by the `Instant` type. - pub const MIN: Instant = Instant { ticks: u64::MIN }; - /// The largest (latest) value that can be represented by the `Instant` type. - pub const MAX: Instant = Instant { ticks: u64::MAX }; - - /// Returns an Instant representing the current time. - pub fn now() -> Instant { - Instant { ticks: driver::now() } - } - - /// Create an Instant from a tick count since system boot. - pub const fn from_ticks(ticks: u64) -> Self { - Self { ticks } - } - - /// Create an Instant from a microsecond count since system boot. - pub const fn from_micros(micros: u64) -> Self { - Self { - ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), - } - } - - /// Create an Instant from a millisecond count since system boot. - pub const fn from_millis(millis: u64) -> Self { - Self { - ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), - } - } - - /// Create an Instant from a second count since system boot. - pub const fn from_secs(seconds: u64) -> Self { - Self { - ticks: seconds * TICKS_PER_SECOND, - } - } - - /// Tick count since system boot. - pub const fn as_ticks(&self) -> u64 { - self.ticks - } - - /// Seconds since system boot. - pub const fn as_secs(&self) -> u64 { - self.ticks / TICKS_PER_SECOND - } - - /// Milliseconds since system boot. - pub const fn as_millis(&self) -> u64 { - self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) - } - - /// Microseconds since system boot. - pub const fn as_micros(&self) -> u64 { - self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) - } - - /// Duration between this Instant and another Instant - /// Panics on over/underflow. - pub fn duration_since(&self, earlier: Instant) -> Duration { - Duration { - ticks: self.ticks.checked_sub(earlier.ticks).unwrap(), - } - } - - /// Duration between this Instant and another Instant - pub fn checked_duration_since(&self, earlier: Instant) -> Option { - if self.ticks < earlier.ticks { - None - } else { - Some(Duration { - ticks: self.ticks - earlier.ticks, - }) - } - } - - /// Returns the duration since the "earlier" Instant. - /// If the "earlier" instant is in the future, the duration is set to zero. - pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { - Duration { - ticks: if self.ticks < earlier.ticks { - 0 - } else { - self.ticks - earlier.ticks - }, - } - } - - /// Duration elapsed since this Instant. - pub fn elapsed(&self) -> Duration { - Instant::now() - *self - } - - /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow. - pub fn checked_add(&self, duration: Duration) -> Option { - self.ticks.checked_add(duration.ticks).map(|ticks| Instant { ticks }) - } - - /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow. - pub fn checked_sub(&self, duration: Duration) -> Option { - self.ticks.checked_sub(duration.ticks).map(|ticks| Instant { ticks }) - } -} - -impl Add for Instant { - type Output = Instant; - - fn add(self, other: Duration) -> Instant { - self.checked_add(other) - .expect("overflow when adding duration to instant") - } -} - -impl AddAssign for Instant { - fn add_assign(&mut self, other: Duration) { - *self = *self + other; - } -} - -impl Sub for Instant { - type Output = Instant; - - fn sub(self, other: Duration) -> Instant { - self.checked_sub(other) - .expect("overflow when subtracting duration from instant") - } -} - -impl SubAssign for Instant { - fn sub_assign(&mut self, other: Duration) { - *self = *self - other; - } -} - -impl Sub for Instant { - type Output = Duration; - - fn sub(self, other: Instant) -> Duration { - self.duration_since(other) - } -} - -impl<'a> fmt::Display for Instant { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} ticks", self.ticks) - } -} diff --git a/embassy/src/time/mod.rs b/embassy/src/time/mod.rs deleted file mode 100644 index 018e01c84..000000000 --- a/embassy/src/time/mod.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! Timekeeping, delays and timeouts. -//! -//! Timekeeping is done with elapsed time since system boot. Time is represented in -//! ticks, where the tick rate is defined by the current driver, usually to match -//! the tick rate of the hardware. -//! -//! Tick counts are 64 bits. At the highest supported tick rate of 1Mhz this supports -//! representing time spans of up to ~584558 years, which is big enough for all practical -//! purposes and allows not having to worry about overflows. -//! -//! [`Instant`] represents a given instant of time (relative to system boot), and [`Duration`] -//! represents the duration of a span of time. They implement the math operations you'd expect, -//! like addition and substraction. -//! -//! # Delays and timeouts -//! -//! [`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time. -//! -//! An implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility -//! with libraries from the ecosystem. -//! -//! # Wall-clock time -//! -//! The `time` module deals exclusively with a monotonically increasing tick count. -//! Therefore it has no direct support for wall-clock time ("real life" datetimes -//! like `2021-08-24 13:33:21`). -//! -//! If persistence across reboots is not needed, support can be built on top of -//! `embassy::time` by storing the offset between "seconds elapsed since boot" -//! and "seconds since unix epoch". -//! -//! # Time driver -//! -//! The `time` module is backed by a global "time driver" specified at build time. -//! Only one driver can be active in a program. -//! -//! All methods and structs transparently call into the active driver. This makes it -//! possible for libraries to use `embassy::time` in a driver-agnostic way without -//! requiring generic parameters. -//! -//! For more details, check the [`driver`] module. - -#![deny(missing_docs)] - -mod delay; -pub mod driver; -mod duration; -mod instant; -mod timer; - -#[cfg(feature = "std")] -mod driver_std; - -#[cfg(feature = "wasm")] -mod driver_wasm; - -pub use delay::{block_for, Delay}; -pub use duration::Duration; -pub use instant::Instant; -pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; - -#[cfg(feature = "time-tick-1000hz")] -const TPS: u64 = 1_000; - -#[cfg(feature = "time-tick-32768hz")] -const TPS: u64 = 32_768; - -#[cfg(feature = "time-tick-1mhz")] -const TPS: u64 = 1_000_000; - -#[cfg(feature = "time-tick-16mhz")] -const TPS: u64 = 16_000_000; - -/// Ticks per second of the global timebase. -/// -/// This value is specified by the `time-tick-*` Cargo features, which -/// should be set by the time driver. Some drivers support a fixed tick rate, others -/// allow you to choose a tick rate with Cargo features of their own. You should not -/// set the `time-tick-*` features for embassy yourself as an end user. -pub const TICKS_PER_SECOND: u64 = TPS; - -const fn gcd(a: u64, b: u64) -> u64 { - if b == 0 { - a - } else { - gcd(b, a % b) - } -} - -pub(crate) const GCD_1K: u64 = gcd(TICKS_PER_SECOND, 1_000); -pub(crate) const GCD_1M: u64 = gcd(TICKS_PER_SECOND, 1_000_000); diff --git a/embassy/src/time/timer.rs b/embassy/src/time/timer.rs deleted file mode 100644 index 2194a4b14..000000000 --- a/embassy/src/time/timer.rs +++ /dev/null @@ -1,151 +0,0 @@ -use core::future::Future; -use core::pin::Pin; -use core::task::{Context, Poll}; - -use futures::future::{select, Either}; -use futures::{pin_mut, Stream}; - -use crate::executor::raw; -use crate::time::{Duration, Instant}; - -/// Error returned by [`with_timeout`] on timeout. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TimeoutError; - -/// Runs a given future with a timeout. -/// -/// If the future completes before the timeout, its output is returned. Otherwise, on timeout, -/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. -pub async fn with_timeout(timeout: Duration, fut: F) -> Result { - let timeout_fut = Timer::after(timeout); - pin_mut!(fut); - match select(fut, timeout_fut).await { - Either::Left((r, _)) => Ok(r), - Either::Right(_) => Err(TimeoutError), - } -} - -/// A future that completes at a specified [Instant](struct.Instant.html). -pub struct Timer { - expires_at: Instant, - yielded_once: bool, -} - -impl Timer { - /// Expire at specified [Instant](struct.Instant.html) - pub fn at(expires_at: Instant) -> Self { - Self { - expires_at, - yielded_once: false, - } - } - - /// Expire after specified [Duration](struct.Duration.html). - /// This can be used as a `sleep` abstraction. - /// - /// Example: - /// ``` no_run - /// # #![feature(type_alias_impl_trait)] - /// # - /// # fn foo() {} - /// use embassy::time::{Duration, Timer}; - /// - /// #[embassy::task] - /// async fn demo_sleep_seconds() { - /// // suspend this task for one second. - /// Timer::after(Duration::from_secs(1)).await; - /// } - /// ``` - pub fn after(duration: Duration) -> Self { - Self { - expires_at: Instant::now() + duration, - yielded_once: false, - } - } -} - -impl Unpin for Timer {} - -impl Future for Timer { - type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if self.yielded_once && self.expires_at <= Instant::now() { - Poll::Ready(()) - } else { - unsafe { raw::register_timer(self.expires_at, cx.waker()) }; - self.yielded_once = true; - Poll::Pending - } - } -} - -/// Asynchronous stream that yields every Duration, indefinitely. -/// -/// This stream will tick at uniform intervals, even if blocking work is performed between ticks. -/// -/// For instance, consider the following code fragment. -/// ``` no_run -/// # #![feature(type_alias_impl_trait)] -/// # -/// use embassy::time::{Duration, Timer}; -/// # fn foo() {} -/// -/// #[embassy::task] -/// async fn ticker_example_0() { -/// loop { -/// foo(); -/// Timer::after(Duration::from_secs(1)).await; -/// } -/// } -/// ``` -/// -/// This fragment will not call `foo` every second. -/// Instead, it will call it every second + the time it took to previously call `foo`. -/// -/// Example using ticker, which will consistently call `foo` once a second. -/// -/// ``` no_run -/// # #![feature(type_alias_impl_trait)] -/// # -/// use embassy::time::{Duration, Ticker}; -/// use futures::StreamExt; -/// # fn foo(){} -/// -/// #[embassy::task] -/// async fn ticker_example_1() { -/// let mut ticker = Ticker::every(Duration::from_secs(1)); -/// loop { -/// foo(); -/// ticker.next().await; -/// } -/// } -/// ``` -pub struct Ticker { - expires_at: Instant, - duration: Duration, -} - -impl Ticker { - /// Creates a new ticker that ticks at the specified duration interval. - pub fn every(duration: Duration) -> Self { - let expires_at = Instant::now() + duration; - Self { expires_at, duration } - } -} - -impl Unpin for Ticker {} - -impl Stream for Ticker { - type Item = (); - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.expires_at <= Instant::now() { - let dur = self.duration; - self.expires_at += dur; - Poll::Ready(Some(())) - } else { - unsafe { raw::register_timer(self.expires_at, cx.waker()) }; - Poll::Pending - } - } -} diff --git a/embassy/src/util/forever.rs b/embassy/src/util/forever.rs deleted file mode 100644 index 3d2af38b1..000000000 --- a/embassy/src/util/forever.rs +++ /dev/null @@ -1,95 +0,0 @@ -use core::cell::UnsafeCell; -use core::mem::MaybeUninit; - -use atomic_polyfill::{AtomicBool, Ordering}; - -/// Type with static lifetime that may be written to once at runtime. -/// -/// This may be used to initialize static objects at runtime, typically in the init routine. -/// This is useful for objects such as Embassy's RTC, which cannot be initialized in a const -/// context. -/// -/// Note: IF a global mutable variable is desired, use a CriticalSectionMutex or ThreadModeMutex instead. -/// -/// ``` -/// use embassy::util::Forever; -/// // Using an integer for the sake of keeping this example self-contained, -/// // see https://github.com/embassy-rs/embassy/wiki/Getting-Started for a more "proper" example. -/// static SOME_INT: Forever =Forever::new(); -/// -/// // put returns a mutable pointer to the object stored in the forever, which may then be passed -/// // around. -/// let mut x = SOME_INT.put(42); -/// assert_eq!(*x, 42); -/// ``` -pub struct Forever { - used: AtomicBool, - t: UnsafeCell>, -} - -unsafe impl Send for Forever {} -unsafe impl Sync for Forever {} - -impl Forever { - /// Create a new `Forever`. - #[inline(always)] - pub const fn new() -> Self { - Self { - used: AtomicBool::new(false), - t: UnsafeCell::new(MaybeUninit::uninit()), - } - } - - /// Store a value in this `Forever`, returning a mutable reference to it. - /// - /// Using this method, the compiler usually constructs `val` in the stack and then moves - /// it into the `Forever`. If `T` is big, this is likely to cause stack overflows. - /// Considering using [`Signal::put_with`] instead, which will construct it in-place inside the `Forever`. - /// - /// # Panics - /// - /// Panics if this `Forever` already has a value stored in it. - #[inline(always)] - #[allow(clippy::mut_from_ref)] - pub fn put(&'static self, val: T) -> &'static mut T { - self.put_with(|| val) - } - - /// Store the closure return value in this `Forever`, returning a mutable reference to it. - /// - /// The advantage over [`Forever::put`] is that this method allows the closure to construct - /// the `T` value in-place directly inside the `Forever`, saving stack space. - /// - /// # Panics - /// - /// Panics if this `Forever` already has a value stored in it. - #[inline(always)] - #[allow(clippy::mut_from_ref)] - pub fn put_with(&'static self, val: impl FnOnce() -> T) -> &'static mut T { - if self - .used - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - .is_err() - { - panic!("Forever.put() called multiple times"); - } - - let p: &'static mut MaybeUninit = unsafe { &mut *self.t.get() }; - p.write(val()) - } - - /// Unsafely get a mutable reference to the contents of this Forever. - /// - /// # Safety - /// - /// This is undefined behavior if: - /// - /// - The `Forever` has not been initialized yet (with `put' or `put_with`), or - /// - A reference to the contents (mutable or not) already exists. - #[inline(always)] - #[allow(clippy::mut_from_ref)] - pub unsafe fn steal(&self) -> &mut T { - let p: &mut MaybeUninit = &mut *self.t.get(); - p.assume_init_mut() - } -} diff --git a/embassy/src/util/mod.rs b/embassy/src/util/mod.rs deleted file mode 100644 index 3ad760cd4..000000000 --- a/embassy/src/util/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Misc utilities - -mod forever; -mod select; -mod yield_now; - -pub use forever::*; -pub use select::*; -pub use yield_now::*; diff --git a/embassy/src/util/select.rs b/embassy/src/util/select.rs deleted file mode 100644 index 8cecb7fa0..000000000 --- a/embassy/src/util/select.rs +++ /dev/null @@ -1,230 +0,0 @@ -use core::future::Future; -use core::pin::Pin; -use core::task::{Context, Poll}; - -/// Result for [`select`]. -#[derive(Debug, Clone)] -pub enum Either { - /// First future finished first. - First(A), - /// Second future finished first. - Second(B), -} - -/// Wait for one of two futures to complete. -/// -/// This function returns a new future which polls all the futures. -/// When one of them completes, it will complete with its result value. -/// -/// The other future is dropped. -pub fn select(a: A, b: B) -> Select -where - A: Future, - B: Future, -{ - Select { a, b } -} - -/// Future for the [`select`] function. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Select { - a: A, - b: B, -} - -impl Unpin for Select {} - -impl Future for Select -where - A: Future, - B: Future, -{ - type Output = Either; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = unsafe { self.get_unchecked_mut() }; - let a = unsafe { Pin::new_unchecked(&mut this.a) }; - let b = unsafe { Pin::new_unchecked(&mut this.b) }; - if let Poll::Ready(x) = a.poll(cx) { - return Poll::Ready(Either::First(x)); - } - if let Poll::Ready(x) = b.poll(cx) { - return Poll::Ready(Either::Second(x)); - } - Poll::Pending - } -} - -// ==================================================================== - -/// Result for [`select3`]. -#[derive(Debug, Clone)] -pub enum Either3 { - /// First future finished first. - First(A), - /// Second future finished first. - Second(B), - /// Third future finished first. - Third(C), -} - -/// Same as [`select`], but with more futures. -pub fn select3(a: A, b: B, c: C) -> Select3 -where - A: Future, - B: Future, - C: Future, -{ - Select3 { a, b, c } -} - -/// Future for the [`select3`] function. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Select3 { - a: A, - b: B, - c: C, -} - -impl Future for Select3 -where - A: Future, - B: Future, - C: Future, -{ - type Output = Either3; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = unsafe { self.get_unchecked_mut() }; - let a = unsafe { Pin::new_unchecked(&mut this.a) }; - let b = unsafe { Pin::new_unchecked(&mut this.b) }; - let c = unsafe { Pin::new_unchecked(&mut this.c) }; - if let Poll::Ready(x) = a.poll(cx) { - return Poll::Ready(Either3::First(x)); - } - if let Poll::Ready(x) = b.poll(cx) { - return Poll::Ready(Either3::Second(x)); - } - if let Poll::Ready(x) = c.poll(cx) { - return Poll::Ready(Either3::Third(x)); - } - Poll::Pending - } -} - -// ==================================================================== - -/// Result for [`select4`]. -#[derive(Debug, Clone)] -pub enum Either4 { - /// First future finished first. - First(A), - /// Second future finished first. - Second(B), - /// Third future finished first. - Third(C), - /// Fourth future finished first. - Fourth(D), -} - -/// Same as [`select`], but with more futures. -pub fn select4(a: A, b: B, c: C, d: D) -> Select4 -where - A: Future, - B: Future, - C: Future, - D: Future, -{ - Select4 { a, b, c, d } -} - -/// Future for the [`select4`] function. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Select4 { - a: A, - b: B, - c: C, - d: D, -} - -impl Future for Select4 -where - A: Future, - B: Future, - C: Future, - D: Future, -{ - type Output = Either4; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = unsafe { self.get_unchecked_mut() }; - let a = unsafe { Pin::new_unchecked(&mut this.a) }; - let b = unsafe { Pin::new_unchecked(&mut this.b) }; - let c = unsafe { Pin::new_unchecked(&mut this.c) }; - let d = unsafe { Pin::new_unchecked(&mut this.d) }; - if let Poll::Ready(x) = a.poll(cx) { - return Poll::Ready(Either4::First(x)); - } - if let Poll::Ready(x) = b.poll(cx) { - return Poll::Ready(Either4::Second(x)); - } - if let Poll::Ready(x) = c.poll(cx) { - return Poll::Ready(Either4::Third(x)); - } - if let Poll::Ready(x) = d.poll(cx) { - return Poll::Ready(Either4::Fourth(x)); - } - Poll::Pending - } -} - -// ==================================================================== - -/// Future for the [`select_all`] function. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct SelectAll { - inner: [Fut; N], -} - -/// Creates a new future which will select over a list of futures. -/// -/// The returned future will wait for any future within `iter` to be ready. Upon -/// completion the item resolved will be returned, along with the index of the -/// future that was ready. -/// -/// # Panics -/// -/// This function will panic if the array specified contains no items. -pub fn select_all(arr: [Fut; N]) -> SelectAll { - assert!(N > 0); - SelectAll { inner: arr } -} - -impl Future for SelectAll { - type Output = (Fut::Output, usize); - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move, - // its elements also cannot move. Therefore it is safe to access `inner` and pin - // references to the contained futures. - let item = unsafe { - self.get_unchecked_mut() - .inner - .iter_mut() - .enumerate() - .find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) { - Poll::Pending => None, - Poll::Ready(e) => Some((i, e)), - }) - }; - - match item { - Some((idx, res)) => Poll::Ready((res, idx)), - None => Poll::Pending, - } - } -} diff --git a/embassy/src/util/yield_now.rs b/embassy/src/util/yield_now.rs deleted file mode 100644 index 1ebecb916..000000000 --- a/embassy/src/util/yield_now.rs +++ /dev/null @@ -1,25 +0,0 @@ -use core::future::Future; -use core::pin::Pin; -use core::task::{Context, Poll}; - -/// Yield from the current task once, allowing other tasks to run. -pub fn yield_now() -> impl Future { - YieldNowFuture { yielded: false } -} - -struct YieldNowFuture { - yielded: bool, -} - -impl Future for YieldNowFuture { - type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if self.yielded { - Poll::Ready(()) - } else { - self.yielded = true; - cx.waker().wake_by_ref(); - Poll::Pending - } - } -} diff --git a/embassy/src/waitqueue/mod.rs b/embassy/src/waitqueue/mod.rs deleted file mode 100644 index 5c4e1bc3b..000000000 --- a/embassy/src/waitqueue/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Async low-level wait queues - -#[cfg_attr(feature = "executor-agnostic", path = "waker_agnostic.rs")] -mod waker; -pub use waker::*; - -mod multi_waker; -pub use multi_waker::*; diff --git a/embassy/src/waitqueue/multi_waker.rs b/embassy/src/waitqueue/multi_waker.rs deleted file mode 100644 index 325d2cb3a..000000000 --- a/embassy/src/waitqueue/multi_waker.rs +++ /dev/null @@ -1,33 +0,0 @@ -use core::task::Waker; - -use super::WakerRegistration; - -/// Utility struct to register and wake multiple wakers. -pub struct MultiWakerRegistration { - wakers: [WakerRegistration; N], -} - -impl MultiWakerRegistration { - /// Create a new empty instance - pub const fn new() -> Self { - const WAKER: WakerRegistration = WakerRegistration::new(); - Self { wakers: [WAKER; N] } - } - - /// Register a waker. If the buffer is full the function returns it in the error - pub fn register<'a>(&mut self, w: &'a Waker) -> Result<(), &'a Waker> { - if let Some(waker_slot) = self.wakers.iter_mut().find(|waker_slot| !waker_slot.occupied()) { - waker_slot.register(w); - Ok(()) - } else { - Err(w) - } - } - - /// Wake all registered wakers. This clears the buffer - pub fn wake(&mut self) { - for waker_slot in self.wakers.iter_mut() { - waker_slot.wake() - } - } -} diff --git a/embassy/src/waitqueue/waker.rs b/embassy/src/waitqueue/waker.rs deleted file mode 100644 index cdc96507f..000000000 --- a/embassy/src/waitqueue/waker.rs +++ /dev/null @@ -1,97 +0,0 @@ -use core::ptr::{self, NonNull}; -use core::task::Waker; - -use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering}; - -use crate::executor::raw::{task_from_waker, wake_task, TaskHeader}; - -/// Utility struct to register and wake a waker. -/// -/// # Safety -/// -/// This type is optimized for (and only works with) embassy tasks. -#[derive(Debug)] -pub struct WakerRegistration { - waker: Option>, -} - -impl WakerRegistration { - /// Create a new `WakerRegistration`. - pub const fn new() -> Self { - Self { waker: None } - } - - /// Register a waker. Overwrites the previous waker, if any. - pub fn register(&mut self, w: &Waker) { - let w = task_from_waker(w); - match self.waker { - // Optimization: If both the old and new Wakers wake the same task, do nothing. - Some(w2) if w == w2 => {} - Some(w2) => { - // We had a waker registered for another task. Wake it, so the other task can - // reregister itself if it's still interested. - // - // If two tasks are waiting on the same thing concurrently, this will cause them - // to wake each other in a loop fighting over this WakerRegistration. This wastes - // CPU but things will still work. - // - // If the user wants to have two tasks waiting on the same thing they should use - // a more appropriate primitive that can store multiple wakers. - - unsafe { wake_task(w2) } - self.waker = Some(w); - } - None => self.waker = Some(w), - } - } - - /// Wake the registered waker, if any. - pub fn wake(&mut self) { - if let Some(w) = self.waker.take() { - unsafe { wake_task(w) } - } - } - - /// Returns true if a waker is currently registered - pub fn occupied(&self) -> bool { - self.waker.is_some() - } -} - -// SAFETY: `WakerRegistration` effectively contains an `Option`, -// which is `Send` and `Sync`. -unsafe impl Send for WakerRegistration {} -unsafe impl Sync for WakerRegistration {} - -/// Utility struct to atomically register and wake a waker. -/// -/// # Safety -/// -/// This type is optimized for (and only works with) embassy tasks. -pub struct AtomicWaker { - waker: AtomicPtr, -} - -impl AtomicWaker { - /// Create a new `AtomicWaker`. - pub const fn new() -> Self { - Self { - waker: AtomicPtr::new(ptr::null_mut()), - } - } - - /// Register a waker. Overwrites the previous waker, if any. - pub fn register(&self, w: &Waker) { - let w = task_from_waker(w); - self.waker.store(w.as_ptr(), Ordering::Relaxed); - compiler_fence(Ordering::SeqCst); - } - - /// Wake the registered waker, if any. - pub fn wake(&self) { - let w2 = self.waker.load(Ordering::Relaxed); - if let Some(w2) = NonNull::new(w2) { - unsafe { wake_task(w2) }; - } - } -} diff --git a/embassy/src/waitqueue/waker_agnostic.rs b/embassy/src/waitqueue/waker_agnostic.rs deleted file mode 100644 index 64e300eb8..000000000 --- a/embassy/src/waitqueue/waker_agnostic.rs +++ /dev/null @@ -1,92 +0,0 @@ -use core::cell::Cell; -use core::mem; -use core::task::Waker; - -use crate::blocking_mutex::raw::CriticalSectionRawMutex; -use crate::blocking_mutex::Mutex; - -/// Utility struct to register and wake a waker. -#[derive(Debug)] -pub struct WakerRegistration { - waker: Option, -} - -impl WakerRegistration { - /// Create a new `WakerRegistration`. - pub const fn new() -> Self { - Self { waker: None } - } - - /// Register a waker. Overwrites the previous waker, if any. - pub fn register(&mut self, w: &Waker) { - match self.waker { - // Optimization: If both the old and new Wakers wake the same task, we can simply - // keep the old waker, skipping the clone. (In most executor implementations, - // cloning a waker is somewhat expensive, comparable to cloning an Arc). - Some(ref w2) if (w2.will_wake(w)) => {} - _ => { - // clone the new waker and store it - if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) { - // We had a waker registered for another task. Wake it, so the other task can - // reregister itself if it's still interested. - // - // If two tasks are waiting on the same thing concurrently, this will cause them - // to wake each other in a loop fighting over this WakerRegistration. This wastes - // CPU but things will still work. - // - // If the user wants to have two tasks waiting on the same thing they should use - // a more appropriate primitive that can store multiple wakers. - old_waker.wake() - } - } - } - } - - /// Wake the registered waker, if any. - pub fn wake(&mut self) { - if let Some(w) = self.waker.take() { - w.wake() - } - } - - /// Returns true if a waker is currently registered - pub fn occupied(&self) -> bool { - self.waker.is_some() - } -} - -/// Utility struct to register and wake a waker. -pub struct AtomicWaker { - waker: Mutex>>, -} - -impl AtomicWaker { - /// Create a new `AtomicWaker`. - pub const fn new() -> Self { - Self { - waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), - } - } - - /// Register a waker. Overwrites the previous waker, if any. - pub fn register(&self, w: &Waker) { - critical_section::with(|cs| { - let cell = self.waker.borrow(cs); - cell.set(match cell.replace(None) { - Some(w2) if (w2.will_wake(w)) => Some(w2), - _ => Some(w.clone()), - }) - }) - } - - /// Wake the registered waker, if any. - pub fn wake(&self) { - critical_section::with(|cs| { - let cell = self.waker.borrow(cs); - if let Some(w) = cell.replace(None) { - w.wake_by_ref(); - cell.set(Some(w)); - } - }) - } -} diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 0ae7163c3..dd9bcc093 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-boot-nrf-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../../../embassy", features = ["nightly"] } +embassy-util = { version = "0.1.0", path = "../../../../embassy-util" } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly", "nrf52840"] } embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 0b9715e49..3044645a8 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -13,8 +13,8 @@ use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); -#[embassy::main] -async fn main(_s: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main] +async fn main(_s: embassy_executor::executor::Spawner, p: Peripherals) { let mut button = Input::new(p.P0_11, Pull::Up); let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs index a06c20f8b..2f76d20c6 100644 --- a/examples/boot/application/nrf/src/bin/b.rs +++ b/examples/boot/application/nrf/src/bin/b.rs @@ -4,13 +4,13 @@ #![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] -use embassy::time::{Duration, Timer}; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::Peripherals; use panic_reset as _; -#[embassy::main] -async fn main(_s: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main] +async fn main(_s: embassy_executor::executor::Spawner, p: Peripherals) { let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 36fc135fe..313808a0d 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-boot-stm32f3-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../../../embassy", features = ["nightly", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 4ff18d7c7..fd18e9373 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -14,8 +14,8 @@ use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); -#[embassy::main] -async fn main(_s: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main] +async fn main(_s: embassy_executor::executor::Spawner, p: Peripherals) { let flash = Flash::unlock(p.FLASH); let mut flash = BlockingAsync::new(flash); diff --git a/examples/boot/application/stm32f3/src/bin/b.rs b/examples/boot/application/stm32f3/src/bin/b.rs index 4487e586e..934f862d9 100644 --- a/examples/boot/application/stm32f3/src/bin/b.rs +++ b/examples/boot/application/stm32f3/src/bin/b.rs @@ -4,13 +4,13 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use panic_reset as _; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.PA5, Level::High, Speed::Low); diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index ad4a6fa76..2a4741dc7 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-boot-stm32f7-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../../../embassy", features = ["nightly", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 9c7921a1a..10d709cfd 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -14,8 +14,8 @@ use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); -#[embassy::main] -async fn main(_s: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main] +async fn main(_s: embassy_executor::executor::Spawner, p: Peripherals) { let flash = Flash::unlock(p.FLASH); let mut flash = BlockingAsync::new(flash); diff --git a/examples/boot/application/stm32f7/src/bin/b.rs b/examples/boot/application/stm32f7/src/bin/b.rs index aa05bbcdd..c89e8a310 100644 --- a/examples/boot/application/stm32f7/src/bin/b.rs +++ b/examples/boot/application/stm32f7/src/bin/b.rs @@ -4,13 +4,13 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use panic_reset as _; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { Timer::after(Duration::from_millis(300)).await; let mut led = Output::new(p.PB7, Level::High, Speed::Low); diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 5dff770a8..c6420e8ad 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-boot-stm32h7-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../../../embassy", features = ["nightly", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 704979dba..cc363bb32 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -14,8 +14,8 @@ use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); -#[embassy::main] -async fn main(_s: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main] +async fn main(_s: embassy_executor::executor::Spawner, p: Peripherals) { let flash = Flash::unlock(p.FLASH); let mut flash = BlockingAsync::new(flash); diff --git a/examples/boot/application/stm32h7/src/bin/b.rs b/examples/boot/application/stm32h7/src/bin/b.rs index ea0140253..3fa63bdcf 100644 --- a/examples/boot/application/stm32h7/src/bin/b.rs +++ b/examples/boot/application/stm32h7/src/bin/b.rs @@ -4,13 +4,13 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use panic_reset as _; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { Timer::after(Duration::from_millis(300)).await; let mut led = Output::new(p.PB14, Level::High, Speed::Low); diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index de7bea47b..a6936419c 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-boot-stm32l0-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../../../embassy", features = ["nightly", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index ce620347b..fcf3f2ef1 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -4,9 +4,9 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy::time::{Duration, Timer}; use embassy_boot_stm32::FirmwareUpdater; use embassy_embedded_hal::adapter::BlockingAsync; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::Flash; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; @@ -15,8 +15,8 @@ use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); -#[embassy::main] -async fn main(_s: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main] +async fn main(_s: embassy_executor::executor::Spawner, p: Peripherals) { let flash = Flash::unlock(p.FLASH); let mut flash = BlockingAsync::new(flash); diff --git a/examples/boot/application/stm32l0/src/bin/b.rs b/examples/boot/application/stm32l0/src/bin/b.rs index 0b585a14c..46e394c4c 100644 --- a/examples/boot/application/stm32l0/src/bin/b.rs +++ b/examples/boot/application/stm32l0/src/bin/b.rs @@ -4,13 +4,13 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use panic_reset as _; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.PB6, Level::High, Speed::Low); diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 3fd6f639a..5e53cd5f6 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-boot-stm32l1-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../../../embassy", features = ["nightly", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index ce620347b..fcf3f2ef1 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -4,9 +4,9 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy::time::{Duration, Timer}; use embassy_boot_stm32::FirmwareUpdater; use embassy_embedded_hal::adapter::BlockingAsync; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::Flash; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; @@ -15,8 +15,8 @@ use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); -#[embassy::main] -async fn main(_s: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main] +async fn main(_s: embassy_executor::executor::Spawner, p: Peripherals) { let flash = Flash::unlock(p.FLASH); let mut flash = BlockingAsync::new(flash); diff --git a/examples/boot/application/stm32l1/src/bin/b.rs b/examples/boot/application/stm32l1/src/bin/b.rs index 0b585a14c..46e394c4c 100644 --- a/examples/boot/application/stm32l1/src/bin/b.rs +++ b/examples/boot/application/stm32l1/src/bin/b.rs @@ -4,13 +4,13 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use panic_reset as _; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.PB6, Level::High, Speed::Low); diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 7284af662..bbb5e7e1d 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-boot-stm32l4-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../../../embassy", features = ["nightly", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index bf6099355..f092f0239 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -14,8 +14,8 @@ use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); -#[embassy::main] -async fn main(_s: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main] +async fn main(_s: embassy_executor::executor::Spawner, p: Peripherals) { let flash = Flash::unlock(p.FLASH); let mut flash = BlockingAsync::new(flash); diff --git a/examples/boot/application/stm32l4/src/bin/b.rs b/examples/boot/application/stm32l4/src/bin/b.rs index 4487e586e..934f862d9 100644 --- a/examples/boot/application/stm32l4/src/bin/b.rs +++ b/examples/boot/application/stm32l4/src/bin/b.rs @@ -4,13 +4,13 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use panic_reset as _; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.PA5, Level::High, Speed::Low); diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index ca22e6134..62123a870 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-boot-stm32wl-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../../../embassy", features = ["nightly", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index dc1eb9bed..14408a90a 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -14,8 +14,8 @@ use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); -#[embassy::main] -async fn main(_s: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main] +async fn main(_s: embassy_executor::executor::Spawner, p: Peripherals) { let flash = Flash::unlock(p.FLASH); let mut flash = BlockingAsync::new(flash); diff --git a/examples/boot/application/stm32wl/src/bin/b.rs b/examples/boot/application/stm32wl/src/bin/b.rs index f2344bd53..e565fd7c6 100644 --- a/examples/boot/application/stm32wl/src/bin/b.rs +++ b/examples/boot/application/stm32wl/src/bin/b.rs @@ -4,13 +4,13 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use panic_reset as _; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.PB15, Level::High, Speed::Low); diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 8eb98623c..9683bff19 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -8,7 +8,6 @@ description = "Bootloader for nRF chips" defmt = { version = "0.3", optional = true } defmt-rtt = { version = "0.3", optional = true } -embassy = { path = "../../../../embassy", default-features = false } embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } cortex-m = { version = "0.7" } diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index b99a8fbcd..4a3319528 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -8,7 +8,6 @@ description = "Example bootloader for STM32 chips" defmt = { version = "0.3", optional = true } defmt-rtt = { version = "0.3", optional = true } -embassy = { path = "../../../../embassy", default-features = false } embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } cortex-m = { version = "0.7" } diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 0cba77694..91edbd36d 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] +nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } diff --git a/examples/nrf/src/bin/awaitable_timer.rs b/examples/nrf/src/bin/awaitable_timer.rs index 34a657cb9..f2c1d9fa4 100644 --- a/examples/nrf/src/bin/awaitable_timer.rs +++ b/examples/nrf/src/bin/awaitable_timer.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::timer::Timer; use embassy_nrf::{interrupt, Peripherals}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); // default frequency is 1MHz, so this triggers every second diff --git a/examples/nrf/src/bin/blinky.rs b/examples/nrf/src/bin/blinky.rs index 23d16f796..98db6546c 100644 --- a/examples/nrf/src/bin/blinky.rs +++ b/examples/nrf/src/bin/blinky.rs @@ -2,13 +2,13 @@ #![no_main] #![feature(type_alias_impl_trait)] -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); diff --git a/examples/nrf/src/bin/buffered_uart.rs b/examples/nrf/src/bin/buffered_uart.rs index 18dd698bf..f02b7d845 100644 --- a/examples/nrf/src/bin/buffered_uart.rs +++ b/examples/nrf/src/bin/buffered_uart.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::buffered_uarte::{BufferedUarte, State}; use embassy_nrf::{interrupt, uarte, Peripherals}; use embedded_io::asynch::{BufRead, Write}; use futures::pin_mut; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut config = uarte::Config::default(); config.parity = uarte::Parity::EXCLUDED; diff --git a/examples/nrf/src/bin/channel.rs b/examples/nrf/src/bin/channel.rs index c57b91a42..e97c6c5ee 100644 --- a/examples/nrf/src/bin/channel.rs +++ b/examples/nrf/src/bin/channel.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::unwrap; -use embassy::blocking_mutex::raw::ThreadModeRawMutex; -use embassy::channel::mpmc::Channel; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::Peripherals; +use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_util::channel::mpmc::Channel; use {defmt_rtt as _, panic_probe as _}; enum LedState { @@ -18,7 +18,7 @@ enum LedState { static CHANNEL: Channel = Channel::new(); -#[embassy::task] +#[embassy_executor::task] async fn my_task() { loop { CHANNEL.send(LedState::On).await; @@ -28,7 +28,7 @@ async fn my_task() { } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); diff --git a/examples/nrf/src/bin/channel_sender_receiver.rs b/examples/nrf/src/bin/channel_sender_receiver.rs index 847ce2382..bca7bb248 100644 --- a/examples/nrf/src/bin/channel_sender_receiver.rs +++ b/examples/nrf/src/bin/channel_sender_receiver.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::unwrap; -use embassy::blocking_mutex::raw::NoopRawMutex; -use embassy::channel::mpmc::{Channel, Receiver, Sender}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; -use embassy::util::Forever; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin}; use embassy_nrf::Peripherals; +use embassy_util::blocking_mutex::raw::NoopRawMutex; +use embassy_util::channel::mpmc::{Channel, Receiver, Sender}; +use embassy_util::Forever; use {defmt_rtt as _, panic_probe as _}; enum LedState { @@ -19,7 +19,7 @@ enum LedState { static CHANNEL: Forever> = Forever::new(); -#[embassy::task] +#[embassy_executor::task] async fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { loop { sender.send(LedState::On).await; @@ -29,7 +29,7 @@ async fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { } } -#[embassy::task] +#[embassy_executor::task] async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) { let mut led = Output::new(led, Level::Low, OutputDrive::Standard); @@ -41,7 +41,7 @@ async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedSta } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { let channel = CHANNEL.put(Channel::new()); diff --git a/examples/nrf/src/bin/executor_fairness_test.rs b/examples/nrf/src/bin/executor_fairness_test.rs index 5a4221519..b98454936 100644 --- a/examples/nrf/src/bin/executor_fairness_test.rs +++ b/examples/nrf/src/bin/executor_fairness_test.rs @@ -5,12 +5,12 @@ use core::task::Poll; use defmt::{info, unwrap}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Instant, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Instant, Timer}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task] +#[embassy_executor::task] async fn run1() { loop { info!("DING DONG"); @@ -18,14 +18,14 @@ async fn run1() { } } -#[embassy::task] +#[embassy_executor::task] async fn run2() { loop { Timer::at(Instant::from_ticks(0)).await; } } -#[embassy::task] +#[embassy_executor::task] async fn run3() { futures::future::poll_fn(|cx| { cx.waker().wake_by_ref(); @@ -34,7 +34,7 @@ async fn run3() { .await; } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, _p: Peripherals) { unwrap!(spawner.spawn(run1())); unwrap!(spawner.spawn(run2())); diff --git a/examples/nrf/src/bin/gpiote_channel.rs b/examples/nrf/src/bin/gpiote_channel.rs index ad8f37c6e..65c7b4df7 100644 --- a/examples/nrf/src/bin/gpiote_channel.rs +++ b/examples/nrf/src/bin/gpiote_channel.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::gpio::{Input, Pull}; use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Starting!"); diff --git a/examples/nrf/src/bin/gpiote_port.rs b/examples/nrf/src/bin/gpiote_port.rs index 30b87b3a7..7746a7f96 100644 --- a/examples/nrf/src/bin/gpiote_port.rs +++ b/examples/nrf/src/bin/gpiote_port.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::gpio::{AnyPin, Input, Pin as _, Pull}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task(pool_size = 4)] +#[embassy_executor::task(pool_size = 4)] async fn button_task(n: usize, mut pin: Input<'static, AnyPin>) { loop { pin.wait_for_low().await; @@ -18,7 +18,7 @@ async fn button_task(n: usize, mut pin: Input<'static, AnyPin>) { } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { info!("Starting!"); diff --git a/examples/nrf/src/bin/multiprio.rs b/examples/nrf/src/bin/multiprio.rs index 1a4598e21..7050da378 100644 --- a/examples/nrf/src/bin/multiprio.rs +++ b/examples/nrf/src/bin/multiprio.rs @@ -59,14 +59,14 @@ use cortex_m_rt::entry; use defmt::{info, unwrap}; -use embassy::time::{Duration, Instant, Timer}; -use embassy::util::Forever; +use embassy_executor::time::{Duration, Instant, Timer}; use embassy_nrf::executor::{Executor, InterruptExecutor}; use embassy_nrf::interrupt; use embassy_nrf::interrupt::InterruptExt; +use embassy_util::Forever; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task] +#[embassy_executor::task] async fn run_high() { loop { info!(" [high] tick!"); @@ -74,7 +74,7 @@ async fn run_high() { } } -#[embassy::task] +#[embassy_executor::task] async fn run_med() { loop { let start = Instant::now(); @@ -91,7 +91,7 @@ async fn run_med() { } } -#[embassy::task] +#[embassy_executor::task] async fn run_low() { loop { let start = Instant::now(); diff --git a/examples/nrf/src/bin/mutex.rs b/examples/nrf/src/bin/mutex.rs index 92e01976c..5fe7eadb9 100644 --- a/examples/nrf/src/bin/mutex.rs +++ b/examples/nrf/src/bin/mutex.rs @@ -3,16 +3,16 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::blocking_mutex::raw::ThreadModeRawMutex; -use embassy::executor::Spawner; -use embassy::mutex::Mutex; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::Peripherals; +use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_util::mutex::Mutex; use {defmt_rtt as _, panic_probe as _}; static MUTEX: Mutex = Mutex::new(0); -#[embassy::task] +#[embassy_executor::task] async fn my_task() { loop { { @@ -29,7 +29,7 @@ async fn my_task() { } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, _p: Peripherals) { unwrap!(spawner.spawn(my_task())); diff --git a/examples/nrf/src/bin/nvmc.rs b/examples/nrf/src/bin/nvmc.rs index b55ef1f6c..1d4387de7 100644 --- a/examples/nrf/src/bin/nvmc.rs +++ b/examples/nrf/src/bin/nvmc.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::nvmc::Nvmc; use embassy_nrf::Peripherals; use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello NVMC!"); diff --git a/examples/nrf/src/bin/ppi.rs b/examples/nrf/src/bin/ppi.rs index 004a1bfa4..9a60cc0a0 100644 --- a/examples/nrf/src/bin/ppi.rs +++ b/examples/nrf/src/bin/ppi.rs @@ -5,7 +5,7 @@ use core::future::pending; use defmt::info; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::gpiote::{self, InputChannel, InputChannelPolarity}; use embassy_nrf::ppi::Ppi; @@ -13,7 +13,7 @@ use embassy_nrf::Peripherals; use gpiote::{OutputChannel, OutputChannelPolarity}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Starting!"); diff --git a/examples/nrf/src/bin/pubsub.rs b/examples/nrf/src/bin/pubsub.rs index 2c3a355c2..5f33f3e0b 100644 --- a/examples/nrf/src/bin/pubsub.rs +++ b/examples/nrf/src/bin/pubsub.rs @@ -3,10 +3,10 @@ #![feature(type_alias_impl_trait)] use defmt::unwrap; -use embassy::blocking_mutex::raw::ThreadModeRawMutex; -use embassy::channel::pubsub::{DynSubscriber, PubSubChannel, Subscriber}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; +use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_util::channel::pubsub::{DynSubscriber, PubSubChannel, Subscriber}; use {defmt_rtt as _, panic_probe as _}; /// Create the message bus. It has a queue of 4, supports 3 subscribers and 1 publisher @@ -19,7 +19,7 @@ enum Message { C, } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, _p: embassy_nrf::Peripherals) { defmt::info!("Hello World!"); @@ -64,7 +64,7 @@ async fn main(spawner: Spawner, _p: embassy_nrf::Peripherals) { /// A logger task that just awaits the messages it receives /// /// This takes the generic `Subscriber`. This is most performant, but requires you to write down all of the generics -#[embassy::task] +#[embassy_executor::task] async fn fast_logger(mut messages: Subscriber<'static, ThreadModeRawMutex, Message, 4, 3, 1>) { loop { let message = messages.next_message().await; @@ -76,7 +76,7 @@ async fn fast_logger(mut messages: Subscriber<'static, ThreadModeRawMutex, Messa /// Because of this, depeding on how the messages were published, the subscriber might miss some messages /// /// This takes the dynamic `DynSubscriber`. This is not as performant as the generic version, but let's you ignore some of the generics -#[embassy::task] +#[embassy_executor::task] async fn slow_logger(mut messages: DynSubscriber<'static, Message>) { loop { // Do some work @@ -93,7 +93,7 @@ async fn slow_logger(mut messages: DynSubscriber<'static, Message>) { } /// Same as `slow_logger` but it ignores lag results -#[embassy::task] +#[embassy_executor::task] async fn slow_logger_pure(mut messages: DynSubscriber<'static, Message>) { loop { // Do some work diff --git a/examples/nrf/src/bin/pwm.rs b/examples/nrf/src/bin/pwm.rs index aec5dd73a..c8a083294 100644 --- a/examples/nrf/src/bin/pwm.rs +++ b/examples/nrf/src/bin/pwm.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::pwm::{Prescaler, SimplePwm}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; @@ -70,7 +70,7 @@ static DUTY: [u16; 1024] = [ 7255, 7331, 7407, 7484, 7561, 7638, 7716, 7794, 7873, 7952, 8031, 8111, ]; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut pwm = SimplePwm::new_4ch(p.PWM0, p.P0_13, p.P0_14, p.P0_16, p.P0_15); pwm.set_prescaler(Prescaler::Div1); diff --git a/examples/nrf/src/bin/pwm_double_sequence.rs b/examples/nrf/src/bin/pwm_double_sequence.rs index facafa775..cfd8db86b 100644 --- a/examples/nrf/src/bin/pwm_double_sequence.rs +++ b/examples/nrf/src/bin/pwm_double_sequence.rs @@ -3,15 +3,15 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::pwm::{ Config, Prescaler, Sequence, SequenceConfig, SequenceMode, SequencePwm, Sequencer, StartSequence, }; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let seq_words_0: [u16; 5] = [1000, 250, 100, 50, 0]; let seq_words_1: [u16; 4] = [50, 100, 250, 1000]; diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf/src/bin/pwm_sequence.rs index b7cb385c7..b7a04c036 100644 --- a/examples/nrf/src/bin/pwm_sequence.rs +++ b/examples/nrf/src/bin/pwm_sequence.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::pwm::{Config, Prescaler, SequenceConfig, SequencePwm, SingleSequenceMode, SingleSequencer}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let seq_words: [u16; 5] = [1000, 250, 100, 50, 0]; diff --git a/examples/nrf/src/bin/pwm_sequence_ppi.rs b/examples/nrf/src/bin/pwm_sequence_ppi.rs index d98e2ca76..f5c587c35 100644 --- a/examples/nrf/src/bin/pwm_sequence_ppi.rs +++ b/examples/nrf/src/bin/pwm_sequence_ppi.rs @@ -5,7 +5,7 @@ use core::future::pending; use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::gpio::{Input, Pull}; use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; use embassy_nrf::ppi::Ppi; @@ -13,7 +13,7 @@ use embassy_nrf::pwm::{Config, Prescaler, SequenceConfig, SequencePwm, SingleSeq use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let seq_words: [u16; 5] = [1000, 250, 100, 50, 0]; diff --git a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs index 0dee8c949..d6b3f005c 100644 --- a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs +++ b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::pwm::{ Config, Prescaler, SequenceConfig, SequenceLoad, SequencePwm, SingleSequenceMode, SingleSequencer, }; @@ -26,7 +26,7 @@ const RES: u16 = 0x8000; // Provides data to a WS2812b (Neopixel) LED and makes it go blue. The data // line is assumed to be P1_05. -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut config = Config::default(); config.sequence_load = SequenceLoad::Common; diff --git a/examples/nrf/src/bin/pwm_servo.rs b/examples/nrf/src/bin/pwm_servo.rs index 71a90a948..d28a5a17e 100644 --- a/examples/nrf/src/bin/pwm_servo.rs +++ b/examples/nrf/src/bin/pwm_servo.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::pwm::{Prescaler, SimplePwm}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut pwm = SimplePwm::new_1ch(p.PWM0, p.P0_05); // sg90 microervo requires 50hz or 20ms period diff --git a/examples/nrf/src/bin/qdec.rs b/examples/nrf/src/bin/qdec.rs index 9529c7bb6..6bda82f78 100644 --- a/examples/nrf/src/bin/qdec.rs +++ b/examples/nrf/src/bin/qdec.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::qdec::{self, Qdec}; use embassy_nrf::{interrupt, Peripherals}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let irq = interrupt::take!(QDEC); let config = qdec::Config::default(); diff --git a/examples/nrf/src/bin/qspi.rs b/examples/nrf/src/bin/qspi.rs index 96c90f9c8..57e0fdbe2 100644 --- a/examples/nrf/src/bin/qspi.rs +++ b/examples/nrf/src/bin/qspi.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::{assert_eq, info, unwrap}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::{interrupt, qspi, Peripherals}; use {defmt_rtt as _, panic_probe as _}; @@ -14,7 +14,7 @@ const PAGE_SIZE: usize = 4096; #[repr(C, align(4))] struct AlignedBuf([u8; 4096]); -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { // Config for the MX25R64 present in the nRF52840 DK let mut config = qspi::Config::default(); diff --git a/examples/nrf/src/bin/qspi_lowpower.rs b/examples/nrf/src/bin/qspi_lowpower.rs index ce2e40b23..080b27a16 100644 --- a/examples/nrf/src/bin/qspi_lowpower.rs +++ b/examples/nrf/src/bin/qspi_lowpower.rs @@ -5,8 +5,8 @@ use core::mem; use defmt::{info, unwrap}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::{interrupt, qspi, Peripherals}; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +15,7 @@ use {defmt_rtt as _, panic_probe as _}; #[repr(C, align(4))] struct AlignedBuf([u8; 64]); -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, mut p: Peripherals) { let mut irq = interrupt::take!(QSPI); diff --git a/examples/nrf/src/bin/raw_spawn.rs b/examples/nrf/src/bin/raw_spawn.rs index d564b6b26..9199d3aeb 100644 --- a/examples/nrf/src/bin/raw_spawn.rs +++ b/examples/nrf/src/bin/raw_spawn.rs @@ -5,10 +5,10 @@ use core::mem; use cortex_m_rt::entry; use defmt::{info, unwrap}; -use embassy::executor::raw::TaskStorage; -use embassy::executor::Executor; -use embassy::time::{Duration, Timer}; -use embassy::util::Forever; +use embassy_executor::executor::raw::TaskStorage; +use embassy_executor::executor::Executor; +use embassy_executor::time::{Duration, Timer}; +use embassy_util::Forever; use {defmt_rtt as _, panic_probe as _}; async fn run1() { diff --git a/examples/nrf/src/bin/rng.rs b/examples/nrf/src/bin/rng.rs index 08d3abe10..a4314e8b9 100644 --- a/examples/nrf/src/bin/rng.rs +++ b/examples/nrf/src/bin/rng.rs @@ -2,13 +2,13 @@ #![no_main] #![feature(type_alias_impl_trait)] -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::rng::Rng; use embassy_nrf::{interrupt, Peripherals}; use rand::Rng as _; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); diff --git a/examples/nrf/src/bin/saadc.rs b/examples/nrf/src/bin/saadc.rs index cb9289784..65c78d842 100644 --- a/examples/nrf/src/bin/saadc.rs +++ b/examples/nrf/src/bin/saadc.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::saadc::{ChannelConfig, Config, Saadc}; use embassy_nrf::{interrupt, Peripherals}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, mut p: Peripherals) { let config = Config::default(); let channel_config = ChannelConfig::single_ended(&mut p.P0_02); diff --git a/examples/nrf/src/bin/saadc_continuous.rs b/examples/nrf/src/bin/saadc_continuous.rs index 234294eae..d0305736f 100644 --- a/examples/nrf/src/bin/saadc_continuous.rs +++ b/examples/nrf/src/bin/saadc_continuous.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; -use embassy::time::Duration; +use embassy_executor::executor::Spawner; +use embassy_executor::time::Duration; use embassy_nrf::saadc::{ChannelConfig, Config, Saadc, SamplerState}; use embassy_nrf::timer::Frequency; use embassy_nrf::{interrupt, Peripherals}; @@ -12,7 +12,7 @@ use {defmt_rtt as _, panic_probe as _}; // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, mut p: Peripherals) { let config = Config::default(); let channel_1_config = ChannelConfig::single_ended(&mut p.P0_02); @@ -27,7 +27,7 @@ async fn main(_spawner: Spawner, mut p: Peripherals) { // This delay demonstrates that starting the timer prior to running // the task sampler is benign given the calibration that follows. - embassy::time::Timer::after(Duration::from_millis(500)).await; + embassy_executor::time::Timer::after(Duration::from_millis(500)).await; saadc.calibrate().await; let mut bufs = [[[0; 3]; 500]; 2]; diff --git a/examples/nrf/src/bin/self_spawn.rs b/examples/nrf/src/bin/self_spawn.rs index 4b8ac04bc..e0152802e 100644 --- a/examples/nrf/src/bin/self_spawn.rs +++ b/examples/nrf/src/bin/self_spawn.rs @@ -3,19 +3,19 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task(pool_size = 2)] +#[embassy_executor::task(pool_size = 2)] async fn my_task(spawner: Spawner, n: u32) { Timer::after(Duration::from_secs(1)).await; info!("Spawning self! {}", n); unwrap!(spawner.spawn(my_task(spawner, n + 1))); } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, _p: Peripherals) { info!("Hello World!"); unwrap!(spawner.spawn(my_task(spawner, 0))); diff --git a/examples/nrf/src/bin/self_spawn_current_executor.rs b/examples/nrf/src/bin/self_spawn_current_executor.rs index 3c3379ce6..1d8309d77 100644 --- a/examples/nrf/src/bin/self_spawn_current_executor.rs +++ b/examples/nrf/src/bin/self_spawn_current_executor.rs @@ -3,19 +3,19 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task(pool_size = 2)] +#[embassy_executor::task(pool_size = 2)] async fn my_task(n: u32) { Timer::after(Duration::from_secs(1)).await; info!("Spawning self! {}", n); unwrap!(Spawner::for_current_executor().await.spawn(my_task(n + 1))); } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, _p: Peripherals) { info!("Hello World!"); unwrap!(spawner.spawn(my_task(0))); diff --git a/examples/nrf/src/bin/spim.rs b/examples/nrf/src/bin/spim.rs index 62040168a..fd741b21c 100644 --- a/examples/nrf/src/bin/spim.rs +++ b/examples/nrf/src/bin/spim.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::{interrupt, spim, Peripherals}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("running!"); diff --git a/examples/nrf/src/bin/temp.rs b/examples/nrf/src/bin/temp.rs index 939cb39e7..654098e0b 100644 --- a/examples/nrf/src/bin/temp.rs +++ b/examples/nrf/src/bin/temp.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::temp::Temp; use embassy_nrf::{interrupt, Peripherals}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let irq = interrupt::take!(TEMP); let mut temp = Temp::new(p.TEMP, irq); diff --git a/examples/nrf/src/bin/timer.rs b/examples/nrf/src/bin/timer.rs index 64376dd78..61ff1d6db 100644 --- a/examples/nrf/src/bin/timer.rs +++ b/examples/nrf/src/bin/timer.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task] +#[embassy_executor::task] async fn run1() { loop { info!("BIG INFREQUENT TICK"); @@ -16,7 +16,7 @@ async fn run1() { } } -#[embassy::task] +#[embassy_executor::task] async fn run2() { loop { info!("tick"); @@ -24,7 +24,7 @@ async fn run2() { } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, _p: Peripherals) { unwrap!(spawner.spawn(run1())); unwrap!(spawner.spawn(run2())); diff --git a/examples/nrf/src/bin/twim.rs b/examples/nrf/src/bin/twim.rs index fb8372a12..bb7ee9db4 100644 --- a/examples/nrf/src/bin/twim.rs +++ b/examples/nrf/src/bin/twim.rs @@ -7,14 +7,14 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::twim::{self, Twim}; use embassy_nrf::{interrupt, Peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Initializing TWI..."); let config = twim::Config::default(); diff --git a/examples/nrf/src/bin/twim_lowpower.rs b/examples/nrf/src/bin/twim_lowpower.rs index c9c2d503e..ebf3d7109 100644 --- a/examples/nrf/src/bin/twim_lowpower.rs +++ b/examples/nrf/src/bin/twim_lowpower.rs @@ -11,15 +11,15 @@ use core::mem; use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::twim::{self, Twim}; use embassy_nrf::{interrupt, Peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, mut p: Peripherals) { info!("Started!"); let mut irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); diff --git a/examples/nrf/src/bin/uart.rs b/examples/nrf/src/bin/uart.rs index c8c4a67a5..5f363b69e 100644 --- a/examples/nrf/src/bin/uart.rs +++ b/examples/nrf/src/bin/uart.rs @@ -3,11 +3,11 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::{interrupt, uarte, Peripherals}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut config = uarte::Config::default(); config.parity = uarte::Parity::EXCLUDED; diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf/src/bin/uart_idle.rs index 6679b28da..0f455dffd 100644 --- a/examples/nrf/src/bin/uart_idle.rs +++ b/examples/nrf/src/bin/uart_idle.rs @@ -3,11 +3,11 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::{interrupt, uarte, Peripherals}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut config = uarte::Config::default(); config.parity = uarte::Parity::EXCLUDED; diff --git a/examples/nrf/src/bin/uart_split.rs b/examples/nrf/src/bin/uart_split.rs index 1ffb63706..2de5f90c1 100644 --- a/examples/nrf/src/bin/uart_split.rs +++ b/examples/nrf/src/bin/uart_split.rs @@ -3,17 +3,17 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::blocking_mutex::raw::ThreadModeRawMutex; -use embassy::channel::mpmc::Channel; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::peripherals::UARTE0; use embassy_nrf::uarte::UarteRx; use embassy_nrf::{interrupt, uarte, Peripherals}; +use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_util::channel::mpmc::Channel; use {defmt_rtt as _, panic_probe as _}; static CHANNEL: Channel = Channel::new(); -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { let mut config = uarte::Config::default(); config.parity = uarte::Parity::EXCLUDED; @@ -48,7 +48,7 @@ async fn main(spawner: Spawner, p: Peripherals) { } } -#[embassy::task] +#[embassy_executor::task] async fn reader(mut rx: UarteRx<'static, UARTE0>) { let mut buf = [0; 8]; loop { diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index e57cdaf63..93cb05907 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -8,10 +8,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Waker; use defmt::*; -use embassy::blocking_mutex::raw::ThreadModeRawMutex; -use embassy::channel::mpmc::Channel; -use embassy::executor::Spawner; -use embassy::util::Forever; +use embassy_executor::executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; use embassy_nrf::rng::Rng; @@ -19,6 +16,9 @@ use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals, Peripherals}; use embassy_usb::{Builder, Config, UsbDevice}; use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; +use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_util::channel::mpmc::Channel; +use embassy_util::Forever; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -32,12 +32,12 @@ macro_rules! forever { }}; } -#[embassy::task] +#[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { device.run().await } -#[embassy::task] +#[embassy_executor::task] async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { loop { warn!("WAITING for connection"); @@ -66,7 +66,7 @@ async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { } } -#[embassy::task] +#[embassy_executor::task] async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { loop { let pkt = TX_CHANNEL.recv().await; @@ -76,12 +76,12 @@ async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { } } -#[embassy::task] +#[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { stack.run().await } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 539ae6f16..863f3e5dd 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -7,23 +7,22 @@ use core::mem; use core::sync::atomic::{AtomicBool, Ordering}; use defmt::*; -use embassy::channel::signal::Signal; -use embassy::executor::Spawner; -use embassy::time::Duration; -use embassy::util::{select, Either}; +use embassy_executor::executor::Spawner; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State}; +use embassy_util::channel::signal::Signal; +use embassy_util::{select, Either}; use futures::future::join; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; static SUSPENDED: AtomicBool = AtomicBool::new(false); -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; @@ -154,11 +153,11 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } - fn set_idle(&self, id: Option, dur: Duration) { + fn set_idle_ms(&self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } - fn get_idle(&self, id: Option) -> Option { + fn get_idle_ms(&self, id: Option) -> Option { info!("Get idle rate for {:?}", id); None } diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index 516e7ea95..88bf87bd6 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -6,8 +6,8 @@ use core::mem; use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_usb::control::OutResponse; @@ -17,7 +17,7 @@ use futures::future::join; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; @@ -113,11 +113,11 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } - fn set_idle(&self, id: Option, dur: Duration) { + fn set_idle_ms(&self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } - fn get_idle(&self, id: Option) -> Option { + fn get_idle_ms(&self, id: Option) -> Option { info!("Get idle rate for {:?}", id); None } diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index d2200dc5d..7d233d24d 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -6,7 +6,7 @@ use core::mem; use defmt::{info, panic}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_usb::driver::EndpointError; @@ -15,7 +15,7 @@ use embassy_usb_serial::{CdcAcmClass, State}; use futures::future::join; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index 3806da5a0..956315322 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -6,23 +6,23 @@ use core::mem; use defmt::{info, panic, unwrap}; -use embassy::executor::Spawner; -use embassy::util::Forever; +use embassy_executor::executor::Spawner; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals, Peripherals}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; use embassy_usb_serial::{CdcAcmClass, State}; +use embassy_util::Forever; use {defmt_rtt as _, panic_probe as _}; type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; -#[embassy::task] +#[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { device.run().await; } -#[embassy::task] +#[embassy_executor::task] async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) { loop { class.wait_connection().await; @@ -32,7 +32,7 @@ async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) { } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; diff --git a/examples/nrf/src/bin/wdt.rs b/examples/nrf/src/bin/wdt.rs index 280e23bcf..560cb3567 100644 --- a/examples/nrf/src/bin/wdt.rs +++ b/examples/nrf/src/bin/wdt.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_nrf::gpio::{Input, Pull}; use embassy_nrf::wdt::{Config, Watchdog}; use embassy_nrf::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index d0704f203..94c3d8013 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -5,7 +5,8 @@ version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } atomic-polyfill = "0.1.5" diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index 35612a4cf..e53fca1af 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_rp::{gpio, Peripherals}; use gpio::{Level, Output}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.PIN_25, Level::Low); diff --git a/examples/rp/src/bin/button.rs b/examples/rp/src/bin/button.rs index 980e54ea1..02cbc9416 100644 --- a/examples/rp/src/bin/button.rs +++ b/examples/rp/src/bin/button.rs @@ -2,12 +2,12 @@ #![no_main] #![feature(type_alias_impl_trait)] -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let button = Input::new(p.PIN_28, Pull::Up); let mut led = Output::new(p.PIN_25, Level::Low); diff --git a/examples/rp/src/bin/gpio_async.rs b/examples/rp/src/bin/gpio_async.rs index e0f2aa961..ba905b015 100644 --- a/examples/rp/src/bin/gpio_async.rs +++ b/examples/rp/src/bin/gpio_async.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_rp::{gpio, Peripherals}; use gpio::{Input, Level, Output, Pull}; use {defmt_rtt as _, panic_probe as _}; @@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _}; /// high signal on PIN 16. Once the high event/signal occurs the program will /// continue and turn off the LED, and then wait for 2 seconds before completing /// the loop and starting over again. -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut led = Output::new(p.PIN_25, Level::Low); let mut async_input = Input::new(p.PIN_16, Pull::None); diff --git a/examples/rp/src/bin/spi.rs b/examples/rp/src/bin/spi.rs index d97aa94b3..a3160c106 100644 --- a/examples/rp/src/bin/spi.rs +++ b/examples/rp/src/bin/spi.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_rp::spi::Spi; use embassy_rp::{gpio, spi, Peripherals}; use gpio::{Level, Output}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index f4a411ba6..2760b23fa 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -5,8 +5,8 @@ use core::cell::RefCell; use defmt::*; -use embassy::executor::Spawner; -use embassy::time::Delay; +use embassy_executor::executor::Spawner; +use embassy_executor::time::Delay; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi::Spi; use embassy_rp::{spi, Peripherals}; @@ -27,7 +27,7 @@ use crate::touch::Touch; //const DISPLAY_FREQ: u32 = 64_000_000; const TOUCH_FREQ: u32 = 200_000; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/rp/src/bin/uart.rs b/examples/rp/src/bin/uart.rs index 99072253a..0d2954894 100644 --- a/examples/rp/src/bin/uart.rs +++ b/examples/rp/src/bin/uart.rs @@ -2,11 +2,11 @@ #![no_main] #![feature(type_alias_impl_trait)] -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_rp::{uart, Peripherals}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let config = uart::Config::default(); let mut uart = uart::Uart::new(p.UART0, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, config); diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 8787f3c92..54499796b 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-std-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["log"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "time", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] } embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] } diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 74073ee81..202585289 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -1,10 +1,10 @@ #![feature(type_alias_impl_trait)] use clap::Parser; -use embassy::executor::{Executor, Spawner}; -use embassy::util::Forever; +use embassy_executor::executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_util::Forever; use embedded_io::asynch::Write; use heapless::Vec; use log::*; @@ -34,12 +34,12 @@ struct Opts { static_ip: bool, } -#[embassy::task] +#[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { stack.run().await } -#[embassy::task] +#[embassy_executor::task] async fn main_task(spawner: Spawner) { let opts: Opts = Opts::parse(); diff --git a/examples/std/src/bin/serial.rs b/examples/std/src/bin/serial.rs index b1e5b0142..b803d1ef7 100644 --- a/examples/std/src/bin/serial.rs +++ b/examples/std/src/bin/serial.rs @@ -4,15 +4,15 @@ mod serial_port; use async_io::Async; -use embassy::executor::Executor; -use embassy::util::Forever; +use embassy_executor::executor::Executor; +use embassy_util::Forever; use embedded_io::asynch::Read; use log::*; use nix::sys::termios; use self::serial_port::SerialPort; -#[embassy::task] +#[embassy_executor::task] async fn run() { // Open the serial port. let baudrate = termios::BaudRate::B115200; diff --git a/examples/std/src/bin/tick.rs b/examples/std/src/bin/tick.rs index bed9d7dc5..9ca900df8 100644 --- a/examples/std/src/bin/tick.rs +++ b/examples/std/src/bin/tick.rs @@ -1,10 +1,10 @@ #![feature(type_alias_impl_trait)] -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use log::*; -#[embassy::task] +#[embassy_executor::task] async fn run() { loop { info!("tick"); @@ -12,7 +12,7 @@ async fn run() { } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner) { env_logger::builder() .filter_level(log::LevelFilter::Debug) diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 615803258..3ba297636 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -11,6 +11,7 @@ cortex-m-rt = "0.7.0" defmt = "0.3" defmt-rtt = "0.3" panic-probe = "0.3" -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f030f4", "time-driver-any"] } diff --git a/examples/stm32f0/src/bin/hello.rs b/examples/stm32f0/src/bin/hello.rs index 225f1c3ae..c9081ea12 100644 --- a/examples/stm32f0/src/bin/hello.rs +++ b/examples/stm32f0/src/bin/hello.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, _p: Peripherals) -> ! { loop { Timer::after(Duration::from_secs(1)).await; diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index fb0e605d6..9ce553b6d 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32f1-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 09904d4cc..e54593fe5 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Delay, Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Delay, Duration, Timer}; use embassy_stm32::adc::Adc; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f1/src/bin/blinky.rs b/examples/stm32f1/src/bin/blinky.rs index c98d0cdad..5171043e8 100644 --- a/examples/stm32f1/src/bin/blinky.rs +++ b/examples/stm32f1/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f1/src/bin/hello.rs b/examples/stm32f1/src/bin/hello.rs index 82f11bc28..549d1bfba 100644 --- a/examples/stm32f1/src/bin/hello.rs +++ b/examples/stm32f1/src/bin/hello.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::time::Hertz; use embassy_stm32::{Config, Peripherals}; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +15,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, _p: Peripherals) -> ! { loop { info!("Hello World!"); diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index d06315d76..cf7facb79 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::{panic, *}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; @@ -23,7 +23,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, mut p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 6ea6add17..a3fb736da 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32f2-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" diff --git a/examples/stm32f2/src/bin/blinky.rs b/examples/stm32f2/src/bin/blinky.rs index dd20ba85a..48ae2e711 100644 --- a/examples/stm32f2/src/bin/blinky.rs +++ b/examples/stm32f2/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f2/src/bin/pll.rs b/examples/stm32f2/src/bin/pll.rs index b09d64b0b..01e63b15e 100644 --- a/examples/stm32f2/src/bin/pll.rs +++ b/examples/stm32f2/src/bin/pll.rs @@ -5,8 +5,8 @@ use core::convert::TryFrom; use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::rcc::{ APBPrescaler, ClockSrc, HSEConfig, HSESrc, PLL48Div, PLLConfig, PLLMainDiv, PLLMul, PLLPreDiv, PLLSrc, }; @@ -43,7 +43,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, _p: Peripherals) { loop { Timer::after(Duration::from_millis(1000)).await; diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 6912ba765..410e9b3e0 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32f3-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } diff --git a/examples/stm32f3/src/bin/blinky.rs b/examples/stm32f3/src/bin/blinky.rs index 4d0b33f61..7146eaa54 100644 --- a/examples/stm32f3/src/bin/blinky.rs +++ b/examples/stm32f3/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index 45862ddc6..ef5110316 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs @@ -11,14 +11,14 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::blocking_mutex::raw::ThreadModeRawMutex; -use embassy::channel::mpmc::Channel; -use embassy::executor::Spawner; -use embassy::time::{with_timeout, Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{with_timeout, Duration, Timer}; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{AnyPin, Input, Level, Output, Pin, Pull, Speed}; use embassy_stm32::peripherals::PA0; use embassy_stm32::Peripherals; +use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_util::channel::mpmc::Channel; use {defmt_rtt as _, panic_probe as _}; struct Leds<'a> { @@ -99,7 +99,7 @@ enum ButtonEvent { static CHANNEL: Channel = Channel::new(); -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { let button = Input::new(p.PA0, Pull::Down); let button = ExtiInput::new(button, p.EXTI0); @@ -120,14 +120,14 @@ async fn main(spawner: Spawner, p: Peripherals) { spawner.spawn(led_blinker(leds)).unwrap(); } -#[embassy::task] +#[embassy_executor::task] async fn led_blinker(mut leds: Leds<'static>) { loop { leds.show().await; } } -#[embassy::task] +#[embassy_executor::task] async fn button_waiter(mut button: ExtiInput<'static, PA0>) { const DOUBLE_CLICK_DELAY: u64 = 250; const HOLD_DELAY: u64 = 1000; diff --git a/examples/stm32f3/src/bin/button_exti.rs b/examples/stm32f3/src/bin/button_exti.rs index add6712b4..dee06e5de 100644 --- a/examples/stm32f3/src/bin/button_exti.rs +++ b/examples/stm32f3/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index ce16f6de7..be2f6f671 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::flash::Flash; use embassy_stm32::Peripherals; use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello Flash!"); diff --git a/examples/stm32f3/src/bin/hello.rs b/examples/stm32f3/src/bin/hello.rs index 3b89f1c72..bd9953a0e 100644 --- a/examples/stm32f3/src/bin/hello.rs +++ b/examples/stm32f3/src/bin/hello.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::time::Hertz; use embassy_stm32::{Config, Peripherals}; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +16,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, _p: Peripherals) -> ! { loop { info!("Hello World!"); diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 4f2cf9a6f..fba5b286e 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -59,14 +59,14 @@ use cortex_m_rt::entry; use defmt::*; -use embassy::time::{Duration, Instant, Timer}; -use embassy::util::Forever; +use embassy_executor::time::{Duration, Instant, Timer}; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; use embassy_stm32::interrupt::InterruptExt; +use embassy_util::Forever; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task] +#[embassy_executor::task] async fn run_high() { loop { info!(" [high] tick!"); @@ -74,7 +74,7 @@ async fn run_high() { } } -#[embassy::task] +#[embassy_executor::task] async fn run_med() { loop { let start = Instant::now(); @@ -91,7 +91,7 @@ async fn run_med() { } } -#[embassy::task] +#[embassy_executor::task] async fn run_low() { loop { let start = Instant::now(); diff --git a/examples/stm32f3/src/bin/spi_dma.rs b/examples/stm32f3/src/bin/spi_dma.rs index ece1ae6fe..f554c509a 100644 --- a/examples/stm32f3/src/bin/spi_dma.rs +++ b/examples/stm32f3/src/bin/spi_dma.rs @@ -6,14 +6,14 @@ use core::fmt::Write; use core::str::from_utf8; use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; use embassy_stm32::Peripherals; use heapless::String; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 4660f812e..62d165029 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs @@ -5,14 +5,14 @@ use core::fmt::Write; use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::Peripherals; use heapless::String; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index 463d561ec..87b1138f5 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::{panic, *}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; @@ -27,7 +27,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 100c0e608..37464b1e0 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -5,7 +5,8 @@ version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 84ddbfd3c..27ed4fcc5 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Delay, Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Delay, Duration, Timer}; use embassy_stm32::adc::Adc; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/blinky.rs b/examples/stm32f4/src/bin/blinky.rs index 907492b3d..f71fe0989 100644 --- a/examples/stm32f4/src/bin/blinky.rs +++ b/examples/stm32f4/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/button_exti.rs b/examples/stm32f4/src/bin/button_exti.rs index 24ece9927..60dfb362b 100644 --- a/examples/stm32f4/src/bin/button_exti.rs +++ b/examples/stm32f4/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index 13fd2c90b..4f780656e 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::flash::Flash; use embassy_stm32::Peripherals; use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello Flash!"); diff --git a/examples/stm32f4/src/bin/hello.rs b/examples/stm32f4/src/bin/hello.rs index 8e69e89d1..f957656ef 100644 --- a/examples/stm32f4/src/bin/hello.rs +++ b/examples/stm32f4/src/bin/hello.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::time::Hertz; use embassy_stm32::{Config, Peripherals}; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +15,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, _p: Peripherals) -> ! { loop { info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 4f2cf9a6f..fba5b286e 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -59,14 +59,14 @@ use cortex_m_rt::entry; use defmt::*; -use embassy::time::{Duration, Instant, Timer}; -use embassy::util::Forever; +use embassy_executor::time::{Duration, Instant, Timer}; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; use embassy_stm32::interrupt::InterruptExt; +use embassy_util::Forever; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task] +#[embassy_executor::task] async fn run_high() { loop { info!(" [high] tick!"); @@ -74,7 +74,7 @@ async fn run_high() { } } -#[embassy::task] +#[embassy_executor::task] async fn run_med() { loop { let start = Instant::now(); @@ -91,7 +91,7 @@ async fn run_med() { } } -#[embassy::task] +#[embassy_executor::task] async fn run_low() { loop { let start = Instant::now(); diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index b39bbbe28..0b352c2b7 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -3,15 +3,15 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 752ad57bf..6eef19963 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::sdmmc::Sdmmc; use embassy_stm32::time::mhz; use embassy_stm32::{interrupt, Config, Peripherals}; @@ -15,7 +15,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) -> ! { info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/spi_dma.rs b/examples/stm32f4/src/bin/spi_dma.rs index f3c0f2cd5..023ca0971 100644 --- a/examples/stm32f4/src/bin/spi_dma.rs +++ b/examples/stm32f4/src/bin/spi_dma.rs @@ -6,14 +6,14 @@ use core::fmt::Write; use core::str::from_utf8; use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; use embassy_stm32::Peripherals; use heapless::String; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index 039e43bd2..2555998ce 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; use embassy_stm32::{interrupt, Peripherals}; use embedded_io::asynch::BufRead; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/usart_dma.rs b/examples/stm32f4/src/bin/usart_dma.rs index 8d06f8439..7859ba2ae 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs @@ -5,14 +5,14 @@ use core::fmt::Write; use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::Peripherals; use heapless::String; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/wdt.rs b/examples/stm32f4/src/bin/wdt.rs index bfc487c31..48394f4f1 100644 --- a/examples/stm32f4/src/bin/wdt.rs +++ b/examples/stm32f4/src/bin/wdt.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::wdg::IndependentWatchdog; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index b0a548a3f..081bed84f 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32f7-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embedded-io = { version = "0.3.0", features = ["async"] } diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index fc8359622..2a813c050 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Delay, Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Delay, Duration, Timer}; use embassy_stm32::adc::Adc; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f7/src/bin/blinky.rs b/examples/stm32f7/src/bin/blinky.rs index 907492b3d..f71fe0989 100644 --- a/examples/stm32f7/src/bin/blinky.rs +++ b/examples/stm32f7/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f7/src/bin/button_exti.rs b/examples/stm32f7/src/bin/button_exti.rs index 24ece9927..60dfb362b 100644 --- a/examples/stm32f7/src/bin/button_exti.rs +++ b/examples/stm32f7/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 177683c3a..33504af76 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -3,9 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; -use embassy::util::Forever; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; @@ -14,6 +13,7 @@ use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; use embassy_stm32::{interrupt, Config, Peripherals}; +use embassy_util::Forever; use embedded_io::asynch::Write; use rand_core::RngCore; use {defmt_rtt as _, panic_probe as _}; @@ -28,7 +28,7 @@ macro_rules! forever { type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; -#[embassy::task] +#[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { stack.run().await } @@ -39,7 +39,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(spawner: Spawner, p: Peripherals) -> ! { info!("Hello World!"); diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index af66275d4..15864cabb 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::flash::Flash; use embassy_stm32::Peripherals; use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello Flash!"); diff --git a/examples/stm32f7/src/bin/hello.rs b/examples/stm32f7/src/bin/hello.rs index 8e69e89d1..f957656ef 100644 --- a/examples/stm32f7/src/bin/hello.rs +++ b/examples/stm32f7/src/bin/hello.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::info; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::time::Hertz; use embassy_stm32::{Config, Peripherals}; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +15,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, _p: Peripherals) -> ! { loop { info!("Hello World!"); diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index be1c2b152..1f321df17 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::sdmmc::Sdmmc; use embassy_stm32::time::mhz; use embassy_stm32::{interrupt, Config, Peripherals}; @@ -15,7 +15,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) -> ! { info!("Hello World!"); diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index d8551620c..9884d1634 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs @@ -5,14 +5,14 @@ use core::fmt::Write; use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::Peripherals; use heapless::String; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let config = Config::default(); let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, p.DMA1_CH1, NoDma, config); diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index bf23fa822..3dedeac63 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32g0-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" diff --git a/examples/stm32g0/src/bin/blinky.rs b/examples/stm32g0/src/bin/blinky.rs index 907492b3d..f71fe0989 100644 --- a/examples/stm32g0/src/bin/blinky.rs +++ b/examples/stm32g0/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32g0/src/bin/button_exti.rs b/examples/stm32g0/src/bin/button_exti.rs index 924feeb33..0832386ed 100644 --- a/examples/stm32g0/src/bin/button_exti.rs +++ b/examples/stm32g0/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index d3641c489..60c62ad1e 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32g4-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } diff --git a/examples/stm32g4/src/bin/blinky.rs b/examples/stm32g4/src/bin/blinky.rs index cd4883276..ea3c563b4 100644 --- a/examples/stm32g4/src/bin/blinky.rs +++ b/examples/stm32g4/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32g4/src/bin/button_exti.rs b/examples/stm32g4/src/bin/button_exti.rs index 24ece9927..60dfb362b 100644 --- a/examples/stm32g4/src/bin/button_exti.rs +++ b/examples/stm32g4/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index dc4e164ab..7c16d0a3a 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -3,15 +3,15 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index d905031d6..8b1999b30 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32h7-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embedded-io = { version = "0.3.0", features = ["async"] } diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index d8a5d23d7..f50976a30 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Delay, Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Delay, Duration, Timer}; use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::rcc::AdcClockSource; use embassy_stm32::time::mhz; @@ -20,7 +20,7 @@ pub fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, mut p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/blinky.rs b/examples/stm32h7/src/bin/blinky.rs index 7982f4a0b..98ce15cc6 100644 --- a/examples/stm32h7/src/bin/blinky.rs +++ b/examples/stm32h7/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/button_exti.rs b/examples/stm32h7/src/bin/button_exti.rs index 24ece9927..60dfb362b 100644 --- a/examples/stm32h7/src/bin/button_exti.rs +++ b/examples/stm32h7/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index 0d0179e62..69187182f 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs @@ -2,8 +2,8 @@ #![no_main] #![feature(type_alias_impl_trait)] -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::dcmi::{self, *}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::i2c::I2c; @@ -32,7 +32,7 @@ const HEIGHT: usize = 100; static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2]; -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { defmt::info!("Hello World!"); let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(3)); @@ -78,7 +78,7 @@ mod ov7725 { use core::marker::PhantomData; use defmt::Format; - use embassy::time::{Duration, Timer}; + use embassy_executor::time::{Duration, Timer}; use embassy_stm32::rcc::{Mco, McoInstance}; use embedded_hal_async::i2c::I2c; diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 556d472bd..4282fcedd 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -3,9 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; -use embassy::util::Forever; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; @@ -14,6 +13,7 @@ use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; use embassy_stm32::{interrupt, Config, Peripherals}; +use embassy_util::Forever; use embedded_io::asynch::Write; use rand_core::RngCore; use {defmt_rtt as _, panic_probe as _}; @@ -28,7 +28,7 @@ macro_rules! forever { type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; -#[embassy::task] +#[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { stack.run().await } @@ -41,7 +41,7 @@ pub fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(spawner: Spawner, p: Peripherals) -> ! { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index 5f97d2b31..0c477deba 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::flash::Flash; use embassy_stm32::Peripherals; use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello Flash!"); diff --git a/examples/stm32h7/src/bin/fmc.rs b/examples/stm32h7/src/bin/fmc.rs index 27c715ab0..5140a6e22 100644 --- a/examples/stm32h7/src/bin/fmc.rs +++ b/examples/stm32h7/src/bin/fmc.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Delay, Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Delay, Duration, Timer}; use embassy_stm32::fmc::Fmc; use embassy_stm32::time::mhz; use embassy_stm32::{Config, Peripherals}; @@ -18,7 +18,7 @@ pub fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index d7c6da5bd..f2477c7a7 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::low_level::AFType; use embassy_stm32::gpio::Speed; use embassy_stm32::pwm::*; @@ -24,7 +24,7 @@ pub fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/mco.rs b/examples/stm32h7/src/bin/mco.rs index 6f03b5479..83ba3742b 100644 --- a/examples/stm32h7/src/bin/mco.rs +++ b/examples/stm32h7/src/bin/mco.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index 730f637e9..36ed2e4a4 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::pwm::Channel; use embassy_stm32::time::{khz, mhz}; @@ -23,7 +23,7 @@ pub fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/rng.rs b/examples/stm32h7/src/bin/rng.rs index 2b42a6afd..81fb3d162 100644 --- a/examples/stm32h7/src/bin/rng.rs +++ b/examples/stm32h7/src/bin/rng.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::rng::Rng; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index 163807d86..19ae5ade1 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::sdmmc::Sdmmc; use embassy_stm32::time::mhz; use embassy_stm32::{interrupt, Config, Peripherals}; @@ -15,7 +15,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) -> ! { info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/signal.rs b/examples/stm32h7/src/bin/signal.rs index f798b1c92..2fc75c7af 100644 --- a/examples/stm32h7/src/bin/signal.rs +++ b/examples/stm32h7/src/bin/signal.rs @@ -3,15 +3,15 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::channel::signal::Signal; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::Peripherals; +use embassy_util::channel::signal::Signal; use {defmt_rtt as _, panic_probe as _}; static SIGNAL: Signal = Signal::new(); -#[embassy::task] +#[embassy_executor::task] async fn my_sending_task() { let mut counter: u32 = 0; @@ -24,7 +24,7 @@ async fn my_sending_task() { } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, _p: Peripherals) { unwrap!(spawner.spawn(my_sending_task())); diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index f2eb5a3be..dc8cb7f47 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -7,12 +7,12 @@ use core::str::from_utf8; use cortex_m_rt::entry; use defmt::*; -use embassy::executor::Executor; -use embassy::util::Forever; +use embassy_executor::executor::Executor; use embassy_stm32::dma::NoDma; use embassy_stm32::peripherals::SPI3; use embassy_stm32::time::mhz; use embassy_stm32::{spi, Config}; +use embassy_util::Forever; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +24,7 @@ pub fn config() -> Config { config } -#[embassy::task] +#[embassy_executor::task] async fn main_task(mut spi: spi::Spi<'static, SPI3, NoDma, NoDma>) { for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index d72051fda..2631ed30c 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -7,11 +7,11 @@ use core::str::from_utf8; use cortex_m_rt::entry; use defmt::*; -use embassy::executor::Executor; -use embassy::util::Forever; +use embassy_executor::executor::Executor; use embassy_stm32::peripherals::{DMA1_CH3, DMA1_CH4, SPI3}; use embassy_stm32::time::mhz; use embassy_stm32::{spi, Config}; +use embassy_util::Forever; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -23,7 +23,7 @@ pub fn config() -> Config { config } -#[embassy::task] +#[embassy_executor::task] async fn main_task(mut spi: spi::Spi<'static, SPI3, DMA1_CH3, DMA1_CH4>) { for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index fc3db5a33..e491fb39d 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs @@ -4,13 +4,13 @@ use cortex_m_rt::entry; use defmt::*; -use embassy::executor::Executor; -use embassy::util::Forever; +use embassy_executor::executor::Executor; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; +use embassy_util::Forever; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task] +#[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); diff --git a/examples/stm32h7/src/bin/usart_dma.rs b/examples/stm32h7/src/bin/usart_dma.rs index d3325b0c1..aacda45bc 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs @@ -6,14 +6,14 @@ use core::fmt::Write; use cortex_m_rt::entry; use defmt::*; -use embassy::executor::Executor; -use embassy::util::Forever; +use embassy_executor::executor::Executor; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; +use embassy_util::Forever; use heapless::String; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task] +#[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index 678d8c911..12bb0ce9c 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -3,16 +3,16 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::blocking_mutex::raw::ThreadModeRawMutex; -use embassy::channel::mpmc::Channel; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::peripherals::{DMA1_CH1, UART7}; use embassy_stm32::usart::{Config, Uart, UartRx}; use embassy_stm32::Peripherals; +use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_util::channel::mpmc::Channel; use {defmt_rtt as _, panic_probe as _}; -#[embassy::task] +#[embassy_executor::task] async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); @@ -26,7 +26,7 @@ async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { static CHANNEL: Channel = Channel::new(); -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) -> ! { info!("Hello World!"); @@ -45,7 +45,7 @@ async fn main(spawner: Spawner, p: Peripherals) -> ! { } } -#[embassy::task] +#[embassy_executor::task] async fn reader(mut rx: UartRx<'static, UART7, DMA1_CH1>) { let mut buf = [0; 8]; loop { diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index d6093963b..7edda042d 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -8,7 +8,8 @@ default = ["nightly"] nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"] [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} diff --git a/examples/stm32l0/src/bin/blinky.rs b/examples/stm32l0/src/bin/blinky.rs index e027192bc..8cf21effb 100644 --- a/examples/stm32l0/src/bin/blinky.rs +++ b/examples/stm32l0/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l0/src/bin/button.rs b/examples/stm32l0/src/bin/button.rs index 43ea8c2a5..a5e05c3a3 100644 --- a/examples/stm32l0/src/bin/button.rs +++ b/examples/stm32l0/src/bin/button.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l0/src/bin/button_exti.rs b/examples/stm32l0/src/bin/button_exti.rs index d87870a01..22a096af8 100644 --- a/examples/stm32l0/src/bin/button_exti.rs +++ b/examples/stm32l0/src/bin/button_exti.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; @@ -15,7 +15,7 @@ fn config() -> embassy_stm32::Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { let button = Input::new(p.PB2, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI2); diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index a2fec9291..7ad5ae3aa 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::flash::Flash; use embassy_stm32::Peripherals; use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello Flash!"); diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index da58e2f72..29e54c1be 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs @@ -24,8 +24,8 @@ fn config() -> embassy_stm32::Config { config } -#[embassy::main(config = "config()")] -async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main(config = "config()")] +async fn main(_spawner: embassy_executor::executor::Spawner, p: Peripherals) { // SPI for sx127x let spi = spi::Spi::new( p.SPI1, diff --git a/examples/stm32l0/src/bin/raw_spawn.rs b/examples/stm32l0/src/bin/raw_spawn.rs index dfe2cddb6..cd711a430 100644 --- a/examples/stm32l0/src/bin/raw_spawn.rs +++ b/examples/stm32l0/src/bin/raw_spawn.rs @@ -5,10 +5,10 @@ use core::mem; use cortex_m_rt::entry; use defmt::*; -use embassy::executor::raw::TaskStorage; -use embassy::executor::Executor; -use embassy::time::{Duration, Timer}; -use embassy::util::Forever; +use embassy_executor::executor::raw::TaskStorage; +use embassy_executor::executor::Executor; +use embassy_executor::time::{Duration, Timer}; +use embassy_util::Forever; use {defmt_rtt as _, panic_probe as _}; async fn run1() { diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index dba0b281d..74694295c 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; @@ -11,7 +11,7 @@ use embassy_stm32::time::Hertz; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World, folks!"); diff --git a/examples/stm32l0/src/bin/usart_dma.rs b/examples/stm32l0/src/bin/usart_dma.rs index 861241639..1c5ce94d7 100644 --- a/examples/stm32l0/src/bin/usart_dma.rs +++ b/examples/stm32l0/src/bin/usart_dma.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, p.DMA1_CH2, p.DMA1_CH3, Config::default()); diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index 09b1b0b03..b77d97f85 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; use embassy_stm32::{interrupt, Peripherals}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hi!"); diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 7fec60575..d69de9c53 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32l1-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } defmt = "0.3" diff --git a/examples/stm32l1/src/bin/blinky.rs b/examples/stm32l1/src/bin/blinky.rs index bace53d91..58306be94 100644 --- a/examples/stm32l1/src/bin/blinky.rs +++ b/examples/stm32l1/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index fc519b079..78938fe3e 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::flash::Flash; use embassy_stm32::Peripherals; use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello Flash!"); diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 81ccba4e1..05e869e71 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; @@ -11,7 +11,7 @@ use embassy_stm32::time::Hertz; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World, folks!"); diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 7d89bf94a..8ac974c92 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -6,7 +6,8 @@ version = "0.1.0" [features] [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 499ea47dc..93a20d5ea 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::time::Delay; +use embassy_executor::time::Delay; use embassy_stm32::adc::{Adc, Resolution}; use embassy_stm32::pac; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l4/src/bin/blinky.rs b/examples/stm32l4/src/bin/blinky.rs index 54f8e03b2..3d689b5ee 100644 --- a/examples/stm32l4/src/bin/blinky.rs +++ b/examples/stm32l4/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l4/src/bin/button_exti.rs b/examples/stm32l4/src/bin/button_exti.rs index 924feeb33..0832386ed 100644 --- a/examples/stm32l4/src/bin/button_exti.rs +++ b/examples/stm32l4/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l4/src/bin/i2c.rs b/examples/stm32l4/src/bin/i2c.rs index a22b52184..058529ecf 100644 --- a/examples/stm32l4/src/bin/i2c.rs +++ b/examples/stm32l4/src/bin/i2c.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; use embassy_stm32::time::Hertz; @@ -13,7 +13,7 @@ use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) -> ! { let irq = interrupt::take!(I2C2_EV); let mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, irq, NoDma, NoDma, Hertz(100_000)); diff --git a/examples/stm32l4/src/bin/i2c_blocking_async.rs b/examples/stm32l4/src/bin/i2c_blocking_async.rs index 6c4a86703..2dae9c2d5 100644 --- a/examples/stm32l4/src/bin/i2c_blocking_async.rs +++ b/examples/stm32l4/src/bin/i2c_blocking_async.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; use embassy_embedded_hal::adapter::BlockingAsync; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; use embassy_stm32::time::Hertz; @@ -15,7 +15,7 @@ use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) -> ! { let irq = interrupt::take!(I2C2_EV); let i2c = I2c::new(p.I2C2, p.PB10, p.PB11, irq, NoDma, NoDma, Hertz(100_000)); diff --git a/examples/stm32l4/src/bin/i2c_dma.rs b/examples/stm32l4/src/bin/i2c_dma.rs index 48d2e92cf..9e71d404b 100644 --- a/examples/stm32l4/src/bin/i2c_dma.rs +++ b/examples/stm32l4/src/bin/i2c_dma.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::i2c::I2c; use embassy_stm32::time::Hertz; use embassy_stm32::{interrupt, Peripherals}; @@ -12,7 +12,7 @@ use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) -> ! { let irq = interrupt::take!(I2C2_EV); let mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, irq, p.DMA1_CH4, p.DMA1_CH5, Hertz(100_000)); diff --git a/examples/stm32l4/src/bin/rng.rs b/examples/stm32l4/src/bin/rng.rs index 7aaa122ed..ed47fc6c9 100644 --- a/examples/stm32l4/src/bin/rng.rs +++ b/examples/stm32l4/src/bin/rng.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; use embassy_stm32::rng::Rng; use embassy_stm32::{Config, Peripherals}; @@ -22,7 +22,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 20a2ff802..e06b29b81 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -3,8 +3,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; use embassy_embedded_hal::adapter::BlockingAsync; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; @@ -13,7 +13,7 @@ use embassy_stm32::Peripherals; use embedded_hal_async::spi::SpiBus; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l4/src/bin/spi_dma.rs b/examples/stm32l4/src/bin/spi_dma.rs index d0c3609af..e44754bec 100644 --- a/examples/stm32l4/src/bin/spi_dma.rs +++ b/examples/stm32l4/src/bin/spi_dma.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs index 7ae7e9e15..fdd5a85e6 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs @@ -5,14 +5,14 @@ use core::fmt::Write; use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::Peripherals; use heapless::String; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 85eac7b8f..6466994ed 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -6,7 +6,8 @@ version = "0.1.0" [features] [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } diff --git a/examples/stm32l5/src/bin/button_exti.rs b/examples/stm32l5/src/bin/button_exti.rs index c7a6cfa28..99462e597 100644 --- a/examples/stm32l5/src/bin/button_exti.rs +++ b/examples/stm32l5/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l5/src/bin/rng.rs b/examples/stm32l5/src/bin/rng.rs index d3627d2c2..45094374b 100644 --- a/examples/stm32l5/src/bin/rng.rs +++ b/examples/stm32l5/src/bin/rng.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; use embassy_stm32::rng::Rng; use embassy_stm32::{Config, Peripherals}; @@ -21,7 +21,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index d711616ef..9e1df15dd 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -7,10 +7,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Waker; use defmt::*; -use embassy::blocking_mutex::raw::ThreadModeRawMutex; -use embassy::channel::mpmc::Channel; -use embassy::executor::Spawner; -use embassy::util::Forever; +use embassy_executor::executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; use embassy_stm32::rcc::*; @@ -20,6 +17,9 @@ use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config, Peripherals}; use embassy_usb::{Builder, UsbDevice}; use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; +use embassy_util::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_util::channel::mpmc::Channel; +use embassy_util::Forever; use embedded_io::asynch::{Read, Write}; use rand_core::RngCore; use {defmt_rtt as _, panic_probe as _}; @@ -34,12 +34,12 @@ macro_rules! forever { }}; } -#[embassy::task] +#[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { device.run().await } -#[embassy::task] +#[embassy_executor::task] async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { loop { warn!("WAITING for connection"); @@ -68,7 +68,7 @@ async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { } } -#[embassy::task] +#[embassy_executor::task] async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { loop { let pkt = TX_CHANNEL.recv().await; @@ -78,7 +78,7 @@ async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { } } -#[embassy::task] +#[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { stack.run().await } @@ -93,7 +93,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(spawner: Spawner, p: Peripherals) { // Create the driver, from the HAL. let irq = interrupt::take!(USB_FS); diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index d139e6bb1..6aac00881 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -4,8 +4,8 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::rcc::*; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; @@ -27,7 +27,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { // Create the driver, from the HAL. let irq = interrupt::take!(USB_FS); diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 8dab001c6..508bce8a8 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::{panic, *}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::rcc::*; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; @@ -24,7 +24,7 @@ fn config() -> Config { config } -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 22e2e09a1..4ce95be4c 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32u5-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } defmt = "0.3" diff --git a/examples/stm32u5/src/bin/blinky.rs b/examples/stm32u5/src/bin/blinky.rs index 4910e0b98..4f3eabc5e 100644 --- a/examples/stm32u5/src/bin/blinky.rs +++ b/examples/stm32u5/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) -> ! { info!("Hello World!"); diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 812d638fa..dc9107dd0 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32wb-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } defmt = "0.3" diff --git a/examples/stm32wb/src/bin/blinky.rs b/examples/stm32wb/src/bin/blinky.rs index 8ab9b749d..3d8e8391d 100644 --- a/examples/stm32wb/src/bin/blinky.rs +++ b/examples/stm32wb/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32wb/src/bin/button_exti.rs b/examples/stm32wb/src/bin/button_exti.rs index 2ddeb887c..41afaf4d6 100644 --- a/examples/stm32wb/src/bin/button_exti.rs +++ b/examples/stm32wb/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 6be360735..00d63f02d 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-stm32wl-examples" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } diff --git a/examples/stm32wl/src/bin/blinky.rs b/examples/stm32wl/src/bin/blinky.rs index 9393af1c6..e764b4cc3 100644 --- a/examples/stm32wl/src/bin/blinky.rs +++ b/examples/stm32wl/src/bin/blinky.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32wl/src/bin/button_exti.rs b/examples/stm32wl/src/bin/button_exti.rs index 7d5c1b3cb..9f143597d 100644 --- a/examples/stm32wl/src/bin/button_exti.rs +++ b/examples/stm32wl/src/bin/button_exti.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index 6531feae9..46183b8a2 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::{info, unwrap}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::flash::Flash; use embassy_stm32::Peripherals; use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello Flash!"); diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 158c17e18..2db022ea2 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -23,8 +23,8 @@ fn config() -> embassy_stm32::Config { config } -#[embassy::main(config = "config()")] -async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main(config = "config()")] +async fn main(_spawner: embassy_executor::executor::Spawner, p: Peripherals) { unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) } let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High); diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index c5f6e502a..775dfbbfc 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -6,13 +6,13 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy::channel::signal::Signal; use embassy_stm32::dma::NoDma; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::interrupt::{Interrupt, InterruptExt}; use embassy_stm32::subghz::*; use embassy_stm32::{interrupt, Peripherals}; +use embassy_util::channel::signal::Signal; use {defmt_rtt as _, panic_probe as _}; const PING_DATA: &str = "PING"; @@ -57,8 +57,8 @@ fn config() -> embassy_stm32::Config { config } -#[embassy::main(config = "config()")] -async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { +#[embassy_executor::main(config = "config()")] +async fn main(_spawner: embassy_executor::executor::Spawner, p: Peripherals) { let mut led1 = Output::new(p.PB15, Level::High, Speed::Low); let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low); let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low); diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 347252fa3..4e26f023c 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -7,7 +7,8 @@ version = "0.1.0" crate-type = ["cdylib"] [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "wasm", "nightly"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["log"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "wasm", "nightly"] } wasm-logger = "0.2.0" wasm-bindgen = "0.2" diff --git a/examples/wasm/src/lib.rs b/examples/wasm/src/lib.rs index 61757ebd7..2e961e65a 100644 --- a/examples/wasm/src/lib.rs +++ b/examples/wasm/src/lib.rs @@ -1,10 +1,10 @@ #![feature(type_alias_impl_trait)] #![allow(incomplete_features)] -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Timer}; -#[embassy::task] +#[embassy_executor::task] async fn ticker() { let window = web_sys::window().expect("no global `window` exists"); @@ -24,7 +24,7 @@ async fn ticker() { } } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner) { wasm_logger::init(wasm_logger::Config::default()); spawner.spawn(ticker()).unwrap(); diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index fe791d0d7..d19243b9d 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -4,7 +4,8 @@ name = "embassy-rp-tests" version = "0.1.0" [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } defmt = "0.3.0" diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index 0be9d9f24..6f6baf77a 100644 --- a/tests/rp/src/bin/gpio.rs +++ b/tests/rp/src/bin/gpio.rs @@ -3,12 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::{assert, *}; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_rp::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull}; use embassy_rp::Peripherals; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/tests/rp/src/bin/gpio_async.rs b/tests/rp/src/bin/gpio_async.rs index 46df4105c..1098682af 100644 --- a/tests/rp/src/bin/gpio_async.rs +++ b/tests/rp/src/bin/gpio_async.rs @@ -3,14 +3,14 @@ #![feature(type_alias_impl_trait)] use defmt::{assert, *}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Instant, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Instant, Timer}; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::Peripherals; use futures::future::join; use {defmt_rtt as _, panic_probe as _}; -#[embassy::main] +#[embassy_executor::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("embassy-rp gpio_async test"); diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index df8d64449..c1cca99d1 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -13,7 +13,8 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "time-tick-32768hz"] } +embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "time-tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } defmt = "0.3.0" diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index 37cfe7cf4..8eab731bf 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs @@ -5,12 +5,12 @@ #[path = "../example_common.rs"] mod example_common; use defmt::assert; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull, Speed}; use embassy_stm32::Peripherals; use example_common::*; -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 049ba1e9f..6d38b0bbf 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -5,14 +5,14 @@ #[path = "../example_common.rs"] mod example_common; use defmt::assert_eq; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{self, Spi}; use embassy_stm32::time::Hertz; use embassy_stm32::Peripherals; use example_common::*; -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 64337886b..8147c5f08 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -5,13 +5,13 @@ #[path = "../example_common.rs"] mod example_common; use defmt::assert_eq; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::spi::{self, Spi}; use embassy_stm32::time::Hertz; use embassy_stm32::Peripherals; use example_common::*; -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/tests/stm32/src/bin/timer.rs b/tests/stm32/src/bin/timer.rs index 002a8a4d3..76b07ca15 100644 --- a/tests/stm32/src/bin/timer.rs +++ b/tests/stm32/src/bin/timer.rs @@ -5,12 +5,12 @@ #[path = "../example_common.rs"] mod example_common; use defmt::assert; -use embassy::executor::Spawner; -use embassy::time::{Duration, Instant, Timer}; +use embassy_executor::executor::Spawner; +use embassy_executor::time::{Duration, Instant, Timer}; use embassy_stm32::Peripherals; use example_common::*; -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, _p: Peripherals) { info!("Hello World!"); diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index c3468290e..7b60e4b28 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -5,13 +5,13 @@ #[path = "../example_common.rs"] mod example_common; use defmt::assert_eq; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::Peripherals; use example_common::*; -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index 9946f4a56..323c41cae 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -5,12 +5,12 @@ #[path = "../example_common.rs"] mod example_common; use defmt::assert_eq; -use embassy::executor::Spawner; +use embassy_executor::executor::Spawner; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::Peripherals; use example_common::*; -#[embassy::main(config = "config()")] +#[embassy_executor::main(config = "config()")] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); -- cgit