From 3f5070767227989493cdbd8eaacddcbe451dfe58 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 25 Apr 2017 17:54:31 +0300 Subject: [PATCH] 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 --- include/kernel.h | 30 ++++++++++ kernel/queue.c | 25 +++++++++ tests/kernel/fifo/test_fifo_api/src/Makefile | 3 +- tests/kernel/fifo/test_fifo_api/src/main.c | 2 + .../fifo/test_fifo_api/src/test_fifo_cancel.c | 56 +++++++++++++++++++ 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 tests/kernel/fifo/test_fifo_api/src/test_fifo_cancel.c diff --git a/include/kernel.h b/include/kernel.h index eb864e76019..51e60444351 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -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. * diff --git a/kernel/queue.c b/kernel/queue.c index ff024adc911..c817b338d1f 100644 --- a/kernel/queue.c +++ b/kernel/queue.c @@ -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; diff --git a/tests/kernel/fifo/test_fifo_api/src/Makefile b/tests/kernel/fifo/test_fifo_api/src/Makefile index 93b509eae07..4630281b45b 100644 --- a/tests/kernel/fifo/test_fifo_api/src/Makefile +++ b/tests/kernel/fifo/test_fifo_api/src/Makefile @@ -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 diff --git a/tests/kernel/fifo/test_fifo_api/src/main.c b/tests/kernel/fifo/test_fifo_api/src/main.c index 55c091c91e4..e90cea80d66 100644 --- a/tests/kernel/fifo/test_fifo_api/src/main.c +++ b/tests/kernel/fifo/test_fifo_api/src/main.c @@ -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); diff --git a/tests/kernel/fifo/test_fifo_api/src/test_fifo_cancel.c b/tests/kernel/fifo/test_fifo_api/src/test_fifo_cancel.c new file mode 100644 index 00000000000..a880da4fe24 --- /dev/null +++ b/tests/kernel/fifo/test_fifo_api/src/test_fifo_cancel.c @@ -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); +}