Skip to content
This repository has been archived by the owner on Jun 19, 2024. It is now read-only.

Async runtime #5

Merged
merged 51 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
61a3900
add: async runtime implementation
Gavin-Niederman Oct 3, 2023
4d8ddf4
fix: switch to concurrent_queue queues
Gavin-Niederman Oct 3, 2023
c3711d6
fix: add new function to Executor
Gavin-Niederman Oct 3, 2023
5677729
fix: switch to unsafecell instad of mutex
Gavin-Niederman Oct 5, 2023
d730f1a
update todo list
Gavin-Niederman Oct 5, 2023
d475594
start implementing final executor design
Gavin-Niederman Oct 6, 2023
1113e4c
fix: switch to storing futures in raw pointers
Gavin-Niederman Oct 6, 2023
b622dad
fix: task internal now can poll futures
Gavin-Niederman Oct 7, 2023
7b4be88
feat: executor tick and run functions
Gavin-Niederman Oct 7, 2023
840a144
feat: block_on function
Gavin-Niederman Oct 7, 2023
7503003
add: ready macro
Gavin-Niederman Oct 7, 2023
c33ddc9
feat: task_local! macro
Gavin-Niederman Oct 9, 2023
ac725ae
feat: new executor
Gavin-Niederman Oct 9, 2023
40b5f95
fix: add block_on function
Gavin-Niederman Oct 9, 2023
9b8d938
fix: block on requires future to be static
Gavin-Niederman Oct 9, 2023
a3a5ddb
Merge branch 'main' into async_runtime
doinkythederp Oct 9, 2023
d4e94ab
feat: make Robot trait functions async
Gavin-Niederman Oct 9, 2023
4affa3d
add: simple motor stopped future
Gavin-Niederman Oct 9, 2023
430ee75
chore: clippy
Gavin-Niederman Oct 9, 2023
9450fb6
add: wakers and async sleep function
Gavin-Niederman Oct 10, 2023
bff860e
fix: initialize main thread tls
Gavin-Niederman Oct 12, 2023
fa7a840
fix: call init main
Gavin-Niederman Oct 12, 2023
af43133
fix: change task local storage method
Gavin-Niederman Oct 13, 2023
b2fe5bd
add: reactor
Gavin-Niederman Oct 13, 2023
fdccd4c
fix: drive all futures to completion
Gavin-Niederman Oct 14, 2023
e5ec7a6
feat: future join handles
Gavin-Niederman Oct 14, 2023
6c85e72
docs: document async executor functions
Gavin-Niederman Oct 17, 2023
3635741
fix: remove unused marker field
Gavin-Niederman Oct 17, 2023
8d9b65a
docs(async_runtime): specify the benefits of spawn()
doinkythederp Oct 17, 2023
39c0fa4
fix: correct slightly incorrect docs on spawn
Gavin-Niederman Oct 17, 2023
3b73a5e
feat: support panic messages in simulator
doinkythederp Oct 17, 2023
6fd1957
fix: panic messages
doinkythederp Oct 17, 2023
b686f34
add: basic example
Gavin-Niederman Oct 18, 2023
6ca9686
feat: complete_timeout
Gavin-Niederman Oct 18, 2023
046da3c
refactor: rename task_local to os_task_local
Gavin-Niederman Oct 18, 2023
6b8ed48
refactor(async): simplify runtime using async-task
max-niederman Oct 21, 2023
2ab4ce2
fix(async): only poll task when woken
max-niederman Oct 21, 2023
ef4c69c
fix: use CString directly
doinkythederp Oct 21, 2023
e4a1c01
fix: remove std dependency from task init code
max-niederman Oct 21, 2023
7b1a86c
Merge branch 'async_runtime' into async-simplified
max-niederman Oct 21, 2023
16c290e
Merge pull request #6 from max-niederman/async-simplified
Gavin-Niederman Oct 21, 2023
e589833
Merge branch 'main' into async_runtime
Gavin-Niederman Oct 21, 2023
54ee3d2
feat: seperate async and sync robot traits
Gavin-Niederman Oct 22, 2023
72348f1
fix: get rid lockup in task locals
Gavin-Niederman Dec 9, 2023
5698cdf
refactor: change task module structure
Gavin-Niederman Dec 9, 2023
d556ce8
Merge branch 'main' into async_runtime
Gavin-Niederman Dec 11, 2023
731533b
update todo list
Gavin-Niederman Dec 11, 2023
5dd3021
fix: allow async task spawning in an async task
Gavin-Niederman Dec 12, 2023
f51fbdd
fix(examples): sleeps in accessories and typo in basic
Gavin-Niederman Dec 22, 2023
5b34c62
add: async_runtimecomplete_all function
Gavin-Niederman Dec 22, 2023
591fa84
chore: formatting
Gavin-Niederman Dec 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ This is the todo list for the eventual 1.0.0 release of pros-rs
* [ ] Motors
* [x] Internal gearsets
* [ ] (Custom) Gear Ratios
* [ ] Async Runtime (ditch tasks)
* [X] Make Robot Functions Take Self
* [X] PID controllers
* [ ] Feedforward loops
Expand All @@ -32,6 +31,12 @@ This is the todo list for the eventual 1.0.0 release of pros-rs
* [X] Controller data
* [x] Controller printing
* [X] Link
* [X] Async runtime
* [X] Returning top level futures
* [X] Reactor
* [ ] More asynchronous APIs
* [ ] MPSC
* [X] Task Locals

