zephyr/kernel/nanokernel/nano_context.c
Allan Stephens 6ac33f2b9f kernel: Introduce sys_thread_busy_wait() API
Provides a way for a fiber or task to busy wait for a specified
period of time. This is useful in situations where a delay needs
to be performed without switching execution to another context,
such as:

1) It would take longer to switch to another context and then switch
   back again than to simply busy wait.

2) A delay is required by the kernel's main task (i.e. the nanokernel's
   background task or the microkernel's idle task). This task is not
   allowed to voluntarily relinquish the CPU because this would leave
   the kernel with nothing to execute in its place.

Change-Id: Icbe28613014f659e9528893ae58f7b8008c18a61
Original-work-by: Jeff Blais <jeff.blais@windriver.com>
Further-adapted-by: Benjamin Walsh <benjamin.walsh@windriver.com>
Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
2016-02-05 20:24:28 -05:00

283 lines
7.8 KiB
C

/* nanokernel thread support */
/*
* Copyright (c) 2010-2014 Wind River Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2) 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.
*
* 3) Neither the name of Wind River Systems nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
*/
/*
DESCRIPTION
This module provides general purpose thread support, with applies to both
tasks or fibers.
*/
#include <toolchain.h>
#include <sections.h>
#include <nano_private.h>
#include <misc/printk.h>
#include <sys_clock.h>
#include <drivers/system_timer.h>
nano_thread_id_t sys_thread_self_get(void)
{
return _nanokernel.current;
}
nano_context_type_t sys_execution_context_type_get(void)
{
if (_IS_IN_ISR())
return NANO_CTX_ISR;
if ((_nanokernel.current->flags & TASK) == TASK)
return NANO_CTX_TASK;
return NANO_CTX_FIBER;
}
/**
*
* @brief Mark thread as essential to system
*
* This function tags the running fiber or task as essential to system
* option; exceptions raised by this thread will be treated as a fatal
* system error.
*
* @return N/A
*/
void _thread_essential_set(void)
{
_nanokernel.current->flags |= ESSENTIAL;
}
/**
*
* @brief Mark thread as not essential to system
*
* This function tags the running fiber or task as not essential to system
* option; exceptions raised by this thread may be recoverable.
* (This is the default tag for a thread.)
*
* @return N/A
*/
void _thread_essential_clear(void)
{
_nanokernel.current->flags &= ~ESSENTIAL;
}
/**
*
* @brief Is the specified thread essential?
*
* This routine indicates if the specified thread is an essential system
* thread. A NULL thread pointer indicates that the current thread is
* to be queried.
*
* @return Non-zero if specified thread is essential, zero if it is not
*/
int _is_thread_essential(struct tcs *pCtx /* pointer to thread */
)
{
return ((pCtx == NULL) ? _nanokernel.current : pCtx)->flags & ESSENTIAL;
}
/*
* Don't build sys_thread_busy_wait() for ARM, since intrinsics libraries in
* current Zephyr SDK use non-Thumb code that isn't supported on Cortex-M CPUs.
* For the time being any ARM-based application that attempts to use this API
* will get a link error (which is preferable to a mysterious exception).
*/
#ifndef CONFIG_ARM
void sys_thread_busy_wait(uint32_t usec_to_wait)
{
/* use 64-bit math to prevent overflow when multiplying */
uint32_t cycles_to_wait = (uint32_t)(
(uint64_t)usec_to_wait *
(uint64_t)sys_clock_hw_cycles_per_sec /
(uint64_t)USEC_PER_SEC
);
uint32_t start_cycles = _sys_clock_cycle_get();
for (;;) {
uint32_t current_cycles = _sys_clock_cycle_get();
/* this handles the rollover on an unsigned 32-bit value */
if ((current_cycles - start_cycles) >= cycles_to_wait) {
break;
}
}
}
#endif /* CONFIG_ARM */
#ifdef CONFIG_THREAD_CUSTOM_DATA
/**
*
* @brief Set thread's custom data
*
* This routine sets the custom data value for the current task or fiber.
* Custom data is not used by the kernel itself, and is freely available
* for the thread to use as it sees fit.
*
* @param value New to set the thread's custom data to.
*
* @return N/A
*/
void sys_thread_custom_data_set(void *value)
{
_nanokernel.current->custom_data = value;
}
/**
*
* @brief Get thread's custom data
*
* This function returns the custom data value for the current task or fiber.
*
* @return current handle value
*/
void *sys_thread_custom_data_get(void)
{
return _nanokernel.current->custom_data;
}
#endif /* CONFIG_THREAD_CUSTOM_DATA */
#if defined(CONFIG_THREAD_MONITOR)
/**
*
* @brief Thread exit routine
*
* This function is invoked when the specified thread is aborted, either
* normally or abnormally. It is called for the termination of any thread,
* (fibers and tasks).
*
* This routine must be invoked either from a fiber or from a task with
* interrupts locked to guarantee that the list of threads does not change in
* mid-operation. It cannot be called from ISR context.
*
* @return N/A
*/
void _thread_exit(struct tcs *thread)
{
/*
* Remove thread from the list of threads. This singly linked list of
* threads maintains ALL the threads in the system: both tasks and
* fibers regardless of whether they are runnable.
*/
if (thread == _nanokernel.threads) {
_nanokernel.threads = _nanokernel.threads->next_thread;
} else {
struct tcs *prev_thread;
prev_thread = _nanokernel.threads;
while (thread != prev_thread->next_thread) {
prev_thread = prev_thread->next_thread;
}
prev_thread->next_thread = thread->next_thread;
}
}
#endif /* CONFIG_THREAD_MONITOR */
/**
*
* @brief Common thread entry point function
*
* This function serves as the entry point for _all_ threads, i.e. both
* task and fibers are instantiated such that initial execution starts
* here.
*
* This routine invokes the actual task or fiber entry point function and
* passes it three arguments. It also handles graceful termination of the
* task or fiber if the entry point function ever returns.
*
* @internal
* The 'noreturn' attribute is applied to this function so that the compiler
* can dispense with generating the usual preamble that is only required for
* functions that actually return.
*
* @return Does not return
*
*/
FUNC_NORETURN void _thread_entry(
_thread_entry_t pEntry, /* address of app entry point function */
_thread_arg_t parameter1, /* 1st arg to app entry point function */
_thread_arg_t parameter2, /* 2nd arg to app entry point function */
_thread_arg_t parameter3 /* 3rd arg to app entry point function */
)
{
/* Execute the "application" entry point function */
pEntry(parameter1, parameter2, parameter3);
/* Determine if thread can legally terminate itself via "return" */
if (_is_thread_essential(NULL)) {
#ifdef CONFIG_NANOKERNEL
/*
* Nanokernel's background task must always be present,
* so if it has nothing left to do just let it idle forever
*/
while (((_nanokernel.current)->flags & TASK) == TASK) {
nano_cpu_idle();
}
#endif /* CONFIG_NANOKERNEL */
/* Loss of essential thread is a system fatal error */
_NanoFatalErrorHandler(_NANO_ERR_INVALID_TASK_EXIT,
&_default_esf);
}
/* Gracefully terminate the currently executing thread */
#ifdef CONFIG_MICROKERNEL
if (((_nanokernel.current)->flags & TASK) == TASK) {
extern FUNC_NORETURN void _TaskAbort(void);
_TaskAbort();
} else
#endif /* CONFIG_MICROKERNEL */
{
fiber_abort();
}
/*
* Compiler can't tell that fiber_abort() won't return and issues
* a warning unless we explicitly tell it that control never gets this
* far.
*/
CODE_UNREACHABLE;
}