-
Notifications
You must be signed in to change notification settings - Fork 2k
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
sys/sched_rr: Add a round robin scheduler module #16126
Merged
+644
−2
Merged
Changes from 1 commit
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
f54325b
gitingnore: *bin -> bin and *.bin to not ignore round_robin
kfessel 8c3aad1
sys/sched_round_robin: Add a round robin scheduler module
kfessel be2aa39
examples/thread-duel: add a duelling threads example
kfessel fc3f5f5
test/sys_sched_round_robin: add test for sys_sched_round_robin
kfessel 64b783b
examples/thread-duel: improve duelling threads example
kfessel 2594032
test/sys_sched_round_robin: use sleep instead of mutex
kfessel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
examples/thread-duel: improve duelling threads example
- 2025.04-devel
- 2025.01
- 2025.01-devel
- 2025.01-RC2
- 2025.01-RC1
- 2024.10
- 2024.10-devel
- 2024.10-RC2
- 2024.10-RC1
- 2024.07
- 2024.07-devel
- 2024.07-RC1
- 2024.04
- 2024.04-devel
- 2024.04-RC2
- 2024.04-RC1
- 2024.01
- 2024.01-devel
- 2024.01-RC2
- 2024.01-RC1
- 2023.10
- 2023.10-devel
- 2023.10-RC3
- 2023.10-RC2
- 2023.10-RC1
- 2023.07
- 2023.07-devel
- 2023.07-RC5
- 2023.07-RC4
- 2023.07-RC3
- 2023.07-RC2
- 2023.07-RC1
- 2023.04
- 2023.04-devel
- 2023.04-RC3
- 2023.04-RC2
- 2023.04-RC1
- 2023.01
- 2023.01-devel
- 2023.01-RC3
- 2023.01-RC2
- 2023.01-RC1
- 2022.10
- 2022.10-devel
- 2022.10-RC3
- 2022.10-RC2
- 2022.10-RC1
- 2022.07
- 2022.07-devel
- 2022.07-RC4
- 2022.07-RC3
- 2022.07-RC2
- 2022.07-RC1
- 2022.04
- 2022.04-devel
- 2022.04-RC7
- 2022.04-RC6
- 2022.04-RC5
- 2022.04-RC4
- 2022.04-RC3
- 2022.04-RC2
- 2022.04-RC1
- 2022.01
- 2022.01-RC2
- 2022.01-RC1
commit 64b783b9fa63df793efbb651cc70e9efe7dd5bdb
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 \ | ||
# |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,45 @@ | ||
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 | ||
it counts up by the amount of work it did and then it rests. | ||
Every thread will do some work (busy waiting). | ||
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 | ||
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 | ||
- `bad_wait`: take breaks by busy waiting and therefore hogging the CPU | ||
- `yield_wait`: take no explicit breaks but yields (to higher or equal priority threads) | ||
- `no_wait`: never take a break | ||
- `bad_wait`: takes breaks by busy waiting and therefore hogging the CPU | ||
- `yield_wait`: takes no explicit breaks but yields (to higher or equal priority threads) | ||
- `no_wait`: never takes a break | ||
|
||
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) | ||
|
||
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 | ||
(or the first one to get it). | ||
|
||
In this example Round Robin scheduling is enabled by default, | ||
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.: | ||
``` | ||
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. | ||
thread 2 will do 2 work bad_wait (hog the cpu while waiting). | ||
Will set: | ||
- 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 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) | ||
thread 3 will never have done work. | ||
|
||
The result will be: | ||
- 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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,49 +20,57 @@ | |
|
||
#define PRINT_STEPS 10 | ||
#define WORK_SCALE 1000 | ||
#define STEPS_PER_SET 10 | ||
|
||
__attribute__((unused)) | ||
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); | ||
}__attribute__((unused)) | ||
} | ||
|
||
static void (* const do_work)(uint32_t us) = bad_wait; | ||
|
||
__attribute__((unused)) | ||
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); | ||
}__attribute__((unused)) | ||
} | ||
|
||
__attribute__((unused)) | ||
static void yield_wait(uint32_t unused) | ||
{ | ||
(void) unused; | ||
/* do not wait just yield */ | ||
thread_yield(); | ||
}__attribute__((unused)) | ||
} | ||
|
||
__attribute__((unused)) | ||
static void no_wait(uint32_t unused) | ||
{ | ||
(void) unused; | ||
/* 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 */ | ||
}; | ||
|
||
/* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment would maybe be better in the thread handler than on the thread config. |
||
* the following are threads that count and wait with different strategies and | ||
* print their current count in steps. | ||
* the ration of active (doing hard work like checking the timer) | ||
* to passive (wait to be informed when a certain time is there) waiting | ||
* 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) | ||
{ | ||
nice_wait(200 * US_PER_MS); /*always be nice at start*/ | ||
nice_wait(200 * US_PER_MS); /* always be nice at start */ | ||
#ifdef DEVELHELP | ||
const char *name = thread_get_active()->name; | ||
#else | ||
|
@@ -71,17 +79,18 @@ void * thread_worker(void * d) | |
|
||
uint32_t w = 0; | ||
struct worker_config *wc = d; | ||
/* this is doing 10 Steps which divided into work (busy waiting) | ||
* and not work of these 10 steps up to 10 might be work but not more | ||
* if the given value is out of range work ratio is set to 5 */ | ||
uint32_t work = wc->work; | ||
if (work > 10) { | ||
work = 5; | ||
/* Each set consists of STEPS_PER_SET steps which are divided into work (busy waiting) | ||
* and resting. | ||
* E.g. if there are 10 steps per set, the maximum workload is 10, which means no rest. | ||
* If the given value is out of range work ratio is set to half of STEPS_PER_SET */ | ||
uint32_t work = wc->workload; | ||
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; | ||
fjmolinas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/*work some time and rest*/ | ||
/* work some time and rest */ | ||
for (;;) { | ||
if (w - step >= PRINT_STEPS) { | ||
fjmolinas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#ifdef DEVELHELP | ||
|
@@ -91,18 +100,18 @@ void * thread_worker(void * d) | |
#endif | ||
step = w; | ||
kfessel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
bad_wait(work * WORK_SCALE); | ||
do_work(work * WORK_SCALE); | ||
w += work; | ||
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 | ||
* 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 | ||
#define THREAD_1 {no_wait, 5} | ||
|
@@ -116,23 +125,28 @@ void * thread_worker(void * d) | |
#define THREAD_3 {no_wait, 5} | ||
#endif | ||
|
||
/*a TINY Stack should be enough*/ | ||
#ifndef WORKER_STACKSIZE | ||
#define WORKER_STACKSIZE (THREAD_STACKSIZE_TINY+THREAD_EXTRA_STACKSIZE_PRINTF) | ||
#endif | ||
|
||
int main(void) | ||
{ | ||
{ | ||
fjmolinas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
static char stack[THREAD_STACKSIZE_DEFAULT]; | ||
static struct worker_config wc = THREAD_1; /* 0-10 workness*/ | ||
static char stack[WORKER_STACKSIZE]; | ||
static struct worker_config wc = THREAD_1; /* 0-10 workness */ | ||
thread_create(stack, sizeof(stack), 7, THREAD_CREATE_STACKTEST, | ||
thread_worker, &wc, "T1"); | ||
} | ||
{ | ||
static char stack[THREAD_STACKSIZE_DEFAULT]; | ||
static struct worker_config wc = THREAD_2; /* 0-10 workness*/ | ||
static char stack[WORKER_STACKSIZE]; | ||
static struct worker_config wc = THREAD_2; /* 0-10 workness */ | ||
thread_create(stack, sizeof(stack), 7, THREAD_CREATE_STACKTEST, | ||
thread_worker, &wc, "T2"); | ||
} | ||
{ | ||
static char stack[THREAD_STACKSIZE_DEFAULT]; | ||
static struct worker_config wc = THREAD_3; /* 0-10 workness*/ | ||
static char stack[WORKER_STACKSIZE]; | ||
static struct worker_config wc = THREAD_3; /* 0-10 workness */ | ||
thread_create(stack, sizeof(stack), 7, THREAD_CREATE_STACKTEST, | ||
thread_worker, &wc, "T3"); | ||
} | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be better to not call this
PRINT_STEPS
but something likeSTEPS_PER_SET
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or add another define
STEPS_PER_SET
if you want to keep both distinct.