Skip to content

Commit

Permalink
kernel: queue, fifo: Add cancel_wait operation.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
pfalcon committed May 1, 2017
1 parent e588ca0 commit 28a2e89
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 1 deletion.
30 changes: 30 additions & 0 deletions include/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.
*
Expand Down
20 changes: 20 additions & 0 deletions kernel/queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,26 @@ 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;
}
}

irq_unlock(key);
}

void k_queue_insert(struct k_queue *queue, void *prev, void *data)
{
struct k_thread *first_pending_thread;
Expand Down
3 changes: 2 additions & 1 deletion tests/kernel/fifo/test_fifo_api/src/Makefile
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions tests/kernel/fifo/test_fifo_api/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
Expand Down
56 changes: 56 additions & 0 deletions tests/kernel/fifo/test_fifo_api/src/test_fifo_cancel.c
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 28a2e89

Please sign in to comment.