## API

Expand Down
43 changes: 42 additions & 1 deletion pros-sys/src/rtos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub type task_t = *const core::ffi::c_void;
pub type task_fn_t = Option<unsafe extern "C" fn(arg1: *mut ::core::ffi::c_void)>;
pub type mutex_t = *const core::ffi::c_void;

const CURRENT_TASK: task_t = 0 as task_t;
const CURRENT_TASK: task_t = core::ptr::null();

extern "C" {
/** Gets the number of milliseconds since PROS initialized.
Expand Down Expand Up @@ -280,4 +280,45 @@ extern "C" {
\param mutex
Mutex to unlock.*/
pub fn mutex_delete(mutex: mutex_t);

/** Sets a value in a task's thread local storage array.

This function is intended for advanced users only.

Parameters:
xTaskToSet The handle of the task to which the thread local data is being written. A task can write to its own thread local data by using NULL as the parameter value.
xIndex The index into the thread local storage array to which data is being written.

The number of available array indexes is set by the configNUM_THREAD_LOCAL_STORAGE_POINTERS compile time configuration constant in FreeRTOSConfig.h.
pvValue The value to write into the index specified by the xIndex parameter.

Example usage:

See the examples provided on the thread local storage array documentation page. */
pub fn vTaskSetThreadLocalStoragePointer(
xTaskToSet: task_t,
xIndex: i32,
pvValue: *const core::ffi::c_void,
);

/** Retrieves a value from a task's thread local storage array.

This function is intended for advanced users only.

Parameters:
xTaskToQuery The handle of the task from which the thread local data is being read. A task can read its own thread local data by using NULL as the parameter value.
xIndex The index into the thread local storage array from which data is being read.

The number of available array indexes is set by the configNUM_THREAD_LOCAL_STORAGE_POINTERS compile time configuration constant in FreeRTOSConfig.h.

Returns:
The values stored in index position xIndex of the thread local storage array of task xTaskToQuery.

Example usage:

See the examples provided on the thread local storage array documentation page. */
pub fn pvTaskGetThreadLocalStoragePointer(
xTaskToQuery: task_t,
xIndex: i32,
) -> *const core::ffi::c_void;
}
6 changes: 6 additions & 0 deletions pros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ snafu = { version = "0.7.5", default-features = false, features = [
"rust_1_61",
] }
no_std_io = { version = "0.6.0", features = ["alloc"] }
futures = { version = "0.3.28", default-features = false, features = ["alloc"] }
slab = { version = "0.4.9", default-features = false }
hashbrown = { version = "0.14.1", default-features = true }
async-trait = "0.1.73"
async-task = { version = "4.5.0", default-features = false }
waker-fn = "1.1.1"

[target.'cfg(target_arch = "wasm32")'.dependencies]
dlmalloc = { version = "0.2.4", features = ["global"] }
17 changes: 14 additions & 3 deletions pros/examples/accessories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@ use pros::prelude::*;

#[derive(Debug, Default)]
struct ExampleRobot;
impl Robot for ExampleRobot {
fn opcontrol(&mut self) -> pros::Result {
#[async_trait]
doinkythederp marked this conversation as resolved.
Show resolved Hide resolved
impl AsyncRobot for ExampleRobot {
async fn opcontrol(&mut self) -> pros::Result {
let handle = pros::async_runtime::spawn(async {
for _ in 0..5 {
println!("Hello from async!");
sleep(Duration::from_millis(1000));
Gavin-Niederman marked this conversation as resolved.
Show resolved Hide resolved
}
});

pros::async_runtime::block_on(handle);

// Create a new motor plugged into port 2. The motor will brake when not moving.
let motor = Motor::new(2, BrakeMode::Brake)?;
motor.wait_until_stopped().await?;
// Create a controller, specifically controller 1.
let controller = Controller::Master;

Expand Down Expand Up @@ -43,7 +54,7 @@ impl Robot for ExampleRobot {
}
}
}
robot!(ExampleRobot);
async_robot!(ExampleRobot);

fn left_button_callback() {
println!("Left button pressed!");
Expand Down
16 changes: 16 additions & 0 deletions pros/examples/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![no_std]
#![no_main]

use pros::prelude::*;

#[derive(Default)]
pub struct Robot;
#[async_trait]
impl AsyncRobot for Robot {
async fn opcontrol(&mut self) -> pros::Result {
println!("basic exasmple");
Gavin-Niederman marked this conversation as resolved.
Show resolved Hide resolved

Ok(())
}
}
async_robot!(Robot);
87 changes: 87 additions & 0 deletions pros/src/async_runtime/executor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use core::{
cell::RefCell,
future::Future,
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll},
};

use alloc::{collections::VecDeque, sync::Arc};
use async_task::{Runnable, Task};
use waker_fn::waker_fn;

use crate::os_task_local;

use super::reactor::Reactor;

os_task_local! {
pub(crate) static EXECUTOR: Executor = Executor::new();
}

pub(crate) struct Executor {
queue: RefCell<VecDeque<Runnable>>,
pub(crate) reactor: RefCell<Reactor>,
}

impl !Send for Executor {}
impl !Sync for Executor {}

impl Executor {
pub fn new() -> Self {
Self {
queue: RefCell::new(VecDeque::new()),
reactor: RefCell::new(Reactor::new()),
}
}

pub fn spawn<T>(&'static self, future: impl Future<Output = T> + 'static) -> Task<T> {
// SAFETY: `runnable` will never be moved off this thread or shared with another thread because of the `!Send + !Sync` bounds on `Self`.
// Both `future` and `schedule` are `'static` so they cannot be used after being freed.
// TODO: Make sure that the waker can never be sent off the thread.
Gavin-Niederman marked this conversation as resolved.
Show resolved Hide resolved
let (runnable, task) = unsafe {
async_task::spawn_unchecked(future, |runnable| {
self.queue.borrow_mut().push_back(runnable)
})
};

runnable.schedule();

task
}

pub(crate) fn tick(&self) -> bool {
self.reactor.borrow_mut().tick();

let runnable = {
let mut queue = self.queue.borrow_mut();
queue.pop_front()
};
match runnable {
Some(runnable) => {
runnable.run();
true
}
None => false,
}
}

pub fn block_on<R>(&self, mut task: Task<R>) -> R {
let woken = Arc::new(AtomicBool::new(true));

let waker = waker_fn({
let woken = woken.clone();
move || woken.store(true, Ordering::Relaxed)
});
let mut cx = Context::from_waker(&waker);

loop {
if woken.swap(false, Ordering::Relaxed) {
if let Poll::Ready(output) = Pin::new(&mut task).poll(&mut cx) {
return output;
}
}

self.tick();
}
}
}
19 changes: 19 additions & 0 deletions pros/src/async_runtime/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use core::future::Future;

use async_task::Task;

pub(crate) mod executor;
pub(crate) mod reactor;

/// Runs a future in the background without having to await it
/// To get the the return value you can call [`JoinHandle.join`](JoinHandle::join).
pub fn spawn<T>(future: impl Future<Output = T> + 'static) -> Task<T> {
executor::EXECUTOR.with(|e| e.spawn(future))
}

/// Blocks the current task untill a return value can be extracted from the provided future.
/// Does not poll all futures to completion.
/// If you want to complete all futures, use the [`complete`] function.
pub fn block_on<F: Future + 'static>(future: F) -> F::Output {
Gavin-Niederman marked this conversation as resolved.
Show resolved Hide resolved
executor::EXECUTOR.with(|e| e.block_on(spawn(future)))
}
37 changes: 37 additions & 0 deletions pros/src/async_runtime/reactor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use core::task::Waker;

use alloc::collections::BTreeMap;

pub struct Sleepers {
sleepers: BTreeMap<u32, Waker>,
}

impl Sleepers {
pub fn push(&mut self, waker: Waker, target: u32) {
self.sleepers.insert(target, waker);
}

pub fn pop(&mut self) -> Option<Waker> {
self.sleepers.pop_first().map(|(_, waker)| waker)
}
}

pub struct Reactor {
pub(crate) sleepers: Sleepers,
}

impl Reactor {
pub fn new() -> Self {
Self {
sleepers: Sleepers {
sleepers: BTreeMap::new(),
},
}
}

pub fn tick(&mut self) {
if let Some(sleeper) = self.sleepers.pop() {
sleeper.wake()
}
}
}
Loading
Loading