You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
6.0 KiB
255 lines
6.0 KiB
/* |
|
* Copyright (C) 2013 Freie Universität Berlin |
|
* |
|
* 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. |
|
*/ |
|
|
|
/** |
|
* @ingroup core_thread |
|
* @{ |
|
* |
|
* @file |
|
* @brief Threading implementation |
|
* |
|
* @author Kaspar Schleiser <kaspar@schleiser.de> |
|
* |
|
* @} |
|
*/ |
|
|
|
#include <errno.h> |
|
#include <stdio.h> |
|
|
|
#include "assert.h" |
|
#include "thread.h" |
|
#include "irq.h" |
|
|
|
#define ENABLE_DEBUG (0) |
|
#include "debug.h" |
|
#include "bitarithm.h" |
|
#include "sched.h" |
|
|
|
volatile thread_t *thread_get(kernel_pid_t pid) |
|
{ |
|
if (pid_is_valid(pid)) { |
|
return sched_threads[pid]; |
|
} |
|
return NULL; |
|
} |
|
|
|
int thread_getstatus(kernel_pid_t pid) |
|
{ |
|
volatile thread_t *t = thread_get(pid); |
|
return t ? (int) t->status : STATUS_NOT_FOUND; |
|
} |
|
|
|
#ifdef DEVELHELP |
|
const char *thread_getname(kernel_pid_t pid) |
|
{ |
|
volatile thread_t *t = thread_get(pid); |
|
return t ? t->name : NULL; |
|
} |
|
#endif |
|
|
|
void thread_sleep(void) |
|
{ |
|
if (irq_is_in()) { |
|
return; |
|
} |
|
|
|
unsigned state = irq_disable(); |
|
sched_set_status((thread_t *)sched_active_thread, STATUS_SLEEPING); |
|
irq_restore(state); |
|
thread_yield_higher(); |
|
} |
|
|
|
int thread_wakeup(kernel_pid_t pid) |
|
{ |
|
DEBUG("thread_wakeup: Trying to wakeup PID %" PRIkernel_pid "...\n", pid); |
|
|
|
unsigned old_state = irq_disable(); |
|
|
|
thread_t *other_thread = (thread_t *) thread_get(pid); |
|
|
|
if (!other_thread) { |
|
DEBUG("thread_wakeup: Thread does not exist!\n"); |
|
} |
|
else if (other_thread->status == STATUS_SLEEPING) { |
|
DEBUG("thread_wakeup: Thread is sleeping.\n"); |
|
|
|
sched_set_status(other_thread, STATUS_RUNNING); |
|
|
|
irq_restore(old_state); |
|
sched_switch(other_thread->priority); |
|
|
|
return 1; |
|
} |
|
else { |
|
DEBUG("thread_wakeup: Thread is not sleeping!\n"); |
|
} |
|
|
|
irq_restore(old_state); |
|
return STATUS_NOT_FOUND; |
|
} |
|
|
|
void thread_yield(void) |
|
{ |
|
unsigned old_state = irq_disable(); |
|
thread_t *me = (thread_t *)sched_active_thread; |
|
if (me->status >= STATUS_ON_RUNQUEUE) { |
|
clist_lpoprpush(&sched_runqueues[me->priority]); |
|
} |
|
irq_restore(old_state); |
|
|
|
thread_yield_higher(); |
|
} |
|
|
|
void thread_add_to_list(list_node_t *list, thread_t *thread) |
|
{ |
|
assert (thread->status < STATUS_ON_RUNQUEUE); |
|
|
|
uint16_t my_prio = thread->priority; |
|
list_node_t *new_node = (list_node_t*)&thread->rq_entry; |
|
|
|
while (list->next) { |
|
thread_t *list_entry = container_of((clist_node_t*)list->next, thread_t, rq_entry); |
|
if (list_entry->priority > my_prio) { |
|
break; |
|
} |
|
list = list->next; |
|
} |
|
|
|
new_node->next = list->next; |
|
list->next = new_node; |
|
} |
|
|
|
#ifdef DEVELHELP |
|
uintptr_t thread_measure_stack_free(char *stack) |
|
{ |
|
uintptr_t *stackp = (uintptr_t *)stack; |
|
|
|
/* assume that the comparison fails before or after end of stack */ |
|
/* assume that the stack grows "downwards" */ |
|
while (*stackp == (uintptr_t) stackp) { |
|
stackp++; |
|
} |
|
|
|
uintptr_t space_free = (uintptr_t) stackp - (uintptr_t) stack; |
|
return space_free; |
|
} |
|
#endif |
|
|
|
kernel_pid_t thread_create(char *stack, int stacksize, char priority, int flags, thread_task_func_t function, void *arg, const char *name) |
|
{ |
|
if (priority >= SCHED_PRIO_LEVELS) { |
|
return -EINVAL; |
|
} |
|
|
|
#ifdef DEVELHELP |
|
int total_stacksize = stacksize; |
|
#else |
|
(void) name; |
|
#endif |
|
|
|
/* 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; |
|
} |
|
|
|
/* make room for the thread control block */ |
|
stacksize -= sizeof(thread_t); |
|
|
|
/* round down the stacksize to a multiple of thread_t alignments (usually 16/32bit) */ |
|
stacksize -= stacksize % ALIGN_OF(thread_t); |
|
|
|
if (stacksize < 0) { |
|
DEBUG("thread_create: stacksize is too small!\n"); |
|
} |
|
/* allocate our thread control block at the top of our stackspace */ |
|
thread_t *cb = (thread_t *) (stack + stacksize); |
|
|
|
#if defined(DEVELHELP) || defined(SCHED_TEST_STACK) |
|
if (flags & THREAD_CREATE_STACKTEST) { |
|
/* assign each int of the stack the value of it's address */ |
|
uintptr_t *stackmax = (uintptr_t *) (stack + stacksize); |
|
uintptr_t *stackp = (uintptr_t *) stack; |
|
|
|
while (stackp < stackmax) { |
|
*stackp = (uintptr_t) stackp; |
|
stackp++; |
|
} |
|
} |
|
else { |
|
/* create stack guard */ |
|
*(uintptr_t *) stack = (uintptr_t) stack; |
|
} |
|
#endif |
|
|
|
unsigned state = irq_disable(); |
|
|
|
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; |
|
break; |
|
} |
|
} |
|
if (pid == KERNEL_PID_UNDEF) { |
|
DEBUG("thread_create(): too many threads!\n"); |
|
|
|
irq_restore(state); |
|
|
|
return -EOVERFLOW; |
|
} |
|
|
|
sched_threads[pid] = cb; |
|
|
|
cb->pid = pid; |
|
cb->sp = thread_stack_init(function, arg, stack, stacksize); |
|
|
|
#if defined(DEVELHELP) || defined(SCHED_TEST_STACK) || defined(MODULE_MPU_STACK_GUARD) |
|
cb->stack_start = stack; |
|
#endif |
|
|
|
#ifdef DEVELHELP |
|
cb->stack_size = total_stacksize; |
|
cb->name = name; |
|
#endif |
|
|
|
cb->priority = priority; |
|
cb->status = 0; |
|
|
|
cb->rq_entry.next = NULL; |
|
|
|
#ifdef MODULE_CORE_MSG |
|
cb->wait_data = NULL; |
|
cb->msg_waiters.next = NULL; |
|
cib_init(&(cb->msg_queue), 0); |
|
cb->msg_array = NULL; |
|
#endif |
|
|
|
sched_num_threads++; |
|
|
|
DEBUG("Created thread %s. PID: %" PRIkernel_pid ". Priority: %u.\n", name, cb->pid, priority); |
|
|
|
if (flags & THREAD_CREATE_SLEEPING) { |
|
sched_set_status(cb, STATUS_SLEEPING); |
|
} |
|
else { |
|
sched_set_status(cb, STATUS_PENDING); |
|
|
|
if (!(flags & THREAD_CREATE_WOUT_YIELD)) { |
|
irq_restore(state); |
|
sched_switch(priority); |
|
return pid; |
|
} |
|
} |
|
|
|
irq_restore(state); |
|
|
|
return pid; |
|
}
|
|
|