Skip to content

Support for Zephyr kernel objects #5

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

Merged
merged 26 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bacd922
zephyr: sys::sync: move semaphores into their own module
d3zd3z Oct 15, 2024
c0434a2
zephyr: kobj_define: Add sys Mutex and sys Condvar
d3zd3z Oct 15, 2024
4d4ee9b
zephyr: sys: sync: Mutex: Implement Send/Sync
d3zd3z Oct 15, 2024
2731deb
samples: philosophers: Add sysmutex version
d3zd3z Oct 15, 2024
102478e
zephyr: sync: Create Mutex/Condvar
d3zd3z Oct 15, 2024
2a8a82d
samples: philosophers: Add sync using Mutex/Condvar
d3zd3z Oct 15, 2024
1c8d82a
zephyr: sys: Create queue type based on k_queue
d3zd3z Oct 15, 2024
9187024
zephyr: sync: channel: Create unbounded channels
d3zd3z Oct 15, 2024
1e62073
samples: philosophers: Add implementation for channels
d3zd3z Oct 15, 2024
099aaa7
samples: philosophers: Add statistics
d3zd3z Oct 16, 2024
2413a53
zephyr: Remove 'unsafe' from sys::Mutex::unlock
d3zd3z Oct 17, 2024
2717d8b
zephyr: sync: Remove unsafe block from unlock
d3zd3z Oct 17, 2024
93448bf
zephyr: object: Make StaticKernelObject::new unsafe
d3zd3z Oct 17, 2024
058740f
zephyr: object: Reorder declarations
d3zd3z Oct 17, 2024
43c0c8a
zephyr: sys: sync: Remove Clone from Mutex and Semaphore
d3zd3z Oct 21, 2024
aaad7ee
zephry: object: Create Fixed concept
d3zd3z Oct 24, 2024
fba3393
zephyr: sys: sync: sempahore: Add dynamic support
d3zd3z Oct 24, 2024
ad0924d
samples: philosophers: Add dynamic semaphore example
d3zd3z Oct 24, 2024
8cdf4a6
zephyr: sys: sync: Add Mutex::new()
d3zd3z Oct 24, 2024
56f1d31
samples: philosophers: Change sys::Mutex to dynamic
d3zd3z Oct 24, 2024
4850c4d
zephyr: sys: queue: Add Queue::new()
d3zd3z Oct 24, 2024
f12e568
zephyr: sync: channel: Fully dynamically allocated
d3zd3z Oct 24, 2024
150ec35
samples: philosophers: Convert channel sync to dynamic
d3zd3z Oct 24, 2024
70e2710
zephyr: sync: Add Mutex::new and Condvar::new
d3zd3z Oct 24, 2024
7758fe5
samples: philosophers: Switch condsync to dynamic
d3zd3z Oct 24, 2024
d78650f
CI: Clean up the work the CI build does
d3zd3z Oct 25, 2024
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
12 changes: 8 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
path: zephyr-rust-lang
path: apptest

- name: Set up Python
uses: actions/setup-python@v5
Expand All @@ -36,7 +36,7 @@ jobs:
- name: Setup Zephyr project
uses: zephyrproject-rtos/action-zephyr-setup@v1
with:
app-path: zephyr-rust-lang
app-path: apptest
manifest-file-name: ci-manifest.yml
toolchains: arm-zephyr-eabi:riscv64-zephyr-elf

Expand All @@ -51,9 +51,13 @@ jobs:
rustup target add thumbv8m.main-none-eabi

- name: Build firmware
working-directory: zephyr-rust-lang
working-directory: apptest
shell: bash
run: |
cargo --version

west twister -T samples -T tests -v --inline-logs --integration
lscpu
df -h

west twister -M all -T samples -T tests -v --inline-logs --integration -j 4 \
$(cat etc/platforms.txt)
8 changes: 8 additions & 0 deletions etc/platforms.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-p mps2/an385
-p mps2/an521/cpu0
-p qemu_cortex_m0
-p qemu_cortex_m3
-p qemu_riscv32
-p qemu_riscv32/qemu_virt_riscv32/smp
-p qemu_riscv64
-p qemu_riscv64/qemu_virt_riscv64/smp
30 changes: 27 additions & 3 deletions samples/philosophers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,36 @@ source "Kconfig.zephyr"

choice
prompt "Select Synchronization implementation"
default SYNC_SYS_SEMAPHORE
default SYNC_CHANNEL

config SYNC_SYS_SEMAPHORE
bool "Use sys::Semaphore to synchronize forks"
help
Use to have the dining philosophers sample use sys::Semaphore, with one per form,
to synchronize.
Use to have the dining philosophers sample use sys::Semaphore, with one per fork, to
synchronize.

config SYNC_SYS_DYNAMIC_SEMAPHORE
bool "Use a dynamic sys::Semaphore to synchronize forks"
help
Use to have the dining philosophers sample use sys::Semaphore, with one per fork, to
synchronize. The Semaphores will be dynamically allocated.

config SYNC_SYS_MUTEX
bool "Use sys::Semaphore to synchronize forks"
help
Use to have the dining philosophers sample use sys::Mutex, with one per fork, to
synchronize.

config SYNC_CONDVAR
bool "Use sync::Condvar and sync::Mutex to synchronize forks"
help
Use to have the dining philosophers sample use a single data structure, protected
by a sync::Mutex and coordinated with a sync::Condvar, to synchronize.

config SYNC_CHANNEL
bool "Use sync::channel to synchronize forks"
help
Use to have the dining philosophers sample use a worker thread, communicating via
channels to synchronize.

endchoice
25 changes: 21 additions & 4 deletions samples/philosophers/sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ common:
regex:
# Match the statistics, and make sure that each philosopher has at least 10 (two digits)
# meals.
# - "^\\[\\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}\\]"
#
# Until the stastics have been implemented, just match on one of the children thinking
- "^Child 5 thinking \\(\\d+ ticks.*"
- "^\\[\\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}\\]"
tags: rust
filter: CONFIG_RUST_SUPPORTED
tests:
Expand All @@ -20,3 +17,23 @@ tests:
min_ram: 32
extra_configs:
- CONFIG_SYNC_SYS_SEMAPHORE=y
sample.rust.philosopher.dynsemaphore:
tags: introduction
min_ram: 32
extra_configs:
- CONFIG_SYNC_SYS_DYNAMIC_SEMAPHORE=y
sample.rust.philosopher.sysmutex:
tags: introduction
min_ram: 32
extra_configs:
- CONFIG_SYNC_SYS_MUTEX=y
sample.rust.philosopher.condvar:
tags: introduction
min_ram: 32
extra_configs:
- CONFIG_SYNC_CONDVAR=y
sample.rust.philosopher.channel:
tags: introduction
min_ram: 32
extra_configs:
- CONFIG_SYNC_CHANNEL=y
167 changes: 167 additions & 0 deletions samples/philosophers/src/channel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Copyright (c) 2023 Linaro LTD
// SPDX-License-Identifier: Apache-2.0

//! Synchronizer using channels
//!
//! Synchronize between the philosophers using channels to communicate with a thread that handles
//! the messages.

extern crate alloc;

use alloc::vec::Vec;
use alloc::boxed::Box;

use zephyr::sync::channel::{self, Receiver, Sender};
use zephyr::{
kobj_define,
sync::Arc,
};

use crate::{NUM_PHIL, ForkSync};

/// An implementation of ForkSync that uses a server commnicated with channels to perform the
/// synchronization.
#[derive(Debug)]
struct ChannelSync {
command: Sender<Command>,
reply_send: Sender<()>,
reply_recv: Receiver<()>,
}

#[derive(Debug)]
enum Command {
Acquire(usize, Sender<()>),
Release(usize),
}

/// This implements a single Fork on the server side for the ChannelSync.
enum ChannelFork {
/// The fork is free,
Free,
/// The work is in use, nobody is waiting.
InUse,
/// The fork is in use, and someone is waiting on it.
InUseWait(Sender<()>),
}

impl Default for ChannelFork {
fn default() -> Self {
ChannelFork::Free
}
}

impl ChannelFork {
/// Attempt to aquire the work. If it is free, reply to the sender, otherwise, track them to
/// reply to them when the fork is freed up.
fn acquire(&mut self, reply: Sender<()>) {
// For debugging, just stop here, and wait for a stack report.
let next = match *self {
ChannelFork::Free => {
// Reply immediately that this fork is free.
reply.send(()).unwrap();
ChannelFork::InUse
}
ChannelFork::InUse => {
// The fork is being used, become the waiter.
ChannelFork::InUseWait(reply)
}
ChannelFork::InUseWait(_) => {
// There is already a wait. Something has gone wrong as this should never happen.
panic!("Mutliple waiters on fork");
}
};
*self = next;
}

/// Release the fork. This is presumably sent from the same sender that requested it, although
/// this is not checked.
fn release(&mut self) {
let next = match self {
ChannelFork::Free => {
// An error case, the fork is not in use, it shouldn't be freed.
panic!("Release of fork that is not in use");
}
ChannelFork::InUse => {
// The fork is in use, and nobody else is waiting.
ChannelFork::Free
}
ChannelFork::InUseWait(waiter) => {
// The fork is in use by us, and someone else is waiting. Tell the other waiter
// they now have the work.
waiter.send(()).unwrap();
ChannelFork::InUse
}
};
*self = next;
}
}

