2013-11-27 16:28:31 +01:00
|
|
|
/*
|
2013-06-18 17:21:38 +02:00
|
|
|
* Copyright (C) 2013 Freie Universität Berlin
|
2010-09-22 15:10:42 +02:00
|
|
|
*
|
2014-07-31 19:45:27 +02:00
|
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
|
|
* directory for more details.
|
2013-11-27 16:28:31 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ingroup core_thread
|
2010-09-22 15:10:42 +02:00
|
|
|
* @{
|
2013-11-27 16:28:31 +01:00
|
|
|
*
|
|
|
|
* @file thread.c
|
|
|
|
* @brief Threading implementation
|
|
|
|
*
|
2014-01-28 11:50:12 +01:00
|
|
|
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
2013-11-27 16:28:31 +01:00
|
|
|
*
|
2010-09-22 15:10:42 +02:00
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "thread.h"
|
|
|
|
#include "kernel.h"
|
2014-02-15 18:49:27 +01:00
|
|
|
#include "irq.h"
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2013-07-24 00:36:06 +02:00
|
|
|
#define ENABLE_DEBUG (0)
|
2010-09-22 15:10:42 +02:00
|
|
|
#include "debug.h"
|
2013-07-16 16:36:37 +02:00
|
|
|
#include "kernel_internal.h"
|
2010-09-22 15:10:42 +02:00
|
|
|
#include "bitarithm.h"
|
|
|
|
#include "hwtimer.h"
|
2010-10-28 11:22:57 +02:00
|
|
|
#include "sched.h"
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2014-08-13 09:26:27 +02:00
|
|
|
volatile tcb_t *thread_get(kernel_pid_t pid)
|
2013-06-20 18:18:29 +02:00
|
|
|
{
|
2014-10-10 15:02:15 +02:00
|
|
|
if (pid_is_valid(pid)) {
|
2014-08-13 09:26:27 +02:00
|
|
|
return sched_threads[pid];
|
2013-06-20 18:18:29 +02:00
|
|
|
}
|
2014-08-13 09:26:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2014-08-13 09:26:27 +02:00
|
|
|
int thread_getstatus(kernel_pid_t pid)
|
|
|
|
{
|
|
|
|
volatile tcb_t *t = thread_get(pid);
|
|
|
|
return t ? t->status : STATUS_NOT_FOUND;
|
2010-09-22 15:10:42 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 22:57:56 +02:00
|
|
|
const char *thread_getname(kernel_pid_t pid)
|
2013-10-17 15:25:43 +02:00
|
|
|
{
|
2014-08-13 09:26:27 +02:00
|
|
|
volatile tcb_t *t = thread_get(pid);
|
|
|
|
return t ? t->name : NULL;
|
2013-10-17 15:25:43 +02:00
|
|
|
}
|
|
|
|
|
2014-05-07 12:36:32 +02:00
|
|
|
void thread_sleep(void)
|
2013-06-20 18:18:29 +02:00
|
|
|
{
|
2013-06-24 22:37:35 +02:00
|
|
|
if (inISR()) {
|
2013-06-20 18:18:29 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-09-22 15:10:42 +02:00
|
|
|
dINT();
|
2014-04-10 22:28:35 +02:00
|
|
|
sched_set_status((tcb_t *)sched_active_thread, STATUS_SLEEPING);
|
2012-11-01 13:18:17 +01:00
|
|
|
eINT();
|
2014-10-18 01:24:49 +02:00
|
|
|
thread_yield_higher();
|
2010-09-22 15:10:42 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 22:57:56 +02:00
|
|
|
int thread_wakeup(kernel_pid_t pid)
|
2013-06-20 18:18:29 +02:00
|
|
|
{
|
2014-07-06 22:57:56 +02:00
|
|
|
DEBUG("thread_wakeup: Trying to wakeup PID %" PRIkernel_pid "...\n", pid);
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2014-10-27 16:18:40 +01:00
|
|
|
unsigned old_state = disableIRQ();
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2014-02-13 23:17:36 +01:00
|
|
|
tcb_t *other_thread = (tcb_t *) sched_threads[pid];
|
|
|
|
if (other_thread && other_thread->status == STATUS_SLEEPING) {
|
2010-11-02 11:40:10 +01:00
|
|
|
DEBUG("thread_wakeup: Thread is sleeping.\n");
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2014-02-13 23:17:36 +01:00
|
|
|
sched_set_status(other_thread, STATUS_RUNNING);
|
|
|
|
|
|
|
|
restoreIRQ(old_state);
|
2014-04-25 18:20:42 +02:00
|
|
|
sched_switch(other_thread->priority);
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2010-11-04 16:21:39 +01:00
|
|
|
return 1;
|
2013-06-20 18:18:29 +02:00
|
|
|
}
|
|
|
|
else {
|
2010-11-02 11:40:10 +01:00
|
|
|
DEBUG("thread_wakeup: Thread is not sleeping!\n");
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2014-02-13 23:17:36 +01:00
|
|
|
restoreIRQ(old_state);
|
2010-09-22 15:10:42 +02:00
|
|
|
return STATUS_NOT_FOUND;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-06 01:59:41 +02:00
|
|
|
#ifdef DEVELHELP
|
2014-06-03 21:53:15 +02:00
|
|
|
uintptr_t thread_measure_stack_free(char *stack)
|
2013-06-20 18:18:29 +02:00
|
|
|
{
|
2014-06-03 21:53:15 +02:00
|
|
|
uintptr_t *stackp = (uintptr_t *)stack;
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2014-01-20 10:42:59 +01:00
|
|
|
/* assume that the comparison fails before or after end of stack */
|
|
|
|
/* assume that the stack grows "downwards" */
|
2014-06-03 21:53:15 +02:00
|
|
|
while (*stackp == (uintptr_t) stackp) {
|
2010-09-22 15:10:42 +02:00
|
|
|
stackp++;
|
2013-06-20 18:18:29 +02:00
|
|
|
}
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2014-06-03 21:53:15 +02:00
|
|
|
uintptr_t space_free = (uintptr_t) stackp - (uintptr_t) stack;
|
2014-01-20 10:42:59 +01:00
|
|
|
return space_free;
|
2010-09-22 15:10:42 +02:00
|
|
|
}
|
2014-06-06 01:59:41 +02:00
|
|
|
#endif
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2014-10-19 23:10:14 +02:00
|
|
|
kernel_pid_t thread_create(char *stack, int stacksize, char priority, int flags, thread_task_func_t function, void *arg, const char *name)
|
2010-09-22 15:10:42 +02:00
|
|
|
{
|
2014-06-03 21:53:15 +02:00
|
|
|
if (priority >= SCHED_PRIO_LEVELS) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-06-06 01:55:37 +02:00
|
|
|
#ifdef DEVELHELP
|
2010-11-04 16:47:24 +01:00
|
|
|
int total_stacksize = stacksize;
|
2014-06-06 01:55:37 +02:00
|
|
|
#endif
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2014-06-03 21:53:15 +02:00
|
|
|
/* align the stack on a 16/32bit boundary */
|
|
|
|
uintptr_t misalignment = (uintptr_t) stack % ALIGN_OF(void *);
|
|
|
|
if (misalignment) {
|
|
|
|
misalignment = ALIGN_OF(void *) - misalignment;
|
|
|
|
stack += misalignment;
|
|
|
|
stacksize -= misalignment;
|
2010-11-09 17:01:52 +01:00
|
|
|
}
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2014-06-03 21:53:15 +02:00
|
|
|
/* make room for the thread control block */
|
|
|
|
stacksize -= sizeof(tcb_t);
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2014-06-03 21:53:15 +02:00
|
|
|
/* round down the stacksize to a multiple of tcb_t alignments (usually 16/32bit) */
|
|
|
|
stacksize -= stacksize % ALIGN_OF(tcb_t);
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2014-06-03 21:53:15 +02:00
|
|
|
/* allocate our thread control block at the top of our stackspace */
|
|
|
|
tcb_t *cb = (tcb_t *) (stack + stacksize);
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2014-06-06 01:59:41 +02:00
|
|
|
#ifdef DEVELHELP
|
2013-06-24 22:37:35 +02:00
|
|
|
if (flags & CREATE_STACKTEST) {
|
2010-09-22 15:10:42 +02:00
|
|
|
/* assign each int of the stack the value of it's address */
|
2014-06-03 21:53:15 +02:00
|
|
|
uintptr_t *stackmax = (uintptr_t *) (stack + stacksize);
|
|
|
|
uintptr_t *stackp = (uintptr_t *) stack;
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2013-06-24 22:37:35 +02:00
|
|
|
while (stackp < stackmax) {
|
2014-06-03 21:53:15 +02:00
|
|
|
*stackp = (uintptr_t) stackp;
|
2010-09-22 15:10:42 +02:00
|
|
|
stackp++;
|
|
|
|
}
|
2013-06-20 18:18:29 +02:00
|
|
|
}
|
|
|
|
else {
|
2010-09-22 15:10:42 +02:00
|
|
|
/* create stack guard */
|
2014-06-03 21:53:15 +02:00
|
|
|
*stack = (uintptr_t) stack;
|
2010-09-22 15:10:42 +02:00
|
|
|
}
|
2014-06-06 01:59:41 +02:00
|
|
|
#endif
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2013-06-24 22:37:35 +02:00
|
|
|
if (!inISR()) {
|
2010-09-22 15:10:42 +02:00
|
|
|
dINT();
|
|
|
|
}
|
|
|
|
|
2014-08-13 09:08:50 +02:00
|
|
|
kernel_pid_t pid = KERNEL_PID_UNDEF;
|
|
|
|
for (kernel_pid_t i = KERNEL_PID_FIRST; i <= KERNEL_PID_LAST; ++i) {
|
|
|
|
if (sched_threads[i] == NULL) {
|
|
|
|
pid = i;
|
2010-09-22 15:10:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-08-13 09:08:50 +02:00
|
|
|
if (pid == KERNEL_PID_UNDEF) {
|
2010-09-22 15:10:42 +02:00
|
|
|
DEBUG("thread_create(): too many threads!\n");
|
|
|
|
|
2013-06-24 22:37:35 +02:00
|
|
|
if (!inISR()) {
|
2010-09-22 15:10:42 +02:00
|
|
|
eINT();
|
|
|
|
}
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2010-09-22 15:10:42 +02:00
|
|
|
return -EOVERFLOW;
|
|
|
|
}
|
|
|
|
|
2014-08-13 09:08:50 +02:00
|
|
|
sched_threads[pid] = cb;
|
|
|
|
|
|
|
|
cb->pid = pid;
|
2014-03-04 20:20:01 +01:00
|
|
|
cb->sp = thread_stack_init(function, arg, stack, stacksize);
|
2010-10-25 15:40:01 +02:00
|
|
|
cb->stack_start = stack;
|
2014-06-06 01:55:37 +02:00
|
|
|
|
|
|
|
#ifdef DEVELHELP
|
2010-11-04 16:47:24 +01:00
|
|
|
cb->stack_size = total_stacksize;
|
2014-06-06 01:55:37 +02:00
|
|
|
#endif
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2010-10-25 15:40:01 +02:00
|
|
|
cb->priority = priority;
|
|
|
|
cb->status = 0;
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2010-11-26 14:21:48 +01:00
|
|
|
cb->rq_entry.next = NULL;
|
|
|
|
cb->rq_entry.prev = NULL;
|
|
|
|
|
2010-10-25 15:40:01 +02:00
|
|
|
cb->name = name;
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2010-10-25 15:40:01 +02:00
|
|
|
cb->wait_data = NULL;
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2014-05-07 00:41:21 +02:00
|
|
|
cb->msg_waiters.first = NULL;
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2013-06-20 18:18:29 +02:00
|
|
|
cib_init(&(cb->msg_queue), 0);
|
2010-11-26 15:02:15 +01:00
|
|
|
cb->msg_array = NULL;
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2014-04-10 22:28:35 +02:00
|
|
|
sched_num_threads++;
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2014-07-06 22:57:56 +02:00
|
|
|
DEBUG("Created thread %s. PID: %" PRIkernel_pid ". Priority: %u.\n", name, cb->pid, priority);
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2013-06-24 22:37:35 +02:00
|
|
|
if (flags & CREATE_SLEEPING) {
|
2010-10-25 15:40:01 +02:00
|
|
|
sched_set_status(cb, STATUS_SLEEPING);
|
2013-06-20 18:18:29 +02:00
|
|
|
}
|
|
|
|
else {
|
2010-10-25 15:40:01 +02:00
|
|
|
sched_set_status(cb, STATUS_PENDING);
|
2013-06-20 18:18:29 +02:00
|
|
|
|
2013-06-24 22:37:35 +02:00
|
|
|
if (!(flags & CREATE_WOUT_YIELD)) {
|
|
|
|
if (!inISR()) {
|
2010-09-22 15:10:42 +02:00
|
|
|
eINT();
|
2014-10-18 01:24:49 +02:00
|
|
|
sched_switch(priority);
|
2013-06-20 18:18:29 +02:00
|
|
|
}
|
|
|
|
else {
|
2010-10-28 11:22:57 +02:00
|
|
|
sched_context_switch_request = 1;
|
2010-09-22 15:10:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-10 22:28:35 +02:00
|
|
|
if (!inISR() && sched_active_thread != NULL) {
|
2010-09-22 15:10:42 +02:00
|
|
|
eINT();
|
|
|
|
}
|
|
|
|
|
|
|
|
return pid;
|
|
|
|
}
|