Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: basic rtos task api #15

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub mod httpd;
#[cfg(feature = "alloc")]
// TODO: Ideally should not need "alloc" (also for performance reasons)
pub mod log;
pub mod misc;
#[cfg(esp_idf_config_lwip_ipv4_napt)]
pub mod napt;
pub mod netif;
Expand All @@ -43,6 +44,9 @@ pub mod nvs_storage;
pub mod ota;
pub mod ping;
pub mod sysloop;
#[cfg(feature = "alloc")]
pub mod task;
pub mod time;
#[cfg(feature = "alloc")] // TODO: Expose a subset which does not require "alloc"
pub mod wifi;

Expand Down
11 changes: 11 additions & 0 deletions src/misc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use esp_idf_sys::*;

pub fn get_default_efuse_mac() -> Result<[u8; 6], EspError> {
let mut mac = [0; 6];
unsafe { esp!(esp_efuse_mac_get_default(mac.as_mut_ptr()))? }
Ok(mac)
}

pub fn restart() {
unsafe { esp_restart() };
}
1 change: 1 addition & 0 deletions src/private/cstr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern crate alloc;

#[cfg(feature = "alloc")]
pub fn set_str(buf: &mut [u8], s: &str) {
assert!(s.len() < buf.len());
let cs = CString::new(s).unwrap();
let ss: &[u8] = cs.as_bytes_with_nul();
buf[..ss.len()].copy_from_slice(ss);
Expand Down
1 change: 1 addition & 0 deletions src/private/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod common;
pub mod cstr;
pub mod net;
pub mod wait;

mod stubs;

Expand Down
97 changes: 97 additions & 0 deletions src/private/wait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use core::time::Duration;
#[cfg(feature = "std")]
use std::sync::{Condvar, Mutex};

use embedded_svc::mutex::Mutex as _;
#[cfg(not(feature = "std"))]
use esp_idf_sys::*;

#[cfg(not(feature = "std"))]
use super::time::micros_since_boot;

pub struct Waiter {
#[cfg(feature = "std")]
cvar: Condvar,
#[cfg(feature = "std")]
running: Mutex<bool>,
#[cfg(not(feature = "std"))]
running: EspMutex<bool>,
}

impl Waiter {
pub fn new() -> Self {
Waiter {
#[cfg(feature = "std")]
cvar: Condvar::new(),
#[cfg(feature = "std")]
running: Mutex::new(false),
#[cfg(not(feature = "std"))]
running: EspMutex::new(false),
}
}

pub fn start(&self) {
self.running.with_lock(|running| *running = true);
}

#[cfg(feature = "std")]
pub fn wait(&self) {
if !self.running.with_lock(|running| *running) {
return;
}

let _running = self
.cvar
.wait_while(self.running.lock().unwrap(), |running| *running)
.unwrap();
}

#[cfg(not(feature = "std"))]
pub fn wait(&self) {
while self.running.with_lock(|running| *running) {
unsafe { vTaskDelay(500) };
}
}

/// return = !timeout (= success)
#[cfg(feature = "std")]
pub fn wait_timeout(&self, dur: Duration) -> bool {
if !self.running.with_lock(|running| *running) {
return true;
}

let (_running, res) = self
.cvar
.wait_timeout_while(self.running.lock().unwrap(), dur, |running| *running)
.unwrap();

return !res.timed_out();
}

/// return = !timeout (= success)
#[cfg(not(feature = "std"))]
pub fn wait_timeout(&self, dur: Duration) {
let now = micros_since_boot();
let end = now + dur.as_micros();

while self.running.with_lock(|running| *running) {
if micros_since_boot() > end {
return false;
}
unsafe { vTaskDelay(500) };
}

return true;
}

#[cfg(feature = "std")]
pub fn notify(&self) {
*self.running.lock().unwrap() = false;
self.cvar.notify_all();
}

#[cfg(not(feature = "std"))]
pub fn notify(&self) {
self.running.with_lock(|running| *running = false);
}
}
130 changes: 130 additions & 0 deletions src/task.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use core::time::Duration;

use log::*;

use esp_idf_sys::c_types::*;
use esp_idf_sys::*;

use crate::private::cstr::CString;

#[allow(non_upper_case_globals)]
const pdPASS: c_int = 1;

pub struct TaskHandle(TaskHandle_t);

impl TaskHandle {
pub fn stop(&self) {
unsafe { vTaskDelete(self.0) };
}
}

struct TaskInternal {
name: String,
f: Box<dyn FnOnce() -> anyhow::Result<()>>,
}

pub struct TaskConfig {
stack_size: u32,
priority: u32,
}

impl Default for TaskConfig {
fn default() -> Self {
TaskConfig {
stack_size: DEFAULT_THREAD_STACKSIZE,
priority: DEFAULT_THREAD_PRIO,
}
}
}

impl TaskConfig {
pub fn new(stack_size: u32, priority: u32) -> Self {
TaskConfig {
stack_size,
priority,
}
}

pub fn stack_size(self, stack_size: u32) -> Self {
TaskConfig { stack_size, ..self }
}

pub fn priority(self, priority: u32) -> Self {
TaskConfig { priority, ..self }
}

pub fn spawn<F>(
self,
name: impl AsRef<str>,
f: F,
) -> Result<TaskHandle, IdfError>
where
F: FnOnce() -> anyhow::Result<()>,
F: Send + 'static {
let parameters = TaskInternal {
name: name.as_ref().to_string(),
f: Box::new(f),
};
let parameters = Box::into_raw(Box::new(parameters)) as *mut _;

info!("starting task {:?}", name.as_ref());

let name = CString::new(name.as_ref()).unwrap();
let mut handle: TaskHandle_t = core::ptr::null_mut();
let res = unsafe {
xTaskCreatePinnedToCore(
Some(esp_idf_svc_task),
name.as_ptr(),
self.stack_size,
parameters,
self.priority,
&mut handle,
tskNO_AFFINITY as i32,
)
};
if res != pdPASS {
return Err(EspError::from(ESP_ERR_NO_MEM as i32).unwrap().into());
}

Ok(TaskHandle(handle))
}
}

pub fn spawn<F>(
name: impl AsRef<str>,
f: F,
) -> Result<TaskHandle, IdfError>
where
F: FnOnce() -> anyhow::Result<()>,
F: Send + 'static,
{
TaskConfig::default().spawn(name, f)
}

extern "C" fn esp_idf_svc_task(args: *mut c_void) {
let internal = unsafe { Box::from_raw(args as *mut TaskInternal) };

info!("started task {:?}", internal.name);

match (internal.f)() {
Err(e) => {
panic!("unexpected error in task {:?}: {:?}", internal.name, e);
}
Ok(_) => {}
}

info!("destroying task {:?}", internal.name);

unsafe { vTaskDelete(core::ptr::null_mut() as _) };
}

#[allow(non_upper_case_globals)]
pub const TICK_PERIOD_MS: u32 = 1000 / configTICK_RATE_HZ;

/// sleep tells FreeRTOS to put the current thread to sleep for at least the specified duration,
/// this is not an exact duration and can't be shorter than the rtos tick period.
pub fn sleep(duration: Duration) {
unsafe {
vTaskDelay(duration.as_millis() as u32 / TICK_PERIOD_MS);
}
}
7 changes: 7 additions & 0 deletions src/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use core::convert::TryInto;

use esp_idf_sys::*;

pub fn micros_since_boot() -> u64 {
unsafe { esp_timer_get_time() }.try_into().unwrap()
}