From 67a8e9c3729d7d899a1c5772d2d5d6fd9f2dc4c9 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Wed, 14 Aug 2024 21:57:06 +0200 Subject: [PATCH] feat(tests): add `threading-lock` test This demonstrates how multiple threads can wait for the same lock and get unblocked by priority, and within a prio by FIFO order. --- Cargo.lock | 10 +++++ Cargo.toml | 1 + tests/laze.yml | 1 + tests/threading-lock/Cargo.toml | 12 ++++++ tests/threading-lock/laze.yml | 6 +++ tests/threading-lock/src/main.rs | 72 ++++++++++++++++++++++++++++++++ 6 files changed, 102 insertions(+) create mode 100644 tests/threading-lock/Cargo.toml create mode 100644 tests/threading-lock/laze.yml create mode 100644 tests/threading-lock/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 786c531c7..ae5e39d86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4319,6 +4319,16 @@ dependencies = [ "riot-rs-boards", ] +[[package]] +name = "threading-lock" +version = "0.1.0" +dependencies = [ + "embassy-executor", + "portable-atomic", + "riot-rs", + "riot-rs-boards", +] + [[package]] name = "time" version = "0.3.36" diff --git a/Cargo.toml b/Cargo.toml index b7cf96b22..c56d2f577 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "tests/gpio", "tests/gpio-interrupt-nrf", "tests/gpio-interrupt-stm32", + "tests/threading-lock", ] exclude = ["src/lib"] diff --git a/tests/laze.yml b/tests/laze.yml index 881af4c9e..36505d1b3 100644 --- a/tests/laze.yml +++ b/tests/laze.yml @@ -3,3 +3,4 @@ subdirs: - gpio - gpio-interrupt-nrf - gpio-interrupt-stm32 + - threading-lock diff --git a/tests/threading-lock/Cargo.toml b/tests/threading-lock/Cargo.toml new file mode 100644 index 000000000..14d8711c6 --- /dev/null +++ b/tests/threading-lock/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "threading-lock" +version = "0.1.0" +authors = ["Elena Frank "] +edition.workspace = true +publish = false + +[dependencies] +embassy-executor = { workspace = true } +riot-rs = { path = "../../src/riot-rs", features = ["threading", "time"] } +riot-rs-boards = { path = "../../src/riot-rs-boards" } +portable-atomic = "1.6.0" diff --git a/tests/threading-lock/laze.yml b/tests/threading-lock/laze.yml new file mode 100644 index 000000000..a5be23acf --- /dev/null +++ b/tests/threading-lock/laze.yml @@ -0,0 +1,6 @@ +apps: + - name: threading-lock + selects: + - ?release + - executor-thread + - sw/threading diff --git a/tests/threading-lock/src/main.rs b/tests/threading-lock/src/main.rs new file mode 100644 index 000000000..7ecdc1d08 --- /dev/null +++ b/tests/threading-lock/src/main.rs @@ -0,0 +1,72 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] +#![feature(used_with_arg)] + +use portable_atomic::{AtomicUsize, Ordering}; +use riot_rs::thread::{lock::Lock, thread_flags, ThreadId}; + +static LOCK: Lock = Lock::new(); +static RUN_ORDER: AtomicUsize = AtomicUsize::new(0); +static LOCK_ORDER: AtomicUsize = AtomicUsize::new(0); + +#[riot_rs::thread(autostart, priority = 1)] +fn thread0() { + assert_eq!(RUN_ORDER.fetch_add(1, Ordering::AcqRel), 0); + + LOCK.acquire(); + + // Unblock other threads in the order of their IDs. + // + // Because all other threads have higher priorities, setting + // a flag will each time cause a context switch and give each + // thread the chance to run and try acquire the lock. + thread_flags::set(ThreadId::new(1), 0b1); + thread_flags::set(ThreadId::new(2), 0b1); + thread_flags::set(ThreadId::new(3), 0b1); + + assert_eq!(LOCK_ORDER.fetch_add(1, Ordering::AcqRel), 0); + + LOCK.release(); + + // Wait for other threads to complete. + thread_flags::wait_all(0b111); + riot_rs::debug::log::info!("Test passed!"); +} + +#[riot_rs::thread(autostart, priority = 2)] +fn thread1() { + thread_flags::wait_one(0b1); + assert_eq!(RUN_ORDER.fetch_add(1, Ordering::AcqRel), 1); + + LOCK.acquire(); + assert_eq!(LOCK_ORDER.fetch_add(1, Ordering::AcqRel), 2); + LOCK.release(); + + thread_flags::set(ThreadId::new(0), 0b1); +} + +#[riot_rs::thread(autostart, priority = 3)] +fn thread2() { + thread_flags::wait_one(0b1); + assert_eq!(RUN_ORDER.fetch_add(1, Ordering::AcqRel), 2); + + LOCK.acquire(); + // Expect to be the second thread that acquires the lock. + assert_eq!(LOCK_ORDER.fetch_add(1, Ordering::AcqRel), 1); + LOCK.release(); + + thread_flags::set(ThreadId::new(0), 0b10); +} + +#[riot_rs::thread(autostart, priority = 2)] +fn thread3() { + thread_flags::wait_one(0b1); + assert_eq!(RUN_ORDER.fetch_add(1, Ordering::AcqRel), 3); + + LOCK.acquire(); + assert_eq!(LOCK_ORDER.fetch_add(1, Ordering::AcqRel), 3); + LOCK.release(); + + thread_flags::set(ThreadId::new(0), 0b100); +}