fix cond.c after sem refactor

add comments about atomicity of cond_wait

add cond examples
This commit is contained in:
Chris Copeland 2024-05-16 23:53:40 -07:00
parent 9cc098fdd9
commit 4da8e59ea0
Signed by: chrisnc
GPG Key ID: 14550DA72485DF30
9 changed files with 173 additions and 26 deletions

View File

@ -35,6 +35,10 @@ hercules = []
sitara-vim = []
sitara-intc = []
[[example]]
name = "cond"
path = "rust/examples/cond.rs"
[[example]]
name = "donate"
path = "rust/examples/donate.rs"

View File

@ -1,5 +1,6 @@
Import("env")
env.Program("cond.cpp")
env.Program("mutex.cpp")
env.Program("notify.cpp")
env.Program("once.cpp")

51
cxx/examples/cond.cpp Normal file
View File

@ -0,0 +1,51 @@
#include <rt/mutex.hpp>
#include <rt/cond.hpp>
#include <rt/task.hpp>
#include <rt/trap.hpp>
#include <rt/assert.h>
static rt::mutex mutex;
static rt::cond cond;
static unsigned x = 0;
static bool flag = false;
#define ITERATIONS 10000
static void signaler(void)
{
rt::task::drop_privilege();
for (;;)
{
rt::lock_guard lock(mutex);
flag = true;
cond.signal();
}
}
static void incrementer(void)
{
rt::task::drop_privilege();
rt::lock_guard lock(mutex);
for (int i = 0; i < ITERATIONS; ++i)
{
while (!flag)
{
cond.wait(mutex);
}
++x;
flag = false;
}
rt_trap();
}
static void timeout(void)
{
rt::task::sleep(100);
rt_assert(false, "timed out");
}
RT_TASK(signaler, RT_STACK_MIN, 2);
RT_TASK(incrementer, RT_STACK_MIN, 1);
RT_TASK(incrementer, RT_STACK_MIN, 1);
RT_TASK(timeout, RT_STACK_MIN, 0);

View File

@ -1,5 +1,6 @@
Import("env")
env.Program("cond.c")
env.Program("donate.c")
env.Program("empty.c")
env.Program("fair.c")

52
examples/cond.c Normal file
View File

@ -0,0 +1,52 @@
#include <rt/assert.h>
#include <rt/mutex.h>
#include <rt/cond.h>
#include <rt/task.h>
#include <rt/trap.h>
static RT_MUTEX(mutex);
static RT_COND(cond);
static unsigned x = 0;
static bool flag = false;
#define ITERATIONS 10000
static void signaler(void)
{
rt_task_drop_privilege();
for (;;)
{
rt_mutex_lock(&mutex);
flag = true;
rt_cond_signal(&cond);
rt_mutex_unlock(&mutex);
}
}
static void incrementer(void)
{
rt_task_drop_privilege();
rt_mutex_lock(&mutex);
for (int i = 0; i < ITERATIONS; ++i)
{
while (!flag)
{
rt_cond_wait(&cond, &mutex);
}
++x;
flag = false;
}
rt_mutex_unlock(&mutex);
rt_trap();
}
static void timeout(void)
{
rt_task_sleep(100);
rt_assert(false, "timed out");
}
RT_TASK(signaler, RT_STACK_MIN, 2);
RT_TASK(incrementer, RT_STACK_MIN, 1);
RT_TASK(incrementer, RT_STACK_MIN, 1);
RT_TASK(timeout, RT_STACK_MIN, 0);

40
rust/examples/cond.rs Normal file
View File

@ -0,0 +1,40 @@
#![no_main]
use core::sync::atomic::{AtomicBool, Ordering};
rt::mutex!(MUTEX, u32, 0);
rt::condvar!(COND);
static FLAG: AtomicBool = AtomicBool::new(false);
const ITERATIONS: i32 = 10000;
fn signaler() {
rt::task::drop_privilege();
loop {
let _g = MUTEX.lock();
FLAG.store(true, Ordering::Relaxed);
COND.signal();
}
}
fn incrementer() {
rt::task::drop_privilege();
let mut g = MUTEX.lock();
for _ in 0..ITERATIONS {
COND.wait_while(&g, |_| !FLAG.load(Ordering::Relaxed));
*g += 1;
FLAG.store(false, Ordering::Relaxed);
}
rt::trap();
}
fn timeout() {
rt::task::sleep(100);
assert!(false, "timed out");
}
rt::task!(signaler, rt::task::STACK_MIN, 2);
rt::task!(incrementer, rt::task::STACK_MIN, 1);
rt::task!(incrementer, rt::task::STACK_MIN, 1);
rt::task!(timeout, rt::task::STACK_MIN, 0);

