/* * Copyright (c) 2001-2007 Jakub Jermar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup kernel_generic_proc * @{ */ /** @file */ #ifndef KERN_THREAD_H_ #define KERN_THREAD_H_ #include <synch/waitq.h> #include <proc/task.h> #include <time/timeout.h> #include <cpu.h> #include <synch/spinlock.h> #include <adt/odict.h> #include <mm/slab.h> #include <arch/cpu.h> #include <mm/tlb.h> #include <udebug/udebug.h> #include <abi/proc/thread.h> #include <abi/sysinfo.h> #include <arch.h> #define THREAD CURRENT->thread #define THREAD_NAME_BUFLEN 20 extern const char *thread_states[]; /* Thread flags */ typedef enum { THREAD_FLAG_NONE = 0, /** Thread executes in user space. */ THREAD_FLAG_USPACE = (1 << 0), /** Thread will be attached by the caller. */ THREAD_FLAG_NOATTACH = (1 << 1), /** Thread accounting doesn't affect accumulated task accounting. */ THREAD_FLAG_UNCOUNTED = (1 << 2) } thread_flags_t; /** Thread structure. There is one per thread. */ typedef struct thread { atomic_refcount_t refcount; link_t rq_link; /**< Run queue link. */ link_t wq_link; /**< Wait queue link. */ link_t th_link; /**< Links to threads within containing task. */ /** Link to @c threads ordered dictionary. */ odlink_t lthreads; /** Tracking variable for thread_wait/thread_wakeup */ atomic_int sleep_state; /** * If true, the thread is terminating. * It will not go to sleep in interruptible synchronization functions * and will call thread_exit() before returning to userspace. */ volatile bool interrupted; /** Wait queue in which this thread sleeps. Used for debug printouts. */ _Atomic(waitq_t *) sleep_queue; /** Waitq for thread_join_timeout(). */ waitq_t join_wq; /** Thread accounting. */ atomic_time_stat_t ucycles; atomic_time_stat_t kcycles; /** Architecture-specific data. */ thread_arch_t arch; #ifdef CONFIG_UDEBUG /** * If true, the scheduler will print a stack trace * to the kernel console upon scheduling this thread. */ atomic_int_fast8_t btrace; /** Debugging stuff */ udebug_thread_t udebug; #endif /* CONFIG_UDEBUG */ /* * Immutable fields. * * These fields are only modified during initialization, and are not * changed at any time between initialization and destruction. * Can be accessed without synchronization in most places. */ /** Thread ID. */ thread_id_t tid; /** Function implementing the thread. */ void (*thread_code)(void *); /** Argument passed to thread_code() function. */ void *thread_arg; char name[THREAD_NAME_BUFLEN]; /** Thread is executed in user space. */ bool uspace; /** Thread doesn't affect accumulated accounting. */ bool uncounted; /** Containing task. */ task_t *task; /** Thread's kernel stack. */ uint8_t *kstack; /* * Local fields. * * These fields can be safely accessed from code that _controls execution_ * of this thread. Code controls execution of a thread if either: * - it runs in the context of said thread AND interrupts are disabled * (interrupts can and will access these fields) * - the thread is not running, and the code accessing it can legally * add/remove the thread to/from a runqueue, i.e., either: * - it is allowed to enqueue thread in a new runqueue * - it holds the lock to the runqueue containing the thread * */ /** * From here, the stored context is restored * when the thread is scheduled. */ context_t saved_context; // TODO: we only need one of the two bools below /** * True if this thread is executing copy_from_uspace(). * False otherwise. */ bool in_copy_from_uspace; /** * True if this thread is executing copy_to_uspace(). * False otherwise. */ bool in_copy_to_uspace; /* * FPU context is a special case. If lazy FPU switching is disabled, * it acts as a regular local field. However, if lazy switching is enabled, * the context is synchronized via CPU->fpu_lock */ #ifdef CONFIG_FPU fpu_context_t fpu_context; #endif bool fpu_context_exists; /* The thread will not be migrated if nomigrate is non-zero. */ unsigned int nomigrate; /** Thread was migrated to another CPU and has not run yet. */ bool stolen; /** * Thread state (state_t). * This is atomic because we read it via some commands for debug output, * otherwise it could just be a regular local. */ atomic_int_fast32_t state; /** Thread CPU. */ _Atomic(cpu_t *) cpu; /** Thread's priority. Implemented as index to CPU->rq */ atomic_int_fast32_t priority; /** Last sampled cycle. */ uint64_t last_cycle; } thread_t; IRQ_SPINLOCK_EXTERN(threads_lock); extern odict_t threads; extern void thread_init(void); extern thread_t *thread_create(void (*)(void *), void *, task_t *, thread_flags_t, const char *); extern void thread_wire(thread_t *, cpu_t *); extern void thread_attach(thread_t *, task_t *); extern void thread_start(thread_t *); extern void thread_requeue_sleeping(thread_t *); extern void thread_exit(void) __attribute__((noreturn)); extern void thread_interrupt(thread_t *); enum sleep_state { SLEEP_INITIAL, SLEEP_ASLEEP, SLEEP_WOKE, }; typedef enum { THREAD_OK, THREAD_TERMINATING, } thread_termination_state_t; typedef enum { THREAD_WAIT_SUCCESS, THREAD_WAIT_TIMEOUT, } thread_wait_result_t; extern thread_termination_state_t thread_wait_start(void); extern thread_wait_result_t thread_wait_finish(deadline_t); extern void thread_wakeup(thread_t *); static inline thread_t *thread_ref(thread_t *thread) { refcount_up(&thread->refcount); return thread; } static inline thread_t *thread_try_ref(thread_t *thread) { if (refcount_try_up(&thread->refcount)) return thread; else return NULL; } extern void thread_put(thread_t *); #ifndef thread_create_arch extern errno_t thread_create_arch(thread_t *, thread_flags_t); #endif #ifndef thr_constructor_arch extern void thr_constructor_arch(thread_t *); #endif #ifndef thr_destructor_arch extern void thr_destructor_arch(thread_t *); #endif extern void thread_sleep(uint32_t); extern void thread_usleep(uint32_t); extern errno_t thread_join(thread_t *); extern errno_t thread_join_timeout(thread_t *, uint32_t, unsigned int); extern void thread_detach(thread_t *); extern void thread_yield(void); extern void thread_print_list(bool); extern thread_t *thread_find_by_id(thread_id_t); extern size_t thread_count(void); extern thread_t *thread_first(void); extern thread_t *thread_next(thread_t *); extern void thread_update_accounting(bool); extern thread_t *thread_try_get(thread_t *); extern void thread_migration_disable(void); extern void thread_migration_enable(void); #ifdef CONFIG_UDEBUG extern void thread_stack_trace(thread_id_t); #endif /** Fpu context slab cache. */ extern slab_cache_t *fpu_context_cache; /* Thread syscall prototypes. */ extern sys_errno_t sys_thread_create(uspace_ptr_uspace_arg_t, uspace_ptr_char, size_t, uspace_ptr_thread_id_t); extern sys_errno_t sys_thread_exit(int); extern sys_errno_t sys_thread_get_id(uspace_ptr_thread_id_t); extern sys_errno_t sys_thread_usleep(uint32_t); extern sys_errno_t sys_thread_udelay(uint32_t); #endif /** @} */