blob: 5b7c0243f755df7993fafb8488b332cca083ea09 [file] [log] [blame]
/*
* Copyright (c) 2012-2014 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Test semaphore APIs
*
* This modules tests the following semaphore routines:
*
* task_sem_group_reset()
* task_sem_group_give()
* task_sem_reset()
* task_sem_give()
* task_sem_count_get()
* task_sem_take()
* isr_sem_give()
* fiber_sem_give()
*/
#define __printf_like(f, a)
#include <zephyr.h>
#include <tc_util.h>
#include <util_test_common.h>
extern void trigger_isrSemaSignal(ksem_t semaphore);
extern void releaseTestFiber(void);
#define N_TESTS 10 /* number of tests to run */
#define OBJ_TIMEOUT SECONDS(1)
extern ksem_t simpleSem;
extern ksem_t altSem;
extern ksem_t hpSem;
extern ksem_t manyBlockSem;
extern ksem_t group1Sem;
extern ksem_t group2Sem;
extern ksem_t group3Sem;
extern ksem_t group4Sem;
extern ksem_t blockHpSem;
extern ksem_t blockMpSem;
extern ksem_t blockLpSem;
extern ksem_t semList[];
/**
*
* @brief Signal semaphore that has no waiting tasks from ISR
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int simpleSemaTest(void)
{
int signalCount;
int i;
int status;
/*
* Signal the semaphore several times from an ISR. After each signal,
* check the signal count.
*/
for (i = 0; i < 5; i++) {
trigger_isrSemaSignal(simpleSem);
task_sleep(10); /* Time for low priority task to run. */
signalCount = task_sem_count_get(simpleSem);
if (signalCount != (i + 1)) {
TC_ERROR("<signalCount> error. Expected %d, got %d\n",
i + 1, signalCount);
return TC_FAIL;
}
}
/*
* Signal the semaphore several times from a task. After each signal,
* check the signal count.
*/
for (i = 5; i < 10; i++) {
task_sem_give(simpleSem);
signalCount = task_sem_count_get(simpleSem);
if (signalCount != (i + 1)) {
TC_ERROR("<signalCount> error. Expected %d, got %d\n",
i + 1, signalCount);
return TC_FAIL;
}
}
/*
* Test the semaphore without wait. Check the signal count after each
* attempt (it should be decrementing by 1 each time).
*/
for (i = 9; i >= 4; i--) {
status = task_sem_take(simpleSem, TICKS_NONE);
if (status != RC_OK) {
TC_ERROR("task_sem_take(SIMPLE_SEM) error. Expected %d, not %d.\n",
RC_OK, status);
return TC_FAIL;
}
signalCount = task_sem_count_get(simpleSem);
if (signalCount != i) {
TC_ERROR("<signalCount> error. Expected %d, not %d\n",
i, signalCount);
return TC_FAIL;
}
}
task_sem_reset(simpleSem);
/*
* The semaphore's signal count should now be zero (0). Test the
* semaphore without wait. It should fail.
*/
for (i = 0; i < 10; i++) {
status = task_sem_take(simpleSem, TICKS_NONE);
if (status != RC_FAIL) {
TC_ERROR("task_sem_take(SIMPLE_SEM) error. Expected %d, got %d.\n",
RC_FAIL, status);
return TC_FAIL;
}
signalCount = task_sem_count_get(simpleSem);
if (signalCount != 0) {
TC_ERROR("<signalCount> error. Expected %d, not %d\n",
0, signalCount);
return TC_FAIL;
}
}
return TC_PASS;
}
/**
*
* @brief Test the waiting of a semaphore
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int simpleSemaWaitTest(void)
{
int status;
int i;
for (i = 0; i < 5; i++) {
/* Wait one second for SIMPLE_SEM. Timeout is expected. */
status = task_sem_take(simpleSem, OBJ_TIMEOUT);
if (status != RC_TIME) {
TC_ERROR("task_sem_take() error. Expected %d, got %d\n",
RC_TIME, status);
return TC_FAIL;
}
}
/*
* Signal the semaphore upon which the alternate task is waiting. The
* alternate task (which is at a lower priority) will cause SIMPLE_SEM
* to be signalled, thus waking this task.
*/
task_sem_give(altSem);
status = task_sem_take(simpleSem, OBJ_TIMEOUT);
if (status != RC_OK) {
TC_ERROR("task_sem_take() error. Expected %d, got %d\n",
RC_OK, status);
return TC_FAIL;
}
/*
* Note that task_sem_take(TICKS_UNLIMITED) has been tested when waking up
* the alternate task. Since previous tests had this task waiting, the
* alternate task must have had the time to enter the state where it is
* waiting for the ALTTASK_SEM semaphore to be given. Thus, we do not need
* to test for it here.
*
* Now wait on SIMPLE_SEM again. This time it will be woken up by an
* ISR signalling the semaphore.
*/
status = task_sem_take(simpleSem, OBJ_TIMEOUT);
if (status != RC_OK) {
TC_ERROR("task_sem_take() error. Expected %d, got %d\n",
RC_OK, status);
return TC_FAIL;
}
return TC_PASS;
}
/**
*
* @brief Test for a group of semaphores
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int simpleGroupTest(void)
{
int i;
int j;
int status;
ksem_t value;
/* Ensure that the semaphores in the group are reset */
task_sem_group_reset(semList);
for (i = 0; semList[i] != ENDLIST; i++) {
status = task_sem_count_get(semList[i]);
if (status != 0) {
TC_ERROR("task_sem_count_get() returned %d not %d\n", status, 0);
return TC_FAIL;
}
}
/* Timeout while waiting for a semaphore from the group */
value = task_sem_group_take(semList, OBJ_TIMEOUT);
if (value != ENDLIST) {
TC_ERROR("task_sem_group_take() returned %d not %d\n",
value, ENDLIST);
return TC_FAIL;
}
/* Signal the semaphores in the group */
for (i = 0; i < 10; i++) {
task_sem_group_give(semList);
for (j = 0; semList[j] != ENDLIST; j++) {
status = task_sem_count_get(semList[j]);
if (status != i + 1) {
TC_ERROR("task_sem_count_get() returned %d not %d\n",
status, i + 1);
return TC_FAIL;
}
}
}
/* Get the semaphores */
for (i = 9; i >= 5; i--) {
value = task_sem_group_take(semList, 0);
for (j = 0; semList[j] != ENDLIST; j++) {
status = task_sem_count_get(semList[j]);
if (status != (value == semList[j] ? i : 10)) {
TC_ERROR("task_sem_count_get(0x%x) returned %d not %d\n",
semList[j], status, (value == semList[j]) ? i : 10);
return TC_FAIL;
}
}
}
/* Reset the semaphores in the group */
task_sem_group_reset(semList);
for (i = 0; semList[i] != ENDLIST; i++) {
status = task_sem_count_get(semList[i]);
if (status != 0) {
TC_ERROR("task_sem_count_get() returned %d not %d\n", status, 0);
return TC_FAIL;
}
}
return TC_PASS;
}
/**
*
* @brief Test a group of semaphores with waiting
*
* This routine tests the waiting feature on a group of semaphores. Note that
* timing out on a wait has already been tested so it need not be done again.
*
* @return TC_PASS on success, TC_FAIL on failure
*/
int simpleGroupWaitTest(void)
{
int i;
ksem_t sema;
task_sem_give(altSem); /* Wake the alternate task */
/*
* Wait for a semaphore to be signalled by the alternate task.
* Each semaphore in the group will be tested.
*/
for (i = 0; semList[i] != ENDLIST; i++) {
sema = task_sem_group_take(semList, TICKS_UNLIMITED);
if (sema != semList[i]) {
TC_ERROR("task_sem_group_take() error. Expected %d, not %d\n",
(int) semList[i], (int) sema);
return TC_FAIL;
}
}
/*
* In the current implementation of semaphore groups, the taken
* semaphore is the first semaphore in 'semList'. Note that the
* semaphore taken is implementation-defined behavior and may
* change in the future.
*/
for (i = 0; i < 4; i++) {
sema = task_sem_group_take(semList, TICKS_UNLIMITED);
if (sema != semList[i]) {
TC_ERROR("task_sem_group_take() error. Expected %d, not %d\n",
(int) semList[i], (int) sema);
return TC_FAIL;
}
}
/*
* Again wait for a semaphore to be signalled. This time, the alternate
* task will trigger an interrupt that signals the semaphore.
*/
for (i = 0; semList[i] != ENDLIST; i++) {
sema = task_sem_group_take(semList, TICKS_UNLIMITED);
if (sema != semList[i]) {
TC_ERROR("task_sem_group_take() error. Expected %d, not %d\n",
(int) semList[i], (int) sema);
return TC_FAIL;
}
}
return TC_PASS;
}
/**
*
* @brief Test semaphore signaling from fiber
*
* Routine starts a fiber and does the following tests:
* - fiber signals the semaphore N times, task checks that task_sem_count_get is N
* - task waits on a semaphore and fiber signals it
* - task waits on a semaphore group and fiber signals each of them once. Task
* checks which of the semaphores has been signaled
*
* See also: testFiber.c
*
* @return TC_PASS on success or TC_FAIL on failure
*/
static int simpleFiberSemTest(void)
{
int signalCount;
int status;
int i;
ksem_t sema;
task_sem_reset(simpleSem);
task_sem_group_reset(semList);
/* let the fiber signal the semaphore and wait on it */
releaseTestFiber();
status = task_sem_take(simpleSem, OBJ_TIMEOUT);
if (status != RC_OK) {
TC_ERROR("task_sem_take() error. Expected %d, got %d\n",
RC_OK, status);
return TC_FAIL;
}
/* release the fiber and let it signal the semaphore N_TESTS times */
releaseTestFiber();
signalCount = task_sem_count_get(simpleSem);
if (signalCount != N_TESTS) {
TC_ERROR("<signalCount> error. Expected %d, got %d\n",
N_TESTS, signalCount);
return TC_FAIL;
}
/* wait on the semaphore group while the fiber signals each semaphore in it */
for (i = 0; semList[i] != ENDLIST; i++) {
releaseTestFiber();
sema = task_sem_group_take(semList, OBJ_TIMEOUT);
if (sema != semList[i]) {
TC_ERROR("task_sem_group_take() error. Expected %d, not %d\n",
(int) semList[i], (int) sema);
return TC_FAIL;
}
}
return TC_PASS;
}
/**
*
* @brief A high priority task
*
* @return TC_PASS or TC_FAIL
*/
int HighPriTask(void)
{
int status;
/* Wait until task is activated */
status = task_sem_take(hpSem, TICKS_UNLIMITED);
if (status != RC_OK) {
TC_ERROR("%s priority task failed to wait on %s: %d\n",
"High", "HIGH_PRI_SEM", status);
return TC_FAIL;
}
/* Wait on a semaphore along with other tasks */
status = task_sem_take(manyBlockSem, TICKS_UNLIMITED);
if (status != RC_OK) {
TC_ERROR("%s priority task failed to wait on %s: %d\n",
"High", "MANY_BLOCKED_SEM", status);
return TC_FAIL;
}
/* Inform Regression test HP task is no longer blocked on MANY_BLOCKED_SEM*/
task_sem_give(blockHpSem);
return TC_PASS;
}
/**
*
* @brief A low priority task
*
* @return TC_PASS or TC_FAIL
*/
int LowPriTask(void)
{
int status;
/* Wait on a semaphore along with other tasks */
status = task_sem_take(manyBlockSem, TICKS_UNLIMITED);
if (status != RC_OK) {
TC_ERROR("%s priority task failed to wait on %s: %d\n",
"Low", "MANY_BLOCKED_SEM", status);
return TC_FAIL;
}
/* Inform Regression test LP task is no longer blocked on MANY_BLOCKED_SEM*/
task_sem_give(blockLpSem);
return TC_PASS;
}
/**
*
* @brief Alternate task in the test suite
*
* This routine runs at a lower priority than RegressionTask().
*
* @return TC_PASS or TC_FAIL
*/
int AlternateTask(void)
{
int status;
int i;
/* Wait until it is time to continue */
status = task_sem_take(altSem, TICKS_UNLIMITED);
if (status != RC_OK) {
TC_ERROR("task_sem_take() error. Expected %d, got %d\n",
RC_OK, status);
return TC_FAIL;
}
/*
* After signalling the semaphore upon which the main (RegressionTask) task
* is waiting, control will pass back to the main (RegressionTask) task.
*/
task_sem_give(simpleSem);
/*
* Control has returned to the alternate task. Trigger an ISR that will
* signal the semaphore upon which RegressionTask is waiting.
*/
trigger_isrSemaSignal(simpleSem);
/* Wait for RegressionTask to wake this task up */
status = task_sem_take(altSem, TICKS_UNLIMITED);
if (status != RC_OK) {
TC_ERROR("task_sem_take() error. Expected %d, got %d",
RC_OK, status);
return TC_FAIL;
}
/* Wait on a semaphore that will have many waiters */
status = task_sem_take(manyBlockSem, TICKS_UNLIMITED);
if (status != RC_OK) {
TC_ERROR("task_sem_take() error. Expected %d, got %d",
RC_OK, status);
return TC_FAIL;
}
/* Inform Regression test MP task is no longer blocked on MANY_BLOCKED_SEM*/
task_sem_give(blockMpSem);
/* Wait until the alternate task is needed again */
status = task_sem_take(altSem, TICKS_UNLIMITED);
if (status != RC_OK) {
TC_ERROR("task_sem_take() error. Expected %d, got %d",
RC_OK, status);
return TC_FAIL;
}
for (i = 0; semList[i] != ENDLIST; i++) {
task_sem_give(semList[i]);
/* Context switch back to Regression Task */
}
task_sem_group_give(semList);
/* Context switch back to Regression Task */
for (i = 0; semList[i] != ENDLIST; i++) {
trigger_isrSemaSignal(semList[i]);
/* Context switch back to Regression Task */
}
return TC_PASS;
}
/**
*
* @brief Entry point to semaphore test suite
*
* This is the entry point to the semaphore test suite.
*
* @return TC_PASS or TC_FAIL
*/
int RegressionTask(void)
{
int tcRC;
ksem_t value;
ksem_t semBlockList[4];
semBlockList[0] = blockHpSem;
semBlockList[1] = blockMpSem;
semBlockList[2] = blockLpSem;
semBlockList[3] = ENDLIST;
/* Signal a semaphore that has no waiting tasks. */
TC_PRINT("Signal and test a semaphore without blocking\n");
tcRC = simpleSemaTest();
if (tcRC != TC_PASS) {
return TC_FAIL;
}
/* Wait on a semaphore. */
TC_PRINT("Signal and test a semaphore with blocking\n");
tcRC = simpleSemaWaitTest();
if (tcRC != TC_PASS) {
return TC_FAIL;
}
/*
* Have many tasks wait on a semaphore (MANY_BLOCKED_SEM). They will
* block in the following order:
* LowPriorityTask (low priority)
* HighPriorityTask (high priority)
* AlternateTask (medium priority)
*
* When the semaphore (MANY_BLOCKED_SEM) is signalled, HighPriorityTask
* will be woken first.
*
*/
TC_PRINT("Testing many tasks blocked on the same semaphore\n");
task_sleep(OBJ_TIMEOUT); /* Time for low priority task to run. */
task_sem_give(hpSem); /* Wake high priority task */
task_sem_give(altSem); /* Wake alternate task (medium priority) */
task_sleep(OBJ_TIMEOUT); /* Give alternate task time to run */
task_sem_give(manyBlockSem);
task_sleep(OBJ_TIMEOUT); /* Ensure high priority task can run */
value = task_sem_group_take(semBlockList, OBJ_TIMEOUT);
if (value != blockHpSem) {
TC_ERROR("%s priority task did not get semaphore: 0x%x\n",
"High", value);
return TC_FAIL;
}
task_sem_give(manyBlockSem);
task_sleep(OBJ_TIMEOUT); /* Ensure medium priority task can run */
value = task_sem_group_take(semBlockList, OBJ_TIMEOUT);
if (value != blockMpSem) {
TC_ERROR("%s priority task did not get semaphore: 0x%x\n",
"Medium", value);
return TC_FAIL;
}
task_sem_give(manyBlockSem);
task_sleep(OBJ_TIMEOUT); /* Ensure low priority task can run */
value = task_sem_group_take(semBlockList, OBJ_TIMEOUT);
if (value != blockLpSem) {
TC_ERROR("%s priority task did not get semaphore: 0x%x\n",
"Low", value);
return TC_FAIL;
}
value = task_sem_group_take(semBlockList, OBJ_TIMEOUT);
if (value != ENDLIST) {
TC_ERROR("Group test Expecting ENDLIST, but got 0x%x\n",
value);
return TC_FAIL;
}
TC_PRINT("Testing semaphore groups without blocking\n");
tcRC = simpleGroupTest();
if (tcRC != TC_PASS) {
return TC_FAIL;
}
TC_PRINT("Testing semaphore groups with blocking\n");
tcRC = simpleGroupWaitTest();
if (tcRC != TC_PASS) {
return TC_FAIL;
}
TC_PRINT("Testing semaphore release by fiber\n");
tcRC = simpleFiberSemTest();
if (tcRC != TC_PASS) {
return TC_FAIL;
}
return TC_PASS;
}