mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
examples/thread-duel: improve duelling threads example
This commit is contained in:
parent
fc3f5f562b
commit
64b783b9fa
12
examples/thread_duel/Makefile.ci
Normal file
12
examples/thread_duel/Makefile.ci
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
BOARD_INSUFFICIENT_MEMORY := \
|
||||||
|
arduino-duemilanove \
|
||||||
|
arduino-leonardo \
|
||||||
|
arduino-nano \
|
||||||
|
arduino-uno \
|
||||||
|
atmega328p \
|
||||||
|
atmega328p-xplained-mini \
|
||||||
|
nucleo-f031k6 \
|
||||||
|
nucleo-l011k4 \
|
||||||
|
samd10-xmini \
|
||||||
|
stm32f030f4-demo \
|
||||||
|
#
|
@ -1,38 +1,45 @@
|
|||||||
Thread-Duel
|
Thread-Duel
|
||||||
============
|
============
|
||||||
|
|
||||||
This is a thread duel application to show RIOTs multi-threading abilities
|
This is a thread duel application to show RIOTs abilities to run multiple-threads
|
||||||
|
concurrently, even if they are neither cooperative nor dividable into different scheduler priorities,
|
||||||
|
by using the optional round-robin scheduler module.
|
||||||
|
|
||||||
Every thread will do some work (busy waiting) after the work is done
|
Every thread will do some work (busy waiting).
|
||||||
it counts up by the amount of work it did and then it rests.
|
After the work is done it counts up by the amount of work it did and then it rests.
|
||||||
There are different resting strategies and these have a huge
|
There are different resting strategies and these have a huge
|
||||||
influence on thread fairness and scheduling.
|
influence on thread fairness and scheduling.
|
||||||
|
|
||||||
resting strategies for the threads of this example are:
|
Resting strategies for the threads of this example are:
|
||||||
- `nice_wait`: does nice breaks giving other threads time to use the CPU
|
- `nice_wait`: does nice breaks giving other threads time to use the CPU
|
||||||
- `bad_wait`: take breaks by busy waiting and therefore hogging the CPU
|
- `bad_wait`: takes breaks by busy waiting and therefore hogging the CPU
|
||||||
- `yield_wait`: take no explicit breaks but yields (to higher or equal priority threads)
|
- `yield_wait`: takes no explicit breaks but yields (to higher or equal priority threads)
|
||||||
- `no_wait`: never take a break
|
- `no_wait`: never takes a break
|
||||||
|
|
||||||
After completing a batch of work (and rest) a thread will print information on the work done.
|
After completing a batch of work (and rest) a thread will print information on the work done.
|
||||||
(Printing is done in steps to avoid flooding)
|
(Printing is done in steps to avoid flooding)
|
||||||
|
|
||||||
If one thread (all are same priority) does rests `bad_wait`ing or `no_wait`ing at all,
|
If one thread (all are same priority) follows `bad_wait` or `no_wait` strategy,
|
||||||
scheduling without round robin will see all CPU time be hogged by that one thread
|
scheduling without round robin will see all CPU time be hogged by that one thread
|
||||||
(or the first one to get it).
|
(or the first one to get it).
|
||||||
|
|
||||||
In this example Round Robin scheduling is enabled by default,
|
In this example Round Robin scheduling is enabled by default,
|
||||||
to disable compile with `RR=0`
|
to disable compile with `RR=0`
|
||||||
|
|
||||||
change the behaviour of the different thread by adding `CFLAGS='-DTHREAD_1={no_wait,5}'`
|
Change the behaviour of the different threads by adding
|
||||||
|
`CFLAGS='-DTHREAD_1={<rest_strategy>,<work>}'`
|
||||||
|
|
||||||
e.g.:
|
e.g.:
|
||||||
```
|
```
|
||||||
CFLAGS='-DTHREAD_1={yield_wait,3} -DTHREAD_2={bad_wait,2}' RR=0 make
|
CFLAGS='-DTHREAD_1={yield_wait,3} -DTHREAD_2={bad_wait,2}' RR=0 make
|
||||||
```
|
```
|
||||||
will set thread 1 to be yield_waiting and to 3 works in one batch after that it will yield.
|
Will set:
|
||||||
thread 2 will do 2 work bad_wait (hog the cpu while waiting).
|
- thread 1 to follow `yield_waiting` strategy and
|
||||||
|
to complete 3 works in one batch after that, it will yield.
|
||||||
|
- thread 2 will do 2 work and follow `bad_wait` (hog the cpu while waiting).
|
||||||
the Round Robin scheduling is not activated.
|
the Round Robin scheduling is not activated.
|
||||||
the result will be a CPU hogged by thread 2 which will only do work for 20% of the time (really bad)
|
|
||||||
(thread 1 will have done 3 work but will never even get the chance to print that)
|
The result will be:
|
||||||
thread 3 will never have done work.
|
- CPU hogged by thread 2, which will only do work for 20% of the time (really bad)
|
||||||
|
- thread 1 will have done 3 work but will never even get the chance to print that
|
||||||
|
- thread 3 will never have done any work.
|
||||||
|
@ -20,31 +20,44 @@
|
|||||||
|
|
||||||
#define PRINT_STEPS 10
|
#define PRINT_STEPS 10
|
||||||
#define WORK_SCALE 1000
|
#define WORK_SCALE 1000
|
||||||
|
#define STEPS_PER_SET 10
|
||||||
|
|
||||||
|
__attribute__((unused))
|
||||||
static void bad_wait(uint32_t us)
|
static void bad_wait(uint32_t us)
|
||||||
{
|
{
|
||||||
/*keep the CPU busy waiting for some time to pass simulate working*/
|
/* keep the CPU busy waiting for some time to pass simulate working */
|
||||||
ztimer_spin(ZTIMER_USEC, us);
|
ztimer_spin(ZTIMER_USEC, us);
|
||||||
}__attribute__((unused))
|
}
|
||||||
|
|
||||||
|
static void (* const do_work)(uint32_t us) = bad_wait;
|
||||||
|
|
||||||
|
__attribute__((unused))
|
||||||
static void nice_wait(uint32_t us)
|
static void nice_wait(uint32_t us)
|
||||||
{
|
{
|
||||||
/*be nice give the CPU some time to do other things or rest*/
|
/* be nice give the CPU some time to do other things or rest */
|
||||||
ztimer_sleep(ZTIMER_USEC, us);
|
ztimer_sleep(ZTIMER_USEC, us);
|
||||||
}__attribute__((unused))
|
}
|
||||||
|
|
||||||
|
__attribute__((unused))
|
||||||
static void yield_wait(uint32_t unused)
|
static void yield_wait(uint32_t unused)
|
||||||
{
|
{
|
||||||
(void) unused;
|
(void) unused;
|
||||||
/* do not wait just yield */
|
/* do not wait just yield */
|
||||||
thread_yield();
|
thread_yield();
|
||||||
}__attribute__((unused))
|
}
|
||||||
|
|
||||||
|
__attribute__((unused))
|
||||||
static void no_wait(uint32_t unused)
|
static void no_wait(uint32_t unused)
|
||||||
{
|
{
|
||||||
(void) unused;
|
(void) unused;
|
||||||
/* do not wait */
|
/* do not wait */
|
||||||
}__attribute__((unused))
|
}
|
||||||
|
|
||||||
|
/* worker_config is a small configuration structure for the thread_worker */
|
||||||
|
struct worker_config {
|
||||||
|
void (*waitfn)(uint32_t); /**< the resting strategy */
|
||||||
|
uint32_t workload; /**< the amount of work to do per set */
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the following are threads that count and wait with different strategies and
|
* the following are threads that count and wait with different strategies and
|
||||||
@ -52,17 +65,12 @@ static void no_wait(uint32_t unused)
|
|||||||
* the ration of active (doing hard work like checking the timer)
|
* the ration of active (doing hard work like checking the timer)
|
||||||
* to passive (wait to be informed when a certain time is there) waiting
|
* to passive (wait to be informed when a certain time is there) waiting
|
||||||
* is determined by there value given to the thread.
|
* is determined by there value given to the thread.
|
||||||
* the restless threads do never pause.
|
* no_wait and yield_wait threads are restless an therefore never pause.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct worker_config {
|
|
||||||
void (*waitfn) (unsigned);
|
|
||||||
uint32_t work;
|
|
||||||
};
|
|
||||||
|
|
||||||
void * thread_worker(void * d)
|
void * thread_worker(void * d)
|
||||||
{
|
{
|
||||||
nice_wait(200 * US_PER_MS); /*always be nice at start*/
|
nice_wait(200 * US_PER_MS); /* always be nice at start */
|
||||||
#ifdef DEVELHELP
|
#ifdef DEVELHELP
|
||||||
const char *name = thread_get_active()->name;
|
const char *name = thread_get_active()->name;
|
||||||
#else
|
#else
|
||||||
@ -71,17 +79,18 @@ void * thread_worker(void * d)
|
|||||||
|
|
||||||
uint32_t w = 0;
|
uint32_t w = 0;
|
||||||
struct worker_config *wc = d;
|
struct worker_config *wc = d;
|
||||||
/* this is doing 10 Steps which divided into work (busy waiting)
|
/* Each set consists of STEPS_PER_SET steps which are divided into work (busy waiting)
|
||||||
* and not work of these 10 steps up to 10 might be work but not more
|
* and resting.
|
||||||
* if the given value is out of range work ratio is set to 5 */
|
* E.g. if there are 10 steps per set, the maximum workload is 10, which means no rest.
|
||||||
uint32_t work = wc->work;
|
* If the given value is out of range work ratio is set to half of STEPS_PER_SET */
|
||||||
if (work > 10) {
|
uint32_t work = wc->workload;
|
||||||
work = 5;
|
if (work > STEPS_PER_SET) {
|
||||||
|
work = STEPS_PER_SET / 2;
|
||||||
}
|
}
|
||||||
uint32_t rest = (10 - work);
|
uint32_t rest = (STEPS_PER_SET - work);
|
||||||
uint32_t step = 0;
|
uint32_t step = 0;
|
||||||
|
|
||||||
/*work some time and rest*/
|
/* work some time and rest */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (w - step >= PRINT_STEPS) {
|
if (w - step >= PRINT_STEPS) {
|
||||||
#ifdef DEVELHELP
|
#ifdef DEVELHELP
|
||||||
@ -91,18 +100,18 @@ void * thread_worker(void * d)
|
|||||||
#endif
|
#endif
|
||||||
step = w;
|
step = w;
|
||||||
}
|
}
|
||||||
bad_wait(work * WORK_SCALE);
|
do_work(work * WORK_SCALE);
|
||||||
w += work;
|
w += work;
|
||||||
wc->waitfn(rest * WORK_SCALE);
|
wc->waitfn(rest * WORK_SCALE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
/* no_wait -> a restless thread always working until it is suspended
|
|
||||||
* yield_wait -> a restless thread that yields before continuing with the next work package
|
|
||||||
* bad_wait -> a thread that does pause very intensely
|
|
||||||
* nice_wait -> a thread does nice breaks giving other threads time to do something
|
* nice_wait -> a thread does nice breaks giving other threads time to do something
|
||||||
|
* bad_wait -> a thread that waits by spinning (intensely looking at the clock)
|
||||||
|
* yield_wait -> a restless thread that yields before continuing with the next work package
|
||||||
|
* no_wait -> a restless thread always working until it is preempted
|
||||||
*/
|
*/
|
||||||
/* yield_wait and nice_wait threads are able to work in "parallel" without sched_round_robin*/
|
/* yield_wait and nice_wait threads are able to work in "parallel" without sched_round_robin */
|
||||||
|
|
||||||
#ifndef THREAD_1
|
#ifndef THREAD_1
|
||||||
#define THREAD_1 {no_wait, 5}
|
#define THREAD_1 {no_wait, 5}
|
||||||
@ -116,23 +125,28 @@ void * thread_worker(void * d)
|
|||||||
#define THREAD_3 {no_wait, 5}
|
#define THREAD_3 {no_wait, 5}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*a TINY Stack should be enough*/
|
||||||
|
#ifndef WORKER_STACKSIZE
|
||||||
|
#define WORKER_STACKSIZE (THREAD_STACKSIZE_TINY+THREAD_EXTRA_STACKSIZE_PRINTF)
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
static char stack[THREAD_STACKSIZE_DEFAULT];
|
static char stack[WORKER_STACKSIZE];
|
||||||
static struct worker_config wc = THREAD_1; /* 0-10 workness*/
|
static struct worker_config wc = THREAD_1; /* 0-10 workness */
|
||||||
thread_create(stack, sizeof(stack), 7, THREAD_CREATE_STACKTEST,
|
thread_create(stack, sizeof(stack), 7, THREAD_CREATE_STACKTEST,
|
||||||
thread_worker, &wc, "T1");
|
thread_worker, &wc, "T1");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
static char stack[THREAD_STACKSIZE_DEFAULT];
|
static char stack[WORKER_STACKSIZE];
|
||||||
static struct worker_config wc = THREAD_2; /* 0-10 workness*/
|
static struct worker_config wc = THREAD_2; /* 0-10 workness */
|
||||||
thread_create(stack, sizeof(stack), 7, THREAD_CREATE_STACKTEST,
|
thread_create(stack, sizeof(stack), 7, THREAD_CREATE_STACKTEST,
|
||||||
thread_worker, &wc, "T2");
|
thread_worker, &wc, "T2");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
static char stack[THREAD_STACKSIZE_DEFAULT];
|
static char stack[WORKER_STACKSIZE];
|
||||||
static struct worker_config wc = THREAD_3; /* 0-10 workness*/
|
static struct worker_config wc = THREAD_3; /* 0-10 workness */
|
||||||
thread_create(stack, sizeof(stack), 7, THREAD_CREATE_STACKTEST,
|
thread_create(stack, sizeof(stack), 7, THREAD_CREATE_STACKTEST,
|
||||||
thread_worker, &wc, "T3");
|
thread_worker, &wc, "T3");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user