Skip to content

Commit a53f76b

Browse files
committed
zephry: Start of 'work'
The work module provides various interfaces for interacting with Zephyr work queues. This is a beginning of that support. Signed-off-by: David Brown <[email protected]>
1 parent d66701b commit a53f76b

File tree

11 files changed

+893
-0
lines changed

11 files changed

+893
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(work_philosophers)
7+
8+
rust_cargo_application()

samples/work-philosophers/Cargo.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright (c) 2024 Linaro LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
[package]
5+
# This must be rustapp for now.
6+
name = "rustapp"
7+
version = "3.7.0"
8+
edition = "2021"
9+
description = "A sample hello world application in Rust"
10+
license = "Apache-2.0 or MIT"
11+
12+
[lib]
13+
crate-type = ["staticlib"]
14+
15+
[dependencies]
16+
zephyr = "3.7.0"
17+
18+
# Dependencies that are used by build.rs.
19+
[build-dependencies]
20+
zephyr-build = "3.7.0"
21+
22+
[profile.release]
23+
debug-assertions = true
24+
overflow-checks = true
25+
debug = true

samples/work-philosophers/Kconfig

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright (c) 2024 Linaro LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
mainmenu "Rust Dining Philosphers"
5+
6+
source "Kconfig.zephyr"
7+
8+
choice
9+
prompt "Select Synchronization implementation"
10+
default SYNC_CHANNEL
11+
12+
config SYNC_SYS_SEMAPHORE
13+
bool "Use sys::Semaphore to synchronize forks"
14+
help
15+
Use to have the dining philosophers sample use sys::Semaphore, with one per fork, to
16+
synchronize.
17+
18+
config SYNC_SYS_DYNAMIC_SEMAPHORE
19+
bool "Use a dynamic sys::Semaphore to synchronize forks"
20+
help
21+
Use to have the dining philosophers sample use sys::Semaphore, with one per fork, to
22+
synchronize. The Semaphores will be dynamically allocated.
23+
24+
config SYNC_SYS_MUTEX
25+
bool "Use sys::Semaphore to synchronize forks"
26+
help
27+
Use to have the dining philosophers sample use sys::Mutex, with one per fork, to
28+
synchronize.
29+
30+
config SYNC_CONDVAR
31+
bool "Use sync::Condvar and sync::Mutex to synchronize forks"
32+
help
33+
Use to have the dining philosophers sample use a single data structure, protected
34+
by a sync::Mutex and coordinated with a sync::Condvar, to synchronize.
35+
36+
config SYNC_CHANNEL
37+
bool "Use sync::channel to synchronize forks"
38+
help
39+
Use to have the dining philosophers sample use a worker thread, communicating via
40+
channels to synchronize.
41+
42+
config SYNC_WORKQUEUE
43+
bool "Use workqueues to simulate the philosophers"
44+
help
45+
Use workqueues to simulate the philosophers.
46+
47+
endchoice
48+
49+
if SYNC_CHANNEL
50+
config USE_BOUNDED_CHANNELS
51+
bool "Should channel sync use bounded channels?"
52+
default y
53+
help
54+
If set, the channel-based communication will use bounded channels with bounds calculated
55+
to not ever block.
56+
endif
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) 2024 Linaro LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# This board doesn't have a serial console, so use RTT.
5+
CONFIG_UART_CONSOLE=n
6+
CONFIG_RTT_CONSOLE=y
7+
CONFIG_USE_SEGGER_RTT=y

samples/work-philosophers/build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright (c) 2023 Linaro LTD
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// This crate needs access to kconfig variables. This is an example of how to do that. The
5+
// zephyr-build must be a build dependency.
6+
7+
fn main() {
8+
zephyr_build::export_bool_kconfig();
9+
}

samples/work-philosophers/prj.conf

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (c) 2024 Linaro LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
CONFIG_RUST=y
5+
CONFIG_RUST_ALLOC=y
6+
CONFIG_MAIN_STACK_SIZE=8192
7+
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
8+
9+
CONFIG_POLL=y
10+
11+
# CONFIG_USERSPACE=y
12+
13+
# Some debugging
14+
CONFIG_THREAD_MONITOR=y
15+
CONFIG_THREAD_ANALYZER=y
16+
CONFIG_THREAD_ANALYZER_USE_PRINTK=y
17+
CONFIG_THREAD_ANALYZER_AUTO=n
18+
# CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=15

