blob: 57006ecc8a61b636a7b37c1c88b7e89e26587fe3 [file] [log] [blame]
/*
* Copyright (c) 2012-2015 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* @file
* @brief Test nanokernel semaphore APIs
*
* This module tests four basic scenarios with the usage of the following
* semaphore routines:
*
* nano_sem_init
* nano_fiber_sem_give, nano_fiber_sem_take
* nano_task_sem_give, nano_task_sem_take
* nano_isr_sem_give, nano_isr_sem_take
*
* Scenario #1:
* A task, fiber or ISR does not wait for the semaphore when taking it.
*
* Scenario #2:
* A task or fiber must wait for the semaphore to be given before it gets it.
*
* Scenario #3:
* Multiple fibers pend on the same semaphore.
*
* Scenario #4:
* Timeout scenarios with multiple semaphores and fibers.
*/
#include <tc_util.h>
#include <arch/cpu.h>
#include <misc/util.h>
#include <irq_offload.h>
#include <util_test_common.h>
#if defined(CONFIG_ASSERT) && defined(CONFIG_DEBUG)
#define FIBER_STACKSIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE)
#else
#define FIBER_STACKSIZE (384 + CONFIG_TEST_EXTRA_STACKSIZE)
#endif
#define FIBER_PRIORITY 4
typedef struct {
struct nano_sem *sem; /* ptr to semaphore */
int data; /* data */
} ISR_SEM_INFO;
typedef enum {
STS_INIT = -1,
STS_TASK_WOKE_FIBER,
STS_FIBER_WOKE_TASK,
STS_ISR_WOKE_TASK
} SEM_TEST_STATE;
static SEM_TEST_STATE semTestState;
static ISR_SEM_INFO isrSemInfo;
static struct nano_sem testSem;
static int fiberDetectedFailure = 0;
static struct nano_timer timer;
static void *timerData[1];
static char __stack fiberStack[FIBER_STACKSIZE];
static struct nano_sem multi_waiters;
static struct nano_sem reply_multi_waiters;
/**
*
* @brief Take a semaphore
*
* This routine is the ISR handler for _trigger_nano_isr_sem_take(). It takes a
* semaphore within the context of an ISR.
*
* @param data pointer to ISR handler parameter
*
* @return N/A
*/
static void my_isr_sem_take(void *data)
{
ISR_SEM_INFO *pInfo = (ISR_SEM_INFO *) data;
pInfo->data = nano_isr_sem_take(pInfo->sem, TICKS_NONE);
}
static void _trigger_nano_isr_sem_take(void)
{
irq_offload(my_isr_sem_take, &isrSemInfo);
}
/**
*
* @brief Give a semaphore
*
* This routine is the ISR handler for _trigger_nano_isr_sem_take(). It gives a
* semaphore within the context of an ISR.
*
* @param data pointer to ISR handler parameter
*
* @return N/A
*/
static void my_isr_sem_give(void *data)
{
ISR_SEM_INFO *pInfo = (ISR_SEM_INFO *) data;
nano_isr_sem_give(pInfo->sem);
pInfo->data = 1; /* Indicate semaphore has been given */
}
static void _trigger_nano_isr_sem_give(void)
{
irq_offload(my_isr_sem_give, &isrSemInfo);
}
/**
*
* @brief Give and take the semaphore in a fiber without blocking
*
* This test gives and takes the test semaphore in a fiber
* without blocking on the semaphore.
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int testSemFiberNoWait(void)
{
int i;
TC_PRINT("Giving and taking a semaphore in a fiber (non-blocking)\n");
/*
* Give the semaphore many times and then make sure that it can only be
* taken that many times.
*/
for (i = 0; i < 32; i++) {
nano_fiber_sem_give(&testSem);
}
for (i = 0; i < 32; i++) {
if (nano_fiber_sem_take(&testSem, TICKS_NONE) != 1) {
TC_ERROR(" *** Expected nano_fiber_sem_take() to succeed, not fail\n");
goto errorReturn;
}
}
if (nano_fiber_sem_take(&testSem, TICKS_NONE) != 0) {
TC_ERROR(" *** Expected nano_fiber_sem_take() to fail, not succeed\n");
goto errorReturn;
}
return TC_PASS;
errorReturn:
fiberDetectedFailure = 1;
return TC_FAIL;
}
/**
*
* @brief Entry point for the fiber portion of the semaphore tests
*
* NOTE: The fiber portion of the tests have higher priority than the task
* portion of the tests.
*
* @param arg1 unused
* @param arg2 unused
*
* @return N/A
*/
static void fiberEntry(int arg1, int arg2)
{
int rv; /* return value from a test */
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
rv = testSemFiberNoWait();
if (rv != TC_PASS) {
return;
}
/*
* At this point <testSem> is not available. Wait for <testSem> to become
* available (the main task will give it).
*/
nano_fiber_sem_take(&testSem, TICKS_UNLIMITED);
semTestState = STS_TASK_WOKE_FIBER;
/*
* Delay for two seconds. This gives the main task time to print
* any messages (very important if I/O link is slow!), and wait
* on <testSem>. Once the delay is done, this fiber will give <testSem>
* thus waking the main task.
*/
nano_fiber_timer_start(&timer, SECONDS(2));
nano_fiber_timer_test(&timer, TICKS_UNLIMITED);
/*
* The main task is now waiting on <testSem>. Give the semaphore <testSem>
* to wake it.
*/
nano_fiber_sem_give(&testSem);
/*
* Some small delay must be done so that the main task can process the
* semaphore signal.
*/
semTestState = STS_FIBER_WOKE_TASK;
nano_fiber_timer_start(&timer, SECONDS(2));
nano_fiber_timer_test(&timer, TICKS_UNLIMITED);
/*
* The main task should be waiting on <testSem> again. This time, instead
* of giving the semaphore from the semaphore, give it from an ISR to wake
* the main task.
*/
isrSemInfo.data = 0;
isrSemInfo.sem = &testSem;
_trigger_nano_isr_sem_give();
if (isrSemInfo.data == 1) {
semTestState = STS_ISR_WOKE_TASK;
}
}
/**
*
* @brief Initialize nanokernel objects
*
* This routine initializes the nanokernel objects used in the semaphore tests.
*
* @return N/A
*/
void initNanoObjects(void)
{
nano_sem_init(&testSem);
nano_sem_init(&multi_waiters);
nano_sem_init(&reply_multi_waiters);
nano_timer_init(&timer, timerData);
TC_PRINT("Nano objects initialized\n");
}
/**
*
* @brief Give and take the semaphore in an ISR without blocking
*
* This test gives and takes the test semaphore in the context of an ISR without
* blocking on the semaphore.
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int testSemIsrNoWait(void)
{
int i;
TC_PRINT("Giving and taking a semaphore in an ISR (non-blocking)\n");
/*
* Give the semaphore many times and then make sure that it can only be
* taken that many times.
*/
isrSemInfo.sem = &testSem;
for (i = 0; i < 32; i++) {
_trigger_nano_isr_sem_give();
}
for (i = 0; i < 32; i++) {
isrSemInfo.data = 0;
_trigger_nano_isr_sem_take();
if (isrSemInfo.data != 1) {
TC_ERROR(" *** Expected nano_isr_sem_take() to succeed, not fail\n");
goto errorReturn;
}
}
_trigger_nano_isr_sem_take();
if (isrSemInfo.data != 0) {
TC_ERROR(" *** Expected nano_isr_sem_take() to fail, not succeed!\n");
goto errorReturn;
}
return TC_PASS;
errorReturn:
return TC_FAIL;
}
/**
*
* @brief Give and take the semaphore in a task without blocking
*
* This test gives and takes the test semaphore in a task without
* blocking on the semaphore.
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int testSemTaskNoWait(void)
{
int i; /* loop counter */
TC_PRINT("Giving and taking a semaphore in a task (non-blocking)\n");
/*
* Give the semaphore many times and then make sure that it can only be
* taken that many times.
*/
for (i = 0; i < 32; i++) {
nano_task_sem_give(&testSem);
}
for (i = 0; i < 32; i++) {
if (nano_task_sem_take(&testSem, TICKS_NONE) != 1) {
TC_ERROR(" *** Expected nano_task_sem_take() to succeed, not fail\n");
goto errorReturn;
}
}
if (nano_task_sem_take(&testSem, TICKS_NONE) != 0) {
TC_ERROR(" *** Expected nano_task_sem_take() to fail, not succeed!\n");
goto errorReturn;
}
return TC_PASS;
errorReturn:
return TC_FAIL;
}
/**
*
* @brief Perform tests that wait on a semaphore
*
* This routine works with fiberEntry() to perform the tests that wait on
* a semaphore.
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int testSemWait(void)
{
if (fiberDetectedFailure != 0) {
TC_ERROR(" *** Failure detected in the fiber.");
return TC_FAIL;
}
nano_task_sem_give(&testSem); /* Wake the fiber. */
if (semTestState != STS_TASK_WOKE_FIBER) {
TC_ERROR(" *** Expected task to wake fiber. It did not.\n");
return TC_FAIL;
}
TC_PRINT("Semaphore from the task woke the fiber\n");
nano_task_sem_take(&testSem, TICKS_UNLIMITED); /* Wait on <testSem> */
if (semTestState != STS_FIBER_WOKE_TASK) {
TC_ERROR(" *** Expected fiber to wake task. It did not.\n");
return TC_FAIL;
}
TC_PRINT("Semaphore from the fiber woke the task\n");
nano_task_sem_take(&testSem, TICKS_UNLIMITED); /* Wait on <testSem> again. */
if (semTestState != STS_ISR_WOKE_TASK) {
TC_ERROR(" *** Expected ISR to wake task. It did not.\n");
return TC_FAIL;
}
TC_PRINT("Semaphore from the ISR woke the task.\n");
return TC_PASS;
}
/*
* Multiple-waiters test
*
* NUM_WAITERS fibers pend on the multi_waiters semaphore, then the task give
* the semaphore NUM_WAITERS times. Each time, the first fiber in the queue
* wakes up, is context-switched to, and gives the reply_multi_waiters
* semaphore, for a total of NUM_WAITERS times. The task finally must be able
* to obtain the reply_multi_waiters semaphore NUM_WAITERS times.
*/
#define NUM_WAITERS 3
static char __stack fiber_multi_waiters_stacks[NUM_WAITERS][FIBER_STACKSIZE];
/**
*
* @brief Fiber entry point for multiple-waiters test
*
* @return N/A
*/
static void fiber_multi_waiters(int arg1, int arg2)
{
TC_PRINT("multiple-waiter fiber %d trying to get semaphore...\n", arg1);
nano_fiber_sem_take(&multi_waiters, TICKS_UNLIMITED);
TC_PRINT("multiple-waiter fiber %d acquired semaphore, sending reply\n",
arg1);
nano_fiber_sem_give(&reply_multi_waiters);
}
/**
*
* @brief Task part of multiple-waiter test, repeatable
*
* @return N/A
*/
static int do_test_multiple_waiters(void)
{
int ii;
/* pend all fibers one the same semaphore */
for (ii = 0; ii < NUM_WAITERS; ii++) {
task_fiber_start(fiber_multi_waiters_stacks[ii], FIBER_STACKSIZE,
fiber_multi_waiters, ii, 0, FIBER_PRIORITY, 0);
}
/* wake up all the fibers: the task is preempted each time */
for (ii = 0; ii < NUM_WAITERS; ii++) {
nano_task_sem_give(&multi_waiters);
}
/* reply_multi_waiters will have been given once for each fiber */
for (ii = 0; ii < NUM_WAITERS; ii++) {
if (!nano_task_sem_take(&reply_multi_waiters, TICKS_NONE)) {
TC_ERROR(" *** Cannot take sem supposedly given by waiters.\n");
return TC_FAIL;
}
}
TC_PRINT("Task took multi-waiter reply semaphore %d times, as expected.\n",
NUM_WAITERS);
if (nano_task_sem_take(&multi_waiters, TICKS_NONE)) {
TC_ERROR(" *** multi_waiters should have been empty.\n");
return TC_FAIL;
}
if (nano_task_sem_take(&reply_multi_waiters, TICKS_NONE)) {
TC_ERROR(" *** reply_multi_waiters should have been empty.\n");
return TC_FAIL;
}
return TC_PASS;
}
/**
*
* @brief Entry point for multiple-waiters test
*
* @return N/A
*/
static int test_multiple_waiters(void)
{
TC_PRINT("First pass\n");
if (do_test_multiple_waiters() == TC_FAIL) {
TC_ERROR(" *** First pass test failed.\n");
return TC_FAIL;
}
/*
* Verify a wait q that has been emptied has been reset correctly, so
* redo the test.
*/
TC_PRINT("Second pass\n");
if (do_test_multiple_waiters() == TC_FAIL) {
TC_ERROR(" *** Second pass test failed.\n");
return TC_FAIL;
}
return TC_PASS;
}
/* timeout tests
*
* Test the nano_xxx_sem_wait_timeout() APIs.
*
* First, the task waits with a timeout and times out. Then it wait with a
* timeout, but gets the semaphore in time.
*
* Then, multiple timeout tests are done for the fibers, to test the ordering
* of queueing/dequeueing when timeout occurs, first on one semaphore, then on
* multiple semaphores.
*
* Finally, multiple fibers pend on one semaphore, and they all get the
* semaphore in time, except the last one: this tests that the timeout is
* recomputed correctly when timeouts are aborted.
*/
#include <tc_nano_timeout_common.h>
static struct nano_sem sem_timeout[2];
struct nano_fifo timeout_order_fifo;
struct reply_packet {
void *link_in_fifo;
int reply;
};
struct timeout_order_data {
void *link_in_fifo;
struct nano_sem *sem;
int32_t timeout;
int timeout_order;
int q_order;
};
struct timeout_order_data timeout_order_data[] = {
{0, &sem_timeout[0], TIMEOUT(2), 2, 0},
{0, &sem_timeout[0], TIMEOUT(4), 4, 1},
{0, &sem_timeout[0], TIMEOUT(0), 0, 2},
{0, &sem_timeout[0], TIMEOUT(1), 1, 3},
{0, &sem_timeout[0], TIMEOUT(3), 3, 4},
};
struct timeout_order_data timeout_order_data_mult_sem[] = {
{0, &sem_timeout[1], TIMEOUT(0), 0, 0},
{0, &sem_timeout[0], TIMEOUT(3), 3, 1},
{0, &sem_timeout[0], TIMEOUT(5), 5, 2},
{0, &sem_timeout[1], TIMEOUT(8), 8, 3},
{0, &sem_timeout[1], TIMEOUT(7), 7, 4},
{0, &sem_timeout[0], TIMEOUT(1), 1, 5},
{0, &sem_timeout[0], TIMEOUT(6), 6, 6},
{0, &sem_timeout[0], TIMEOUT(2), 2, 7},
{0, &sem_timeout[1], TIMEOUT(4), 4, 8},
};
#define TIMEOUT_ORDER_NUM_FIBERS ARRAY_SIZE(timeout_order_data_mult_sem)
static char __stack timeout_stacks[TIMEOUT_ORDER_NUM_FIBERS][FIBER_STACKSIZE];
/* a fiber sleeps then gives a semaphore */
static void test_fiber_give_timeout(int sem, int timeout)
{
fiber_sleep((int32_t)timeout);
nano_fiber_sem_give((struct nano_sem *)sem);
}
/* a fiber pends on a semaphore then times out */
static void test_fiber_pend_and_timeout(int data, int unused)
{
struct timeout_order_data *the_data = (void *)data;
int32_t orig_ticks = sys_tick_get();
int rv;
ARG_UNUSED(unused);
rv = nano_fiber_sem_take(the_data->sem, the_data->timeout);
if (rv) {
TC_ERROR(" *** timeout of %d did not time out.\n",
the_data->timeout);
return;
}
if (!is_timeout_in_range(orig_ticks, the_data->timeout)) {
return;
}
nano_fiber_fifo_put(&timeout_order_fifo, the_data);
}
/* the task spins several fibers that pend and timeout on sempahores */
static int test_multiple_fibers_pending(struct timeout_order_data *test_data,
int test_data_size)
{
int ii;
for (ii = 0; ii < test_data_size; ii++) {
task_fiber_start(timeout_stacks[ii], FIBER_STACKSIZE,
test_fiber_pend_and_timeout,
(int)&test_data[ii], 0,
FIBER_PRIORITY, 0);
}
for (ii = 0; ii < test_data_size; ii++) {
struct timeout_order_data *data =
nano_task_fifo_get(&timeout_order_fifo, TICKS_UNLIMITED);
if (data->timeout_order == ii) {
TC_PRINT(" got fiber (q order: %d, t/o: %d, sem: %p) as expected\n",
data->q_order, data->timeout, data->sem);
} else {
TC_ERROR(" *** fiber %d woke up, expected %d\n",
data->timeout_order, ii);
return TC_FAIL;
}
}
return TC_PASS;
}
/* a fiber pends on a semaphore with a timeout and gets the semaphore in time */
static void test_fiber_pend_and_get_sem(int data, int unused)
{
struct timeout_order_data *the_data = (void *)data;
int rv;
ARG_UNUSED(unused);
rv = nano_fiber_sem_take(the_data->sem, the_data->timeout);
if (!rv) {
TC_PRINT(" *** fiber (q order: %d, t/o: %d, sem: %p) timed out!\n",
the_data->q_order, the_data->timeout, the_data->sem);
return;
}
nano_fiber_fifo_put(&timeout_order_fifo, the_data);
}
/* the task spins fibers that get the sem in time, except the last one */
static int test_multiple_fibers_get_sem(struct timeout_order_data *test_data,
int test_data_size)
{
struct timeout_order_data *data;
int ii;
for (ii = 0; ii < test_data_size-1; ii++) {
task_fiber_start(timeout_stacks[ii], FIBER_STACKSIZE,
test_fiber_pend_and_get_sem,
(int)&test_data[ii], 0,
FIBER_PRIORITY, 0);
}
task_fiber_start(timeout_stacks[ii], FIBER_STACKSIZE,
test_fiber_pend_and_timeout,
(int)&test_data[ii], 0,
FIBER_PRIORITY, 0);
for (ii = 0; ii < test_data_size-1; ii++) {
nano_task_sem_give(test_data[ii].sem);
data = nano_task_fifo_get(&timeout_order_fifo, TICKS_UNLIMITED);
if (data->q_order == ii) {
TC_PRINT(" got fiber (q order: %d, t/o: %d, sem: %p) as expected\n",
data->q_order, data->timeout, data->sem);
} else {
TC_ERROR(" *** fiber %d woke up, expected %d\n",
data->q_order, ii);
return TC_FAIL;
}
}
data = nano_task_fifo_get(&timeout_order_fifo, TICKS_UNLIMITED);
if (data->q_order == ii) {
TC_PRINT(" got fiber (q order: %d, t/o: %d, sem: %p) as expected\n",
data->q_order, data->timeout, data->sem);
} else {
TC_ERROR(" *** fiber %d woke up, expected %d\n",
data->timeout_order, ii);
return TC_FAIL;
}
return TC_PASS;
}
static void test_fiber_ticks_special_values(int packet, int special_value)
{
struct reply_packet *reply_packet = (void *)packet;
reply_packet->reply =
nano_fiber_sem_take(&sem_timeout[0], special_value);
nano_fiber_fifo_put(&timeout_order_fifo, reply_packet);
}
/* the timeout test entry point */
static int test_timeout(void)
{
int64_t orig_ticks;
int32_t timeout;
int rv;
int test_data_size;
struct reply_packet reply_packet;
nano_sem_init(&sem_timeout[0]);
nano_sem_init(&sem_timeout[1]);
nano_fifo_init(&timeout_order_fifo);
/* test nano_task_sem_take() with timeout */
timeout = 10;
orig_ticks = sys_tick_get();
rv = nano_task_sem_take(&sem_timeout[0], timeout);
if (rv) {
TC_ERROR(" *** timeout of %d did not time out.\n", timeout);
return TC_FAIL;
}
if ((sys_tick_get() - orig_ticks) < timeout) {
TC_ERROR(" *** task did not wait long enough on timeout of %d.\n",
timeout);
return TC_FAIL;
}
/* test nano_task_sem_take() with timeout of 0 */
rv = nano_task_sem_take(&sem_timeout[0], 0);
if (rv) {
TC_ERROR(" *** timeout of 0 did not time out.\n");
return TC_FAIL;
}
/* test nano_task_sem_take() with timeout > 0 */
TC_PRINT("test nano_task_sem_take() with timeout > 0\n");
timeout = 3;
orig_ticks = sys_tick_get();
rv = nano_task_sem_take(&sem_timeout[0], timeout);
if (rv) {
TC_ERROR(" *** timeout of %d did not time out.\n",
timeout);
return TC_FAIL;
}
if (!is_timeout_in_range(orig_ticks, timeout)) {
return TC_FAIL;
}
TC_PRINT("nano_task_sem_take() timed out as expected\n");
/*
* test nano_task_sem_take() with a timeout and fiber that gives
* the semaphore on time
*/
timeout = 5;
orig_ticks = sys_tick_get();
task_fiber_start(timeout_stacks[0], FIBER_STACKSIZE,
test_fiber_give_timeout, (int)&sem_timeout[0],
timeout,
FIBER_PRIORITY, 0);
rv = nano_task_sem_take(&sem_timeout[0], (int)(timeout + 5));
if (!rv) {
TC_ERROR(" *** timed out even if semaphore was given in time.\n");
return TC_FAIL;
}
if (!is_timeout_in_range(orig_ticks, timeout)) {
return TC_FAIL;
}
TC_PRINT("nano_task_sem_take() got sem in time, as expected\n");
/*
* test nano_task_sem_take() with TICKS_NONE and the
* semaphore unavailable.
*/
if (nano_task_sem_take(&sem_timeout[0], TICKS_NONE)) {
TC_ERROR("task with TICKS_NONE got sem, but shouldn't have\n");
return TC_FAIL;
}
TC_PRINT("task with TICKS_NONE did not get sem, as expected\n");
/*
* test nano_task_sem_take() with TICKS_NONE and the
* semaphore available.
*/
nano_task_sem_give(&sem_timeout[0]);
if (!nano_task_sem_take(&sem_timeout[0], TICKS_NONE)) {
TC_ERROR("task with TICKS_NONE did not get available sem\n");
return TC_FAIL;
}
TC_PRINT("task with TICKS_NONE got available sem, as expected\n");
/*
* test nano_task_sem_take() with TICKS_UNLIMITED and the
* semaphore available.
*/
TC_PRINT("Trying to take available sem with TICKS_UNLIMITED:\n"
" will hang the test if it fails.\n");
nano_task_sem_give(&sem_timeout[0]);
if (!nano_task_sem_take(&sem_timeout[0], TICKS_UNLIMITED)) {
TC_ERROR(" *** This will never be hit!!! .\n");
return TC_FAIL;
}
TC_PRINT("task with TICKS_UNLIMITED got available sem, as expected\n");
/* test fiber with timeout of TICKS_NONE not getting empty semaphore */
task_fiber_start(timeout_stacks[0], FIBER_STACKSIZE,
test_fiber_ticks_special_values,
(int)&reply_packet, TICKS_NONE, FIBER_PRIORITY, 0);
if (!nano_task_fifo_get(&timeout_order_fifo, TICKS_NONE)) {
TC_ERROR(" *** fiber should have run and filled the fifo.\n");
return TC_FAIL;
}
if (reply_packet.reply != 0) {
TC_ERROR(" *** fiber should not have obtained the semaphore.\n");
return TC_FAIL;
}
TC_PRINT("fiber with TICKS_NONE did not get sem, as expected\n");
/* test fiber with timeout of TICKS_NONE getting full semaphore */
nano_task_sem_give(&sem_timeout[0]);
task_fiber_start(timeout_stacks[0], FIBER_STACKSIZE,
test_fiber_ticks_special_values,
(int)&reply_packet, TICKS_NONE, FIBER_PRIORITY, 0);
if (!nano_task_fifo_get(&timeout_order_fifo, TICKS_NONE)) {
TC_ERROR(" *** fiber should have run and filled the fifo.\n");
return TC_FAIL;
}
if (reply_packet.reply != 1) {
TC_ERROR(" *** fiber should have obtained the semaphore.\n");
return TC_FAIL;
}
TC_PRINT("fiber with TICKS_NONE got available sem, as expected\n");
/* test fiber with timeout of TICKS_UNLIMITED getting full semaphore */
nano_task_sem_give(&sem_timeout[0]);
task_fiber_start(timeout_stacks[0], FIBER_STACKSIZE,
test_fiber_ticks_special_values,
(int)&reply_packet, TICKS_UNLIMITED, FIBER_PRIORITY, 0);
if (!nano_task_fifo_get(&timeout_order_fifo, TICKS_NONE)) {
TC_ERROR(" *** fiber should have run and filled the fifo.\n");
return TC_FAIL;
}
if (reply_packet.reply != 1) {
TC_ERROR(" *** fiber should have obtained the semaphore.\n");
return TC_FAIL;
}
TC_PRINT("fiber with TICKS_UNLIMITED got available sem, as expected\n");
/* test multiple fibers pending on the same sem with different timeouts */
test_data_size = ARRAY_SIZE(timeout_order_data);
TC_PRINT("testing timeouts of %d fibers on same sem\n", test_data_size);
rv = test_multiple_fibers_pending(timeout_order_data, test_data_size);
if (rv != TC_PASS) {
TC_ERROR(" *** fibers did not time out in the right order\n");
return TC_FAIL;
}
/* test multiple fibers pending on different sems with different timeouts */
test_data_size = ARRAY_SIZE(timeout_order_data_mult_sem);
TC_PRINT("testing timeouts of %d fibers on different sems\n",
test_data_size);
rv = test_multiple_fibers_pending(timeout_order_data_mult_sem,
test_data_size);
if (rv != TC_PASS) {
TC_ERROR(" *** fibers did not time out in the right order\n");
return TC_FAIL;
}
/*
* test multiple fibers pending on same sem with different timeouts, but
* getting the semaphore in time, except the last one.
*/
test_data_size = ARRAY_SIZE(timeout_order_data);
TC_PRINT("testing %d fibers timing out, but obtaining the sem in time\n"
"(except the last one, which times out)\n",
test_data_size);
rv = test_multiple_fibers_get_sem(timeout_order_data, test_data_size);
if (rv != TC_PASS) {
TC_ERROR(" *** fibers did not get the sem in the right order\n");
return TC_FAIL;
}
return TC_PASS;
}
/**
*
* @brief Entry point to semaphore tests
*
* This is the entry point to the semaphore tests.
*
* @return N/A
*/
void main(void)
{
int rv; /* return value from tests */
TC_START("Test Nanokernel Semaphores");
initNanoObjects();
rv = testSemTaskNoWait();
if (rv != TC_PASS) {
goto doneTests;
}
rv = testSemIsrNoWait();
if (rv != TC_PASS) {
goto doneTests;
}
semTestState = STS_INIT;
/*
* Start the fiber. The fiber will be given a higher priority than the
* main task.
*/
task_fiber_start(fiberStack, FIBER_STACKSIZE, fiberEntry,
0, 0, FIBER_PRIORITY, 0);
rv = testSemWait();
if (rv != TC_PASS) {
goto doneTests;
}
rv = test_multiple_waiters();
if (rv != TC_PASS) {
goto doneTests;
}
rv = test_timeout();
if (rv != TC_PASS) {
goto doneTests;
}
doneTests:
TC_END_RESULT(rv);
TC_END_REPORT(rv);
}