impl ChannelSync {
pub fn new(
command: Sender<Command>,
reply: (Sender<()>, Receiver<()>)) -> ChannelSync
{
ChannelSync {
command,
reply_send: reply.0,
reply_recv: reply.1,
}
}
}

/// Generate a syncer out of a ChannelSync.
#[allow(dead_code)]
pub fn get_channel_syncer() -> Vec<Arc<dyn ForkSync>> {
let (cq_send, cq_recv) = channel::unbounded();
let reply_queues = [(); NUM_PHIL].each_ref().map(|()| {
channel::unbounded()
});
let syncer = reply_queues.into_iter().map(|rqueue| {
let item = Box::new(ChannelSync::new(cq_send.clone(), rqueue))
as Box<dyn ForkSync>;
Arc::from(item)
});

let channel_child = CHANNEL_THREAD.init_once(CHANNEL_STACK.init_once(()).unwrap()).unwrap();
channel_child.spawn(move || {
channel_thread(cq_recv);
});

syncer.collect()
}

/// The thread that handles channel requests.
///
/// Spawned when we are using the channel syncer.
fn channel_thread(cq_recv: Receiver<Command>) {
let mut forks = [(); NUM_PHIL].each_ref().map(|_| ChannelFork::default());

loop {
match cq_recv.recv().unwrap() {
Command::Acquire(fork, reply) => {
forks[fork].acquire(reply);
}
Command::Release(fork) => {
forks[fork].release();
}
}
}
}

impl ForkSync for ChannelSync {
fn take(&self, index: usize) {
self.command.send(Command::Acquire(index, self.reply_send.clone())).unwrap();
// When the reply comes, we know we have the resource.
self.reply_recv.recv().unwrap();
}

fn release(&self, index: usize) {
self.command.send(Command::Release(index)).unwrap();
// Release does not have a reply.
}
}

kobj_define! {
static CHANNEL_STACK: ThreadStack<2054>;
static CHANNEL_THREAD: StaticThread;
}
50 changes: 50 additions & 0 deletions samples/philosophers/src/condsync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2024 Linaro LTD
// SPDX-License-Identifier: Apache-2.0

//! # sync::Mutex/sync::Condvar implementation of ForkSync
//!
//! This implementation of the Fork synchronizer uses a single data object, protected by a
//! `sync::Mutex`, and coordinated by a `sync::Condvar`.

use crate::{
ForkSync,
NUM_PHIL,
};
use zephyr::sync::Mutex;
use zephyr::sync::Condvar;
// use zephyr::time::Forever;

#[derive(Debug)]
pub struct CondSync {
/// The lock that holds the flag for each philosopher.
lock: Mutex<[bool; NUM_PHIL]>,
/// Condition variable to wake other threads.
cond: Condvar,
}

impl CondSync {
#[allow(dead_code)]
pub fn new() -> CondSync {
CondSync {
lock: Mutex::new([false; NUM_PHIL]),
cond: Condvar::new(),
}
}
}

impl ForkSync for CondSync {
fn take(&self, index: usize) {
let mut lock = self.lock.lock().unwrap();
while lock[index] {
lock = self.cond.wait(lock).unwrap();
}
lock[index] = true;
}

fn release(&self, index: usize) {
let mut lock = self.lock.lock().unwrap();
lock[index] = false;
// No predictible waiter, so must wake everyone.
self.cond.notify_all();
}
}
51 changes: 51 additions & 0 deletions samples/philosophers/src/dynsemsync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) 2023 Linaro LTD
// SPDX-License-Identifier: Apache-2.0

//! Semaphore based sync.
//!
//! This is the simplest type of sync, which uses a single semaphore per fork.

extern crate alloc;

use alloc::vec::Vec;
use alloc::boxed::Box;

use zephyr::{
sync::Arc, sys::sync::Semaphore, time::Forever
};

use crate::{ForkSync, NUM_PHIL};

#[derive(Debug)]
pub struct SemSync {
/// The forks for this philosopher. This is a big excessive, as we really don't need all of
/// them, but the ForSync code uses the index here.
forks: [Arc<Semaphore>; NUM_PHIL],
}

impl ForkSync for SemSync {
fn take(&self, index: usize) {
self.forks[index].take(Forever).unwrap();
}

fn release(&self, index: usize) {
self.forks[index].give();
}
}

#[allow(dead_code)]
pub fn dyn_semaphore_sync() -> Vec<Arc<dyn ForkSync>> {
let forks = [(); NUM_PHIL].each_ref().map(|()| {
Arc::new(Semaphore::new(1, 1).unwrap())
});

let syncers = (0..NUM_PHIL).map(|_| {
let syncer = SemSync {
forks: forks.clone(),
};
let item = Box::new(syncer) as Box<dyn ForkSync>;
Arc::from(item)
}).collect();

syncers
}
Loading