samples/work-philosophers/sample.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
sample:
2+
description: Philosphers, in Rust
3+
name: philosophers rust
4+
common:
5+
harness: console
6+
harness_config:
7+
type: one_line
8+
regex:
9+
# Match the statistics, and make sure that each philosopher has at least 10 (two digits)
10+
# meals.
11+
- "c:\\[\\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}\\]"
12+
tags: rust
13+
filter: CONFIG_RUST_SUPPORTED
14+
tests:
15+
sample.rust.philosopher.semaphore:
16+
tags: introduction
17+
min_ram: 32
18+
extra_configs:
19+
- CONFIG_SYNC_SYS_SEMAPHORE=y
20+
sample.rust.philosopher.dynsemaphore:
21+
tags: introduction
22+
min_ram: 32
23+
extra_configs:
24+
- CONFIG_SYNC_SYS_DYNAMIC_SEMAPHORE=y
25+
sample.rust.philosopher.sysmutex:
26+
tags: introduction
27+
min_ram: 32
28+
extra_configs:
29+
- CONFIG_SYNC_SYS_MUTEX=y
30+
sample.rust.philosopher.condvar:
31+
tags: introduction
32+
min_ram: 32
33+
extra_configs:
34+
- CONFIG_SYNC_CONDVAR=y
35+
sample.rust.philosopher.channel_bounded:
36+
tags: introduction
37+
min_ram: 32
38+
extra_configs:
39+
- CONFIG_SYNC_CHANNEL=y
40+
- CONFIG_USE_BOUNDED_CHANNELS=y
41+
sample.rust.philosopher.channel_unbounded:
42+
tags: introduction
43+
min_ram: 32
44+
extra_configs:
45+
- CONFIG_SYNC_CHANNEL=y
46+
- CONFIG_USE_BOUNDED_CHANNELS=n