View File

@ -5,8 +5,8 @@ set -e
cargo build --release --examples
examples=(
donate empty float mutex notify once queue rwlock sem simple sleep water-sem
water-condvar
cond donate empty float mutex notify once queue rwlock sem simple sleep
water-sem water-condvar
)
exitcode=0

View File

@ -11,37 +11,38 @@
void rt_cond_signal(struct rt_cond *cond)
{
rt_logf("%s cond signal\n", rt_task_name());
rt_sem_post(&cond->sem);
}
void rt_cond_broadcast(struct rt_cond *cond)
{
rt_logf("%s cond broadcast\n", rt_task_name());
rt_sem_post_n(&cond->sem, INT_MAX);
}
void rt_cond_wait(struct rt_cond *cond, struct rt_mutex *mutex)
{
/* Decrement the semaphore while still holding the mutex so that
* signals from higher priority tasks on the same monitor can see
* there is a waiter. */
const int value =
rt_atomic_fetch_sub(&cond->sem.value, 1, RT_ATOMIC_ACQUIRE);
rt_logf("%s cond wait, new value %d\n", rt_task_name(), value - 1);
if (value > 0)
if (rt_sem_trywait(&cond->sem))
{
/* The condition was signaled after the caller evaluated the predicate,
* so let the caller re-evaluate it without releasing the mutex. */
/* If the condition was signaled after the caller evaluated the
* predicate, let the caller re-evaluate it without releasing the
* mutex. */
return;
}
/* If the condition wasn't signaled, unlock the mutex and wait for a
* signal. If the condition gets signaled after unlocking the mutex but
* before the syscall executes, the syscall will return if no other waiter
* woke up, so these two steps happen "atomically" in the sense that
* pthread_cond_wait defines, in that a signal sent by another task after
* it acquires the mutex can be delivered to this task as though it were
* already blocked. Note: if there was already a waiter, that waiter will
* take the signal instead, even if it is lower priority. This case can
* happen even before the mutex is unlocked as well if signals are made
* without holding the mutex, because semaphore posts will wake waiting
* tasks first, rather than allow tasks that haven't begun waiting yet to
* decrement. Once multiple tasks are actually waiting, the signal will
* wake up the highest-priority among them. */
rt_mutex_unlock(mutex);
rt_logf("%s cond wait, waiting\n", rt_task_name());
rt_syscall_sem_wait(&cond->sem);
rt_logf("%s cond wait, awoken\n", rt_task_name());
rt_mutex_lock(mutex);
}
@ -49,10 +50,7 @@ bool rt_cond_timedwait(struct rt_cond *cond, struct rt_mutex *mutex,
unsigned long ticks)
{
unsigned long start_tick = rt_tick_count();
const int value =
rt_atomic_fetch_sub(&cond->sem.value, 1, RT_ATOMIC_ACQUIRE);
rt_logf("%s cond wait, new value %d\n", rt_task_name(), value - 1);
if (value > 0)
if (rt_sem_trywait(&cond->sem))
{
return true;
}

View File

@ -5,11 +5,11 @@ set -e
scons -j $(nproc)
examples=(
donate empty float mutex notify once queue rwlock sem simple sleep tls
cond donate empty float mutex notify once queue rwlock sem simple sleep tls
water/barrier water/cond water/sem cycle/mutex cycle/notify cycle/queue
cycle/sem cycle/sleep cycle/yield cxx/mutex cxx/notify cxx/once cxx/queue
cxx/rwlock cxx/sem cxx/sleep cxx/water/barrier cxx/water/cond cxx/water/sem
signal/mutex-irq signal/sem-irq
cycle/sem cycle/sleep cycle/yield cxx/cond cxx/mutex cxx/notify cxx/once
cxx/queue cxx/rwlock cxx/sem cxx/sleep cxx/water/barrier cxx/water/cond
cxx/water/sem signal/mutex-irq signal/sem-irq
)
exitcode=0