blob: 72b76fc99f00046b45249f979585b81f70a2a3b0 [file] [log] [blame]
/* thread.c - test nanokernel CPU and thread APIs */
/*
* Copyright (c) 2012-2015 Wind River Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
DESCRIPTION
This module tests the following CPU and thread related routines:
fiber_fiber_start(), task_fiber_start(), fiber_yield(),
sys_thread_self_get(), sys_execution_context_type_get(), nano_cpu_idle(),
irq_lock(), irq_unlock(),
irq_offload(), nanoCpuExcConnect(),
irq_enable(), irq_disable(),
*/
#include <tc_util.h>
#include <nano_private.h>
#include <arch/cpu.h>
#include <irq_offload.h>
#include <util_test_common.h>
/*
* Include board.h from platform to get IRQ number.
* NOTE: Cortex-M3/M4 does not need IRQ numbers
*/
#if !defined(CONFIG_CPU_CORTEX_M3_M4)
#include <board.h>
#endif
#define FIBER_STACKSIZE 384
#define FIBER_PRIORITY 4
#define THREAD_SELF_CMD 0
#define EXEC_CTX_TYPE_CMD 1
#define UNKNOWN_COMMAND -1
/*
* Get the timer type dependent IRQ number. If timer type
* is not defined in platform, generate an error
*/
#if defined(CONFIG_HPET_TIMER)
#define TICK_IRQ CONFIG_HPET_TIMER_IRQ
#elif defined(CONFIG_LOAPIC_TIMER)
#define TICK_IRQ CONFIG_LOAPIC_TIMER_IRQ
#elif defined(CONFIG_ALTERA_AVALON_TIMER)
#define TICK_IRQ TIMER_0_IRQ
#elif defined(CONFIG_ARCV2_TIMER)
#define TICK_IRQ IRQ_TIMER0
#elif defined(CONFIG_CPU_CORTEX_M3_M4)
/*
* The Cortex-M3/M4 use the SYSTICK exception for the system timer, which is
* not considered an IRQ by the irq_enable/Disable APIs.
*/
#else
/* generate an error */
#error Timer type is not defined for this platform
#endif
/* Nios II doesn't have a power saving instruction, so nano_cpu_idle()
* returns immediately
*/
#if !defined(CONFIG_NIOS2)
#define HAS_POWERSAVE_INSTRUCTION
#endif
/* Divide-by-zero exception test here is x86-specific */
#if defined(CONFIG_X86)
#define CONNECT_EXCEPTIONS 1
#endif
typedef struct {
int command; /* command to process */
int error; /* error value (if any) */
union {
void *data; /* pointer to data to use or return */
int value; /* value to be passed or returned */
};
} ISR_INFO;
typedef int (* disable_interrupt_func)(int);
typedef void (* enable_interrupt_func)(int);
#ifdef CONNECT_EXCEPTIONS
static volatile int excHandlerExecuted;
#endif
static struct nano_sem wakeFiber;
static struct nano_timer timer;
static struct nano_sem reply_timeout;
struct nano_fifo timeout_order_fifo;
static void *timerData[1];
static int fiberDetectedError = 0;
static char __stack fiberStack1[FIBER_STACKSIZE];
static char __stack fiberStack2[FIBER_STACKSIZE];
static int fiberEvidence = 0;
static ISR_INFO isrInfo;
/**
*
* @brief Handler to perform various actions from within an ISR context
*
* This routine is the ISR handler for _trigger_isrHandler(). It performs
* the command requested in <isrInfo.command>.
*
* @return N/A
*/
void isr_handler(void *data)
{
ARG_UNUSED(data);
switch (isrInfo.command) {
case THREAD_SELF_CMD:
isrInfo.data = (void *) sys_thread_self_get();
break;
case EXEC_CTX_TYPE_CMD:
isrInfo.value = sys_execution_context_type_get();
break;
default:
isrInfo.error = UNKNOWN_COMMAND;
break;
}
}
static void _trigger_isrHandler(void)
{
irq_offload(isr_handler, NULL);
}
#ifdef CONNECT_EXCEPTIONS
/**
*
* @brief Divide by zero exception handler
*
* This handler is part of a test that is only interested in detecting the
* error so that we know the exception connect code is working. It simply
* adds 2 to the EIP to skip over the offending instruction:
* f7 f9 idiv %ecx
* thereby preventing the infinite loop of divide-by-zero errors which would
* arise if control simply returns to that instruction.
*
* @return N/A
*/
void exc_divide_error_handler(NANO_ESF *pEsf)
{
pEsf->eip += 2;
excHandlerExecuted = 1; /* provide evidence that the handler executed */
}
#endif
/**
*
* @brief Initialize nanokernel objects
*
* This routine initializes the nanokernel objects used in this module's tests.
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int initNanoObjects(void)
{
nano_sem_init(&wakeFiber);
nano_timer_init(&timer, timerData);
nano_fifo_init(&timeout_order_fifo);
#ifdef CONNECT_EXCEPTIONS
nanoCpuExcConnect(IV_DIVIDE_ERROR, exc_divide_error_handler);
#endif
return TC_PASS;
}
#ifdef HAS_POWERSAVE_INSTRUCTION
/**
*
* @brief Test the nano_cpu_idle() routine
*
* This tests the nano_cpu_idle() routine. The first thing it does is align to
* a tick boundary. The only source of interrupts while the test is running is
* expected to be the tick clock timer which should wake the CPU. Thus after
* each call to nano_cpu_idle(), the tick count should be one higher.
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int nano_cpu_idleTest(void)
{
int tick; /* current tick count */
int i; /* loop variable */
/* Align to a "tick boundary". */
tick = sys_tick_get_32();
while (tick == sys_tick_get_32()) {
}
tick = sys_tick_get_32();
for (i = 0; i < 5; i++) { /* Repeat the test five times */
nano_cpu_idle();
tick++;
if (sys_tick_get_32() != tick) {
return TC_FAIL;
}
}
return TC_PASS;
}
#endif
/**
*
* @brief A wrapper for irq_lock()
*
* @return irq_lock() return value
*/
int irq_lockWrapper(int unused)
{
ARG_UNUSED(unused);
return irq_lock();
}
/**
*
* @brief A wrapper for irq_unlock()
*
* @return N/A
*/
void irq_unlockWrapper(int imask)
{
irq_unlock(imask);
}
/**
*
* @brief A wrapper for irq_disable()
*
* @return <irq>
*/
int irq_disableWrapper(int irq)
{
irq_disable(irq);
return irq;
}
/**
*
* @brief A wrapper for irq_enable()
*
* @return N/A
*/
void irq_enableWrapper(int irq)
{
irq_enable(irq);
}
/**
*
* @brief Test routines for disabling and enabling ints
*
* This routine tests the routines for disabling and enabling interrupts. These
* include irq_lock() and irq_unlock(), irq_disable() and irq_enable().
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int nanoCpuDisableInterruptsTest(disable_interrupt_func disableRtn,
enable_interrupt_func enableRtn, int irq)
{
unsigned long long count = 0;
unsigned long long i = 0;
int tick;
int tick2;
int imask;
/* Align to a "tick boundary" */
tick = sys_tick_get_32();
while (sys_tick_get_32() == tick) {
}
tick++;
while (sys_tick_get_32() == tick) {
count++;
}
/*
* Inflate <count> so that when we loop later, many ticks should have
* elapsed during the loop. This later loop will not exactly match the
* previous loop, but it should be close enough in structure that when
* combined with the inflated count, many ticks will have passed.
*/
count <<= 4;
imask = disableRtn(irq);
tick = sys_tick_get_32();
for (i = 0; i < count; i++) {
sys_tick_get_32();
}
tick2 = sys_tick_get_32();
/*
* Re-enable interrupts before returning (for both success and failure
* cases).
*/
enableRtn(imask);
if (tick2 != tick) {
return TC_FAIL;
}
/* Now repeat with interrupts unlocked. */
for (i = 0; i < count; i++) {
sys_tick_get_32();
}
return (tick == sys_tick_get_32()) ? TC_FAIL : TC_PASS;
}
/**
*
* @brief Test the various nanoCtxXXX() routines from a task
*
* This routines tests the sys_thread_self_get() and
* sys_execution_context_type_get() routines from both a task and an ISR (that
* interrupted a task). Checking those routines with fibers are done
* elsewhere.
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int nanoCtxTaskTest(void)
{
nano_thread_id_t self_thread_id;
TC_PRINT("Testing sys_thread_self_get() from an ISR and task\n");
self_thread_id = sys_thread_self_get();
isrInfo.command = THREAD_SELF_CMD;
isrInfo.error = 0;
_trigger_isrHandler();
if ((isrInfo.error != 0) || (isrInfo.data != (void *) self_thread_id)) {
/*
* Either the ISR detected an error, or the ISR context ID does not
* match the interrupted task's thread ID.
*/
return TC_FAIL;
}
TC_PRINT("Testing sys_execution_context_type_get() from an ISR\n");
isrInfo.command = EXEC_CTX_TYPE_CMD;
isrInfo.error = 0;
_trigger_isrHandler();
if ((isrInfo.error != 0) || (isrInfo.value != NANO_CTX_ISR)) {
return TC_FAIL;
}
TC_PRINT("Testing sys_execution_context_type_get() from a task\n");
if (sys_execution_context_type_get() != NANO_CTX_TASK) {
return TC_FAIL;
}
return TC_PASS;
}
/**
*
* @brief Test the various context/thread routines from a fiber
*
* This routines tests the sys_thread_self_get() and
* sys_execution_context_type_get() routines from both a fiber and an ISR (that
* interrupted a fiber). Checking those routines with tasks are done
* elsewhere.
*
* This routine may set <fiberDetectedError> to the following values:
* 1 - if fiber ID matches that of the task
* 2 - if thread ID taken during ISR does not match that of the fiber
* 3 - sys_execution_context_type_get() when called from an ISR is not
* NANO_TYPE_ISR
* 4 - sys_execution_context_type_get() when called from a fiber is not
* NANO_TYPE_FIBER
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int nanoCtxFiberTest(nano_thread_id_t task_thread_id)
{
nano_thread_id_t self_thread_id;
self_thread_id = sys_thread_self_get();
if (self_thread_id == task_thread_id) {
fiberDetectedError = 1;
return TC_FAIL;
}
isrInfo.command = THREAD_SELF_CMD;
isrInfo.error = 0;
_trigger_isrHandler();
if ((isrInfo.error != 0) || (isrInfo.data != (void *) self_thread_id)) {
/*
* Either the ISR detected an error, or the ISR context ID does not
* match the interrupted fiber's thread ID.
*/
fiberDetectedError = 2;
return TC_FAIL;
}
isrInfo.command = EXEC_CTX_TYPE_CMD;
isrInfo.error = 0;
_trigger_isrHandler();
if ((isrInfo.error != 0) || (isrInfo.value != NANO_CTX_ISR)) {
fiberDetectedError = 3;
return TC_FAIL;
}
if (sys_execution_context_type_get() != NANO_CTX_FIBER) {
fiberDetectedError = 4;
return TC_FAIL;
}
return TC_PASS;
}
/**
*
* @brief Entry point to the fiber's helper
*
* This routine is the entry point to the fiber's helper fiber. It is used to
* help test the behaviour of the fiber_yield() routine.
*
* @param arg1 unused
* @param arg2 unused
*
* @return N/A
*/
static void fiberHelper(int arg1, int arg2)
{
nano_thread_id_t self_thread_id;
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
/*
* This fiber starts off at a higher priority than fiberEntry(). Thus, it
* should execute immediately.
*/
fiberEvidence++;
/* Test that helper will yield to a fiber of equal priority */
self_thread_id = sys_thread_self_get();
self_thread_id->prio++; /* Lower priority to that of fiberEntry() */
fiber_yield(); /* Yield to fiber of equal priority */
fiberEvidence++;
/* <fiberEvidence> should now be 2 */
}
/**
*
* @brief Test the fiber_yield() routine
*
* This routine tests the fiber_yield() routine. It starts another fiber
* (thus also testing fiber_fiber_start()) and checks that behaviour of
* fiber_yield() against the cases of there being a higher priority fiber,
* a lower priority fiber, and another fiber of equal priority.
*
* On error, it may set <fiberDetectedError> to one of the following values:
* 10 - helper fiber ran prematurely
* 11 - fiber_yield() did not yield to a higher priority fiber
* 12 - fiber_yield() did not yield to an equal prioirty fiber
* 13 - fiber_yield() yielded to a lower priority fiber
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int fiber_yieldTest(void)
{
nano_thread_id_t self_thread_id;
/*
* Start a fiber of higher priority. Note that since the new fiber is
* being started from a fiber, it will not automatically switch to the
* fiber as it would if done from a task.
*/
self_thread_id = sys_thread_self_get();
fiberEvidence = 0;
fiber_fiber_start(fiberStack2, FIBER_STACKSIZE, fiberHelper,
0, 0, FIBER_PRIORITY - 1, 0);
if (fiberEvidence != 0) {
/* ERROR! Helper spawned at higher */
fiberDetectedError = 10; /* priority ran prematurely. */
return TC_FAIL;
}
/*
* Test that the fiber will yield to the higher priority helper.
* <fiberEvidence> is still 0.
*/
fiber_yield();
if (fiberEvidence == 0) {
/* ERROR! Did not yield to higher */
fiberDetectedError = 11; /* priority fiber. */
return TC_FAIL;
}
if (fiberEvidence > 1) {
/* ERROR! Helper did not yield to */
fiberDetectedError = 12; /* equal priority fiber. */
return TC_FAIL;
}
/*
* Raise the priority of fiberEntry(). Calling fiber_yield() should
* not result in switching to the helper.
*/
self_thread_id->prio--;
fiber_yield();
if (fiberEvidence != 1) {
/* ERROR! Context switched to a lower */
fiberDetectedError = 13; /* priority fiber! */
return TC_FAIL;
}
/*
* Block on <wakeFiber>. This will allow the helper fiber to complete.
* The main task will wake this fiber.
*/
nano_fiber_sem_take(&wakeFiber, TICKS_UNLIMITED);
return TC_PASS;
}
/**
*
* @brief Entry point to fiber started by the task
*
* This routine is the entry point to the fiber started by the task.
*
* @param task_thread_id thread ID of the spawning task
* @param arg1 unused
*
* @return N/A
*/
static void fiberEntry(int task_thread_id, int arg1)
{
int rv;
ARG_UNUSED(arg1);
fiberEvidence++; /* Prove to the task that the fiber has run */
nano_fiber_sem_take(&wakeFiber, TICKS_UNLIMITED);
rv = nanoCtxFiberTest((nano_thread_id_t) task_thread_id);
if (rv != TC_PASS) {
return;
}
/* Allow the task to print any messages before the next test runs */
nano_fiber_sem_take(&wakeFiber, TICKS_UNLIMITED);
rv = fiber_yieldTest();
if (rv != TC_PASS) {
return;
}
}
/*
* Timeout tests
*
* Test the fiber_sleep() API, as well as the fiber_delayed_start() ones.
*/
#include <tc_nano_timeout_common.h>
struct timeout_order_data {
void *link_in_fifo;
int32_t timeout;
int timeout_order;
int q_order;
};
struct timeout_order_data timeout_order_data[] = {
{0, TIMEOUT(2), 2, 0},
{0, TIMEOUT(4), 4, 1},
{0, TIMEOUT(0), 0, 2},
{0, TIMEOUT(1), 1, 3},
{0, TIMEOUT(5), 5, 4},
{0, TIMEOUT(6), 6, 5},
{0, TIMEOUT(3), 3, 6},
};
#define NUM_TIMEOUT_FIBERS ARRAY_SIZE(timeout_order_data)
static char __stack timeout_stacks[NUM_TIMEOUT_FIBERS][FIBER_STACKSIZE];
/* a fiber busy waits, then reports through a fifo */
static void test_fiber_busy_wait(int ticks, int unused)
{
ARG_UNUSED(unused);
uint32_t usecs = ticks * sys_clock_us_per_tick;
TC_PRINT(" fiber busy waiting for %d usecs (%d ticks)\n",
usecs, ticks);
sys_thread_busy_wait(usecs);
TC_PRINT(" fiber busy waiting completed\n");
/*
* Ideally the test should verify that the correct number of ticks
* have elapsed. However, when run under QEMU the tick interrupt
* may be processed on a very irregular basis, meaning that far
* fewer than the expected number of ticks may occur for a given
* number of clock cycles vs. what would ordinarily be expected.
*
* Consequently, the best we can do for now to test busy waiting is
* to invoke the API and verify that it returns. (If it takes way
* too long, or never returns, the main test task may be able to
* time out and report an error.)
*/
nano_fiber_sem_give(&reply_timeout);
}
/* a fiber sleeps and times out, then reports through a fifo */
static void test_fiber_sleep(int timeout, int arg2)
{
int64_t orig_ticks = sys_tick_get();
TC_PRINT(" fiber sleeping for %d ticks\n", timeout);
fiber_sleep(timeout);
TC_PRINT(" fiber back from sleep\n");
if (!is_timeout_in_range(orig_ticks, timeout)) {
return;
}
nano_fiber_sem_give(&reply_timeout);
}
/* a fiber is started with a delay, then it reports that it ran via a fifo */
void delayed_fiber(int num, int unused)
{
struct timeout_order_data *data = &timeout_order_data[num];
ARG_UNUSED(unused);
TC_PRINT(" fiber (q order: %d, t/o: %d) is running\n",
data->q_order, data->timeout);
nano_fiber_fifo_put(&timeout_order_fifo, data);
}
static int test_timeout(void)
{
int32_t timeout;
int rv;
int ii;
struct timeout_order_data *data;
/* test sys_thread_busy_wait() */
TC_PRINT("Testing sys_thread_busy_wait()\n");
timeout = 2;
task_fiber_start(timeout_stacks[0], FIBER_STACKSIZE,
test_fiber_busy_wait, (int)timeout, 0,
FIBER_PRIORITY, 0);
rv = nano_task_sem_take(&reply_timeout, timeout + 2);
if (!rv) {
rv = TC_FAIL;
TC_ERROR(" *** task timed out waiting for sys_thread_busy_wait()\n");
return TC_FAIL;
}
/* test fiber_sleep() */
TC_PRINT("Testing fiber_sleep()\n");
timeout = 5;
task_fiber_start(timeout_stacks[0], FIBER_STACKSIZE,
test_fiber_sleep, (int)timeout, 0,
FIBER_PRIORITY, 0);
rv = nano_task_sem_take(&reply_timeout, timeout + 5);
if (!rv) {
rv = TC_FAIL;
TC_ERROR(" *** task timed out waiting for fiber on fiber_sleep().\n");
return TC_FAIL;
}
/* test fiber_delayed_start() without cancellation */
TC_PRINT("Testing fiber_delayed_start() without cancellation\n");
for (ii = 0; ii < NUM_TIMEOUT_FIBERS; ii++) {
(void)task_fiber_delayed_start(timeout_stacks[ii], FIBER_STACKSIZE,
delayed_fiber, ii, 0, 5, 0,
timeout_order_data[ii].timeout);
}
for (ii = 0; ii < NUM_TIMEOUT_FIBERS; ii++) {
data = nano_task_fifo_get(&timeout_order_fifo, TIMEOUT_TWO_INTERVALS);
if (!data) {
TC_ERROR(" *** timeout while waiting for delayed fiber\n");
return TC_FAIL;
}
if (data->timeout_order != ii) {
TC_ERROR(" *** wrong delayed fiber ran (got %d, expected %d)\n",
data->timeout_order, ii);
return TC_FAIL;
}
TC_PRINT(" got fiber (q order: %d, t/o: %d) as expected\n",
data->q_order, data->timeout);
}
/* ensure no more fibers fire */
data = nano_task_fifo_get(&timeout_order_fifo, TIMEOUT_TWO_INTERVALS);
if (data) {
TC_ERROR(" *** got something on the fifo, but shouldn't have...\n");
return TC_FAIL;
}
/* test fiber_delayed_start() with cancellation */
TC_PRINT("Testing fiber_delayed_start() with cancellations\n");
int cancellations[] = {0, 3, 4, 6};
int num_cancellations = ARRAY_SIZE(cancellations);
int next_cancellation = 0;
nano_thread_id_t delayed_fibers[NUM_TIMEOUT_FIBERS];
for (ii = 0; ii < NUM_TIMEOUT_FIBERS; ii++) {
delayed_fibers[ii] =
task_fiber_delayed_start(timeout_stacks[ii], FIBER_STACKSIZE,
delayed_fiber, ii, 0, 5, 0,
timeout_order_data[ii].timeout);
}
for (ii = 0; ii < NUM_TIMEOUT_FIBERS; ii++) {
int jj;
if (ii == cancellations[next_cancellation]) {
TC_PRINT(" cancelling [q order: %d, t/o: %d, t/o order: %d]\n",
timeout_order_data[ii].q_order,
timeout_order_data[ii].timeout, ii);
for (jj = 0; jj < NUM_TIMEOUT_FIBERS; jj++) {
if (timeout_order_data[jj].timeout_order == ii) {
break;
}
}
task_fiber_delayed_start_cancel(delayed_fibers[jj]);
++next_cancellation;
continue;
}
data = nano_task_fifo_get(&timeout_order_fifo, TIMEOUT_TEN_INTERVALS);
if (!data) {
TC_ERROR(" *** timeout while waiting for delayed fiber\n");
return TC_FAIL;
}
if (data->timeout_order != ii) {
TC_ERROR(" *** wrong delayed fiber ran (got %d, expected %d)\n",
data->timeout_order, ii);
return TC_FAIL;
}
TC_PRINT(" got (q order: %d, t/o: %d, t/o order %d) as expected\n",
data->q_order, data->timeout);
}
if (num_cancellations != next_cancellation) {
TC_ERROR(" *** wrong number of cancellations (expected %d, got %d\n",
num_cancellations, next_cancellation);
return TC_FAIL;
}
/* ensure no more fibers fire */
data = nano_task_fifo_get(&timeout_order_fifo, TIMEOUT_TWO_INTERVALS);
if (data) {
TC_ERROR(" *** got something on the fifo, but shouldn't have...\n");
return TC_FAIL;
}
return TC_PASS;
}
/**
*
* @brief Entry point to timer tests
*
* This is the entry point to the CPU and thread tests.
*
* @return N/A
*/
void main(void)
{
int rv; /* return value from tests */
TC_START("Test Nanokernel CPU and thread routines");
TC_PRINT("Initializing nanokernel objects\n");
rv = initNanoObjects();
if (rv != TC_PASS) {
goto doneTests;
}
#ifdef HAS_POWERSAVE_INSTRUCTION
TC_PRINT("Testing nano_cpu_idle()\n");
rv = nano_cpu_idleTest();
if (rv != TC_PASS) {
goto doneTests;
}
#endif
TC_PRINT("Testing interrupt locking and unlocking\n");
rv = nanoCpuDisableInterruptsTest(irq_lockWrapper,
irq_unlockWrapper, -1);
if (rv != TC_PASS) {
goto doneTests;
}
#ifdef TICK_IRQ
/* Disable interrupts coming from the timer. */
TC_PRINT("Testing irq_disable() and irq_enable()\n");
rv = nanoCpuDisableInterruptsTest(irq_disableWrapper,
irq_enableWrapper, TICK_IRQ);
if (rv != TC_PASS) {
goto doneTests;
}
#endif
rv = nanoCtxTaskTest();
if (rv != TC_PASS) {
goto doneTests;
}
TC_PRINT("Spawning a fiber from a task\n");
fiberEvidence = 0;
task_fiber_start(fiberStack1, FIBER_STACKSIZE, fiberEntry,
(int) sys_thread_self_get(), 0, FIBER_PRIORITY, 0);
if (fiberEvidence != 1) {
rv = TC_FAIL;
TC_ERROR(" - fiber did not execute as expected!\n");
goto doneTests;
}
/*
* The fiber ran, now wake it so it can test sys_thread_self_get and
* sys_execution_context_type_get.
*/
TC_PRINT("Fiber to test sys_thread_self_get() and sys_execution_context_type_get\n");
nano_task_sem_give(&wakeFiber);
if (fiberDetectedError != 0) {
rv = TC_FAIL;
TC_ERROR(" - failure detected in fiber; fiberDetectedError = %d\n",
fiberDetectedError);
goto doneTests;
}
TC_PRINT("Fiber to test fiber_yield()\n");
nano_task_sem_give(&wakeFiber);
if (fiberDetectedError != 0) {
rv = TC_FAIL;
TC_ERROR(" - failure detected in fiber; fiberDetectedError = %d\n",
fiberDetectedError);
goto doneTests;
}
nano_task_sem_give(&wakeFiber);
rv = test_timeout();
if (rv != TC_PASS) {
goto doneTests;
}
#ifdef CONNECT_EXCEPTIONS
/*
* Test divide by zero exception handler.
*
* WARNING: This code has been very carefully crafted so that it does
* what it is supposed to. Both "error" and "excHandlerExecuted" must be
* volatile to prevent the compiler from issuing a "divide by zero"
* warning (since otherwise in knows "excHandlerExecuted" is zero),
* and to ensure the compiler issues the two byte "idiv" instruction
* that the exception handler is designed to deal with.
*/
volatile int error; /* used to create a divide by zero error */
TC_PRINT("Verifying exception handler installed\n");
excHandlerExecuted = 0;
error = error / excHandlerExecuted;
TC_PRINT("excHandlerExecuted: %d\n", excHandlerExecuted);
rv = (excHandlerExecuted == 1) ? TC_PASS : TC_FAIL;
#endif
doneTests:
TC_END_RESULT(rv);
TC_END_REPORT(rv);
}