diff --git a/.gitignore b/.gitignore index da66475f..ae858ab7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -Cargo.lock -layout.ld -platform -target +/Cargo.lock +/layout.ld +/platform +/target diff --git a/.travis.yml b/.travis.yml index 8efe4b77..94ba1ed7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ branches: language: rust rust: - - nightly-2020-01-16 + - nightly-2020-04-06 os: - linux diff --git a/CHANGELOG.md b/CHANGELOG.md index 7af66945..c0369268 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,10 @@ - To retrieve the value of an asynchronous `value`, use `value.await` - This is only possible within an `async fn`, so either - Make the caller `fn` of `.await` an `async fn` - - Not recommended: Use `core::executor::block_on(value)` to retrieve the `value` + - Not recommended: Use `libtock::executor::block_on(value)` to retrieve the `value` - Most API functions, including `main()`, return a `Result` - All drivers can exclusively be retrieved by `retrieve_drivers` which returns a `Drivers` singleton. Drivers can be shared between different tasks only if it is safe to do so. +- The low-level functions have been moved to a new crate called `libtock-core`. This crate is intended to be less experimental and more stable. ### Changed APIs @@ -42,6 +43,8 @@ - Targets without support for atomics can be built - The `TockAllocator` is no longer included by default and needs to to be opted-in via `--features=alloc` - `hardware_test.rs` is now called `libtock_test.rs` to make clear that the intent is to test the correctness of `libtock-rs`, not the hardware or the kernel +- The panic handler can now be customized using the `custom_panic_handler` feature +- The error alloc handler can now be customized using the `custom_alloc_error_handler` feature ## a8bb4fa9be504517d5533511fd8e607ea61f1750 (0.1.0) diff --git a/Cargo.toml b/Cargo.toml index eb56daae..0f3a7048 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ custom_alloc_error_handler = ["libtock-core/custom_alloc_error_handler"] __internal_disable_gpio_in_integration_test = [] [dependencies] -core = { package = "async-support", path = "async-support" } libtock-core = { path = "core" } libtock_codegen = { path = "codegen" } futures = { version = "0.3.1", default-features = false, features = ["unstable", "cfg-target-has-atomic"] } @@ -58,7 +57,6 @@ lto = true [workspace] members = [ - "async-support", "codegen", "core", "test-runner" diff --git a/async-support/Cargo.toml b/async-support/Cargo.toml deleted file mode 100644 index 9287c6bf..00000000 --- a/async-support/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "async-support" -version = "0.1.0" -authors = ["Woyten "] -edition = "2018" -description = "`libtock` specific patch of `core` for `async`-`await` syntax in a `#![no_std]` environment. It implements the methods `core::future::poll_with_tls_context` and `core::future::from_generator` which are currently missing in `core`." - -[dependencies] diff --git a/async-support/src/lib.rs b/async-support/src/lib.rs deleted file mode 100644 index 05946b41..00000000 --- a/async-support/src/lib.rs +++ /dev/null @@ -1,109 +0,0 @@ -#![feature(generator_trait)] -#![no_std] - -pub use core::*; - -pub mod future { - pub use core::future::Future; - use core::ops::Generator; - use core::pin::Pin; - use core::task::Poll; - - pub fn poll_with_tls_context(f: Pin<&mut F>) -> Poll - where - F: Future, - { - crate::executor::poll(f) - } - - pub fn from_generator>( - generator: G, - ) -> impl Future { - crate::executor::from_generator(generator) - } -} - -pub mod executor { - use core::future::Future; - use core::ops::Generator; - use core::ops::GeneratorState; - use core::pin::Pin; - use core::ptr; - use core::task::Context; - use core::task::Poll; - use core::task::RawWaker; - use core::task::RawWakerVTable; - use core::task::Waker; - - extern "Rust" { - #[link_name = "libtock::syscalls::raw::yieldk"] - fn yieldk(); - } - - /// # Safety - /// - /// [[block_on]] yields whenever a future cannot make any progress at present. Yielding is considered unsafe. - pub unsafe fn block_on(mut future: impl Future) -> T { - // Contract described in the Rustdoc: "A value, once pinned, must remain pinned forever (...).". - // IOW calling Pin::new_unchecked is safe as long as no &mut future is leaked after pinning. - let mut pinned_future = Pin::new_unchecked(&mut future); - - loop { - match poll(pinned_future.as_mut()) { - Poll::Pending => yieldk(), - Poll::Ready(value) => { - return value; - } - } - } - } - - pub(crate) fn poll(pinned_future: Pin<&mut F>) -> Poll { - let waker = unsafe { Waker::from_raw(get_dummy_waker()) }; - let mut context = Context::from_waker(&waker); - pinned_future.poll(&mut context) - } - - // Since Tock OS comes with waking-up functionality built-in, we use dummy wakers that do nothing at all. - fn get_dummy_waker() -> RawWaker { - fn clone(_x: *const ()) -> RawWaker { - get_dummy_waker() - } - - fn do_nothing(_x: *const ()) {} - - // This vtable implements the methods required for managing the lifecycle of the wakers. - // Our wakers are dummies, so those functions don't do anything. - static DUMMY_WAKER_VTABLE: RawWakerVTable = - RawWakerVTable::new(clone, do_nothing, do_nothing, do_nothing); - - // The wakers don't have any implementation, so the instance can simply be null. - RawWaker::new(ptr::null(), &DUMMY_WAKER_VTABLE) - } - - pub(crate) fn from_generator>( - generator: G, - ) -> impl Future { - GeneratorFuture { generator } - } - - struct GeneratorFuture { - generator: G, - } - - impl> Future for GeneratorFuture { - type Output = G::Return; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - // Pin::map_unchecked_mut is safe as long as the move and drop guarantees are propagated through the mapping. - // This is trivially satisfied since our future is only a newtype decorator of the generator. - let pinned_generator = - unsafe { self.map_unchecked_mut(|future| &mut future.generator) }; - - match pinned_generator.resume() { - GeneratorState::Yielded(()) => Poll::Pending, - GeneratorState::Complete(out) => Poll::Ready(out), - } - } - } -} diff --git a/build.rs b/build.rs index 6d7920ec..b24d3d2e 100644 --- a/build.rs +++ b/build.rs @@ -6,21 +6,25 @@ use std::io::BufReader; use std::path::Path; use std::process; +static LAYOUT_FILE_NAME: &str = "layout.ld"; + fn main() { - static ENV_VAR: &str = "PLATFORM"; - static FILE_NAME: &str = "platform"; + static PLATFORM_ENV_VAR: &str = "PLATFORM"; + static PLATFORM_FILE_NAME: &str = "platform"; static APP_HEAP_SIZE: &str = "APP_HEAP_SIZE"; static KERNEL_HEAP_SIZE: &str = "KERNEL_HEAP_SIZE"; - println!("cargo:rerun-if-env-changed={}", ENV_VAR); + println!("cargo:rerun-if-env-changed={}", PLATFORM_ENV_VAR); println!("cargo:rerun-if-env-changed={}", APP_HEAP_SIZE); println!("cargo:rerun-if-env-changed={}", KERNEL_HEAP_SIZE); - println!("cargo:rerun-if-changed={}", FILE_NAME); + println!("cargo:rerun-if-changed={}", PLATFORM_FILE_NAME); + println!("cargo:rerun-if-changed={}", LAYOUT_FILE_NAME); - let platform_name = read_env_var(ENV_VAR).or_else(|| read_board_name_from_file(FILE_NAME)); + let platform_name = + read_env_var(PLATFORM_ENV_VAR).or_else(|| read_board_name_from_file(PLATFORM_FILE_NAME)); if let Some(platform_name) = platform_name { - println!("cargo:rustc-env={}={}", ENV_VAR, platform_name); - copy_linker_file(&platform_name.trim()); + println!("cargo:rustc-env={}={}", PLATFORM_ENV_VAR, platform_name); + copy_linker_file(platform_name.trim()); } else { println!( "cargo:warning=No platform specified. \ @@ -66,5 +70,5 @@ fn copy_linker_file(platform_name: &str) { println!("Cannot find layout file {:?}", path); process::exit(1); } - fs::copy(linker_file_name, "layout.ld").unwrap(); + fs::copy(linker_file_name, LAYOUT_FILE_NAME).unwrap(); } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index ccb0bfbc..e13fc97a 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -98,7 +98,7 @@ fn try_generate_main_wrapped( MAIN_INVOKED = true; } let _block = async #block; - unsafe {::core::executor::block_on(_block) } + unsafe { ::libtock::executor::block_on(_block) } } )) } @@ -126,7 +126,7 @@ mod tests { let _block = async { method_call().await; }; - unsafe { ::core::executor::block_on(_block) } + unsafe { ::libtock::executor::block_on(_block) } } )) .unwrap(); diff --git a/core/Cargo.toml b/core/Cargo.toml index 31103467..94f3c8f4 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -10,5 +10,5 @@ custom_panic_handler = [] custom_alloc_error_handler = [] [dependencies] -linked_list_allocator = { optional = true, version = "=0.6.5", default-features = false } +linked_list_allocator = { optional = true, version = "=0.8.1", default-features = false } libtock_codegen = { path = "../codegen" } diff --git a/core/src/entry_point/start_item_arm.rs b/core/src/entry_point/start_item_arm.rs index c3bfab7c..c9390f61 100644 --- a/core/src/entry_point/start_item_arm.rs +++ b/core/src/entry_point/start_item_arm.rs @@ -105,7 +105,7 @@ pub unsafe extern "C" fn _start( bl rust_start" : // No output operands : "{r0}"(app_start), "{r1}"(mem_start), "{r3}"(app_heap_break) // Input operands - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r12", + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r8", "r12", "cc", "memory" // Clobbers : "volatile" // Options ); diff --git a/core/src/syscalls/mod.rs b/core/src/syscalls/mod.rs index 833c356a..bae938b1 100644 --- a/core/src/syscalls/mod.rs +++ b/core/src/syscalls/mod.rs @@ -10,23 +10,7 @@ use crate::result::SubscribeError; use crate::shared_memory::SharedMemory; pub mod raw { - use super::platform; - - pub use platform::*; - - /// # Safety - /// - /// Yielding in the main function should be safe. Nevertheless, yielding manually is not required as this is already achieved by the `async` runtime. - /// - /// When yielding in callbacks, two problems can arise: - /// - The guarantees of `FnMut` are violated. In this case, make sure your callback has `Fn` behavior. - /// - Callbacks can get executed in a nested manner and overflow the stack quickly. - /// - /// This function is exported as `libtock::syscalls::raw::yieldk`. Do not reference this name directly. Its only purpose is to establish a back-channel from `async-support`, a patched version of `core` to `libtock-rs` via linking. This workaround has been chosen to keep the `core` crate free of dependencies on platform-specific syscall implementations and is expected to get removed as soon as possible. - #[export_name = "libtock::syscalls::raw::yieldk"] - pub unsafe fn yieldk() { - platform::yieldk() - } + pub use super::platform::*; } pub fn subscribe, T>( diff --git a/core/src/syscalls/platform.rs b/core/src/syscalls/platform.rs index 66e79f0a..3d039d51 100644 --- a/core/src/syscalls/platform.rs +++ b/core/src/syscalls/platform.rs @@ -3,9 +3,15 @@ use core::cell::RefCell; use std::vec::Vec; /// yield for a callback fired by the kernel +/// /// # Safety -/// Yielding inside a callback conflicts with Rust's safety guarantees. For example, -/// a FnMut closure could be triggered multiple times making a &mut a shared reference. +/// +/// Yielding in the main function should be safe. Nevertheless, yielding manually +/// is not required as this is already achieved by the `async` runtime. +/// +/// When yielding in callbacks, two problems can arise: +/// - The guarantees of `FnMut` are violated. In this case, make sure your callback has `Fn` behavior. +/// - Callbacks can get executed in a nested manner and overflow the stack quickly. pub unsafe fn yieldk() { EVENTS.with(|e| e.borrow_mut().push(Event::YieldK)); } diff --git a/rust-toolchain b/rust-toolchain index 20d9cdab..8bd4ff0a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2020-01-16 +nightly-2020-04-06 diff --git a/src/console.rs b/src/console.rs index ee6f50a9..a94fc13a 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,9 +1,9 @@ use crate::callback::Identity0Consumer; +use crate::executor; use crate::futures; use crate::result::TockResult; use crate::syscalls; use core::cell::Cell; -use core::executor; use core::fmt; use core::mem; diff --git a/src/drivers.rs b/src/drivers.rs index 8d8e8ab2..185f6ea2 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -5,7 +5,6 @@ use crate::gpio::GpioDriverFactory; use crate::leds::LedsDriverFactory; use crate::result::OtherError; use crate::result::TockError; -use crate::result::TockResult; use crate::rng::RngDriver; use crate::sensors::ninedof::NinedofDriver; use crate::sensors::AmbientLightSensor; @@ -37,12 +36,12 @@ pub struct Drivers { } /// Retrieve [Drivers] struct. Returns struct only once. -pub fn retrieve_drivers() -> TockResult { +pub fn retrieve_drivers() -> Result { static mut DRIVER_TAKEN: bool = false; unsafe { if DRIVER_TAKEN { - Err(TockError::Other(OtherError::DriverAlreadyTaken)) + Err(DriversAlreadyTakenError) } else { DRIVER_TAKEN = true; Ok(retrieve_drivers_unsafe()) @@ -79,3 +78,11 @@ const DRIVERS: Drivers = Drivers { humidity_sensor: HumiditySensor, ninedof: NinedofDriver, }; + +pub struct DriversAlreadyTakenError; + +impl From for TockError { + fn from(_: DriversAlreadyTakenError) -> Self { + TockError::Other(OtherError::DriversAlreadyTaken) + } +} diff --git a/src/executor.rs b/src/executor.rs new file mode 100644 index 00000000..9204616a --- /dev/null +++ b/src/executor.rs @@ -0,0 +1,50 @@ +use crate::syscalls; +use core::pin::Pin; +use core::ptr; +use core::task::Context; +use core::task::Poll; +use core::task::RawWaker; +use core::task::RawWakerVTable; +use core::task::Waker; +use futures::Future; + +/// # Safety +/// +/// [[block_on]] yields whenever a future cannot make any progress at present. Yielding is considered unsafe. +pub unsafe fn block_on(mut future: impl Future) -> T { + // Contract described in the Rustdoc: "A value, once pinned, must remain pinned forever (...).". + // IOW calling Pin::new_unchecked is safe as long as no &mut future is leaked after pinning. + let mut pinned_future = Pin::new_unchecked(&mut future); + + loop { + match poll(pinned_future.as_mut()) { + Poll::Pending => syscalls::raw::yieldk(), + Poll::Ready(value) => { + return value; + } + } + } +} + +fn poll(pinned_future: Pin<&mut F>) -> Poll { + let waker = unsafe { Waker::from_raw(get_dummy_waker()) }; + let mut context = Context::from_waker(&waker); + pinned_future.poll(&mut context) +} + +// Since Tock OS comes with waking-up functionality built-in, we use dummy wakers that do nothing at all. +fn get_dummy_waker() -> RawWaker { + fn clone(_x: *const ()) -> RawWaker { + get_dummy_waker() + } + + fn do_nothing(_x: *const ()) {} + + // This vtable implements the methods required for managing the lifecycle of the wakers. + // Our wakers are dummies, so those functions don't do anything. + static DUMMY_WAKER_VTABLE: RawWakerVTable = + RawWakerVTable::new(clone, do_nothing, do_nothing, do_nothing); + + // The wakers don't have any implementation, so the instance can simply be null. + RawWaker::new(ptr::null(), &DUMMY_WAKER_VTABLE) +} diff --git a/src/lib.rs b/src/lib.rs index 0aabb1ad..152e223e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(any(target_arch = "arm", target_arch = "riscv32"), no_std)] +#![cfg_attr(not(test), no_std)] pub mod adc; pub mod ble_composer; @@ -8,6 +8,7 @@ pub mod console; pub mod debug; pub mod drivers; pub mod electronics; +pub mod executor; pub mod futures; pub mod gpio; pub mod leds; diff --git a/src/result.rs b/src/result.rs index 74b4786a..84af6bfd 100644 --- a/src/result.rs +++ b/src/result.rs @@ -50,8 +50,8 @@ pub enum OtherError { GpioDriverInvalidState, TimerDriverDurationOutOfRange, TimerDriverErroneousClockFrequency, - DriverAlreadyTaken, - OutOfRangeError, + DriversAlreadyTaken, + OutOfRange, } impl From for TockError { @@ -63,7 +63,7 @@ impl From for TockError { pub struct OutOfRangeError; impl From for TockError { - fn from(_other: OutOfRangeError) -> Self { - TockError::Other(OtherError::OutOfRangeError) + fn from(_: OutOfRangeError) -> Self { + TockError::Other(OtherError::OutOfRange) } } diff --git a/src/sensors/mod.rs b/src/sensors/mod.rs index c4b962f1..f3a0e7a8 100644 --- a/src/sensors/mod.rs +++ b/src/sensors/mod.rs @@ -1,9 +1,9 @@ +use crate::executor; use crate::futures; use crate::result::TockResult; use crate::syscalls; use core::cell::Cell; use core::convert::From; -use core::executor; use core::fmt; use core::mem; diff --git a/src/sensors/ninedof.rs b/src/sensors/ninedof.rs index e4686900..b5e7191a 100644 --- a/src/sensors/ninedof.rs +++ b/src/sensors/ninedof.rs @@ -1,8 +1,8 @@ +use crate::executor; use crate::futures; use crate::result::TockResult; use crate::syscalls; use core::cell::Cell; -use core::executor; use core::fmt; use core::mem;