📄 task.c
字号:
while (owned->next != m) owned = owned->next;
owned->next = m->next;
}
if ((!current_task->owned_mutex) && (get_task_state(current_task) == TASK_STATE_WAITING_TO_STOP)) {
stop_task(current_task, 0);
}
} else {
restore_interrupts(interrupts);
return 1;
}
restore_interrupts(interrupts);
yield();
return 0;
}
// (documentation in task.h)
int16_t get_semaphore_value(semaphore_t *s) {
interrupt_store_t interrupts = disable_interrupts(); // We are reading a 16-bit value!
int16_t ans = s->value;
restore_interrupts(interrupts);
return ans;
}
// (documentation in task.h)
uint8_t wait_for_min_value(semaphore_t *p, int16_t value) {
interrupt_store_t interrupts;
if ((!current_task->pri) || (executing_isr())) {
return 1;
}
interrupts = disable_interrupts();
if (!semaphore_triggered(p->value, value)) {
current_task->waiting_semaphore = p;
current_task->waiting_semaphore_min_value = value;
restore_interrupts(interrupts);
yield();
} else {
restore_interrupts(interrupts);
}
return 0;
}
// (documentation in task.h)
uint8_t wait_for_increment_of(semaphore_t *p, uint16_t amount) {
interrupt_store_t interrupts;
int16_t v;
if ((!current_task->pri) || (executing_isr())) {
return 1;
}
interrupts = disable_interrupts();
v = p->value + amount;
restore_interrupts(interrupts);
return wait_for_min_value(p, v);
}
/**
* \brief \internal Perform a task switch
*
* Note that interrupts must be disabled before we enter, and the stack pointer should be set up to use
* the system stack.
*/
void switch_task() { /* __attribute__ ((naked)) */
uint8_t min_priority = 0;
// ans will contain the next task to run
task_t *ans = 0;
// Our round-robin algorithm is to check the task list starting at the current task. Hence the task we will end up
// with will be the current task only if it is the only task available.
task_t *checking = current_task;
// We set the system.interrupted_task flag so that if any ISR executes during the brief re-enabling
// of interrupts below, they won't attempt a task switch.
system.interrupted_task = 1;
__asm__ volatile ("sei\n nop\n nop\n cli\n" ::); // Give any pending interrupts a chance to run
do {
if (get_task_state(checking) != TASK_STATE_STOPPED) {
// First, if the task is waiting on a semaphore, and the semaphore is triggered, then mark it as no
// longer waiting on the semaphore
if (checking->waiting_semaphore) {
if (semaphore_triggered((checking->waiting_semaphore)->value, checking->waiting_semaphore_min_value)) {
checking->waiting_semaphore = (semaphore_t*)0;
}
}
// Now, if the task is available (i.e. not waiting on a semaphore), and if it's priority is at least the
// minimum priority of the available tasks found so far, then it is our candidate for the next task to run
if (checking->pri >= min_priority && !checking->waiting_semaphore) {
min_priority = checking->pri;
ans = checking;
}
}
checking = checking->next;
if (!checking) {
// If we have reached the end of the task list, then go back to the start
checking = first_task;
}
} while (checking != current_task); // Stop when we have looped through the task list completely
current_task = ans; // This is why it is so important to have an always-available idle task!!!
SP = (uint16_t) ans->sp; // Restore the stack pointer
system.interrupted_task = 0;
switch (get_task_state(current_task)) {
case TASK_STATE_RUNNING:
case TASK_STATE_CLEANING_UP:
case TASK_STATE_WAITING_TO_STOP:
restore_cpu_context(); // The task has previously saved its context onto its stack
__asm__ volatile ("ret\n" ::);
case TASK_STATE_STARTING:
set_task_state(current_task, TASK_STATE_RUNNING);
// Retrieve the init data from the stack, placing it into the registers that mark the value of the argument
// to the called function. Then, perform a ret, which will start executing task_starter()
__asm__ volatile ("pop r25\npop r24\nret\n" ::);
break;
case TASK_STATE_STOPPING:
set_task_state(current_task, TASK_STATE_CLEANING_UP);
break;
default: ;
}
__asm__ volatile ("ret" ::); // This will start executing task_stopper()
}
// (documentation in task.h)
void increment_semaphore_by(semaphore_t *s, uint16_t amount) {
interrupt_store_t interrupts = disable_interrupts();
s->value += amount;
restore_interrupts(interrupts);
if ((!executing_isr()) && (rtos_started())) {
yield(); // There may now be a higher-priority task that can run
}
}
// (documentation in task.h)
int16_t get_current_mbox_version(mailbox_t *m) {
interrupt_store_t interrupts = disable_interrupts();
int16_t ans = m->reading_semaphore.value; // Reading a 16-bit value!
restore_interrupts(interrupts);
return ans;
}
// (documentation in task.h)
void *read_mbox(mailbox_t *m, int16_t *version) {
interrupt_store_t interrupts;
void *ans = 0;
if ((!rtos_started()) || (executing_isr())) return 0;
interrupts = disable_interrupts();
if (!current_task->waiting_on_mbox) { // We can only "lock" one mailbox at a time
ans = m->data;
// Now, "lock" the mailbox against writing
current_task->waiting_on_mbox = m;
current_task->waiting_on_mbox_version = m->reading_semaphore.value;
// Return the version number (if required)
if (version) *version = m->reading_semaphore.value;
}
restore_interrupts(interrupts);
return ans;
}
// (documentation in task.h)
void *read_mbox_min_version(mailbox_t *m, int16_t *version) {
interrupt_store_t interrupts;
void *ans = 0;
if ((!rtos_started()) || (executing_isr()) || (!(current_task->pri))) return 0;
interrupts = disable_interrupts();
if (!current_task->waiting_on_mbox) {
current_task->waiting_on_mbox = m;
current_task->waiting_on_mbox_version = *version;
while (!semaphore_triggered(m->reading_semaphore.value, *version)) {
restore_interrupts(interrupts);
increment_semaphore_by(&(m->writing_semaphore), 1); // Writers can be waiting for readers!
wait_for_min_value(&(m->reading_semaphore), *version);
cli();
}
ans = m->data;
*version = m->reading_semaphore.value;
}
restore_interrupts(interrupts);
return ans;
}
// (documentation in task.h)
mailbox_t *release_mbox_read() {
interrupt_store_t interrupts;
mailbox_t *ans = 0;
if (!rtos_started()) return 0;
interrupts = disable_interrupts();
ans = current_task->waiting_on_mbox;
current_task->waiting_on_mbox = 0; // Writing a 16-bit value!
restore_interrupts(interrupts);
if (ans) {
increment_semaphore_by(&(ans->writing_semaphore), 1);
}
return ans;
}
// (documentation in task.h)
void initialise_mbox(mailbox_t *m, void *data, const int16_t version) {
m->data = data;
m->reading_semaphore.value = version;
m->writing_semaphore.value = 0;
}
/**
* \internal \brief Find out if a mailbox is "empty" (i.e. if there is no-one waiting to read it)
*
* Note that interrupts must be disabled before we enter
*/
int8_t mbox_is_empty(mailbox_t *m) {
task_t *t = first_task;
while (t) {
if (t->waiting_on_mbox == m) {
if (semaphore_triggered(m->reading_semaphore.value, t->waiting_on_mbox_version)) {
// Task t is waiting for the current version of this mailbox
return 0;
}
}
t = t->next;
}
return 1;
}
// (documentation in task.h)
void write_mbox(mailbox_t *m, void *data, uint8_t wait_for_receivers, uint8_t wait_for_empty_nullify) {
interrupt_store_t interrupts;
uint8_t receivers = 0;
uint8_t wait_for_empty = wait_for_empty_nullify;
if ((!rtos_started()) || (executing_isr()) || (!(current_task->pri))) return;
interrupts = disable_interrupts();
// First, wait for the mailbox to be empty - i.e. wait for everyone that wants the current version
// of the mailbox to get it - we are not allowed to over-write whats in the mailbox until everyone
// is finished with the old data that is in there.
while (!mbox_is_empty(m)) {
int16_t v = m->writing_semaphore.value + 1; // Need to read 16-bit value with disabled interrupts
restore_interrupts(interrupts);
wait_for_min_value(&(m->writing_semaphore), v);
cli();
}
// Now, we wait for the specified number of receivers
while (wait_for_receivers > receivers) {
task_t *t = first_task;
receivers = 0;
while (t && (wait_for_receivers > receivers)) {
if ((t->waiting_on_mbox == m) && (!semaphore_triggered(m->reading_semaphore.value, t->waiting_on_mbox_version))) {
// Task t is waiting on this mailbox
receivers++;
}
t = t->next;
}
if (wait_for_receivers > receivers) {
// We haven't got the required number of receivers yet - wait on the writing semaphore
int16_t v = m->writing_semaphore.value + 1;
restore_interrupts(interrupts);
wait_for_min_value(&(m->writing_semaphore), v);
cli();
}
}
// The mailbox is empty, and we have the required number of receivers, so put the data into the mailbox,
// and increment the mailbox version (which doubles as the reading semaphore value)
m->data = data;
m->reading_semaphore.value++;
// Have we been requested to wait for everyone to read the data?
if (wait_for_empty) {
// v is the version of the data we have published
int16_t v = m->reading_semaphore.value;
while (wait_for_empty) {
int8_t empty = 1;
task_t *t = first_task;
while (t && empty) {
if (t->waiting_on_mbox == m) {
if (semaphore_triggered(v, t->waiting_on_mbox_version)) {
// Task t is waiting to read our data
empty = 0;
}
}
t = t->next;
}
if (empty) {
wait_for_empty = 0;
if (wait_for_empty_nullify > 1) {
// Publish another version of the mailbox data - one with a null pointer
// (It is safe to do this, as the mailbox is "empty" - i.e. no-one is waiting
// for what we've just published).
m->data = 0;
m->reading_semaphore.value++;
}
} else {
// There are still tasks waiting to read our data - wait on the writing semaphore
int16_t val = m->writing_semaphore.value + 1;
restore_interrupts(interrupts);
wait_for_min_value(&(m->writing_semaphore), val);
cli();
}
}
}
restore_interrupts(interrupts);
yield();
}
// (documentation in task.h)
int8_t write_mbox_now(mailbox_t *m, void *data) {
interrupt_store_t interrupts;
if (!rtos_started()) return 1;
interrupts = disable_interrupts();
if (!mbox_is_empty(m)) {
restore_interrupts(interrupts);
return 2;
}
m->data = data;
m->reading_semaphore.value++;
restore_interrupts(interrupts);
if (!executing_isr()) yield();
return 0;
}
// (documentation in task.h)
void wait_for_receiver(mailbox_t *m) {
interrupt_store_t interrupts;
if ((!rtos_started()) || (executing_isr()) || (!(current_task->pri))) return;
interrupts = disable_interrupts();
while (1) {
task_t *t = first_task;
int16_t v;
while (t) {
if ((t->waiting_on_mbox == m) && (!semaphore_triggered(m->reading_semaphore.value, t->waiting_on_mbox_version))) {
restore_interrupts(interrupts);
return;
}
t = t->next;
}
v = m->writing_semaphore.value + 1;
restore_interrupts(interrupts);
wait_for_min_value(&(m->writing_semaphore), v);
cli();
}
}
// (documentation in task.h)
interrupt_store_t disable_interrupts() {
interrupt_store_t ans = (SREG & _BV(SREG_I));
cli();
return ans;
}
// (documentation in task.h)
void restore_interrupts(interrupt_store_t interrupts) {
if (interrupts) {
sei();
} else {
cli();
}
}
// (documentation in task.h)
void yield() { /* __attribute__ ((naked)) */
save_cpu_context();
current_task->sp = (uint8_t *)SP;
SP = (uint16_t) system.stack_top;
switch_task();
}
// (documentation in task.h)
void task_switcher_start(void (*idle)(void*), void *idle_data, uint16_t idle_stacklen, uint16_t system_stacklen) { /* __attribute__ ((naked)) */
cli();
create_task(idle, 0, idle_data, idle_stacklen, 0, 0);
system.stack_top = malloc(system_stacklen);
system.stack_top += system_stacklen - 1;
system.interrupted_task = 0;
task_stopping_semaphore.value = 0;
current_task = first_task;
SP = (uint16_t) system.stack_top;
switch_task();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -