📄 task.c
字号:
/*
* Copyright (C) 2007-2008 Chris O'Byrne
*
* This file is part of YAVRTOS (see http://www.chris.obyrne.com/yavrtos/)
*
* YAVROTS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* YAVROTS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with YAVROTS. If not, see <http://www.gnu.org/licenses/>.
*
* The author can be contacted at <chris@obyrne.com>
*
* version 1.7, 2008 Mar 01
*/
#include "task.h"
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
/**
* \brief Has a semaphore been triggered?
*
* It is quite critical that this algorithm be correct, even when the semaphore values roll over
*/
#define semaphore_triggered(current, required) ((current) - (required) >= 0)
/**
* \ingroup taskstategroup
* \brief Starting
*
* The task is starting, and the stack only contains a return address for task_starter()
*
* This state is set by create_task()
*/
#define TASK_STATE_STARTING 1
/**
* \ingroup taskstategroup
* \brief Running
*
* The task is running, and the stack contains the entire CPU context.
*
* This state is set by the scheduler the first time the task is scheduled
*/
#define TASK_STATE_RUNNING 2
/**
* \ingroup taskstategroup
* \brief Waiting for the task to release all of its mutexes before stopping
*
* The task is running, and the stack contains the entire CPU context
*
* This state is set by stop_task() when the \c wait_for_mutexes parameter is set and the task owns mutexes
*/
#define TASK_STATE_WAITING_TO_STOP 3
/**
* \ingroup taskstategroup
* \brief Stopping
*
* The task is stopping, and the stack only contains a return address for task_stopper()
*
* This state is set by stop_task() when there is no need, or desire, to wait for mutexes. Note that a task
* will commit suicide on lock_off() if it is in TASK_STATE_WAITING_TO_STOP and has just released its last
* mutex.
*/
#define TASK_STATE_STOPPING 4
/**
* \ingroup taskstategroup
* \brief Cleaning up
*
* The task is stopping, and the stack contains the entire CPU context
*
* This state is set by the scheduler the first time the task is scheduled after having had its state set to
* TASK_STATE_STOPPING
*/
#define TASK_STATE_CLEANING_UP 5
/**
* \ingroup taskstategroup
* \brief Stopped
*
* The task is completely dead, and this entry in the task list is available for new tasks.
*
* This state is set by reserve_task(), and when the task has completely stopped.
*/
#define TASK_STATE_STOPPED 6
/// \internal Set the state of a task - this macro makes it easier to put other information into task.status
#define set_task_state(taskptr,req_state) (taskptr)->status = (req_state)
/// \internal Get the state of a task - this macro makes it easier to put other information into task.status
#define get_task_state(taskptr) ((taskptr)->status)
/// \internal Are we currently executing an ISR?
#define executing_isr() (system.interrupted_task)
/// \internal Has the RTOS been started?
#define rtos_started() (current_task)
/// \internal Pointer to the first task in our linked list
task_t *first_task = 0;
// The current task
task_t *current_task = 0;
/// \internal A semaphore we use to not return from stop_task() until the task in question has actually stopped
semaphore_t task_stopping_semaphore;
/// \internal The system stack, and a flag indicating whether the CPU is currently processing an interrupt
struct system_struct system;
/**
* \brief \internal The entry point for all tasks
*
* First, interrupts are enabled, so that the tick interrupt can happen. Then, the task procedure is
* executed. When the task procedure exits, if the task priority is zero, it is re-executed. If the task
* priority isn't zero, the task is shut down.
*/
void task_starter() __attribute__ ((naked));
void task_starter(void *p) {
sei();
do {
(*(current_task->proc))(p);
} while (!current_task->pri);
stop_task(current_task, 0);
}
/**
* \brief \internal The entry point for all tasks that are stopping
*
* If this task is being stopped by a call to stop_task() with the \c wait_for_mutexes parameter set, then
* the task will continue to run as normal until the last mutex is released. When that happens, then this
* task stopper will start running on the task.
*
* First, interrupts are enabled, then any mailbox being read is released
*
* If the task has a cleanup procedure defined (the \c cleanup argument to create_task()), it is called
*
* Then, the task state is set to stopped, all mutexes and mailboxes are released, the (internal) task stopping
* semaphore is signalled, and a task switch is executed.
*/
void task_stopper() __attribute__ ((naked));
void task_stopper() {
sei();
release_mbox_read();
if (*current_task->cleanup) {
(*(current_task->cleanup))();
}
cli();
set_task_state(current_task, TASK_STATE_STOPPED);
if (current_task->waiting_on_mbox) {
current_task->waiting_on_mbox->writing_semaphore.value++;
current_task->waiting_on_mbox = 0;
}
while (current_task->owned_mutex) {
current_task->owned_mutex->notification.value++;
current_task->owned_mutex->owner = 0;
current_task->owned_mutex = current_task->owned_mutex->next;
}
task_stopping_semaphore.value++;
SP = (uint16_t) system.stack_top;
switch_task();
}
// (documentation in task.h)
int8_t stop_task(task_t *t, uint8_t wait_for_mutexes) {
interrupt_store_t interrupts;
// We can only be stopped by ourselves, or by a higher-priority task (see below)
if ((t != current_task) && (t->pri > current_task->pri) && (!executing_isr())) {
return -1;
}
if (!t->pri) return -2; // Cannot stop an idle task!
interrupts = disable_interrupts();
switch (get_task_state(t)) {
case TASK_STATE_STARTING:
set_task_state(t,TASK_STATE_STOPPED);
break;
case TASK_STATE_RUNNING:
case TASK_STATE_WAITING_TO_STOP:
if ((wait_for_mutexes) && (t->owned_mutex)) {
set_task_state(t, TASK_STATE_WAITING_TO_STOP); // The task will commit suicide when it calls lock_off() on its last mutex
} else { // (!wait_for_mutexes) || (!t->owned_mutex) -- the task is ready to die!
t->waiting_semaphore = 0; // Otherwise, the task would never get re-scheduled!
t->sp = t->stack;
// The next time the task is scheduled, it will start executing task_stopper()
*((t->sp)--) = (((uint16_t)task_stopper) & 0xFF);
*((t->sp)--) = (((uint16_t)task_stopper) >> 8) & 0xFF;
set_task_state(t,TASK_STATE_STOPPING);
}
// NOTE THE FALL THROUGH!
case TASK_STATE_STOPPING:
case TASK_STATE_CLEANING_UP:
if (executing_isr()) break;
if (t != current_task) {
do {
// This is why we can only be stopped by a higher-priority task - the task must not re-start
// until after we have processed the fact that it has stopped
switch (get_task_state(t)) {
case TASK_STATE_WAITING_TO_STOP:
case TASK_STATE_STOPPING:
case TASK_STATE_CLEANING_UP:
// (The task could be re-started by a higher pri task than us, but it won't get any
// further than "starting")
current_task->waiting_semaphore = &task_stopping_semaphore;
current_task->waiting_semaphore_min_value = task_stopping_semaphore.value+1;
restore_interrupts(interrupts);
yield();
cli();
break;
default:
restore_interrupts(interrupts);
return 0;
}
} while (1);
} else {
SP = (uint16_t) system.stack_top;
switch_task();
}
break;
case TASK_STATE_STOPPED:
restore_interrupts(interrupts);
return 1;
default: ;
}
restore_interrupts(interrupts);
return 0;
}
// (documentation in task.h)
task_t *reserve_task(uint16_t stacklen, uint8_t pri, mutex_t *memory_mutex) {
interrupt_store_t interrupts = 0;
task_t *ans = 0;
if ((memory_mutex) && (!executing_isr()) && (rtos_started()) && (current_task->pri)) {
lock_on(memory_mutex);
} else {
memory_mutex = 0;
interrupts = disable_interrupts();
}
ans = malloc(sizeof(task_t));
if (memory_mutex) {
lock_off(memory_mutex);
} else {
restore_interrupts(interrupts);
}
if (!ans) return 0;
if (memory_mutex) {
lock_on(memory_mutex);
} else {
cli();
}
ans->stack = malloc(stacklen) + stacklen - 1;
if (memory_mutex) {
lock_off(memory_mutex);
} else {
restore_interrupts(interrupts);
}
if (!ans->stack) return 0;
ans->stacklen = stacklen;
ans->pri = pri;
ans->next = 0;
ans->waiting_semaphore = 0;
ans->waiting_on_mbox = 0;
ans->owned_mutex = 0;
set_task_state(ans, TASK_STATE_STOPPED);
interrupts = disable_interrupts();
if (!first_task) {
first_task = ans;
} else {
task_t *next = first_task;
while (next->next) {
next = next->next;
}
next->next = ans;
}
restore_interrupts(interrupts);
return ans;
}
// (documentation in task.h)
task_t *create_task(void (*proc)(void*), void (*cleanup)(), void *init_data, uint16_t stacklen, uint8_t pri, mutex_t *memory_mutex) {
interrupt_store_t interrupts;
task_t *ans = 0;
interrupts = disable_interrupts();
while (!ans) {
task_t *t = first_task;
while (t) {
// We must find a task struct not only with a big enough stack, but also with the same pri, so
// that any task stopping this task will get to process the stop before the task re-starts
if ((get_task_state(t) == TASK_STATE_STOPPED) && (t->pri == pri) && (t->stacklen >= stacklen)) {
if (ans) {
if (ans->stacklen > t->stacklen) ans = t;
} else {
ans = t;
}
}
t = t->next;
}
if (!ans) {
restore_interrupts(interrupts); // We want interrupts disabled as little as possible
reserve_task(stacklen, pri, memory_mutex); // Next time through the loop, we should "find" this task entry!
cli();
}
}
ans->proc = proc;
ans->cleanup = cleanup;
ans->sp = ans->stack;
// When the task gets scheduled, it will start executing task_starter()
*((ans->sp)--) = (((uint16_t)task_starter) & 0xFF);
*((ans->sp)--) = (((uint16_t)task_starter) >> 8) & 0xFF;
// Set up the init data on the stack
*((ans->sp)--) = (((uint16_t)init_data) & 0xFF);
*((ans->sp)--) = (((uint16_t)init_data) >> 8) & 0xFF;
ans->waiting_semaphore = 0;
ans->waiting_on_mbox = 0;
ans->owned_mutex = 0;
set_task_state(ans, TASK_STATE_STARTING);
restore_interrupts(interrupts);
if ((rtos_started()) && (!executing_isr()) && (pri > current_task->pri)) yield();
return ans;
}
// (documentation in task.h)
uint8_t lock_on(mutex_t *m) {
interrupt_store_t interrupts;
if ((!current_task->pri) || (executing_isr())) {
return 1;
}
interrupts = disable_interrupts();
while (m->owner != current_task) {
if (m->owner) {
int16_t value = m->notification.value + 1; // We need to do this while interrupts are disabled!
restore_interrupts(interrupts);
wait_for_min_value(&(m->notification), value);
cli();
} else {
mutex_t *owned = current_task->owned_mutex;
m->owner = current_task;
m->next = 0;
if (!owned) {
current_task->owned_mutex = m;
} else {
while (owned->next) owned = owned->next;
owned->next = m;
}
}
}
restore_interrupts(interrupts);
return 0;
}
// (documentation in task.h)
uint8_t lock_off(mutex_t *m) {
interrupt_store_t interrupts;
if (executing_isr()) return 1;
interrupts = disable_interrupts();
if (m->owner == current_task) {
mutex_t *owned = current_task->owned_mutex;
m->owner = 0;
m->notification.value++;
if (owned == m) {
current_task->owned_mutex = m->next;
} else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -