blob: d48495425580a94c9f972a600f60523c16d37d21 [file] [log] [blame]
/*
* Copyright (c) 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.
*/
#include <zephyr.h>
#include <tc_util.h>
#include <misc/__assert.h>
#include <misc/util.h>
/* timeout tests
*
* Test the nano_xxx_fifo_wait_timeout() APIs.
*
* First, the task waits with a timeout and times out. Then it wait with a
* timeout, but gets the data in time.
*
* Then, multiple timeout tests are done for the fibers, to test the ordering
* of queueing/dequeueing when timeout occurs, first on one fifo, then on
* multiple fifos.
*
* Finally, multiple fibers pend on one fifo, and they all get the
* data in time, except the last one: this tests that the timeout is
* recomputed correctly when timeouts are aborted.
*/
#include <tc_nano_timeout_common.h>
#define FIBER_PRIORITY 5
#define FIBER_STACKSIZE 256
struct scratch_fifo_packet {
void *link_in_fifo;
void *data_if_needed;
};
struct reply_packet {
void *link_in_fifo;
int reply;
};
#define NUM_SCRATCH_FIFO_PACKETS 20
struct scratch_fifo_packet scratch_fifo_packets[NUM_SCRATCH_FIFO_PACKETS];
struct nano_fifo scratch_fifo_packets_fifo;
void *get_scratch_packet(void)
{
void *packet = nano_fifo_get(&scratch_fifo_packets_fifo, TICKS_NONE);
__ASSERT_NO_MSG(packet);
return packet;
}
void put_scratch_packet(void *packet)
{
nano_fifo_put(&scratch_fifo_packets_fifo, packet);
}
static struct nano_fifo fifo_timeout[2];
struct nano_fifo timeout_order_fifo;
struct timeout_order_data {
void *link_in_fifo;
struct nano_fifo *fifo;
int32_t timeout;
int timeout_order;
int q_order;
};
struct timeout_order_data timeout_order_data[] = {
{0, &fifo_timeout[0], TIMEOUT(2), 2, 0},
{0, &fifo_timeout[0], TIMEOUT(4), 4, 1},
{0, &fifo_timeout[0], TIMEOUT(0), 0, 2},
{0, &fifo_timeout[0], TIMEOUT(1), 1, 3},
{0, &fifo_timeout[0], TIMEOUT(3), 3, 4},
};
struct timeout_order_data timeout_order_data_mult_fifo[] = {
{0, &fifo_timeout[1], TIMEOUT(0), 0, 0},
{0, &fifo_timeout[0], TIMEOUT(3), 3, 1},
{0, &fifo_timeout[0], TIMEOUT(5), 5, 2},
{0, &fifo_timeout[1], TIMEOUT(8), 8, 3},
{0, &fifo_timeout[1], TIMEOUT(7), 7, 4},
{0, &fifo_timeout[0], TIMEOUT(1), 1, 5},
{0, &fifo_timeout[0], TIMEOUT(6), 6, 6},
{0, &fifo_timeout[0], TIMEOUT(2), 2, 7},
{0, &fifo_timeout[1], TIMEOUT(4), 4, 8},
};
#define TIMEOUT_ORDER_NUM_FIBERS ARRAY_SIZE(timeout_order_data_mult_fifo)
static char __stack timeout_stacks[TIMEOUT_ORDER_NUM_FIBERS][FIBER_STACKSIZE];
/* a fiber sleeps then puts data on the fifo */
static void test_fiber_put_timeout(int fifo, int timeout)
{
fiber_sleep((int32_t)timeout);
nano_fiber_fifo_put((struct nano_fifo *)fifo, get_scratch_packet());
}
/* a fiber pends on a fifo then times out */
static void test_fiber_pend_and_timeout(int data, int unused)
{
struct timeout_order_data *d = (void *)data;
int32_t orig_ticks = sys_tick_get();
void *packet;
ARG_UNUSED(unused);
packet = nano_fiber_fifo_get(d->fifo, d->timeout);
if (packet) {
TC_ERROR(" *** timeout of %d did not time out.\n",
d->timeout);
return;
}
if (!is_timeout_in_range(orig_ticks, d->timeout)) {
return;
}
nano_fiber_fifo_put(&timeout_order_fifo, d);
}
/* the task spins several fibers that pend and timeout on fifos */
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, fifo %p) as expected\n",
data->q_order, data->timeout, data->fifo);
} 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 fifo with a timeout and gets the data in time */
static void test_fiber_pend_and_get_data(int data, int unused)
{
struct timeout_order_data *d = (void *)data;
void *packet;
ARG_UNUSED(unused);
packet = nano_fiber_fifo_get(d->fifo, d->timeout);
if (!packet) {
TC_PRINT(" *** fiber (q order: %d, t/o: %d, fifo %p) timed out!\n",
d->q_order, d->timeout, d->fifo);
return;
}
put_scratch_packet(packet);
nano_fiber_fifo_put(&timeout_order_fifo, d);
}
/* the task spins fibers that get fifo data in time, except the last one */
static int test_multiple_fibers_get_data(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_data,
(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_fifo_put(test_data[ii].fifo, get_scratch_packet());
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, fifo %p) as expected\n",
data->q_order, data->timeout, data->fifo);
} 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, fifo %p) as expected\n",
data->q_order, data->timeout, data->fifo);
} else {
TC_ERROR(" *** fiber %d woke up, expected %d\n",
data->timeout_order, ii);
return TC_FAIL;
}
return TC_PASS;
}
/* try getting data on fifo with special timeout value, return result in fifo */
static void test_fiber_ticks_special_values(int packet, int special_value)
{
struct reply_packet *reply_packet = (void *)packet;
reply_packet->reply =
!!nano_fiber_fifo_get(&fifo_timeout[0], special_value);
nano_fiber_fifo_put(&timeout_order_fifo, reply_packet);
}
/* the timeout test entry point */
int test_fifo_timeout(void)
{
int64_t orig_ticks;
int32_t timeout;
int rv;
void *packet, *scratch_packet;
int test_data_size;
int ii;
struct reply_packet reply_packet;
nano_fifo_init(&fifo_timeout[0]);
nano_fifo_init(&fifo_timeout[1]);
nano_fifo_init(&timeout_order_fifo);
nano_fifo_init(&scratch_fifo_packets_fifo);
for (ii = 0; ii < NUM_SCRATCH_FIFO_PACKETS; ii++) {
scratch_fifo_packets[ii].data_if_needed = (void *)ii;
nano_task_fifo_put(&scratch_fifo_packets_fifo,
&scratch_fifo_packets[ii]);
}
/* test nano_task_fifo_get() with timeout */
timeout = 10;
orig_ticks = sys_tick_get();
packet = nano_task_fifo_get(&fifo_timeout[0], timeout);
if (packet) {
TC_ERROR(" *** timeout of %d did not time out.\n", timeout);
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
if ((sys_tick_get() - orig_ticks) < timeout) {
TC_ERROR(" *** task did not wait long enough on timeout of %d.\n",
timeout);
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
/* test nano_task_fifo_get with timeout of 0 */
packet = nano_task_fifo_get(&fifo_timeout[0], 0);
if (packet) {
TC_ERROR(" *** timeout of 0 did not time out.\n");
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
/* test nano_task_fifo_get with timeout > 0 */
TC_PRINT("test nano_task_fifo_get with timeout > 0\n");
timeout = 3;
orig_ticks = sys_tick_get();
packet = nano_task_fifo_get(&fifo_timeout[0], timeout);
if (packet) {
TC_ERROR(" *** timeout of %d did not time out.\n",
timeout);
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
if (!is_timeout_in_range(orig_ticks, timeout)) {
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
TC_PRINT("nano_task_fifo_get timed out as expected\n");
/*
* test nano_task_fifo_get with a timeout and fiber that puts
* data on the fifo on time
*/
timeout = 5;
orig_ticks = sys_tick_get();
task_fiber_start(timeout_stacks[0], FIBER_STACKSIZE,
test_fiber_put_timeout, (int)&fifo_timeout[0],
timeout,
FIBER_PRIORITY, 0);
packet = nano_task_fifo_get(&fifo_timeout[0], (int)(timeout + 5));
if (!packet) {
TC_ERROR(" *** data put in time did not return valid pointer.\n");
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
put_scratch_packet(packet);
if (!is_timeout_in_range(orig_ticks, timeout)) {
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
TC_PRINT("nano_task_fifo_get got fifo in time, as expected\n");
/*
* test nano_task_fifo_get with TICKS_NONE and no data
* unavailable.
*/
if (nano_task_fifo_get(&fifo_timeout[0], TICKS_NONE)) {
TC_ERROR("task with TICKS_NONE got data, but shouldn't have\n");
return TC_FAIL;
}
TC_PRINT("task with TICKS_NONE did not get data, as expected\n");
/*
* test nano_task_fifo_get with TICKS_NONE and some data
* available.
*/
scratch_packet = get_scratch_packet();
nano_task_fifo_put(&fifo_timeout[0], scratch_packet);
if (!nano_task_fifo_get(&fifo_timeout[0], TICKS_NONE)) {
TC_ERROR("task with TICKS_NONE did not get available data\n");
return TC_FAIL;
}
put_scratch_packet(scratch_packet);
TC_PRINT("task with TICKS_NONE got available data, as expected\n");
/*
* test nano_task_fifo_get with TICKS_UNLIMITED and the
* data available.
*/
TC_PRINT("Trying to take available data with TICKS_UNLIMITED:\n"
" will hang the test if it fails.\n");
scratch_packet = get_scratch_packet();
nano_task_fifo_put(&fifo_timeout[0], scratch_packet);
if (!nano_task_fifo_get(&fifo_timeout[0], TICKS_UNLIMITED)) {
TC_ERROR(" *** This will never be hit!!! .\n");
return TC_FAIL;
}
put_scratch_packet(scratch_packet);
TC_PRINT("task with TICKS_UNLIMITED got available data, as expected\n");
/* test fiber with timeout of TICKS_NONE not getting data on empty fifo */
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 data.\n");
return TC_FAIL;
}
TC_PRINT("fiber with TICKS_NONE did not get data, as expected\n");
/* test fiber with timeout of TICKS_NONE getting data when available */
scratch_packet = get_scratch_packet();
nano_task_fifo_put(&fifo_timeout[0], scratch_packet);
task_fiber_start(timeout_stacks[0], FIBER_STACKSIZE,
test_fiber_ticks_special_values,
(int)&reply_packet, TICKS_NONE, FIBER_PRIORITY, 0);
put_scratch_packet(scratch_packet);
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 data.\n");
return TC_FAIL;
}
TC_PRINT("fiber with TICKS_NONE got available data, as expected\n");
/* test fiber with TICKS_UNLIMITED timeout getting data when availalble */
scratch_packet = get_scratch_packet();
nano_task_fifo_put(&fifo_timeout[0], scratch_packet);
task_fiber_start(timeout_stacks[0], FIBER_STACKSIZE,
test_fiber_ticks_special_values,
(int)&reply_packet, TICKS_UNLIMITED, FIBER_PRIORITY, 0);
put_scratch_packet(scratch_packet);
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 data.\n");
return TC_FAIL;
}
TC_PRINT("fiber with TICKS_UNLIMITED got available data, as expected\n");
/* test multiple fibers pending on the same fifo with different timeouts */
test_data_size = ARRAY_SIZE(timeout_order_data);
TC_PRINT("testing timeouts of %d fibers on same fifo\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");
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
/* test mult. fibers pending on different fifos with different timeouts */
test_data_size = ARRAY_SIZE(timeout_order_data_mult_fifo);
TC_PRINT("testing timeouts of %d fibers on different fifos\n",
test_data_size);
rv = test_multiple_fibers_pending(timeout_order_data_mult_fifo,
test_data_size);
if (rv != TC_PASS) {
TC_ERROR(" *** fibers did not time out in the right order\n");
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
/*
* test multiple fibers pending on same fifo with different timeouts, but
* getting the data in time, except the last one.
*/
test_data_size = ARRAY_SIZE(timeout_order_data);
TC_PRINT("testing %d fibers timing out, but obtaining the data in time\n"
"(except the last one, which times out)\n",
test_data_size);
rv = test_multiple_fibers_get_data(timeout_order_data, test_data_size);
if (rv != TC_PASS) {
TC_ERROR(" *** fibers did not get the data in the right order\n");
TC_END_RESULT(TC_FAIL);
return TC_FAIL;
}
TC_END_RESULT(TC_PASS);
return TC_PASS;
}