kernel: queue, fifo: Add cancel_wait operation.
Currently, a queue/fifo getter chooses how long to wait for an element. But there are scenarios when putter would know better, there should be a way to expire getter's timeout to make it run again. k_queue_cancel_wait() and k_fifo_cancel_wait() functions do just that. They cause corresponding *_get() functions to return with NULL value, as if timeout expired on getter's side (even K_FOREVER). This can be used to signal out of band conditions from putter to getter, e.g. end of processing, error, configuration change, etc. A specific event would be communicated to getter by other means (e.g. using existing shared context structures). Without this call, achieving the same effect would require e.g. calling k_fifo_put() with a pointer to a special sentinal memory structure - such structure would need to be allocated somewhere and somehow, and getter would need to recognize it from a normal data item. Having cancel_wait() functions offers an elegant alternative. From this perspective, these calls can be seen as an equivalent to e.g. k_fifo_put(fifo, NULL), except that such call won't work in practice. Change-Id: I47b7f690dc325a80943082bcf5345c41649e7024 Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
This commit is contained in:
parent
bca1077d3c
commit
3f50707672
5 changed files with 115 additions and 1 deletions
|
@ -1274,6 +1274,20 @@ struct k_queue {
|
|||
*/
|
||||
extern void k_queue_init(struct k_queue *queue);
|
||||
|
||||
/**
|
||||
* @brief Cancel waiting on a queue.
|
||||
*
|
||||
* This routine causes first thread pending on @a queue, if any, to
|
||||
* return from k_queue_get() call with NULL value (as if timeout expired).
|
||||
*
|
||||
* @note Can be called by ISRs.
|
||||
*
|
||||
* @param queue Address of the queue.
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
extern void k_queue_cancel_wait(struct k_queue *queue);
|
||||
|
||||
/**
|
||||
* @brief Append an element to the end of a queue.
|
||||
*
|
||||
|
@ -1445,6 +1459,22 @@ struct k_fifo {
|
|||
#define k_fifo_init(fifo) \
|
||||
k_queue_init((struct k_queue *) fifo)
|
||||
|
||||
/**
|
||||
* @brief Cancel waiting on a fifo.
|
||||
*
|
||||
* This routine causes first thread pending on @a fifo, if any, to
|
||||
* return from k_fifo_get() call with NULL value (as if timeout
|
||||
* expired).
|
||||
*
|
||||
* @note Can be called by ISRs.
|
||||
*
|
||||
* @param fifo Address of the fifo.
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
#define k_fifo_cancel_wait(fifo) \
|
||||
k_queue_cancel_wait((struct k_queue *) fifo)
|
||||
|
||||
/**
|
||||
* @brief Add an element to a fifo.
|
||||
*
|
||||
|
|
|
@ -77,6 +77,31 @@ static inline int handle_poll_event(struct k_queue *queue)
|
|||
#endif
|
||||
}
|
||||
|
||||
void k_queue_cancel_wait(struct k_queue *queue)
|
||||
{
|
||||
struct k_thread *first_pending_thread;
|
||||
unsigned int key;
|
||||
|
||||
key = irq_lock();
|
||||
|
||||
first_pending_thread = _unpend_first_thread(&queue->wait_q);
|
||||
|
||||
if (first_pending_thread) {
|
||||
prepare_thread_to_run(first_pending_thread, NULL);
|
||||
if (!_is_in_isr() && _must_switch_threads()) {
|
||||
(void)_Swap(key);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (handle_poll_event(queue)) {
|
||||
(void)_Swap(key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
void k_queue_insert(struct k_queue *queue, void *prev, void *data)
|
||||
{
|
||||
struct k_thread *first_pending_thread;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
include $(ZEPHYR_BASE)/tests/Makefile.test
|
||||
|
||||
obj-y = main.o test_fifo_contexts.o test_fifo_fail.o test_fifo_loop.o
|
||||
obj-y = main.o test_fifo_contexts.o test_fifo_fail.o test_fifo_loop.o \
|
||||
test_fifo_cancel.o
|
||||
|
|
|
@ -17,6 +17,7 @@ extern void test_fifo_thread2isr(void);
|
|||
extern void test_fifo_isr2thread(void);
|
||||
extern void test_fifo_get_fail(void);
|
||||
extern void test_fifo_loop(void);
|
||||
extern void test_fifo_cancel_wait(void);
|
||||
extern void test_fifo_is_empty_thread(void);
|
||||
extern void test_fifo_is_empty_isr(void);
|
||||
|
||||
|
@ -29,6 +30,7 @@ void test_main(void *p1, void *p2, void *p3)
|
|||
ztest_unit_test(test_fifo_isr2thread),
|
||||
ztest_unit_test(test_fifo_get_fail),
|
||||
ztest_unit_test(test_fifo_loop),
|
||||
ztest_unit_test(test_fifo_cancel_wait),
|
||||
ztest_unit_test(test_fifo_is_empty_thread),
|
||||
ztest_unit_test(test_fifo_is_empty_isr));
|
||||
ztest_run_test_suite(test_fifo_api);
|
||||
|
|
56
tests/kernel/fifo/test_fifo_api/src/test_fifo_cancel.c
Normal file
56
tests/kernel/fifo/test_fifo_api/src/test_fifo_cancel.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Linaro Limited
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "test_fifo.h"
|
||||
|
||||
#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE)
|
||||
#define LIST_LEN 2
|
||||
/**TESTPOINT: init via K_FIFO_DEFINE*/
|
||||
K_FIFO_DEFINE(kfifo_c);
|
||||
|
||||
struct k_fifo fifo_c;
|
||||
|
||||
static char __noinit __stack tstack[STACK_SIZE];
|
||||
|
||||
static void t_cancel_wait_entry(void *p1, void *p2, void *p3)
|
||||
{
|
||||
k_sleep(50);
|
||||
k_fifo_cancel_wait((struct k_fifo *)p1);
|
||||
}
|
||||
|
||||
static void tfifo_thread_thread(struct k_fifo *pfifo)
|
||||
{
|
||||
k_tid_t tid = k_thread_spawn(tstack, STACK_SIZE,
|
||||
t_cancel_wait_entry, pfifo, NULL, NULL,
|
||||
K_PRIO_PREEMPT(0), 0, 0);
|
||||
u32_t start_t = k_uptime_get_32();
|
||||
void *ret = k_fifo_get(pfifo, 500);
|
||||
u32_t dur = k_uptime_get_32() - start_t;
|
||||
/* While we observed the side effect of the last statement
|
||||
* ( call to k_fifo_cancel_wait) of the thread, it's not fact
|
||||
* that it returned, within the thread. Then it may happen
|
||||
* that the test runner below will try to create another
|
||||
* thread in the same stack space, then 1st thread returns
|
||||
* from the call, leading to crash.
|
||||
*/
|
||||
k_thread_abort(tid);
|
||||
zassert_is_null(ret,
|
||||
"k_fifo_get didn't get 'timeout expired' status");
|
||||
/* 61 includes fuzz factor */
|
||||
zassert_true(dur < 61,
|
||||
"k_fifo_get didn't get cancelled in expected timeframe");
|
||||
}
|
||||
|
||||
/*test cases*/
|
||||
void test_fifo_cancel_wait(void)
|
||||
{
|
||||
/**TESTPOINT: init via k_fifo_init*/
|
||||
k_fifo_init(&fifo_c);
|
||||
tfifo_thread_thread(&fifo_c);
|
||||
|
||||
/**TESTPOINT: test K_FIFO_DEFINEed fifo*/
|
||||
tfifo_thread_thread(&kfifo_c);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue