add rust interfaces and examples
Co-Authored-By: Hugo van der Wijst <hugo@wij.st>
This commit is contained in:
parent
13208548e8
commit
95c584e414
|
@ -1,2 +1,5 @@
|
|||
/build/
|
||||
.sconsign.dblite
|
||||
|
||||
/target/
|
||||
/Cargo.lock
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Crate"
|
|
@ -0,0 +1,84 @@
|
|||
[package]
|
||||
name = "rt"
|
||||
version = "0.0.2"
|
||||
authors = ["Chris Copeland <chris@chrisnc.net>"]
|
||||
description = "A real-time operating system capable of full preemption"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://git.rtng.org/rt/rt"
|
||||
documentation = "https://docs.rs/rt"
|
||||
keywords = ["real-time", "rtos"]
|
||||
categories = ["concurrency", "embedded", "no-std"]
|
||||
edition = "2021"
|
||||
rust-version = "1.64.0"
|
||||
build = "rust/build.rs"
|
||||
autoexamples = false
|
||||
include = ["/src/", "/include/", "/arch/", "/rust/", "!SCons*", "!Dockerfile", "!*.bash"]
|
||||
|
||||
[lib]
|
||||
path = "rust/src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
paste = "1.0.14"
|
||||
|
||||
[dev-dependencies]
|
||||
atomic_float = "0.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.66.1"
|
||||
cc = "1.0"
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
lto = "on"
|
||||
debug = true
|
||||
|
||||
[[example]]
|
||||
name = "donate"
|
||||
path = "rust/examples/donate.rs"
|
||||
|
||||
[[example]]
|
||||
name = "empty"
|
||||
path = "rust/examples/empty.rs"
|
||||
|
||||
[[example]]
|
||||
name = "float"
|
||||
path = "rust/examples/float.rs"
|
||||
|
||||
[[example]]
|
||||
name = "mutex"
|
||||
path = "rust/examples/mutex.rs"
|
||||
|
||||
[[example]]
|
||||
name = "notify"
|
||||
path = "rust/examples/notify.rs"
|
||||
|
||||
[[example]]
|
||||
name = "once"
|
||||
path = "rust/examples/once.rs"
|
||||
|
||||
[[example]]
|
||||
name = "queue"
|
||||
path = "rust/examples/queue.rs"
|
||||
|
||||
[[example]]
|
||||
name = "rwlock"
|
||||
path = "rust/examples/rwlock.rs"
|
||||
|
||||
[[example]]
|
||||
name = "semaphore"
|
||||
path = "rust/examples/semaphore.rs"
|
||||
|
||||
[[example]]
|
||||
name = "simple"
|
||||
path = "rust/examples/simple.rs"
|
||||
|
||||
[[example]]
|
||||
name = "sleep"
|
||||
path = "rust/examples/sleep.rs"
|
||||
|
||||
[[example]]
|
||||
name = "water-semaphore"
|
||||
path = "rust/examples/water/semaphore.rs"
|
|
@ -223,6 +223,10 @@ __attribute__((noreturn)) void rt_idle(void)
|
|||
|
||||
void rt_start(void)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
fprintf(stderr, "running rt on macOS is not supported due to a bug in pthread_kill\n");
|
||||
exit(1);
|
||||
#endif
|
||||
block_all_signals(NULL);
|
||||
|
||||
rt_task_cycle_resume();
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
FROM rust:bookworm
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
bear \
|
||||
clang \
|
||||
gcc-arm-none-eabi \
|
||||
gdb \
|
||||
less \
|
||||
libc++-dev \
|
||||
llvm \
|
||||
scons
|
||||
|
||||
RUN useradd rt
|
||||
USER rt
|
|
@ -0,0 +1,105 @@
|
|||
use std::{env, path::PathBuf};
|
||||
|
||||
static SOURCES: &[&str] = &[
|
||||
"barrier.c",
|
||||
"cond.c",
|
||||
"list.c",
|
||||
"mutex.c",
|
||||
"notify.c",
|
||||
"queue.c",
|
||||
"rt.c",
|
||||
"rwlock.c",
|
||||
"sem.c",
|
||||
"sleep.c",
|
||||
];
|
||||
|
||||
struct Arch<'a> {
|
||||
name: &'a str,
|
||||
sources: &'a [&'a str],
|
||||
flags: &'a [&'a str],
|
||||
defs: &'a [(&'a str, &'a str)],
|
||||
}
|
||||
|
||||
static PTHREAD_ARCH: Arch = Arch {
|
||||
name: "pthread",
|
||||
sources: &["pthread.c"],
|
||||
flags: &["-pthread"],
|
||||
defs: &[("_POSIX_C_SOURCE", "200809L")],
|
||||
};
|
||||
|
||||
static ARM_ARCH: Arch = Arch {
|
||||
name: "arm",
|
||||
sources: &["arm.c", "syscall.S"],
|
||||
flags: &["-fshort-enums"], // arm-none-eabi-gcc uses this by default, but clang/bindgen don't
|
||||
defs: &[],
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
|
||||
let arch = match (target_arch.as_str(), target_os.as_str()) {
|
||||
("arm", "none") => &ARM_ARCH,
|
||||
(_, "linux") => &PTHREAD_ARCH,
|
||||
(_, "macos") => &PTHREAD_ARCH,
|
||||
_ => panic!("Unsupported (arch, os) combination: ({target_arch}, {target_os})"),
|
||||
};
|
||||
|
||||
let src_dir = root_dir.join("src");
|
||||
let include_dir = root_dir.join("include");
|
||||
let arch_dir = root_dir.join("arch").join(arch.name);
|
||||
let arch_include_dir = arch_dir.join("include");
|
||||
|
||||
let mut cc = cc::Build::new();
|
||||
cc.files(SOURCES.iter().map(|s| src_dir.join(s)));
|
||||
cc.files(arch.sources.iter().map(|s| arch_dir.join(s)));
|
||||
cc.include(&include_dir);
|
||||
cc.include(arch_dir.join("include"));
|
||||
cc.flag("-std=c17");
|
||||
for flag in arch.flags {
|
||||
cc.flag(flag);
|
||||
}
|
||||
for &(name, val) in arch.defs {
|
||||
cc.define(name, val);
|
||||
}
|
||||
|
||||
// TODO: Enable LTO when rust options request it and the C compiler's LTO is compatible.
|
||||
|
||||
let target = env::var("TARGET").unwrap();
|
||||
|
||||
// TODO: Provide a better way to specify Cortex-R variants.
|
||||
// For now armebv7r is always a Hercules.
|
||||
if target.contains("armebv7r") {
|
||||
cc.include(arch_dir.join("r/hercules"));
|
||||
}
|
||||
|
||||
cc.compile("rt");
|
||||
|
||||
let wrapper = "rust/wrapper.h";
|
||||
|
||||
println!("cargo:rerun-if-changed={}", wrapper);
|
||||
for d in [&src_dir, &include_dir, &arch_dir] {
|
||||
println!("cargo:rerun-if-changed={}", d.display());
|
||||
}
|
||||
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
||||
bindgen::builder()
|
||||
.header(wrapper)
|
||||
.clang_arg(format!("-I{}", include_dir.display()))
|
||||
.clang_arg(format!("-I{}", arch_include_dir.display()))
|
||||
.clang_args(arch.defs.iter().map(|&(m, v)| format!("-D{m}={v}")))
|
||||
.clang_args(arch.flags)
|
||||
.disable_nested_struct_naming()
|
||||
.use_core()
|
||||
.default_enum_style(bindgen::EnumVariation::NewType {
|
||||
is_bitfield: false,
|
||||
is_global: false,
|
||||
})
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||
.generate()
|
||||
.expect("Failed to generate bindings.")
|
||||
.write_to_file(out_dir.join("bindings.rs"))
|
||||
.expect("Failed to write bindings.");
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -xe
|
||||
|
||||
name="rt-rust"
|
||||
image="$name-builder"
|
||||
|
||||
rust_dir="$(dirname $0)"
|
||||
rt_dir="$(realpath "$rust_dir"/..)"
|
||||
|
||||
docker build --tag "$image" "$rust_dir"
|
||||
|
||||
home_dir="/home/rt"
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--interactive \
|
||||
--volume "$rt_dir:$home_dir" \
|
||||
--workdir "$home_dir" \
|
||||
--cap-add SYS_PTRACE \
|
||||
"$image"
|
|
@ -0,0 +1,91 @@
|
|||
use core::sync::atomic::{AtomicBool, AtomicI32, Ordering};
|
||||
|
||||
const MAX_SEQ: i32 = 9;
|
||||
|
||||
static SEQ: AtomicI32 = AtomicI32::new(0);
|
||||
static OUT_OF_ORDER: AtomicBool = AtomicBool::new(false);
|
||||
rt::mutex!(MUTEX0);
|
||||
rt::mutex!(MUTEX1);
|
||||
rt::mutex!(MUTEX2);
|
||||
|
||||
fn sequence(s: i32) {
|
||||
if SEQ.load(Ordering::Relaxed) == s {
|
||||
SEQ.fetch_add(1, Ordering::Relaxed);
|
||||
} else {
|
||||
OUT_OF_ORDER.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
if OUT_OF_ORDER.load(Ordering::Relaxed) || (SEQ.load(Ordering::Relaxed) > MAX_SEQ) {
|
||||
rt::stop();
|
||||
}
|
||||
}
|
||||
|
||||
fn locker0() {
|
||||
rt::task::drop_privilege();
|
||||
sequence(3);
|
||||
{
|
||||
let _guard0 = MUTEX0.lock();
|
||||
let _guard1 = MUTEX1.lock();
|
||||
sequence(5);
|
||||
}
|
||||
sequence(-1);
|
||||
}
|
||||
|
||||
fn locker1() {
|
||||
rt::task::drop_privilege();
|
||||
sequence(2);
|
||||
let guard1 = MUTEX1.lock();
|
||||
rt::task::sleep(20);
|
||||
sequence(4);
|
||||
let _guard2 = MUTEX2.lock();
|
||||
drop(guard1);
|
||||
sequence(8);
|
||||
rt::task::sleep(20);
|
||||
sequence(-1);
|
||||
}
|
||||
|
||||
fn spinner() {
|
||||
rt::task::drop_privilege();
|
||||
sequence(1);
|
||||
rt::task::sleep(10);
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn donator() {
|
||||
rt::task::drop_privilege();
|
||||
sequence(0);
|
||||
rt::task::sleep(30);
|
||||
{
|
||||
let _guard0 = MUTEX0.lock();
|
||||
sequence(6);
|
||||
}
|
||||
sequence(7);
|
||||
if MUTEX2.timed_lock(10).is_none() {
|
||||
sequence(9);
|
||||
}
|
||||
}
|
||||
|
||||
static TIMED_OUT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn timeout() {
|
||||
rt::task::sleep(100);
|
||||
TIMED_OUT.store(true, Ordering::Relaxed);
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(locker0, rt::task::STACK_MIN, 1);
|
||||
rt::task!(locker1, rt::task::STACK_MIN, 2);
|
||||
rt::task!(spinner, rt::task::STACK_MIN, 3);
|
||||
rt::task!(donator, rt::task::STACK_MIN, 4);
|
||||
rt::task!(timeout, rt::task::STACK_MIN, 5);
|
||||
rt::start();
|
||||
|
||||
if TIMED_OUT.load(Ordering::Relaxed) {
|
||||
panic!("timed out");
|
||||
}
|
||||
|
||||
if OUT_OF_ORDER.load(Ordering::Relaxed) {
|
||||
panic!("out of order");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
fn empty() {
|
||||
rt::task::drop_privilege();
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(empty, rt::task::STACK_MIN, 1);
|
||||
rt::start();
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
use core::sync::atomic::Ordering;
|
||||
|
||||
use atomic_float::AtomicF32;
|
||||
|
||||
static V: AtomicF32 = AtomicF32::new(0.0);
|
||||
|
||||
fn f(arg: u32) {
|
||||
rt::task::drop_privilege();
|
||||
rt::task::enable_fp();
|
||||
|
||||
let mut x = 0.0f32;
|
||||
let a = arg as f32;
|
||||
loop {
|
||||
x += a;
|
||||
V.store(x, Ordering::Relaxed);
|
||||
rt::task::yield_();
|
||||
}
|
||||
}
|
||||
|
||||
fn timeout() {
|
||||
rt::task::drop_privilege();
|
||||
rt::task::sleep(100);
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(f(1), 2 * rt::task::STACK_MIN, 1);
|
||||
rt::task!(f(2), 2 * rt::task::STACK_MIN, 1);
|
||||
rt::task!(timeout, rt::task::STACK_MIN, 2);
|
||||
rt::start();
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
rt::mutex!(MUTEX, u32, 0);
|
||||
|
||||
const NUM_TASKS: u32 = 3;
|
||||
const ITERATIONS: u32 = 10000;
|
||||
|
||||
fn stop_last() {
|
||||
rt::semaphore!(STOP_SEM, NUM_TASKS as i32 - 1);
|
||||
if !STOP_SEM.try_wait() {
|
||||
rt::stop();
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_lock() {
|
||||
rt::task::drop_privilege();
|
||||
for _ in 0..ITERATIONS {
|
||||
*MUTEX.lock() += 1;
|
||||
}
|
||||
stop_last();
|
||||
}
|
||||
|
||||
fn increment_trylock() {
|
||||
rt::task::drop_privilege();
|
||||
for _ in 0..ITERATIONS {
|
||||
let mut guard = loop {
|
||||
if let Some(guard) = MUTEX.try_lock() {
|
||||
break guard;
|
||||
} else {
|
||||
rt::task::sleep(1);
|
||||
}
|
||||
};
|
||||
*guard += 1;
|
||||
}
|
||||
stop_last();
|
||||
}
|
||||
|
||||
fn increment_timedlock() {
|
||||
rt::task::drop_privilege();
|
||||
for _ in 0..ITERATIONS {
|
||||
let mut guard = loop {
|
||||
if let Some(guard) = MUTEX.timed_lock(1) {
|
||||
break guard;
|
||||
}
|
||||
};
|
||||
*guard += 1;
|
||||
}
|
||||
stop_last();
|
||||
}
|
||||
|
||||
static TIMED_OUT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn timeout() {
|
||||
rt::task::sleep(1000);
|
||||
TIMED_OUT.store(true, Ordering::Relaxed);
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(increment_lock, rt::task::STACK_MIN, 1);
|
||||
rt::task!(increment_trylock, rt::task::STACK_MIN, 1);
|
||||
rt::task!(increment_timedlock, rt::task::STACK_MIN, 1);
|
||||
rt::task!(timeout, rt::task::STACK_MIN, 2);
|
||||
rt::start();
|
||||
|
||||
if TIMED_OUT.load(Ordering::Relaxed) {
|
||||
panic!("timed out");
|
||||
}
|
||||
|
||||
let guard = MUTEX.try_lock().expect("mutex should be unlocked");
|
||||
|
||||
if *guard != ITERATIONS * NUM_TASKS {
|
||||
panic!("the mutex did not contain the expected value");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
const N: i32 = 10;
|
||||
rt::notify!(NOTE);
|
||||
|
||||
fn notifier() {
|
||||
rt::task::drop_privilege();
|
||||
for _ in 0..N {
|
||||
rt::task::sleep(5);
|
||||
NOTE.or(1);
|
||||
}
|
||||
|
||||
rt::task::sleep(15);
|
||||
NOTE.post();
|
||||
}
|
||||
|
||||
static WAIT_FAILED: AtomicBool = AtomicBool::new(false);
|
||||
static WRONG_VALUE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn waiter() {
|
||||
rt::task::drop_privilege();
|
||||
for _ in 0..N {
|
||||
match NOTE.timed_wait_clear(1, 10) {
|
||||
Some(v) if v != 1 => WRONG_VALUE.store(true, Ordering::Relaxed),
|
||||
None => WAIT_FAILED.store(true, Ordering::Relaxed),
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
if NOTE.timed_wait(10).is_some() {
|
||||
WAIT_FAILED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(notifier, rt::task::STACK_MIN, 1);
|
||||
rt::task!(waiter, rt::task::STACK_MIN, 1);
|
||||
rt::start();
|
||||
|
||||
if WAIT_FAILED.load(Ordering::Relaxed) {
|
||||
panic!("notify wait failed");
|
||||
}
|
||||
|
||||
if WRONG_VALUE.load(Ordering::Relaxed) {
|
||||
panic!("notify wait returned the wrong value");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
const ITERATIONS: usize = 100;
|
||||
|
||||
rt::once!(ONCE);
|
||||
rt::semaphore!(SEM);
|
||||
|
||||
static X: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
fn f() {
|
||||
X.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn oncer() {
|
||||
rt::task::drop_privilege();
|
||||
for _ in 0..ITERATIONS {
|
||||
ONCE.call_once(f);
|
||||
}
|
||||
}
|
||||
|
||||
fn oncer_stop() {
|
||||
oncer();
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(oncer, rt::task::STACK_MIN, 1);
|
||||
rt::task!(oncer_stop, rt::task::STACK_MIN, 1);
|
||||
rt::start();
|
||||
|
||||
if X.load(Ordering::Relaxed) != 1 {
|
||||
panic!("X did not have the expected value");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
|
||||
rt::queue!(QUEUE, u32, 10);
|
||||
|
||||
fn pusher(mut i: u32) {
|
||||
rt::task::drop_privilege();
|
||||
loop {
|
||||
QUEUE.push(i);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const NPUSHERS: usize = 2;
|
||||
const TASK_INC: u32 = 0x1000000;
|
||||
|
||||
static OUT_OF_ORDER: AtomicBool = AtomicBool::new(false);
|
||||
static NUM_POPPED: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
fn popper() {
|
||||
rt::task::drop_privilege();
|
||||
let mut x: u32;
|
||||
let mut max_elem = [0u32; NPUSHERS];
|
||||
loop {
|
||||
x = QUEUE.pop();
|
||||
NUM_POPPED.fetch_add(1, Ordering::Relaxed);
|
||||
let task = x / TASK_INC;
|
||||
let elem = x % TASK_INC;
|
||||
if elem < max_elem[task as usize] {
|
||||
OUT_OF_ORDER.store(true, Ordering::Release);
|
||||
}
|
||||
max_elem[task as usize] = elem;
|
||||
}
|
||||
}
|
||||
|
||||
fn timeout() {
|
||||
rt::task::drop_privilege();
|
||||
rt::task::sleep(1000);
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(pusher(0), rt::task::STACK_MIN, 1);
|
||||
rt::task!(pusher(TASK_INC), rt::task::STACK_MIN, 1);
|
||||
rt::task!(popper, rt::task::STACK_MIN, 1);
|
||||
rt::task!(timeout, rt::task::STACK_MIN, 2);
|
||||
rt::start();
|
||||
|
||||
if OUT_OF_ORDER.load(Ordering::Acquire) {
|
||||
panic!("queue elements were received out of order");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
struct Point {
|
||||
x: u32,
|
||||
y: u32,
|
||||
}
|
||||
|
||||
rt::rwlock!(POINT, Point, Point { x: 0, y: 0 });
|
||||
|
||||
static MISMATCH: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn reader() {
|
||||
rt::task::drop_privilege();
|
||||
loop {
|
||||
let p = POINT.read();
|
||||
if p.x != p.y {
|
||||
MISMATCH.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn writer() {
|
||||
rt::task::drop_privilege();
|
||||
loop {
|
||||
let mut p = POINT.write();
|
||||
p.x += 1;
|
||||
p.y += 1;
|
||||
drop(p);
|
||||
rt::task::sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn timeout() {
|
||||
rt::task::drop_privilege();
|
||||
rt::task::sleep(50);
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(reader, rt::task::STACK_MIN, 1);
|
||||
rt::task!(reader, rt::task::STACK_MIN, 1);
|
||||
rt::task!(writer, rt::task::STACK_MIN, 1);
|
||||
rt::task!(timeout, rt::task::STACK_MIN, 2);
|
||||
rt::start();
|
||||
|
||||
if MISMATCH.load(Ordering::Relaxed) {
|
||||
panic!("point coordinates did not match");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
const N: i32 = 10;
|
||||
rt::semaphore!(SEM);
|
||||
|
||||
fn poster() {
|
||||
rt::task::drop_privilege();
|
||||
for _ in 0..N {
|
||||
rt::task::sleep(5);
|
||||
SEM.post();
|
||||
}
|
||||
|
||||
rt::task::sleep(15);
|
||||
SEM.post();
|
||||
}
|
||||
|
||||
static WAIT_FAILED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn waiter() {
|
||||
rt::task::drop_privilege();
|
||||
for _ in 0..N {
|
||||
if !SEM.timed_wait(10) {
|
||||
WAIT_FAILED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
if SEM.timed_wait(10) {
|
||||
WAIT_FAILED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(poster, rt::task::STACK_MIN, 1);
|
||||
rt::task!(waiter, rt::task::STACK_MIN, 1);
|
||||
rt::start();
|
||||
if WAIT_FAILED.load(Ordering::Relaxed) {
|
||||
panic!("semaphore wait failed");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
fn simple(arg: usize) {
|
||||
rt::task::drop_privilege();
|
||||
for _ in 0..100 {
|
||||
rt::task::yield_();
|
||||
}
|
||||
|
||||
if arg == 1 {
|
||||
rt::stop();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(simple(0), rt::task::STACK_MIN, 1);
|
||||
rt::task!(simple(1), rt::task::STACK_MIN, 1);
|
||||
rt::start();
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
const NLOOPS: i32 = 5;
|
||||
|
||||
static WRONG_TICK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn sleep_periodic(period: rt::tick::Utick) {
|
||||
rt::task::drop_privilege();
|
||||
let mut last_wake_tick = 0;
|
||||
|
||||
for _ in 0..NLOOPS {
|
||||
rt::task::sleep_periodic(&mut last_wake_tick, period);
|
||||
let wake_tick = rt::tick::count();
|
||||
if wake_tick != last_wake_tick {
|
||||
WRONG_TICK.store(true, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
// Only the second task to finish will call rt_stop.
|
||||
rt::semaphore!(STOP_SEM, 1);
|
||||
if !STOP_SEM.try_wait() {
|
||||
rt::stop();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(sleep_periodic(5), rt::task::STACK_MIN, 2);
|
||||
rt::task!(sleep_periodic(10), rt::task::STACK_MIN, 1);
|
||||
rt::start();
|
||||
|
||||
if WRONG_TICK.load(Ordering::Acquire) {
|
||||
panic!("a task woke up at the wrong tick");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
rt::semaphore!(H2READY);
|
||||
rt::semaphore!(HDONE);
|
||||
static H: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
fn hydrogen() {
|
||||
if (H.fetch_add(1, Ordering::Relaxed) & 1) == 1 {
|
||||
H2READY.post();
|
||||
}
|
||||
HDONE.wait();
|
||||
}
|
||||
|
||||
fn oxygen() {
|
||||
H2READY.wait();
|
||||
make_water();
|
||||
HDONE.post_n(2);
|
||||
}
|
||||
|
||||
include!("water.rs");
|
|
@ -0,0 +1,53 @@
|
|||
static HYDROGEN_BONDED: AtomicU32 = AtomicU32::new(0);
|
||||
static OXYGEN_BONDED: AtomicU32 = AtomicU32::new(0);
|
||||
static WATER_FORMED: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
fn make_water() {
|
||||
WATER_FORMED.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn timeout() {
|
||||
rt::task::drop_privilege();
|
||||
rt::task::sleep(1000);
|
||||
rt::stop();
|
||||
}
|
||||
|
||||
fn oxygen_loop() {
|
||||
rt::task::drop_privilege();
|
||||
loop {
|
||||
oxygen();
|
||||
OXYGEN_BONDED.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrogen_loop() {
|
||||
rt::task::drop_privilege();
|
||||
loop {
|
||||
hydrogen();
|
||||
HYDROGEN_BONDED.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rt::task!(timeout, rt::task::STACK_MIN, 3);
|
||||
rt::task!(hydrogen_loop, rt::task::STACK_MIN, 1);
|
||||
rt::task!(hydrogen_loop, rt::task::STACK_MIN, 1);
|
||||
rt::task!(oxygen_loop, rt::task::STACK_MIN, 2);
|
||||
rt::start();
|
||||
|
||||
let w = WATER_FORMED.load(Ordering::Relaxed);
|
||||
let h = HYDROGEN_BONDED.load(Ordering::Relaxed);
|
||||
let o = OXYGEN_BONDED.load(Ordering::Relaxed);
|
||||
|
||||
/* The oxygen or hydrogen may not have bonded by the time rt_stop is called
|
||||
* after making a water molecule, so allow for o and h to be one molecule's
|
||||
* worth below expected value or exactly equal to it. */
|
||||
let o_lo = w - 1;
|
||||
let o_hi = w;
|
||||
let h_lo = (w - 1) * 2;
|
||||
let h_hi = w * 2;
|
||||
|
||||
if (o < o_lo) || (o > o_hi) || (h < h_lo) || (h > h_hi) {
|
||||
panic!("hydrogen and oxygen bonding counts are incorrect");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(unused)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|
@ -0,0 +1,5 @@
|
|||
use crate::bindings::rt_cycle;
|
||||
|
||||
pub fn cycle() -> u32 {
|
||||
unsafe { rt_cycle() }
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#![no_std]
|
||||
|
||||
pub mod cycle;
|
||||
pub mod sync;
|
||||
pub mod task;
|
||||
pub mod tick;
|
||||
|
||||
pub use paste;
|
||||
|
||||
mod bindings;
|
||||
mod list;
|
||||
|
||||
use bindings::{rt_start, rt_stop};
|
||||
|
||||
pub fn start() {
|
||||
unsafe { rt_start() }
|
||||
}
|
||||
|
||||
pub fn stop() {
|
||||
unsafe { rt_stop() }
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
use crate::bindings::rt_list;
|
||||
|
||||
pub const fn list_init(l: &'static rt_list) -> rt_list {
|
||||
let lp = l as *const rt_list as *mut rt_list;
|
||||
rt_list { prev: lp, next: lp }
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
use core::{
|
||||
cell::UnsafeCell,
|
||||
marker::PhantomPinned,
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{rt_barrier, rt_barrier_wait, rt_cond, rt_mutex},
|
||||
list::list_init,
|
||||
sync::semaphore::c_sem_init,
|
||||
};
|
||||
|
||||
pub struct Barrier {
|
||||
b: UnsafeCell<rt_barrier>,
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
|
||||
unsafe impl Send for Barrier {}
|
||||
unsafe impl Sync for Barrier {}
|
||||
impl UnwindSafe for Barrier {}
|
||||
impl RefUnwindSafe for Barrier {}
|
||||
|
||||
pub struct BarrierWaitResult(bool);
|
||||
|
||||
impl Barrier {
|
||||
pub const fn init(barrier: &'static Barrier, count: u32) -> Barrier {
|
||||
let b = unsafe { &*(barrier.b.get() as *const rt_barrier) };
|
||||
Barrier {
|
||||
b: UnsafeCell::new(rt_barrier {
|
||||
mutex: rt_mutex {
|
||||
holder: 0,
|
||||
wait_list: list_init(&b.mutex.wait_list),
|
||||
list: list_init(&b.mutex.list),
|
||||
},
|
||||
cond: rt_cond {
|
||||
sem: c_sem_init(&b.cond.sem, 0, 0),
|
||||
},
|
||||
level: 0,
|
||||
threshold: count,
|
||||
generation: 0,
|
||||
}),
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait(&self) -> BarrierWaitResult {
|
||||
BarrierWaitResult(unsafe { rt_barrier_wait(self.b.get()) })
|
||||
}
|
||||
}
|
||||
|
||||
impl BarrierWaitResult {
|
||||
#[must_use]
|
||||
pub fn is_leader(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! barrier {
|
||||
($name: ident, $count: expr) => {
|
||||
static $name: $crate::sync::Barrier = $crate::sync::Barrier::init(&$name, $count);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn fast_path() {
|
||||
barrier!(BARRIER, 1);
|
||||
assert!(BARRIER.wait().is_leader());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
use core::{
|
||||
cell::UnsafeCell,
|
||||
marker::PhantomPinned,
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{rt_cond, rt_cond_broadcast, rt_cond_signal, rt_cond_timedwait, rt_cond_wait},
|
||||
sync::{semaphore::c_sem_init, MutexGuard},
|
||||
tick::Utick,
|
||||
};
|
||||
|
||||
pub struct Condvar {
|
||||
c: UnsafeCell<rt_cond>,
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
|
||||
unsafe impl Send for Condvar {}
|
||||
unsafe impl Sync for Condvar {}
|
||||
impl UnwindSafe for Condvar {}
|
||||
impl RefUnwindSafe for Condvar {}
|
||||
|
||||
impl Condvar {
|
||||
pub const fn init(cond: &'static Condvar) -> Condvar {
|
||||
let c = unsafe { &*(cond.c.get() as *const rt_cond) };
|
||||
Condvar {
|
||||
c: UnsafeCell::new(rt_cond {
|
||||
sem: c_sem_init(&c.sem, 0, 0),
|
||||
}),
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signal(&self) {
|
||||
unsafe { rt_cond_signal(self.c.get()) }
|
||||
}
|
||||
|
||||
pub fn broadcast(&self) {
|
||||
unsafe { rt_cond_broadcast(self.c.get()) }
|
||||
}
|
||||
|
||||
pub fn wait<T>(&self, guard: &MutexGuard<T>) {
|
||||
unsafe { rt_cond_wait(self.c.get(), guard.ptr()) }
|
||||
}
|
||||
|
||||
pub fn timedwait<'a, T>(
|
||||
&self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
ticks: Utick,
|
||||
) -> Option<MutexGuard<'a, T>> {
|
||||
if unsafe { rt_cond_timedwait(self.c.get(), guard.ptr(), ticks) } {
|
||||
Some(guard)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! condvar {
|
||||
($name: ident) => {
|
||||
static $name: $crate::sync::Condvar = $crate::sync::Condvar::init(&$name);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn signal() {
|
||||
condvar!(CONDVAR);
|
||||
CONDVAR.signal();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
pub use barrier::{Barrier, BarrierWaitResult};
|
||||
pub use condvar::Condvar;
|
||||
pub use mutex::{Mutex, MutexGuard};
|
||||
pub use notify::Notify;
|
||||
pub use once::Once;
|
||||
pub use queue::Queue;
|
||||
pub use rwlock::RwLock;
|
||||
pub use semaphore::{Semaphore, SemaphoreGuard};
|
||||
|
||||
mod barrier;
|
||||
mod condvar;
|
||||
mod mutex;
|
||||
mod notify;
|
||||
mod once;
|
||||
mod queue;
|
||||
mod rwlock;
|
||||
mod semaphore;
|
|
@ -0,0 +1,147 @@
|
|||
use core::{
|
||||
cell::UnsafeCell,
|
||||
marker::{PhantomData, PhantomPinned},
|
||||
ops::{Deref, DerefMut},
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{rt_mutex, rt_mutex_lock, rt_mutex_timedlock, rt_mutex_trylock, rt_mutex_unlock},
|
||||
list::list_init,
|
||||
tick::Utick,
|
||||
};
|
||||
|
||||
pub struct Mutex<T: ?Sized> {
|
||||
m: UnsafeCell<rt_mutex>,
|
||||
_pin_marker: PhantomPinned,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
|
||||
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
|
||||
impl<T: ?Sized> UnwindSafe for Mutex<T> {}
|
||||
impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
|
||||
|
||||
impl<T> Mutex<T> {
|
||||
pub const fn init(mutex: &'static Mutex<T>, t: T) -> Mutex<T> {
|
||||
let m = unsafe { &*(mutex.m.get() as *const rt_mutex) };
|
||||
Mutex {
|
||||
m: UnsafeCell::new(rt_mutex {
|
||||
holder: 0,
|
||||
wait_list: list_init(&m.wait_list),
|
||||
list: list_init(&m.list),
|
||||
}),
|
||||
_pin_marker: PhantomPinned,
|
||||
data: UnsafeCell::new(t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Mutex<T> {
|
||||
pub fn lock(&self) -> MutexGuard<'_, T> {
|
||||
unsafe {
|
||||
rt_mutex_lock(self.m.get());
|
||||
}
|
||||
MutexGuard::new(self)
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
|
||||
if unsafe { rt_mutex_trylock(self.m.get()) } {
|
||||
Some(MutexGuard::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timed_lock(&self, ticks: Utick) -> Option<MutexGuard<T>> {
|
||||
if unsafe { rt_mutex_timedlock(self.m.get(), ticks) } {
|
||||
Some(MutexGuard::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Probably not useful because this Mutex must be static.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.data.get_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MutexGuard<'a, T: ?Sized + 'a> {
|
||||
lock: &'a Mutex<T>,
|
||||
_unsend_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> MutexGuard<'_, T> {
|
||||
fn new(lock: &Mutex<T>) -> MutexGuard<T> {
|
||||
MutexGuard {
|
||||
lock,
|
||||
_unsend_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not supported yet, so using the _unsend_marker instead.
|
||||
//impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
|
||||
|
||||
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
|
||||
|
||||
impl<T: ?Sized> MutexGuard<'_, T> {
|
||||
// Needs to be pub(crate) so that cond can use it.
|
||||
pub(crate) fn ptr(&self) -> *mut rt_mutex {
|
||||
self.lock.m.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { rt_mutex_unlock(self.lock.m.get()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! mutex {
|
||||
($name: ident, $type: ty, $data: expr) => {
|
||||
static $name: $crate::sync::Mutex<$type> = $crate::sync::Mutex::init(&$name, $data);
|
||||
};
|
||||
|
||||
($name: ident) => {
|
||||
rt::mutex!($name, (), ());
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn fast_path() {
|
||||
mutex!(MUTEX, i32, 0);
|
||||
*MUTEX.lock() += 1;
|
||||
assert!(*MUTEX.lock() == 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_lock() {
|
||||
mutex!(MUTEX, i32, 0);
|
||||
{
|
||||
let guard = MUTEX.try_lock();
|
||||
assert!(MUTEX.try_lock().is_none());
|
||||
guard.map(|mut x| *x += 1);
|
||||
}
|
||||
assert!(*MUTEX.lock() == 1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
use core::{
|
||||
cell::UnsafeCell,
|
||||
marker::PhantomPinned,
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{
|
||||
rt_notify, rt_notify_add, rt_notify_or, rt_notify_post, rt_notify_set, rt_notify_timedwait,
|
||||
rt_notify_timedwait_clear, rt_notify_trywait, rt_notify_trywait_clear, rt_notify_wait,
|
||||
rt_notify_wait_clear,
|
||||
},
|
||||
sync::semaphore::c_sem_init,
|
||||
tick::Utick,
|
||||
};
|
||||
|
||||
pub struct Notify {
|
||||
n: UnsafeCell<rt_notify>,
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
|
||||
unsafe impl Send for Notify {}
|
||||
unsafe impl Sync for Notify {}
|
||||
impl UnwindSafe for Notify {}
|
||||
impl RefUnwindSafe for Notify {}
|
||||
|
||||
impl Notify {
|
||||
pub const fn init(note: &'static Notify, value: u32) -> Notify {
|
||||
let n = unsafe { &*(note.n.get() as *const rt_notify) };
|
||||
Notify {
|
||||
n: UnsafeCell::new(rt_notify {
|
||||
value,
|
||||
sem: c_sem_init(&n.sem, 0, 0),
|
||||
}),
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn post(&self) {
|
||||
unsafe { rt_notify_post(self.n.get()) }
|
||||
}
|
||||
|
||||
pub fn or(&self, v: u32) {
|
||||
unsafe { rt_notify_or(self.n.get(), v) }
|
||||
}
|
||||
|
||||
pub fn add(&self, v: u32) {
|
||||
unsafe { rt_notify_add(self.n.get(), v) }
|
||||
}
|
||||
|
||||
pub fn set(&self, v: u32) {
|
||||
unsafe { rt_notify_set(self.n.get(), v) }
|
||||
}
|
||||
|
||||
pub fn wait(&self) -> u32 {
|
||||
unsafe { rt_notify_wait(self.n.get()) }
|
||||
}
|
||||
|
||||
pub fn wait_clear(&self, c: u32) -> u32 {
|
||||
unsafe { rt_notify_wait_clear(self.n.get(), c) }
|
||||
}
|
||||
|
||||
pub fn try_wait(&self) -> Option<u32> {
|
||||
let mut v = 0;
|
||||
if unsafe { rt_notify_trywait(self.n.get(), &mut v) } {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_wait_clear(&self, c: u32) -> Option<u32> {
|
||||
let mut v = 0;
|
||||
if unsafe { rt_notify_trywait_clear(self.n.get(), &mut v, c) } {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timed_wait(&self, ticks: Utick) -> Option<u32> {
|
||||
let mut v = 0;
|
||||
if unsafe { rt_notify_timedwait(self.n.get(), &mut v, ticks) } {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timed_wait_clear(&self, c: u32, ticks: Utick) -> Option<u32> {
|
||||
let mut v = 0;
|
||||
if unsafe { rt_notify_timedwait_clear(self.n.get(), &mut v, c, ticks) } {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! notify {
|
||||
($name: ident, $value: expr) => {
|
||||
static $name: $crate::sync::Notify = $crate::sync::Notify::init(&$name, $value);
|
||||
};
|
||||
|
||||
($name: ident) => {
|
||||
$crate::notify!($name, 0);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn fast_path() {
|
||||
notify!(NOTIFY);
|
||||
NOTIFY.post();
|
||||
assert!(NOTIFY.wait() == 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ops() {
|
||||
notify!(NOTIFY);
|
||||
NOTIFY.or(1);
|
||||
assert!(NOTIFY.wait() == 1);
|
||||
NOTIFY.add(1);
|
||||
assert!(NOTIFY.wait() == 2);
|
||||
NOTIFY.set(3);
|
||||
assert!(NOTIFY.wait() == 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear() {
|
||||
notify!(NOTIFY);
|
||||
NOTIFY.or(u32::MAX);
|
||||
assert!(NOTIFY.wait_clear(0xFF) == u32::MAX);
|
||||
assert!(NOTIFY.wait() == 0xFFFFFF00);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
use core::sync::atomic::{AtomicI32, Ordering};
|
||||
|
||||
use crate::sync::Mutex;
|
||||
|
||||
pub struct Once {
|
||||
done: AtomicI32,
|
||||
mutex: Mutex<()>,
|
||||
}
|
||||
|
||||
impl Once {
|
||||
pub const fn init(once: &'static Once) -> Once {
|
||||
Once {
|
||||
done: AtomicI32::new(0),
|
||||
mutex: Mutex::init(&once.mutex, ()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_once<F: FnOnce()>(&self, f: F) {
|
||||
if self.done.load(Ordering::Acquire) == 0 {
|
||||
let _guard = self.mutex.lock();
|
||||
if self.done.load(Ordering::Relaxed) == 0 {
|
||||
f();
|
||||
self.done.store(1, Ordering::Release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_completed(&self) -> bool {
|
||||
self.done.load(Ordering::Acquire) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! once {
|
||||
($name: ident) => {
|
||||
static $name: $crate::sync::Once = $crate::sync::Once::init(&$name);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn fast_path() {
|
||||
once!(ONCE);
|
||||
ONCE.call_once(|| {});
|
||||
assert!(ONCE.is_completed());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
use core::{
|
||||
cell::UnsafeCell,
|
||||
ffi::{c_uchar, c_void},
|
||||
marker::{PhantomData, PhantomPinned},
|
||||
mem::{size_of, MaybeUninit},
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{
|
||||
rt_queue, rt_queue_peek, rt_queue_pop, rt_queue_push, rt_queue_timedpeek,
|
||||
rt_queue_timedpop, rt_queue_timedpush, rt_queue_trypeek, rt_queue_trypop, rt_queue_trypush,
|
||||
},
|
||||
sync::semaphore::c_sem_init,
|
||||
tick::Utick,
|
||||
};
|
||||
|
||||
pub struct Queue<T: ?Sized> {
|
||||
q: UnsafeCell<rt_queue>,
|
||||
_phantom_elem: PhantomData<T>,
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + Send> Send for Queue<T> {}
|
||||
unsafe impl<T: ?Sized + Send> Sync for Queue<T> {}
|
||||
impl<T: ?Sized> UnwindSafe for Queue<T> {}
|
||||
impl<T: ?Sized> RefUnwindSafe for Queue<T> {}
|
||||
|
||||
impl<T> Queue<T> {
|
||||
pub const fn init<const N: usize>(
|
||||
queue: &'static Queue<T>,
|
||||
slots: &'static [c_uchar; N],
|
||||
data: &'static [T; N],
|
||||
) -> Queue<T> {
|
||||
let q = unsafe { &*(queue.q.get() as *const rt_queue) };
|
||||
Queue {
|
||||
q: UnsafeCell::new(rt_queue {
|
||||
push_sem: c_sem_init(&q.push_sem, N as i32, i32::max_value()),
|
||||
pop_sem: c_sem_init(&q.pop_sem, 0, i32::max_value()),
|
||||
enq: 0,
|
||||
deq: 0,
|
||||
slots: slots.as_ptr() as *mut c_uchar,
|
||||
data: data.as_ptr() as *mut c_void,
|
||||
num_elems: N,
|
||||
elem_size: size_of::<T>(),
|
||||
}),
|
||||
_phantom_elem: PhantomData,
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&self, elem: T) {
|
||||
unsafe { rt_queue_push(self.q.get(), &elem as *const T as *const c_void) }
|
||||
}
|
||||
|
||||
pub fn pop(&self) -> T {
|
||||
let mut elem = MaybeUninit::<T>::uninit();
|
||||
unsafe {
|
||||
rt_queue_pop(self.q.get(), elem.as_mut_ptr() as *mut c_void);
|
||||
elem.assume_init()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek(&self) -> T {
|
||||
let mut elem = MaybeUninit::<T>::uninit();
|
||||
unsafe {
|
||||
rt_queue_peek(self.q.get(), elem.as_mut_ptr() as *mut c_void);
|
||||
elem.assume_init()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_push(&self, elem: T) -> Result<(), T> {
|
||||
if unsafe { rt_queue_trypush(self.q.get(), &elem as *const T as *const c_void) } {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(elem)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_pop(&self) -> Option<T> {
|
||||
let mut elem = MaybeUninit::<T>::uninit();
|
||||
if unsafe { rt_queue_trypop(self.q.get(), elem.as_mut_ptr() as *mut c_void) } {
|
||||
Some(unsafe { elem.assume_init() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_peek(&self) -> Option<T> {
|
||||
let mut elem = MaybeUninit::<T>::uninit();
|
||||
if unsafe { rt_queue_trypeek(self.q.get(), elem.as_mut_ptr() as *mut c_void) } {
|
||||
Some(unsafe { elem.assume_init() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timed_push(&self, elem: T, ticks: Utick) -> Result<(), T> {
|
||||
if unsafe { rt_queue_timedpush(self.q.get(), &elem as *const T as *const c_void, ticks) } {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(elem)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timed_pop(&self, ticks: Utick) -> Option<T> {
|
||||
let mut elem = MaybeUninit::<T>::uninit();
|
||||
if unsafe { rt_queue_timedpop(self.q.get(), elem.as_mut_ptr() as *mut c_void, ticks) } {
|
||||
Some(unsafe { elem.assume_init() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timed_peek(&self, ticks: Utick) -> Option<T> {
|
||||
let mut elem = MaybeUninit::<T>::uninit();
|
||||
if unsafe { rt_queue_timedpeek(self.q.get(), elem.as_mut_ptr() as *mut c_void, ticks) } {
|
||||
Some(unsafe { elem.assume_init() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! queue {
|
||||
($name: ident, $type: ty, $num: literal) => {
|
||||
$crate::paste::paste! {
|
||||
static mut [< $name _SLOTS >]: [core::ffi::c_uchar; $num] = [0u8; $num];
|
||||
static mut [< $name _DATA >]: [$type; $num] =
|
||||
unsafe { core::mem::transmute([0u8; core::mem::size_of::<$type>() * $num]) };
|
||||
static $name: $crate::sync::Queue<$type> = $crate::sync::Queue::init(
|
||||
&$name,
|
||||
unsafe { &[< $name _SLOTS >] },
|
||||
unsafe { &[< $name _DATA >] },
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
queue!(QUEUE, i32, 10);
|
||||
|
||||
#[test]
|
||||
fn fast_path() {
|
||||
QUEUE.push(1);
|
||||
assert!(QUEUE.pop() == 1);
|
||||
assert!(QUEUE.try_pop().is_none());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
use core::{
|
||||
cell::UnsafeCell,
|
||||
marker::{PhantomData, PhantomPinned},
|
||||
ops::{Deref, DerefMut},
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{
|
||||
rt_cond, rt_mutex, rt_rwlock, rt_rwlock_rdlock, rt_rwlock_unlock, rt_rwlock_wrlock,
|
||||
},
|
||||
list::list_init,
|
||||
sync::semaphore::c_sem_init,
|
||||
};
|
||||
|
||||
pub struct RwLock<T: ?Sized> {
|
||||
l: UnsafeCell<rt_rwlock>,
|
||||
_pin_marker: PhantomPinned,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
|
||||
unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
|
||||
impl<T: ?Sized> UnwindSafe for RwLock<T> {}
|
||||
impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}
|
||||
|
||||
impl<T> RwLock<T> {
|
||||
pub const fn init(lock: &'static RwLock<T>, t: T) -> RwLock<T> {
|
||||
let l = unsafe { &*(lock.l.get() as *const rt_rwlock) };
|
||||
RwLock {
|
||||
l: UnsafeCell::new(rt_rwlock {
|
||||
m: rt_mutex {
|
||||
holder: 0,
|
||||
wait_list: list_init(&l.m.wait_list),
|
||||
list: list_init(&l.m.list),
|
||||
},
|
||||
rcond: rt_cond {
|
||||
sem: c_sem_init(&l.rcond.sem, 0, 0),
|
||||
},
|
||||
wcond: rt_cond {
|
||||
sem: c_sem_init(&l.wcond.sem, 0, 0),
|
||||
},
|
||||
num_readers: 0,
|
||||
num_writers: 0,
|
||||
}),
|
||||
_pin_marker: PhantomPinned,
|
||||
data: UnsafeCell::new(t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> RwLock<T> {
|
||||
pub fn read(&self) -> RwLockReadGuard<'_, T> {
|
||||
unsafe {
|
||||
rt_rwlock_rdlock(self.l.get());
|
||||
}
|
||||
RwLockReadGuard::new(self)
|
||||
}
|
||||
|
||||
pub fn write(&self) -> RwLockWriteGuard<'_, T> {
|
||||
unsafe {
|
||||
rt_rwlock_wrlock(self.l.get());
|
||||
}
|
||||
RwLockWriteGuard::new(self)
|
||||
}
|
||||
|
||||
// Probably not useful because this RwLock must be static.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.data.get_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
|
||||
lock: &'a RwLock<T>,
|
||||
_unsend_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> RwLockReadGuard<'_, T> {
|
||||
fn new(lock: &RwLock<T>) -> RwLockReadGuard<T> {
|
||||
RwLockReadGuard {
|
||||
lock,
|
||||
_unsend_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {}
|
||||
|
||||
impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { rt_rwlock_unlock(self.lock.l.get()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
|
||||
lock: &'a RwLock<T>,
|
||||
_unsend_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> RwLockWriteGuard<'_, T> {
|
||||
fn new(lock: &RwLock<T>) -> RwLockWriteGuard<T> {
|
||||
RwLockWriteGuard {
|
||||
lock,
|
||||
_unsend_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + Sync> Sync for RwLockWriteGuard<'_, T> {}
|
||||
|
||||
impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { rt_rwlock_unlock(self.lock.l.get()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! rwlock {
|
||||
($name: ident, $type: ty, $data: expr) => {
|
||||
static $name: $crate::sync::RwLock<$type> = $crate::sync::RwLock::init(&$name, $data);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn fast_path() {
|
||||
rwlock!(LOCK, i32, 0);
|
||||
*LOCK.write() += 1;
|
||||
assert!(*LOCK.read() == 1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
use core::{
|
||||
cell::UnsafeCell,
|
||||
ffi::c_int,
|
||||
marker::PhantomPinned,
|
||||
mem::{size_of, transmute},
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
ptr::null_mut,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{
|
||||
rt_atomic_int, rt_sem, rt_sem_post, rt_sem_post_n, rt_sem_timedwait, rt_sem_trywait,
|
||||
rt_sem_wait, rt_syscall, rt_syscall_args, rt_syscall_args_sem_post, rt_syscall_record,
|
||||
},
|
||||
list::list_init,
|
||||
tick::Utick,
|
||||
};
|
||||
|
||||
pub struct Semaphore {
|
||||
s: UnsafeCell<rt_sem>,
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
|
||||
unsafe impl Send for Semaphore {}
|
||||
unsafe impl Sync for Semaphore {}
|
||||
impl UnwindSafe for Semaphore {}
|
||||
impl RefUnwindSafe for Semaphore {}
|
||||
|
||||
// Also used by cond and queue.
|
||||
pub(crate) const fn c_sem_init(s: &'static rt_sem, value: i32, max_value: i32) -> rt_sem {
|
||||
rt_sem {
|
||||
value: value as rt_atomic_int,
|
||||
max_value: max_value as c_int,
|
||||
wait_list: list_init(&s.wait_list),
|
||||
num_waiters: 0,
|
||||
post_pending: 0,
|
||||
post_record: rt_syscall_record {
|
||||
next: null_mut(),
|
||||
args: unsafe {
|
||||
let mut x: rt_syscall_args = transmute([0u8; size_of::<rt_syscall_args>()]);
|
||||
x.sem_post = rt_syscall_args_sem_post {
|
||||
sem: s as *const rt_sem as *mut rt_sem,
|
||||
n: 1,
|
||||
};
|
||||
x
|
||||
},
|
||||
syscall: rt_syscall::RT_SYSCALL_SEM_POST,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl Semaphore {
|
||||
pub const fn init(sem: &'static Semaphore, value: i32, max_value: i32) -> Semaphore {
|
||||
let s = unsafe { &*(sem.s.get() as *const rt_sem) };
|
||||
Semaphore {
|
||||
s: UnsafeCell::new(c_sem_init(s, value, max_value)),
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn post(&self) {
|
||||
unsafe { rt_sem_post(self.s.get()) }
|
||||
}
|
||||
|
||||
pub fn post_n(&self, n: i32) {
|
||||
unsafe { rt_sem_post_n(self.s.get(), n) }
|
||||
}
|
||||
|
||||
pub fn wait(&self) {
|
||||
unsafe { rt_sem_wait(self.s.get()) }
|
||||
}
|
||||
|
||||
pub fn try_wait(&self) -> bool {
|
||||
unsafe { rt_sem_trywait(self.s.get()) }
|
||||
}
|
||||
|
||||
pub fn timed_wait(&self, ticks: Utick) -> bool {
|
||||
unsafe { rt_sem_timedwait(self.s.get(), ticks) }
|
||||
}
|
||||
|
||||
pub fn acquire(&self) {
|
||||
self.wait()
|
||||
}
|
||||
|
||||
pub fn release(&self) {
|
||||
self.post()
|
||||
}
|
||||
|
||||
pub fn access(&self) -> SemaphoreGuard {
|
||||
self.acquire();
|
||||
SemaphoreGuard { sem: self }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SemaphoreGuard<'a> {
|
||||
sem: &'a Semaphore,
|
||||
}
|
||||
|
||||
impl<'a> Drop for SemaphoreGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.sem.release();
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! semaphore {
|
||||
($name: ident, $count: expr, $max: expr) => {
|
||||
static $name: $crate::sync::Semaphore = $crate::sync::Semaphore::init(&$name, $count, $max);
|
||||
};
|
||||
|
||||
($name: ident, $count: expr) => {
|
||||
$crate::semaphore!($name, $count, i32::MAX);
|
||||
};
|
||||
|
||||
($name: ident) => {
|
||||
$crate::semaphore!($name, 0, i32::MAX);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! semaphore_binary {
|
||||
($name: ident) => {
|
||||
$crate::semaphore!($name, 0, 1);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn fast_path() {
|
||||
semaphore!(SEM);
|
||||
SEM.post();
|
||||
SEM.post();
|
||||
assert!(SEM.try_wait());
|
||||
assert!(SEM.try_wait());
|
||||
assert!(!SEM.try_wait());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binary_fast_path() {
|
||||
semaphore_binary!(SEM);
|
||||
SEM.post();
|
||||
SEM.post();
|
||||
assert!(SEM.try_wait());
|
||||
assert!(!SEM.try_wait());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
use core::{
|
||||
cell::UnsafeCell,
|
||||
ffi::{c_void, CStr},
|
||||
marker::PhantomPinned,
|
||||
mem::{size_of, transmute},
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
ptr::null_mut,
|
||||
str,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{
|
||||
rt_context_init, rt_syscall, rt_syscall_args, rt_syscall_pend, rt_syscall_push,
|
||||
rt_syscall_record, rt_task, rt_task_drop_privilege, rt_task_enable_fp, rt_task_exit,
|
||||
rt_task_name, rt_task_sleep, rt_task_sleep_periodic, rt_task_state, rt_task_yield,
|
||||
RT_STACK_MIN,
|
||||
},
|
||||
list::list_init,
|
||||
tick::Utick,
|
||||
};
|
||||
|
||||
pub const STACK_MIN: usize = RT_STACK_MIN as usize;
|
||||
|
||||
pub struct Task {
|
||||
t: UnsafeCell<rt_task>,
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
|
||||
unsafe impl Send for Task {}
|
||||
unsafe impl Sync for Task {}
|
||||
impl UnwindSafe for Task {}
|
||||
impl RefUnwindSafe for Task {}
|
||||
|
||||
pub fn yield_() {
|
||||
unsafe { rt_task_yield() }
|
||||
}
|
||||
|
||||
pub fn sleep(ticks: Utick) {
|
||||
unsafe { rt_task_sleep(ticks) }
|
||||
}
|
||||
|
||||
pub fn sleep_periodic(last_wake_tick: &mut Utick, period: Utick) {
|
||||
unsafe { rt_task_sleep_periodic(last_wake_tick, period) }
|
||||
}
|
||||
|
||||
pub fn exit() -> ! {
|
||||
unsafe { rt_task_exit() }
|
||||
}
|
||||
|
||||
pub fn name() -> &'static str {
|
||||
unsafe { str::from_utf8_unchecked(CStr::from_ptr(rt_task_name()).to_bytes()) }
|
||||
}
|
||||
|
||||
pub fn enable_fp() {
|
||||
unsafe { rt_task_enable_fp() }
|
||||
}
|
||||
|
||||
pub fn drop_privilege() {
|
||||
unsafe { rt_task_drop_privilege() }
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub const fn init(task: &'static Task, name: &'static CStr, priority: u32) -> Task {
|
||||
let t = unsafe { &*(task.t.get() as *const rt_task) };
|
||||
Task {
|
||||
t: UnsafeCell::new(rt_task {
|
||||
list: list_init(&t.list),
|
||||
sleep_list: list_init(&t.sleep_list),
|
||||
ctx: null_mut(),
|
||||
list_head: null_mut(),
|
||||
record: rt_syscall_record {
|
||||
next: null_mut(),
|
||||
args: unsafe { transmute([0u8; size_of::<rt_syscall_args>()]) },
|
||||
syscall: rt_syscall::RT_SYSCALL_TASK_READY,
|
||||
},
|
||||
priority,
|
||||
base_priority: priority,
|
||||
state: rt_task_state::RT_TASK_STATE_EXITED,
|
||||
wake_tick: 0,
|
||||
mutex_list: list_init(&t.mutex_list),
|
||||
name: name.as_ptr(),
|
||||
}),
|
||||
_pin_marker: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self, f: extern "C" fn(), stack: &mut [u8]) {
|
||||
let t = self.t.get_mut();
|
||||
unsafe {
|
||||
t.ctx = rt_context_init(Some(f), stack.as_mut_ptr() as *mut c_void, stack.len());
|
||||
rt_syscall_push(&mut t.record);
|
||||
rt_syscall_pend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! task {
|
||||
($fn: ident $params: tt, $stack_size: expr, $priority: expr) => {
|
||||
$crate::paste::paste! {
|
||||
{
|
||||
static mut [< $fn:upper _TASK >]: rt::task::Task = rt::task::Task::init(
|
||||
unsafe { &[< $fn:upper _TASK >] },
|
||||
unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(core::concat!(core::stringify!($fn), "\0").as_bytes()) },
|
||||
$priority,
|
||||
);
|
||||
static mut [< $fn:upper _TASK_STACK >]: [u8; $stack_size] = [0u8; $stack_size];
|
||||
extern "C" fn [< $fn _c >]() {
|
||||
$fn $params
|
||||
}
|
||||
unsafe {
|
||||
[< $fn:upper _TASK >].start([< $fn _c >], [< $fn:upper _TASK_STACK >].as_mut_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($fn: ident, $stack_size: expr, $priority: expr) => {
|
||||
rt::task!($fn(), $stack_size, $priority);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
use crate::bindings::rt_tick_count;
|
||||
|
||||
pub type Utick = core::ffi::c_ulong;
|
||||
|
||||
pub fn count() -> Utick {
|
||||
unsafe { rt_tick_count() }
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cargo build --release
|
||||
|
||||
trap 'if [ "$?" != "0" ]; then echo "test failed!"; fi' EXIT
|
||||
|
||||
examples=(donate empty float mutex notify once queue rwlock semaphore simple sleep water-semaphore)
|
||||
|
||||
for e in "${examples[@]}"; do
|
||||
cargo run --release --example "$e"
|
||||
done
|
|
@ -0,0 +1,13 @@
|
|||
#include <rt/barrier.h>
|
||||
#include <rt/cond.h>
|
||||
#include <rt/cycle.h>
|
||||
#include <rt/idle.h>
|
||||
#include <rt/mutex.h>
|
||||
#include <rt/notify.h>
|
||||
#include <rt/once.h>
|
||||
#include <rt/queue.h>
|
||||
#include <rt/rt.h>
|
||||
#include <rt/rwlock.h>
|
||||
#include <rt/sem.h>
|
||||
#include <rt/task.h>
|
||||
#include <rt/tick.h>
|
Loading…
Reference in New Issue