HelenOS sources
This source file includes following definitions.
- thr_constructor
- thr_destructor
- thread_init
- thread_wire
- thread_start
- thread_create
- thread_destroy
- thread_put
- thread_attach
- thread_exit
- thread_interrupt
- thread_wait_start
- thread_wait_timeout_callback
- thread_wait_finish
- thread_wakeup
- thread_migration_disable
- thread_migration_enable
- thread_sleep
- thread_join
- thread_join_timeout
- thread_detach
- thread_usleep
- thread_yield
- thread_print
- thread_print_list
- thread_exists
- thread_try_get
- thread_update_accounting
- thread_find_by_id
- thread_count
- thread_first
- thread_next
- thread_stack_trace
- threads_getkey
- threads_cmp
- sys_thread_create
- sys_thread_exit
- sys_thread_get_id
- sys_thread_usleep
- sys_thread_udelay
#include <assert.h>
#include <proc/scheduler.h>
#include <proc/thread.h>
#include <proc/task.h>
#include <mm/frame.h>
#include <mm/page.h>
#include <arch/asm.h>
#include <arch/cycle.h>
#include <arch.h>
#include <synch/spinlock.h>
#include <synch/waitq.h>
#include <synch/syswaitq.h>
#include <cpu.h>
#include <str.h>
#include <context.h>
#include <adt/list.h>
#include <adt/odict.h>
#include <time/clock.h>
#include <time/timeout.h>
#include <time/delay.h>
#include <config.h>
#include <arch/interrupt.h>
#include <smp/ipi.h>
#include <atomic.h>
#include <memw.h>
#include <stdio.h>
#include <stdlib.h>
#include <main/uinit.h>
#include <syscall/copy.h>
#include <errno.h>
#include <debug.h>
#include <halt.h>
const char *thread_states[] = {
"Invalid",
"Running",
"Sleeping",
"Ready",
"Entering",
"Exiting",
"Lingering"
};
IRQ_SPINLOCK_INITIALIZE(threads_lock);
odict_t threads;
IRQ_SPINLOCK_STATIC_INITIALIZE(tidlock);
static thread_id_t last_tid = 0;
static slab_cache_t *thread_cache;
static void *threads_getkey(odlink_t *);
static int threads_cmp(void *, void *);
static errno_t thr_constructor(void *obj, unsigned int kmflags)
{
thread_t *thread = (thread_t *) obj;
link_initialize(&thread->rq_link);
link_initialize(&thread->wq_link);
link_initialize(&thread->th_link);
thr_constructor_arch(thread);
kmflags |= FRAME_LOWMEM;
kmflags &= ~FRAME_HIGHMEM;
uintptr_t stack_phys =
frame_alloc(STACK_FRAMES, kmflags, STACK_SIZE - 1);
if (!stack_phys)
return ENOMEM;
thread->kstack = (uint8_t *) PA2KA(stack_phys);
#ifdef CONFIG_UDEBUG
mutex_initialize(&thread->udebug.lock, MUTEX_PASSIVE);
#endif
return EOK;
}
static size_t thr_destructor(void *obj)
{
thread_t *thread = (thread_t *) obj;
thr_destructor_arch(thread);
frame_free(KA2PA(thread->kstack), STACK_FRAMES);
return STACK_FRAMES;
}
void thread_init(void)
{
THREAD = NULL;
atomic_store(&nrdy, 0);
thread_cache = slab_cache_create("thread_t", sizeof(thread_t), _Alignof(thread_t),
thr_constructor, thr_destructor, 0);
odict_initialize(&threads, threads_getkey, threads_cmp);
}
void thread_wire(thread_t *thread, cpu_t *cpu)
{
ipl_t ipl = interrupts_disable();
atomic_set_unordered(&thread->cpu, cpu);
thread->nomigrate++;
interrupts_restore(ipl);
}
void thread_start(thread_t *thread)
{
assert(atomic_get_unordered(&thread->state) == Entering);
thread_requeue_sleeping(thread_ref(thread));
}
thread_t *thread_create(void (*func)(void *), void *arg, task_t *task,
thread_flags_t flags, const char *name)
{
thread_t *thread = (thread_t *) slab_alloc(thread_cache, FRAME_ATOMIC);
if (!thread)
return NULL;
refcount_init(&thread->refcount);
if (thread_create_arch(thread, flags) != EOK) {
slab_free(thread_cache, thread);
return NULL;
}
memsetb(thread->kstack, STACK_SIZE, 0);
irq_spinlock_lock(&tidlock, true);
thread->tid = ++last_tid;
irq_spinlock_unlock(&tidlock, true);
context_create(&thread->saved_context, thread_main_func,
thread->kstack, STACK_SIZE);
current_initialize((current_t *) thread->kstack);
str_cpy(thread->name, THREAD_NAME_BUFLEN, name);
thread->thread_code = func;
thread->thread_arg = arg;
thread->ucycles = ATOMIC_TIME_INITIALIZER();
thread->kcycles = ATOMIC_TIME_INITIALIZER();
thread->uncounted =
((flags & THREAD_FLAG_UNCOUNTED) == THREAD_FLAG_UNCOUNTED);
atomic_init(&thread->priority, 0);
atomic_init(&thread->cpu, NULL);
thread->stolen = false;
thread->uspace =
((flags & THREAD_FLAG_USPACE) == THREAD_FLAG_USPACE);
thread->nomigrate = 0;
atomic_init(&thread->state, Entering);
atomic_init(&thread->sleep_queue, NULL);
thread->in_copy_from_uspace = false;
thread->in_copy_to_uspace = false;
thread->interrupted = false;
atomic_init(&thread->sleep_state, SLEEP_INITIAL);
waitq_initialize(&thread->join_wq);
thread->task = task;
thread->fpu_context_exists = false;
odlink_initialize(&thread->lthreads);
#ifdef CONFIG_UDEBUG
atomic_init(&thread->btrace, false);
udebug_thread_initialize(&thread->udebug);
#endif
if ((flags & THREAD_FLAG_NOATTACH) != THREAD_FLAG_NOATTACH)
thread_attach(thread, task);
return thread;
}
static void thread_destroy(void *obj)
{
thread_t *thread = (thread_t *) obj;
assert_link_not_used(&thread->rq_link);
assert_link_not_used(&thread->wq_link);
assert(thread->task);
ipl_t ipl = interrupts_disable();
irq_spinlock_lock(&threads_lock, false);
odict_remove(&thread->lthreads);
irq_spinlock_unlock(&threads_lock, false);
irq_spinlock_lock(&thread->task->lock, false);
list_remove(&thread->th_link);
if (!thread->uncounted) {
thread->task->ucycles += atomic_time_read(&thread->ucycles);
thread->task->kcycles += atomic_time_read(&thread->kcycles);
}
irq_spinlock_unlock(&thread->task->lock, false);
assert((atomic_get_unordered(&thread->state) == Exiting) || (atomic_get_unordered(&thread->state) == Lingering));
#ifdef CONFIG_FPU_LAZY
cpu_t *cpu = atomic_get_unordered(&thread->cpu);
if (cpu) {
irq_spinlock_lock(&cpu->fpu_lock, false);
if (atomic_get_unordered(&cpu->fpu_owner) == thread)
atomic_set_unordered(&cpu->fpu_owner, NULL);
irq_spinlock_unlock(&cpu->fpu_lock, false);
}
#endif
interrupts_restore(ipl);
task_release(thread->task);
thread->task = NULL;
slab_free(thread_cache, thread);
}
void thread_put(thread_t *thread)
{
if (refcount_down(&thread->refcount)) {
thread_destroy(thread);
}
}
void thread_attach(thread_t *thread, task_t *task)
{
ipl_t ipl = interrupts_disable();
irq_spinlock_lock(&task->lock, false);
task_hold(task);
if (thread->uspace)
atomic_inc(&task->lifecount);
list_append(&thread->th_link, &task->threads);
irq_spinlock_unlock(&task->lock, false);
irq_spinlock_lock(&threads_lock, false);
odict_insert(&thread->lthreads, &threads, NULL);
irq_spinlock_unlock(&threads_lock, false);
interrupts_restore(ipl);
}
void thread_exit(void)
{
if (THREAD->uspace) {
#ifdef CONFIG_UDEBUG
udebug_thread_e_event();
udebug_stoppable_begin();
#endif
if (atomic_predec(&TASK->lifecount) == 0) {
ipc_cleanup();
sys_waitq_task_cleanup();
LOG("Cleanup of task %" PRIu64 " completed.", TASK->taskid);
}
}
scheduler_enter(Exiting);
unreachable();
}
void thread_interrupt(thread_t *thread)
{
assert(thread != NULL);
thread->interrupted = true;
thread_wakeup(thread);
}
thread_termination_state_t thread_wait_start(void)
{
assert(THREAD != NULL);
(void) atomic_exchange_explicit(&THREAD->sleep_state, SLEEP_INITIAL,
memory_order_acquire);
return THREAD->interrupted ? THREAD_TERMINATING : THREAD_OK;
}
static void thread_wait_timeout_callback(void *arg)
{
thread_wakeup(arg);
}
thread_wait_result_t thread_wait_finish(deadline_t deadline)
{
assert(THREAD != NULL);
timeout_t timeout;
if (atomic_load_explicit(&THREAD->sleep_state, memory_order_acquire) !=
SLEEP_INITIAL)
return THREAD_WAIT_SUCCESS;
if (deadline != DEADLINE_NEVER) {
timeout_initialize(&timeout);
timeout_register_deadline(&timeout, deadline,
thread_wait_timeout_callback, THREAD);
}
scheduler_enter(Sleeping);
if (deadline != DEADLINE_NEVER && !timeout_unregister(&timeout)) {
return THREAD_WAIT_TIMEOUT;
} else {
return THREAD_WAIT_SUCCESS;
}
}
void thread_wakeup(thread_t *thread)
{
assert(thread != NULL);
int state = atomic_exchange_explicit(&thread->sleep_state, SLEEP_WOKE,
memory_order_acq_rel);
if (state == SLEEP_ASLEEP) {
thread_requeue_sleeping(thread);
}
}
void thread_migration_disable(void)
{
ipl_t ipl = interrupts_disable();
assert(THREAD);
THREAD->nomigrate++;
interrupts_restore(ipl);
}
void thread_migration_enable(void)
{
ipl_t ipl = interrupts_disable();
assert(THREAD);
assert(THREAD->nomigrate > 0);
if (THREAD->nomigrate > 0)
THREAD->nomigrate--;
interrupts_restore(ipl);
}
void thread_sleep(uint32_t sec)
{
while (sec > 0) {
uint32_t period = (sec > 1000) ? 1000 : sec;
thread_usleep(period * 1000000);
sec -= period;
}
}
errno_t thread_join(thread_t *thread)
{
return thread_join_timeout(thread, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
}
errno_t thread_join_timeout(thread_t *thread, uint32_t usec, unsigned int flags)
{
assert(thread != NULL);
if (thread == THREAD)
return EINVAL;
errno_t rc = _waitq_sleep_timeout(&thread->join_wq, usec, flags);
if (rc == EOK)
thread_put(thread);
return rc;
}
void thread_detach(thread_t *thread)
{
thread_put(thread);
}
void thread_usleep(uint32_t usec)
{
waitq_t wq;
waitq_initialize(&wq);
(void) waitq_sleep_timeout(&wq, usec);
}
void thread_yield(void)
{
assert(THREAD != NULL);
scheduler_enter(Running);
}
static void thread_print(thread_t *thread, bool additional)
{
uint64_t ucycles, kcycles;
char usuffix, ksuffix;
order_suffix(atomic_time_read(&thread->ucycles), &ucycles, &usuffix);
order_suffix(atomic_time_read(&thread->kcycles), &kcycles, &ksuffix);
state_t state = atomic_get_unordered(&thread->state);
char *name;
if (str_cmp(thread->name, "uinit") == 0)
name = thread->task->name;
else
name = thread->name;
if (additional)
printf("%-8" PRIu64 " %p %p %9" PRIu64 "%c %9" PRIu64 "%c ",
thread->tid, thread->thread_code, thread->kstack,
ucycles, usuffix, kcycles, ksuffix);
else
printf("%-8" PRIu64 " %-14s %p %-8s %p %-5" PRIu32 "\n",
thread->tid, name, thread, thread_states[state],
thread->task, thread->task->container);
if (additional) {
cpu_t *cpu = atomic_get_unordered(&thread->cpu);
if (cpu)
printf("%-5u", cpu->id);
else
printf("none ");
if (state == Sleeping) {
printf(" %p", thread->sleep_queue);
}
printf("\n");
}
}
void thread_print_list(bool additional)
{
thread_t *thread;
irq_spinlock_lock(&threads_lock, true);
if (sizeof(void *) <= 4) {
if (additional)
printf("[id ] [code ] [stack ] [ucycles ] [kcycles ]"
" [cpu] [waitqueue]\n");
else
printf("[id ] [name ] [address ] [state ] [task ]"
" [ctn]\n");
} else {
if (additional) {
printf("[id ] [code ] [stack ] [ucycles ] [kcycles ]"
" [cpu] [waitqueue ]\n");
} else
printf("[id ] [name ] [address ] [state ]"
" [task ] [ctn]\n");
}
thread = thread_first();
while (thread != NULL) {
thread_print(thread, additional);
thread = thread_next(thread);
}
irq_spinlock_unlock(&threads_lock, true);
}
static bool thread_exists(thread_t *thread)
{
odlink_t *odlink = odict_find_eq(&threads, thread, NULL);
return odlink != NULL;
}
thread_t *thread_try_get(thread_t *thread)
{
irq_spinlock_lock(&threads_lock, true);
if (thread_exists(thread)) {
thread = thread_try_ref(thread);
} else {
thread = NULL;
}
irq_spinlock_unlock(&threads_lock, true);
return thread;
}
void thread_update_accounting(bool user)
{
assert(interrupts_disabled());
uint64_t time = get_cycle();
if (user)
atomic_time_increment(&THREAD->ucycles, time - THREAD->last_cycle);
else
atomic_time_increment(&THREAD->kcycles, time - THREAD->last_cycle);
THREAD->last_cycle = time;
}
thread_t *thread_find_by_id(thread_id_t thread_id)
{
thread_t *thread;
assert(interrupts_disabled());
assert(irq_spinlock_locked(&threads_lock));
thread = thread_first();
while (thread != NULL) {
if (thread->tid == thread_id)
return thread;
thread = thread_next(thread);
}
return NULL;
}
size_t thread_count(void)
{
assert(interrupts_disabled());
assert(irq_spinlock_locked(&threads_lock));
return odict_count(&threads);
}
thread_t *thread_first(void)
{
odlink_t *odlink;
assert(interrupts_disabled());
assert(irq_spinlock_locked(&threads_lock));
odlink = odict_first(&threads);
if (odlink == NULL)
return NULL;
return odict_get_instance(odlink, thread_t, lthreads);
}
thread_t *thread_next(thread_t *cur)
{
odlink_t *odlink;
assert(interrupts_disabled());
assert(irq_spinlock_locked(&threads_lock));
odlink = odict_next(&cur->lthreads, &threads);
if (odlink == NULL)
return NULL;
return odict_get_instance(odlink, thread_t, lthreads);
}
#ifdef CONFIG_UDEBUG
void thread_stack_trace(thread_id_t thread_id)
{
irq_spinlock_lock(&threads_lock, true);
thread_t *thread = thread_try_ref(thread_find_by_id(thread_id));
irq_spinlock_unlock(&threads_lock, true);
if (thread == NULL) {
printf("No such thread.\n");
return;
}
printf("Scheduling thread stack trace.\n");
atomic_set_unordered(&thread->btrace, true);
thread_wakeup(thread);
thread_put(thread);
}
#endif
static void *threads_getkey(odlink_t *odlink)
{
thread_t *thread = odict_get_instance(odlink, thread_t, lthreads);
return (void *) thread;
}
static int threads_cmp(void *a, void *b)
{
if (a > b)
return -1;
else if (a == b)
return 0;
else
return +1;
}
sys_errno_t sys_thread_create(sysarg_t pc, sysarg_t sp,
uspace_ptr_char uspace_name, size_t name_len)
{
if (name_len > THREAD_NAME_BUFLEN - 1)
name_len = THREAD_NAME_BUFLEN - 1;
char namebuf[THREAD_NAME_BUFLEN];
errno_t rc = copy_from_uspace(namebuf, uspace_name, name_len);
if (rc != EOK)
return (sys_errno_t) rc;
namebuf[name_len] = 0;
uinit_arg_t *kernel_uarg = malloc(sizeof(uinit_arg_t));
if (!kernel_uarg)
return (sys_errno_t) ENOMEM;
kernel_uarg->pc = pc;
kernel_uarg->sp = sp;
thread_t *thread = thread_create(uinit, kernel_uarg, TASK,
THREAD_FLAG_USPACE | THREAD_FLAG_NOATTACH, namebuf);
if (!thread) {
free(kernel_uarg);
return (sys_errno_t) ENOMEM;
}
#ifdef CONFIG_UDEBUG
udebug_thread_b_event_attach(thread, TASK);
#else
thread_attach(thread, TASK);
#endif
thread_start(thread);
thread_put(thread);
return (sys_errno_t) EOK;
}
sys_errno_t sys_thread_exit(int uspace_status)
{
thread_exit();
}
sys_errno_t sys_thread_get_id(uspace_ptr_thread_id_t uspace_thread_id)
{
return (sys_errno_t) copy_to_uspace(uspace_thread_id, &THREAD->tid,
sizeof(THREAD->tid));
}
sys_errno_t sys_thread_usleep(uint32_t usec)
{
thread_usleep(usec);
return 0;
}
sys_errno_t sys_thread_udelay(uint32_t usec)
{
delay(usec);
return 0;
}
HelenOS homepage, sources at GitHub