diff options
Diffstat (limited to 'pk/kernel/pk_thread_core.c')
-rw-r--r-- | pk/kernel/pk_thread_core.c | 573 |
1 files changed, 0 insertions, 573 deletions
diff --git a/pk/kernel/pk_thread_core.c b/pk/kernel/pk_thread_core.c deleted file mode 100644 index 2966eb9b..00000000 --- a/pk/kernel/pk_thread_core.c +++ /dev/null @@ -1,573 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_thread_core.c -/// \brief PK thread APIs -/// -/// The entry points in this file are considered 'core' routines that will -/// always be present at runtime in any PK application that enables threads. - -#include "pk.h" -#include "pk_thread.h" - -#define __PK_THREAD_CORE_C__ - - -// This routine is only used locally. Interrupts must be disabled -// at entry. The caller must also have checked that the priority is free. -// This routine is only called on threads known to be in a suspended state, -// either PK_THREAD_STATE_SUSPENDED_RUNNABLE or -// PK_THREAD_STATE_SUSPENDED_BLOCKED. Mapping a runnable thread adds it to -// the run queue. Mapping a thread pending on a semaphore either takes the -// count and becomes runnable or adds the thread to the pending queue for the -// semaphore. Mapping a sleeping thread requires no further action -// here. Scheduling after the map must be handled by the caller. - -void -__pk_thread_map(PkThread* thread) -{ - PkThreadPriority priority; - - priority = thread->priority; - __pk_priority_map[priority] = thread; - - if (thread->state == PK_THREAD_STATE_SUSPENDED_RUNNABLE) { - - __pk_thread_queue_insert(&__pk_run_queue, priority); - - } else if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) { - - if (thread->semaphore->count) { - - thread->semaphore->count--; - __pk_thread_queue_insert(&__pk_run_queue, priority); - - } else { - - __pk_thread_queue_insert(&(thread->semaphore->pending_threads), - priority); - } - } - - thread->state = PK_THREAD_STATE_MAPPED; - - if (PK_KERNEL_TRACE_ENABLE) { - if (__pk_thread_is_runnable(thread)) { - PK_KERN_TRACE("THREAD_MAPPED_RUNNABLE(%d)", priority); - } else if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) { - PK_KERN_TRACE("THREAD_MAPPED_SEMAPHORE_PEND(%d)", priority); - } else { - PK_KERN_TRACE("THREAD_MAPPED_SLEEPING(%d)", priority); - } - } -} - - -// This routine is only used locally. Interrupts must be disabled -// at entry. This routine is only ever called on threads in the -// PK_THREAD_STATE_MAPPED. Unmapping a thread removes it from the priority -// map, the run queue and any semaphore pend, but does not cancel any -// timers. Scheduling must be handled by the code calling -// __pk_thread_unmap(). - -void -__pk_thread_unmap(PkThread *thread) -{ - PkThreadPriority priority; - - priority = thread->priority; - __pk_priority_map[priority] = 0; - - if (__pk_thread_is_runnable(thread)) { - - thread->state = PK_THREAD_STATE_SUSPENDED_RUNNABLE; - __pk_thread_queue_delete(&__pk_run_queue, priority); - - } else { - - thread->state = PK_THREAD_STATE_SUSPENDED_BLOCKED; - if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) { - __pk_thread_queue_delete(&(thread->semaphore->pending_threads), - priority); - } - } -} - - -// Schedule and run the highest-priority mapped runnable thread. -// -// The priority of the next thread to run is first computed. This may be -// PK_THREADS, indicating that the only thread to run is the idle thread. -// This will always cause (or defer) a 'context switch' to the idle thread. -// Otherwise, if the new thread is not equal to the current thread this will -// also cause (or defer) a context switch. Note that scheduling is defined in -// terms of priorities but actually implemented in terms of PkThread pointers. -// -// If we are not yet in thread mode we're done - threads will be started by -// pk_start_threads() later. If we're in thread context a context switch -// happens immediately. In an interrupt context the switch is deferred to the -// end of interrupt processing. - -void -__pk_schedule(void) -{ - __pk_next_priority = __pk_thread_queue_min(&__pk_run_queue); - __pk_next_thread = __pk_priority_map[__pk_next_priority]; - - if ((__pk_next_thread == 0) || - (__pk_next_thread != __pk_current_thread)) { - - if (__pk_kernel_mode_thread()) { - if (__pk_kernel_context_thread()) { - if (__pk_current_thread != 0) { - __pk_switch(); - } else { - __pk_next_thread_resume(); - } - } else { - __pk_delayed_switch = 1; - } - } - } -} - - -// This routine is only used locally. -// -// Completion and deletion are pretty much the same thing. Completion is -// simply self-deletion of the current thread (which is mapped by -// definition.) The complete/delete APIs have slightly different error -// conditions but are otherwise the same. -// -// Deleting a mapped thread first unmaps (suspends) the thread, which takes -// care of removing the thread from any semaphores it may be pending on. Then -// any outstanding timer is also cancelled. -// -// If the current thread is being deleted we install the idle thread as -// __pk_current_thread, so scheduling is forced and no context is saved on -// the context switch. -// -// Note that we do not create trace events for unmapped threads since the trace -// tag only encodes the priority, which may be in use by a mapped thread. - -void -__pk_thread_delete(PkThread *thread, PkThreadState final_state) -{ - PkMachineContext ctx; - int mapped; - - pk_critical_section_enter(&ctx); - - mapped = __pk_thread_is_mapped(thread); - - if (mapped) { - __pk_thread_unmap(thread); - } - - __pk_timer_cancel(&(thread->timer)); - thread->state = final_state; - - if (mapped) { - - if (PK_KERNEL_TRACE_ENABLE) { - if (final_state == PK_THREAD_STATE_DELETED) { - PK_KERN_TRACE("THREAD_DELETED(%d)", thread->priority); - } else { - PK_KERN_TRACE("THREAD_COMPLETED(%d)", thread->priority); - } - } - - if (thread == __pk_current_thread) { - __pk_current_thread = 0; - } - __pk_schedule(); - } - - pk_critical_section_exit(&ctx); -} - - -// Generic thread timeout -// -// This routine is called as a timer callback either because a sleeping thread -// has timed out or a thread pending on a semaphore has timed out. If the -// thread is not already runnable then the the timeout flag is set, and if the -// thread is mapped it is scheduled. -// -// This implementation allows that a thread blocked on a timer may have been -// made runnable by some other mechanism, such as acquiring a semaphore. In -// order to provide an iteration-free implementation of -// pk_semaphore_release_all(), cancelling any semaphore timeouts is deferred -// until the thread runs again. -// -// Note that we do not create trace events for unmapped threads since the trace -// tag only encodes the priority, which may be in use by a mapped thread. - -void -__pk_thread_timeout(void *arg) -{ - PkMachineContext ctx; - PkThread *thread = (PkThread *)arg; - - pk_critical_section_enter(&ctx); - - switch (thread->state) { - - case PK_THREAD_STATE_MAPPED: - if (!__pk_thread_is_runnable(thread)) { - thread->flags |= PK_THREAD_FLAG_TIMED_OUT; - __pk_thread_queue_insert(&__pk_run_queue, thread->priority); - __pk_schedule(); - } - break; - - case PK_THREAD_STATE_SUSPENDED_RUNNABLE: - break; - - case PK_THREAD_STATE_SUSPENDED_BLOCKED: - thread->flags |= PK_THREAD_FLAG_TIMED_OUT; - thread->state = PK_THREAD_STATE_SUSPENDED_RUNNABLE; - break; - - default: - PK_PANIC(PK_THREAD_TIMEOUT_STATE); - } - - pk_critical_section_exit(&ctx); -} - - -// This routine serves as a container for the PK_START_THREADS_HOOK and -// actually starts threads. The helper routine __pk_call_pk_start_threads() -// arranges this routine to be called with interrupts disabled while running -// on the kernel stack. -// -// The reason for this roundabout is that we want to be able to run a hook -// routine (transparent to the application) that can hand over every last byte -// of free memory to "malloc()" - including the stack of main(). Since we -// always need to run on some stack, we chose to run the hook on the kernel -// stack. However to do this safely we need to make sure -// that no interrupts will happen during this time. When __pk_thread_resume() -// is finally called all stack-based context is lost but it doesn't matter at -// that point - it's a one-way street into thread execution. -// -// This is considered part of pk_start_threads() and so is also considered a -// 'core' routine. - -void -__pk_start_threads(void) -{ - PK_START_THREADS_HOOK; - - __pk_next_thread_resume(); - - PK_PANIC(PK_START_THREADS_RETURNED); -} - - -/// Start PK threads -/// -/// This routine starts the PK thread scheduler infrastructure. This routine -/// must be called after a call of \c pk_initialize(). This routine never -/// returns. Interrupt (+ timer) only configurations of PK need not call this -/// routine. -/// -/// Note: This tiny routine is considered a 'core' routine so that the -/// initialziation code can safely recover all 'init' code space before -/// starting threads. -/// -/// This routine typically does not return - any return value indicates an -/// error; see \ref pk_errors -/// -/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was called twice. - -int -pk_start_threads(void) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(__pk_kernel_mode_thread(), PK_ILLEGAL_CONTEXT_THREAD); - } - - __pk_call_pk_start_threads(); - - return 0; -} - - -/// Resume a suspended thread -/// -/// \param thread The thread to resume -/// -/// PK only allows one thread at a time to run at a given priority, and -/// implements the notion of a thread \e claiming a priority. A suspended -/// thread claims a priority when it is mapped by a call of -/// pk_thread_resume(). This API will succeed only if no other active thread -/// is currently mapped at the priority assigned to the thread. PK provides -/// the pk_thread_at_priority() API which allows an application-level -/// scheduler to correctly manage multiple threads running at the same -/// priority. -/// -/// If the thread was sleeping while suspended it remains asleep. However if -/// the sleep timer timed out while the thread was suspended it will be -/// resumed runnable. -/// -/// If the thread was blocked on a semaphore when it was suspended, then when -/// the thread is resumed it will attempt to reacquire the semaphore. -/// However, if the thread was blocked on a semaphore with timeout while -/// suspended and the timeout interval has passed, the thread will be resumed -/// runnable and see that the semaphore pend timed out. -/// -/// It is not an error to call pk_thread_resume() on a mapped -/// thread. However it is an error to call pk_thread_resume() on a completed -/// or deleted thread. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion, including calls on a \a thread that is -/// already mapped. -/// -/// \retval -PK_INVALID_THREAD_AT_RESUME1 The \a thread is a null (0) pointer. -/// -/// \retval -PK_INVALID_THREAD_AT_RESUME2 The \a thread is not active, -/// i.e. has completed or been deleted. -/// -/// \retval -PK_PRIORITY_IN_USE_AT_RESUME Another thread is already mapped at -/// the priority of the \a thread. - -int -pk_thread_resume(PkThread *thread) -{ - PkMachineContext ctx; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_RESUME1); - } - - pk_critical_section_enter(&ctx); - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF_CRITICAL(!__pk_thread_is_active(thread), - PK_INVALID_THREAD_AT_RESUME2, - &ctx); - } - - if (!__pk_thread_is_mapped(thread)) { - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF_CRITICAL(__pk_priority_map[thread->priority] != 0, - PK_PRIORITY_IN_USE_AT_RESUME, - &ctx); - } - __pk_thread_map(thread); - __pk_schedule(); - } - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - - -/// Suspend a thread -/// -/// Any active thread can be suspended. A suspended thread 1) remains active -/// but will not be scheduled; 2) relinquishes its priority assignment, -/// allowing another thread to be resumed at the suspended thread's priority; -/// and 3) disassociates from any semaphore mutual exclusion it may have been -/// participating in. -/// -/// If a sleeping thread is suspended, the sleep timer remains active but a -/// timeout of the timer simply marks the thread as runnable, but does not -/// resume the thread. -/// -/// If a thread blocked on a semaphore is suspended, the thread no longer -/// participates in the semaphore mutual exclusion. If the thread is later -/// resumed it will attempt to acquire the semaphore again the next time it -/// runs (unless it was blocked with a timeout and the timeout has expired). -/// -/// If a thread blocked on a semaphore with timeout is suspended, the -/// semaphore timeout timer continues to run. If the timer times out while the -/// thread is suspended the thread is simply marked runnable. If the thread is -/// later resumed, the suspended call of \c pk_semaphore_pend() will return the -/// timeout code -PK_SEMAPHORE_PEND_TIMED_OUT. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion, including calls on a \a thread that is -/// already suspended. -/// -/// \retval -PK_INVALID_THREAD_AT_SUSPEND1 The \a thread is a null (0) pointer -/// -/// \retval -PK_INVALID_THREAD_AT_SUSPEND2 The \a thread is not active, -/// i.e. has completed or been deleted. - -int -pk_thread_suspend(PkThread *thread) -{ - PkMachineContext ctx; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF((thread == 0), PK_INVALID_THREAD_AT_SUSPEND1); - } - - pk_critical_section_enter(&ctx); - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF_CRITICAL(!__pk_thread_is_active(thread), - PK_INVALID_THREAD_AT_SUSPEND2, - &ctx); - } - - if (__pk_thread_is_mapped(thread)) { - - PK_KERN_TRACE("THREAD_SUSPENDED(%d)", thread->priority); - __pk_thread_unmap(thread); - __pk_schedule(); - } - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - - -/// Delete a thread -/// -/// Any active thread can be deleted. If a thread is deleted it is removed -/// from the run queue, deleted from the timer queue (if sleeping or blocked -/// on a semaphore with timeout), and deleted from the semaphore mutual -/// exclusion if blocked on a semaphore. The thread control block is then -/// marked as deleted. -/// -/// Once a thread has completed or been deleted the thread structure and -/// thread stack areas can be used for other purposes. -/// -/// \param thread The thread to delete -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors. If a -/// thread deletes itself this API does not return at all. -/// -/// \retval 0 Successful completion, including calls on a \a thread that has -/// completed or had already been deleted. -/// -/// -/// \retval -PK_INVALID_THREAD_AT_DELETE The \a thread is a null (0) pointer. - -int -pk_thread_delete(PkThread *thread) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_DELETE); - } - - __pk_thread_delete(thread, PK_THREAD_STATE_DELETED); - - return PK_OK; -} - - -/// Complete a thread -/// -/// If a thread ever returns from the subroutine defining the thread entry -/// point, the thread is removed from all PK kernel data structures and -/// marked completed. The thread routine can also use the API pk_complete() -/// to make this more explicit if desired. PK makes no distinction between -/// completed and deleted threads, but provides these indications for -/// the benefit of the application. -/// -/// Note that this API is only available from the current thread to mark its -/// own completion. -/// -/// Once a thread has completed or been deleted the thread structure and -/// thread stack areas can be used for other purposes. -/// -/// Any return value indicates an error; see \ref pk_errors. In the event of -/// a successful completion this API does not return to the caller, which is -/// always the thread context being completed. -/// -/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was not called from a thread -/// context. - -// Note: Casting __pk_current_thread removes the 'volatile' attribute. - -int -pk_complete(void) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_UNLESS_THREAD_CONTEXT(); - } - - __pk_thread_delete((PkThread *)__pk_current_thread, - PK_THREAD_STATE_COMPLETED); - - return PK_OK; -} - -/// Sleep a thread for an interval relative to the current time. -/// -/// \param interval A time interval relative to the current timebase. -/// -/// Threads can use this API to sleep for a time relative to the current -/// timebase. The absolute timeout is \c pk_timebase_get() + \a interval. -/// -/// Sleeping threads are not scheduled, although they maintain their -/// priorities. This differs from thread suspension, where the suspended -/// thread relinquishes its priority. When the sleep timer times out the -/// thread becomes runnable again, and will run as soon as it becomes the -/// highest-priority mapped runnable thread. -/// -/// Sleeping threads may also be later suspended. In this case the Sleep timer -/// continues to run, and if it times out before the thread is resumed the -/// thread will be immediately runnable when it is resumed. -/// -/// See the PK specification for a full discussion of how PK handles -/// scheduling events at absolute times "in the past". Briefly stated, if the -/// \a interval is 0 or is so small that the absolute time becomes a "past" -/// time before the Sleep is actually scheduled, the thread will Sleep for the -/// briefest possible period supported by the hardware. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion. -/// -/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was not called from a thread -/// context. - -int -pk_sleep(PkInterval interval) -{ - PkTimebase time; - PkMachineContext ctx; - PkThread *current; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_UNLESS_THREAD_CONTEXT(); - } - - time = pk_timebase_get() + PK_INTERVAL_SCALE(interval); - - pk_critical_section_enter(&ctx); - - current = (PkThread *)__pk_current_thread; - - current->timer.timeout = time; - __pk_timer_schedule(&(current->timer)); - - current->flags |= PK_THREAD_FLAG_TIMER_PEND; - - PK_KERN_TRACE("THREAD_SLEEP(%d)", current->priority); - - __pk_thread_queue_delete(&__pk_run_queue, current->priority); - __pk_schedule(); - - current->flags &= ~(PK_THREAD_FLAG_TIMER_PEND | PK_THREAD_FLAG_TIMED_OUT); - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - -#undef __PK_THREAD_CORE_C__ |