samples/work-philosophers/src/lib.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Copyright (c) 2023 Linaro LTD
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![no_std]
5+
6+
// Cargo tries to detect configs that have typos in them. Unfortunately, the Zephyr Kconfig system
7+
// uses a large number of Kconfigs and there is no easy way to know which ones might conceivably be
8+
// valid. This prevents a warning about each cfg that is used.
9+
#![allow(unexpected_cfgs)]
10+
11+
extern crate alloc;
12+
13+
use zephyr::{printkln, sync::Arc, task::Wake, time::{Duration, NoWait}, work::futures::FutureWorkPoll};
14+
15+
/// How many philosophers. There will be the same number of forks.
16+
const _NUM_PHIL: usize = 6;
17+
18+
// The dining philosophers problem is a simple example of cooperation between multiple threads.
19+
// This implementation use one of several different underlying mechanism to support this cooperation.
20+
21+
// This example uses dynamic dispatch to allow multiple implementations. The intent is to be able
22+
// to periodically shut down all of the philosphers and start them up with a differernt sync
23+
// mechanism. This isn't implemented yet.
24+
25+
/// The philosophers use a fork synchronization mechanism. Essentially, this is 6 locks, and will be
26+
/// implemented in a few different ways to demonstrate/test different mechanmism in Rust. All of
27+
/// them implement The ForkSync trait which provides this mechanism.
28+
/*
29+
trait ForkSync: core::fmt::Debug + Sync + Send {
30+
/// Take the given fork. The are indexed the same as the philosopher index number. This will
31+
/// block until the fork is released.
32+
fn take(&self, index: usize);
33+
34+
/// Release the given fork. Index is the same as take.
35+
fn release(&self, index: usize);
36+
}
37+
*/
38+
39+
#[no_mangle]
40+
extern "C" fn rust_main() {
41+
printkln!("Hello world from Rust on {}",
42+
zephyr::kconfig::CONFIG_BOARD);
43+
printkln!("Time tick: {}", zephyr::time::SYS_FREQUENCY);
44+
45+
let th = phil_thread(0);
46+
printkln!("Size of phil thread: {}", core::mem::size_of_val(&th));
47+
// TODO: How to do as much on the stack as we can, for now, don't worry too much.
48+
// let mut th = pin!(th);
49+
50+
let mut th = FutureWorkPoll::new(th);
51+
let result = th.as_mut().submit(NoWait).unwrap();
52+
if !result.enqueued() {
53+
panic!("Problem submitting initial work: {:?}", result);
54+
}
55+
56+
let result = th.sync_join();
57+
printkln!("th result: {:?}", result);
58+
59+
/*
60+
// TODO: The allocated Arc doesn't work on all Zephyr platforms, but need to make our own copy
61+
// of alloc::task
62+
let waker = Arc::new(PWaker).into();
63+
let mut cx = Context::from_waker(&waker);
64+
65+
// Run the future to completion.
66+
loop {
67+
match th.as_mut().poll(&mut cx) {
68+
Poll::Pending => todo!(),
69+
Poll::Ready(_) => break,
70+
}
71+
}
72+
*/
73+
printkln!("All threads done");
74+
75+
/*
76+
let stats = Arc::new(Mutex::new_from(Stats::default(), STAT_MUTEX.init_once(()).unwrap()));
77+
78+
let syncers = get_syncer();
79+
80+
printkln!("Pre fork");
81+
82+
for (i, syncer) in (0..NUM_PHIL).zip(syncers.into_iter()) {
83+
let child_stat = stats.clone();
84+
let thread = PHIL_THREADS[i].init_once(PHIL_STACKS[i].init_once(()).unwrap()).unwrap();
85+
thread.spawn(move || {
86+
phil_thread(i, syncer, child_stat);
87+
});
88+
}
89+
90+
let delay = Duration::secs_at_least(10);
91+
loop {
92+
// Periodically, printout the stats.
93+
zephyr::time::sleep(delay);
94+
stats.lock().unwrap().show();
95+
}
96+
*/
97+
}
98+
99+
/// Our local workqueue worker.
100+
struct PWaker;
101+
102+
impl Wake for PWaker {
103+
fn wake(this: Arc<Self>) {
104+
// Note that being able to call this 'self' would require the unstable
105+
// `abritrary_self_types` feature.
106+
let _ = this;
107+
todo!()
108+
}
109+
}
110+
111+
async fn phil_thread(n: usize) -> usize {
112+
printkln!("Child {} started", n);
113+
zephyr::work::futures::sleep(Duration::millis_at_least(1000)).await;
114+
printkln!("Child {} done sleeping", n);
115+
42
116+
}
117+
118+
/*
119+
fn phil_thread(n: usize, syncer: Arc<dyn ForkSync>, stats: Arc<Mutex<Stats>>) {
120+
printkln!("Child {} started: {:?}", n, syncer);
121+
122+
// Determine our two forks.
123+
let forks = if n == NUM_PHIL - 1 {
124+
// Per Dijkstra, the last phyilosopher needs to reverse forks, or we deadlock.
125+
(0, n)
126+
} else {
127+
(n, n+1)
128+
};
129+
130+
loop {
131+
{
132+
// printkln!("Child {} hungry", n);
133+
// printkln!("Child {} take left fork", n);
134+
syncer.take(forks.0);
135+
// printkln!("Child {} take right fork", n);
136+
syncer.take(forks.1);
137+
138+
let delay = get_random_delay(n, 25);
139+
// printkln!("Child {} eating ({} ms)", n, delay);
140+
sleep(delay);
141+
stats.lock().unwrap().record_eat(n, delay);
142+
143+
// Release the forks.
144+
// printkln!("Child {} giving up forks", n);
145+
syncer.release(forks.1);
146+
syncer.release(forks.0);
147+
148+
let delay = get_random_delay(n, 25);
149+
// printkln!("Child {} thinking ({} ms)", n, delay);
150+
sleep(delay);
151+
stats.lock().unwrap().record_think(n, delay);
152+
}
153+
}
154+
}
155+
156+
/// Get a random delay, based on the ID of this user, and the current uptime.
157+
fn get_random_delay(id: usize, period: usize) -> Duration {
158+
let tick = (uptime_get() & (usize::MAX as i64)) as usize;
159+
let delay = (tick / 100 * (id + 1)) & 0x1f;
160+
161+
// Use one greater to be sure to never get a delay of zero.
162+
Duration::millis_at_least(((delay + 1) * period) as Tick)
163+
}
164+
165+
/// Instead of just printint out so much information that the data just scolls by, gather
166+
/// statistics.
167+
#[derive(Default)]
168+
struct Stats {
169+
/// How many times each philosopher has gone through the loop.
170+
count: [u64; NUM_PHIL],
171+
/// How much time each philosopher has spent eating.
172+
eating: [u64; NUM_PHIL],
173+
/// How much time each philosopher has spent thinking.
174+
thinking: [u64; NUM_PHIL],
175+
}
176+
177+
impl Stats {
178+
fn record_eat(&mut self, index: usize, time: Duration) {
179+
self.eating[index] += time.to_millis();
180+
}
181+
182+
fn record_think(&mut self, index: usize, time: Duration) {
183+
self.thinking[index] += time.to_millis();
184+
self.count[index] += 1;
185+
}
186+
187+
fn show(&self) {
188+
printkln!("c:{:?}, e:{:?}, t:{:?}", self.count, self.eating, self.thinking);
189+
}
190+
}
191+
192+
kobj_define! {
193+
static PHIL_THREADS: [StaticThread; NUM_PHIL];
194+
static PHIL_STACKS: [ThreadStack<PHIL_STACK_SIZE>; NUM_PHIL];
195+
196+
static STAT_MUTEX: StaticMutex;
197+
}
198+
*/

zephyr/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ cfg-if = "1.0"
2020

2121
# The log create is used if the user desires logging, and calls `set_logger()`.
2222
log = "0.4.22"
23+
arrayvec = { version = "0.7.6", default-features = false }
2324

2425
[dependencies.fugit]
2526
version = "0.3.7"

0 commit comments

Comments
 (0)