From fb01fed39861175ed01d11557f3018d6c2d947a6 Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Mon, 16 Mar 2020 22:24:55 +0100 Subject: [PATCH 01/17] esp:feature added channel component --- components/channel/CMakeLists.txt | 8 + components/channel/channel.c | 80 ++++ components/channel/include/channel.h | 203 +++++++++++ components/linux/CMakeLists.txt | 6 + components/linux/include/list.h | 521 +++++++++++++++++++++++++++ 5 files changed, 818 insertions(+) create mode 100644 components/channel/CMakeLists.txt create mode 100644 components/channel/channel.c create mode 100644 components/channel/include/channel.h create mode 100644 components/linux/CMakeLists.txt create mode 100644 components/linux/include/list.h diff --git a/components/channel/CMakeLists.txt b/components/channel/CMakeLists.txt new file mode 100644 index 0000000..c8f03d5 --- /dev/null +++ b/components/channel/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register( + SRCS + channel.c + INCLUDE_DIRS + include + REQUIRES + linux +) \ No newline at end of file diff --git a/components/channel/channel.c b/components/channel/channel.c new file mode 100644 index 0000000..df638b1 --- /dev/null +++ b/components/channel/channel.c @@ -0,0 +1,80 @@ +#include "channel.h" + +#include +#include + + +// root channel of the unique channel list +static CHANNEL(root, "", NULL, 0, NULL); + +void +channel_register +(Channel *ch) +{ + Channel *curr; + list_for_each_entry(curr, &root.unique, unique) { + if (strcmp(ch->identifier, curr->identifier) == 0) { + list_add(&ch->same, &curr->same); + return; + } + } + + list_add(&ch->unique, &root.unique); +} + +BaseType_t +channel_broadcast +(Channel * const ch, Channel ** pos, const void * const data, const TickType_t timeout) +{ + Channel *curr = *pos; + list_for_each_entry_continue(curr, &ch->same, same) { + if (curr->callback) { + BaseType_t status = curr->callback(curr->ctx, data, timeout, curr->flags); + if (!status) { + *pos = curr; + return status; + }; + } + } + return pdPASS; +} + +BaseType_t +channel_send +(Channel * const ch, const void * const data, const TickType_t timeout, const BaseType_t flags) +{ + if (!ch->callback) { + return pdPASS; + } + return ch->callback(ch->ctx, data, timeout, ch->flags); +} + +void +channel_unregister +(Channel *ch) +{ + int unique = ! list_empty(&ch->unique); + int same = ! list_empty(&ch->same); + + //channel is not registered + if (!unique && !same) { + return; + } + //channel is not in the unique chain so just delete it + if (!unique && same) { + list_del(&ch->same); + return; + } + //channel is in the unique list, but single element so also just delete it + if (unique && !same) { + list_del(&ch->unique); + return; + } + //channel is in unique list and there are others in same list, so replace channel + if (unique && same) { + list_add(&ch->unique, &list_entry(ch->same.next, struct channel, same)->unique); + list_del(&ch->unique); + list_del(&ch->same); + return; + } +} diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h new file mode 100644 index 0000000..c824775 --- /dev/null +++ b/components/channel/include/channel.h @@ -0,0 +1,203 @@ +// Channels are objects which enable iteration over other Channel objects with +// the same name/identifier. +// This implementation resembles a map, mapping identifiers to lists. +// Channels are implemented as an item of two separate lists. +// The "unique" list holds items with different identifiers, +// the "same" list holds items that share the same identifier. +// Each list item itself is a valid list containing only itself; +// multiple items can be combined to a larger list. +// +// This implementation constructs a single valid "unique" list, +// starting with a globally defined root node. All "same" lists of all items +// are considered valid. +// +// Adding to this structure implies iterating the "unique" list, until either +// the same identifier is found (and the new item is added to the "same" list +// of the found item) or the end of the "unique" list reached (and it's +// appended as new "unique" list item). +// +// Each added Channel object is now part of a "same" list which can be directly +// iterated to find other items. +// +// exemplary structure: +// +// root node---. O O +// v | | +// unique identifier list-> O--O--O--O--O +// | | +// O O +// ^ +// same identifier list---' +// +// Each Channel object contains a context pointer, a callback function and +// an additional flags field. A broadcast function is provided to iterate +// through all "same" list items of a Channel object. The callback is issued +// with a data pointer and a timeout value by the caller and the callee +// specific context and flags. This interface is intended to enable dynamic +// management of FreeRTOS queues, where a queue can be registered as a context +// of a Channel and therefore fed by the Channels callback. + +#ifndef __CHANNEL__ +#define __CHANNEL__ + +#include +#include + +#include "list.h" + +typedef BaseType_t (*Channel_callback) (void *ctx, const void * const data, TickType_t timeout, const BaseType_t flags); + +typedef struct channel { + const char *identifier; + struct list_head same; + struct list_head unique; + void *ctx; + BaseType_t flags; + Channel_callback callback; +} Channel; + +#ifndef STR +#define STR(X) _STR(X) +#define _STR(X) #X +#endif + +/** + * CHANNEL_INIT - Macro to provide initialization for channel object + */ +#define CHANNEL_INIT(name, identifier, ctx, flags, callback) { "" identifier "", LIST_HEAD_INIT(name.same), LIST_HEAD_INIT(name.unique), ctx, flags, callback } + +/** + * CHANNEL - Macro to create and initialize channel object + */ +#define CHANNEL(name, identifier, ctx, flags, callback) Channel name = CHANNEL_INIT(name, "" identifier "", ctx, flags, callback) + +/* + * OUTPUT - Macro to create and initialize channel object used by producer + */ +#define OUTPUT(name) CHANNEL(name, STR(name), NULL, 0, NULL) + +/* + * INPUT - Macro to create and initialize channel object used by consumer + */ +#define INPUT(name, ctx, flags, callback) CHANNEL(name, STR(name), ctx, flags, callback) + +/* + * INPUT_QUEUE - Macro to create and initialize channel object used by consumer for queues + */ +#define INPUT_QUEUE(name, queue) CHANNEL(name, STR(name), queue, queueSEND_TO_BACK, &xQueueGenericSend) + +/** + * channel_init - helper function to initialize channel object + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + * @ctx: pointer to context of the callback + * @flags: additional flags for the callback to modify behaviour + * @callback: function to call on new data + */ +static +void +inline __attribute__((always_inline)) +channel_init +(Channel * const ch, const char * const identifier, void * const ctx, const BaseType_t flags, Channel_callback callback) { + ch->identifier = identifier; + INIT_LIST_HEAD(&ch->unique); + INIT_LIST_HEAD(&ch->same); + ch->ctx = ctx; + ch->flags = flags; + ch->callback = callback; +} + +/** + * channel_setContext - helper function to change the context the callback acts on + * @ch: pointer to channel object to modify + * @ctx: pointer to new context + */ +static +void +inline __attribute__((always_inline)) +channel_setContext +(Channel * const ch, void * const ctx) { + ch->ctx = ctx; +} + +/** + * channel_setCallback - helper function to change the callback + * @ch: pointer to channel object to modify + * @callback: function to register as callback + */ +static +void +inline __attribute__((always_inline)) +channel_setCallback +(Channel * const ch, const Channel_callback callback) { + ch->callback = callback; +} + +/** + * channel_init_broadcast - helper function to initialize position element in broadcast + * @ch: pointer to channel object + * @ctx: pointer to position element + */ +static +void +inline __attribute__((always_inline)) +channel_init_broadcast +(Channel * const ch, Channel ** pos) +{ + *pos = ch; +} + +/** + * channel_register - register a channel + * @ch: channel to register + * + * This function adds channel to the message passing structure. + * It doesn't make a difference whether this is a message + * provider or consumer. The difference is only made by the channels + * callback implementation. + */ +void +channel_register +(Channel * const ch); + +/** + * unregister_channel - removes a channel input or output from the message structure + * @ch: channel element to remove + */ +void +channel_deregister +(Channel * const ch); + +/** + * channel_broadcast - send a message to all who registered on this channels identifier + * @ch: the channel structure that holds informations for this channel + * @tmp: iterator pointer, needs to be initialized by channel_init_broadcast or reused to continue broadcast + * @data: the pointer to the message + * @timeout: how many time-slices each callback may block before aborting + * @return: returns pdPass (=1) if broadcast to was successful, if non-one value is returned, iteration was stopped, because a callback failed + * + * Iterates over all elements in the same list, that registered on this channels identifier + * and calls the callback function on it. May return with error_code before all elements were notified, allowing for custom error handling + * Example for skipping failing callbacks: + * Channel **pos; + * channel_init_broadcast(ch,pos); + * while(!channel_broadcast(ch,pos,data,timeout)); + */ +BaseType_t +channel_broadcast +(Channel * const ch, Channel ** tmp, const void * const data, const TickType_t timeout); + +/** + * channel_send - sends a message to a single channel object + * @ch: the channel structure that holds informations for this channel + * @data: the pointer to the message + * @timeout: how many time-slices the callback may block before aborting + * @return: returns pdPass (=1) if send was successful, if non-one value is returned, callback failed + * + * Calls the callback function on the channel. + */ +BaseType_t +channel_send +(Channel * const ch, const void * const data, const TickType_t timeout, const BaseType_t flags); + +#endif diff --git a/components/linux/CMakeLists.txt b/components/linux/CMakeLists.txt new file mode 100644 index 0000000..351ce70 --- /dev/null +++ b/components/linux/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register( +# SRCS + INCLUDE_DIRS + include +# REQUIRES +) \ No newline at end of file diff --git a/components/linux/include/list.h b/components/linux/include/list.h new file mode 100644 index 0000000..e51f8fa --- /dev/null +++ b/components/linux/include/list.h @@ -0,0 +1,521 @@ +/** + * http://www.mcs.anl.gov/~kazutomo/list/list.h + * + * I grub it from linux kernel source code and fix it for user space + * program. Of course, this is a GPL licensed header file. + * + * Here is a recipe to cook list.h for user space program + * + * 1. copy list.h from linux/include/list.h + * 2. remove + * - #ifdef __KERNE__ and its #endif + * - all #include line + * - prefetch() and rcu related functions + * 3. add macro offsetof() and container_of + * + * - kazutomo@mcs.anl.gov + */ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include +/** + * @name from other kernel headers + */ +/*@{*/ + +/** + * Get offset of a member + */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/** + * Casts a member of a structure out to the containing structure + * @param ptr the pointer to the member. + * @param type the type of the container struct this is embedded in. + * @param member the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +/*@}*/ + + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/** + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + + + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use as a start point in + * list_for_each_entry_continue + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - iterate over list of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue - iterate over list of given type + * continuing after existing point safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against + * removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + + + + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL) + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = LIST_POISON1; + n->pprev = LIST_POISON2; +} + + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (n->pprev) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + + + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + + + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ + pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + + +#endif \ No newline at end of file From 7a076faafcbbae2685e253a3903c35ed7d06b0d8 Mon Sep 17 00:00:00 2001 From: drechsler Date: Sat, 28 Mar 2020 03:52:34 +0100 Subject: [PATCH 02/17] channel:feature refactored, added unit test & debug functions --- components/channel/channel.c | 92 ++--- components/channel/include/channel.h | 103 ++++-- components/channel/include/channel_debug.h | 161 +++++++++ components/channel/test/CMakeLists.txt | 10 + components/channel/test/channel_test.c | 398 +++++++++++++++++++++ 5 files changed, 686 insertions(+), 78 deletions(-) create mode 100644 components/channel/include/channel_debug.h create mode 100644 components/channel/test/CMakeLists.txt create mode 100644 components/channel/test/channel_test.c diff --git a/components/channel/channel.c b/components/channel/channel.c index df638b1..8698505 100644 --- a/components/channel/channel.c +++ b/components/channel/channel.c @@ -3,78 +3,90 @@ #include #include - -// root channel of the unique channel list -static CHANNEL(root, "", NULL, 0, NULL); +static CHANNEL(root_channel, "", NULL, 0, NULL); void channel_register (Channel *ch) { Channel *curr; - list_for_each_entry(curr, &root.unique, unique) { + list_for_each_entry(curr, &root_channel.unique, unique) { if (strcmp(ch->identifier, curr->identifier) == 0) { list_add(&ch->same, &curr->same); return; } } - list_add(&ch->unique, &root.unique); -} - -BaseType_t -channel_broadcast -(Channel * const ch, Channel ** pos, const void * const data, const TickType_t timeout) -{ - Channel *curr = *pos; - list_for_each_entry_continue(curr, &ch->same, same) { - if (curr->callback) { - BaseType_t status = curr->callback(curr->ctx, data, timeout, curr->flags); - if (!status) { - *pos = curr; - return status; - }; - } - } - return pdPASS; -} - -BaseType_t -channel_send -(Channel * const ch, const void * const data, const TickType_t timeout, const BaseType_t flags) -{ - if (!ch->callback) { - return pdPASS; - } - return ch->callback(ch->ctx, data, timeout, ch->flags); + list_add(&ch->unique, &root_channel.unique); } void channel_unregister (Channel *ch) { - int unique = ! list_empty(&ch->unique); - int same = ! list_empty(&ch->same); + int isUnique = ! list_empty(&ch->unique); + int isSame = ! list_empty(&ch->same); //channel is not registered - if (!unique && !same) { + if (!isUnique && !isSame) { return; } //channel is not in the unique chain so just delete it - if (!unique && same) { + if (!isUnique && isSame) { list_del(&ch->same); + INIT_LIST_HEAD(&ch->same); return; } //channel is in the unique list, but single element so also just delete it - if (unique && !same) { + if (isUnique && !isSame) { list_del(&ch->unique); + INIT_LIST_HEAD(&ch->unique); return; } //channel is in unique list and there are others in same list, so replace channel - if (unique && same) { - list_add(&ch->unique, &list_entry(ch->same.next, struct channel, same)->unique); - list_del(&ch->unique); + if (isUnique && isSame) { + Channel *nextSame = list_entry(ch->same.next, struct channel, same); + Channel *nextUniq = list_entry(ch->unique.next, struct channel, unique); list_del(&ch->same); + INIT_LIST_HEAD(&ch->same); + list_del(&ch->unique); + INIT_LIST_HEAD(&ch->unique); + list_add(&nextSame->unique, &nextUniq->unique); return; } } + +BaseType_t +channel_send +(const Channel *ch, const void *data, const TickType_t timeout) +{ + if (!ch->callback) { + return pdPASS; + } + return ch->callback(ch->ctx, data, timeout, ch->flags); +} + +BaseType_t +channel_broadcast +(Broadcast *handle) +{ + Channel *ch = handle->ch; + Channel *pos = handle->pos; + void *data = handle->data; + TickType_t timeout = handle->timeout; + list_for_each_entry_continue(pos, (&ch->same), same) { + BaseType_t status; + if (pos->callback) { + status = pos->callback(pos->ctx, + data, + timeout, + pos->flags); + if (!status) { + handle->pos = pos; + return status; + }; + } + } + handle->pos = NULL; + return channel_send(handle->ch, handle->data, handle->timeout); +} diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h index c824775..31e257a 100644 --- a/components/channel/include/channel.h +++ b/components/channel/include/channel.h @@ -56,6 +56,13 @@ typedef struct channel { Channel_callback callback; } Channel; +typedef struct broadcast { + Channel *ch; + Channel *pos; + void *data; + TickType_t timeout; +} Broadcast; + #ifndef STR #define STR(X) _STR(X) #define _STR(X) #X @@ -116,7 +123,7 @@ static void inline __attribute__((always_inline)) channel_setContext -(Channel * const ch, void * const ctx) { +(Channel * ch, void * const ctx) { ch->ctx = ctx; } @@ -129,24 +136,10 @@ static void inline __attribute__((always_inline)) channel_setCallback -(Channel * const ch, const Channel_callback callback) { +(Channel * ch, const Channel_callback callback) { ch->callback = callback; } -/** - * channel_init_broadcast - helper function to initialize position element in broadcast - * @ch: pointer to channel object - * @ctx: pointer to position element - */ -static -void -inline __attribute__((always_inline)) -channel_init_broadcast -(Channel * const ch, Channel ** pos) -{ - *pos = ch; -} - /** * channel_register - register a channel * @ch: channel to register @@ -165,27 +158,8 @@ channel_register * @ch: channel element to remove */ void -channel_deregister -(Channel * const ch); - -/** - * channel_broadcast - send a message to all who registered on this channels identifier - * @ch: the channel structure that holds informations for this channel - * @tmp: iterator pointer, needs to be initialized by channel_init_broadcast or reused to continue broadcast - * @data: the pointer to the message - * @timeout: how many time-slices each callback may block before aborting - * @return: returns pdPass (=1) if broadcast to was successful, if non-one value is returned, iteration was stopped, because a callback failed - * - * Iterates over all elements in the same list, that registered on this channels identifier - * and calls the callback function on it. May return with error_code before all elements were notified, allowing for custom error handling - * Example for skipping failing callbacks: - * Channel **pos; - * channel_init_broadcast(ch,pos); - * while(!channel_broadcast(ch,pos,data,timeout)); - */ -BaseType_t -channel_broadcast -(Channel * const ch, Channel ** tmp, const void * const data, const TickType_t timeout); +channel_unregister +(Channel *ch); /** * channel_send - sends a message to a single channel object @@ -198,6 +172,59 @@ channel_broadcast */ BaseType_t channel_send -(Channel * const ch, const void * const data, const TickType_t timeout, const BaseType_t flags); +(const Channel *ch, const void *data, const TickType_t timeout); + +/** + * channel_broadcast_init - helper function to initialize broadcast handler + * @handle: pointer to broadcast object + * @ch: pointer to channel object initiating the broadcast + * @data: pointer to data to be transferred + * @timeout: number of time-slices each receiver may block + */ +static +void +inline __attribute__((always_inline)) +channel_broadcast_init +(Broadcast *handle, const Channel * ch, const void *data, const TickType_t timeout) +{ + handle->ch = (void*) ch; + handle->pos = (void*) ch; + handle->data = (void*) data; + handle->timeout = timeout; +} + +/** + * channel_broadcast_finished - helper function to determine if broadcast finished + * @ch: pointer to channel object + * @pos: location of pointer where broadcast stopped + */ +static +bool +inline __attribute__((always_inline)) +channel_broadcast_finished +(const Broadcast *handle) +{ + return handle->pos == NULL; +} + +/** + * channel_broadcast - send a message to all who registered on this channels identifier + * @handle: the broadcast handle to act on + * @return: returns callback return value each time iteration was stopped + * + * Iterates over all elements in the same list, that registered on this channels identifier + * and calls the callback function on it. May return with error_code before all elements were notified, allowing for custom error handling + * Example for skipping failing callbacks: + * Channel ch; + * Channel *pos; + * Broadcast handle; + * channel_broadcast_init(&handle,&ch,data,timeout); + * while(!channel_broadcast_finished(&handle)) { + * channel_broadcast(&handle); + * } + */ +BaseType_t +channel_broadcast +(Broadcast *handle); #endif diff --git a/components/channel/include/channel_debug.h b/components/channel/include/channel_debug.h new file mode 100644 index 0000000..3abdcbc --- /dev/null +++ b/components/channel/include/channel_debug.h @@ -0,0 +1,161 @@ +#include "channel.h" + +#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE +#include "esp_log.h" +#include + +/** + * channel_debug_print - print channel attributes + * @prefix: prefix string in debug output + * @ch: channel object to print + */ +static +void +__attribute__((unused)) +channel_debug_print +(const char *prefix, const Channel *ch) +{ + ESP_LOGD(prefix, "@%p identifier:%s", + ch, ch->identifier); + ESP_LOGD(prefix, "@%p same.next:%p", + ch, list_entry(ch->same.next, struct channel, same)); + ESP_LOGD(prefix, "@%p same.prev:%p", + ch, list_entry(ch->same.prev, struct channel, same)); + ESP_LOGD(prefix, "@%p uniq.next:%p", + ch, list_entry(ch->unique.next, struct channel, unique)); + ESP_LOGD(prefix, "@%p uniq.prev:%p", + ch, list_entry(ch->unique.prev, struct channel, unique)); +} + +/** + * channel_debug_print_dot - print channel attributes in dot format + * @prefix: prefix string in debug output + * @ch: channel object to print + */ +static +void +__attribute__((unused)) +channel_debug_print_dot +(const char *prefix, const Channel *ch) +{ + if (strcmp("",ch->identifier)==0) { + ESP_LOGD(prefix, "x%p [label=\"%p\\n%s\", shape=box];", + ch, ch, ch->identifier); + } else { + ESP_LOGD(prefix, "x%p [label=\"%p\\n%s\"];", + ch, ch, ch->identifier); + } + ESP_LOGD(prefix, "x%p -> x%p [label=same, color=red];", + ch, list_entry(ch->same.next, struct channel, same)); + ESP_LOGD(prefix, "x%p -> x%p [label=same, color=red];", + ch, list_entry(ch->same.prev, struct channel, same)); + ESP_LOGD(prefix, "x%p -> x%p [label=unique, color=blue];", + ch, list_entry(ch->unique.next, struct channel, unique)); + ESP_LOGD(prefix, "x%p -> x%p [label=unique, color=blue];", + ch, list_entry(ch->unique.prev, struct channel, unique)); +} + +/** + * channel_debug_printAll - print all reachable channel attributes + * @prefix: prefix string in debug output + * @ch: channel object to start search from + */ +static +void +__attribute__((unused)) +channel_debug_printAll +(const char *prefix, const Channel *ch) +{ + channel_debug_print(prefix, ch); + Channel *chUniq; + list_for_each_entry(chUniq, &ch->unique, unique) { + channel_debug_print(prefix, chUniq); + Channel *currSame; + list_for_each_entry(currSame, &chUniq->same, same) { + channel_debug_print(prefix, currSame); + } + } + Channel *chSame; + list_for_each_entry(chSame, &ch->same, same) { + channel_debug_print(prefix, chSame); + Channel *currUniq; + list_for_each_entry(currUniq, &chSame->unique, unique) { + channel_debug_print(prefix, currUniq); + Channel *currSame; + list_for_each_entry(currSame, &currUniq->same, same) { + channel_debug_print(prefix, currSame); + } + } + } +} + +/** + * channel_debug_printAll_dot - print all reachable channel attributes in dot format + * @prefix: prefix string in debug output + * @ch: channel object to start search from + */ +static +void +__attribute__((unused)) +channel_debug_printAll_dot +(const char *prefix, const Channel *ch) +{ + ESP_LOGD(prefix,"digraph G {"); + ESP_LOGD(prefix,"layout=neato;"); + ESP_LOGD(prefix,"overlap=false;"); + channel_debug_print_dot(prefix, ch); + Channel *chUniq; + list_for_each_entry(chUniq, &ch->unique, unique) { + channel_debug_print_dot(prefix, chUniq); + Channel *currSame; + list_for_each_entry(currSame, &chUniq->same, same) { + channel_debug_print_dot(prefix, currSame); + } + } + Channel *chSame; + list_for_each_entry(chSame, &ch->same, same) { + channel_debug_print_dot(prefix, chSame); + Channel *currUniq; + list_for_each_entry(currUniq, &chSame->unique, unique) { + channel_debug_print_dot(prefix, currUniq); + Channel *currSame; + list_for_each_entry(currSame, &currUniq->same, same) { + channel_debug_print_dot(prefix, currSame); + } + } + } + ESP_LOGD(prefix,"}"); +} + +/** + * channel_debug_resetRoot - search global channel root and reset it + * ATTENTION: THIS ONLY RESETS THE GLOBAL ROOT CHANNEL! + * ALL OTHER CHANNELS OBJECTS WILL BE BROKEN! + * @ch: channel object to start search from + * @return: true if root channel was found, false if not + */ +static +bool +__attribute__((unused)) +channel_debug_resetRoot +(const Channel *ch) +{ + Channel *chUniq; + list_for_each_entry(chUniq, &ch->unique, unique) { + if (strcmp("",chUniq->identifier)==0) { + channel_init(chUniq, "", NULL, 0, NULL); + return true; + } + } + Channel *chSame; + list_for_each_entry(chSame, &ch->same, same) { + Channel *currUniq; + list_for_each_entry(currUniq, &chSame->unique, unique) { + if (strcmp("",currUniq->identifier)==0) { + channel_init(currUniq, "", NULL, 0, NULL); + return true; + } + } + } + return false; +} \ No newline at end of file diff --git a/components/channel/test/CMakeLists.txt b/components/channel/test/CMakeLists.txt new file mode 100644 index 0000000..cd1502a --- /dev/null +++ b/components/channel/test/CMakeLists.txt @@ -0,0 +1,10 @@ +idf_component_register( + SRC_DIRS + "." + INCLUDE_DIRS + "." + REQUIRES + unity + channel + linux +) \ No newline at end of file diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c new file mode 100644 index 0000000..1ece0b5 --- /dev/null +++ b/components/channel/test/channel_test.c @@ -0,0 +1,398 @@ +#include "unity.h" +#include +#include "channel.h" +#include "channel_debug.h" + +static void *callback_ctx; +static void *callback_data; +static TickType_t callback_timeout; +static BaseType_t callback_flags; + +BaseType_t +returnPdPASS +(void *ctx, + const void *data, + TickType_t timeout, + const BaseType_t flags) +{ + callback_ctx = ctx; + callback_data = (void*) data; + callback_timeout = timeout; + callback_flags = flags; + return pdPASS; +} + +BaseType_t +returnErrQUEUE_FULL +(void *ctx, + const void *data, + TickType_t timeout, + const BaseType_t flags) +{ + callback_ctx = ctx; + callback_data = (void*) data; + callback_timeout = timeout; + callback_flags = flags; + return errQUEUE_FULL; +} + +#define TEST_ASSERT_LIST_LENGTH(head, expect) \ +{ \ + uint length = 1; \ + struct list_head *pos; \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + list_for_each(pos,(head)) { \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + length++; \ + } \ + TEST_ASSERT_EQUAL_UINT((expect),length); \ +} + +#define TEST_ASSERT_LIST_OCCURRENCE(head,item,expect) \ +{ \ + uint count = 0; \ + struct list_head *pos; \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + TEST_ASSERT_NOT_NULL((item)->next); \ + TEST_ASSERT_NOT_NULL((item)->prev); \ + list_for_each(pos,(head)) { \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + if ((head) == (item)) { \ + count++; \ + } \ + } \ + TEST_ASSERT_EQUAL_UINT((expect),count); \ +} + +#define TEST_ASSERT_LIST_EMPTY(head) \ +{ \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + TEST_ASSERT_EQUAL_PTR((head)->next, (head)); \ + TEST_ASSERT_EQUAL_PTR((head)->next, (head)); \ + TEST_ASSERT_EQUAL_PTR((head)->next, (head)->prev); \ +} + +TEST_CASE("channel_init", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_init(&ch, identifier, ctx, flags, callback); + + TEST_ASSERT_NOT_NULL(ch.identifier); + TEST_ASSERT_EQUAL_STRING(identifier, ch.identifier); + + TEST_ASSERT_LIST_EMPTY(&ch.same); + TEST_ASSERT_LIST_EMPTY(&ch.unique); + + TEST_ASSERT_NOT_NULL(ch.ctx); + TEST_ASSERT_EQUAL_PTR(ch.ctx, ctx); + + TEST_ASSERT_EQUAL_INT(ch.flags, flags); + + TEST_ASSERT_NOT_NULL(ch.callback); + TEST_ASSERT_EQUAL_PTR(ch.callback, callback); +} + +TEST_CASE("channel_setContext", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx0 = (void*)0x1; + void *ctx1 = (void*)0x2; + BaseType_t flags = 0x3; + Channel_callback callback = &returnPdPASS; + channel_init(&ch, identifier, ctx0, flags, callback); + + channel_setContext(&ch, ctx1); + + TEST_ASSERT_NOT_NULL(ch.ctx); + TEST_ASSERT_EQUAL_PTR(ch.ctx, ctx1); +} + +TEST_CASE("channel_setCallback", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback0 = &returnPdPASS; + Channel_callback callback1 = &returnErrQUEUE_FULL; + channel_init(&ch, identifier, ctx, flags, callback0); + + channel_setCallback(&ch, callback1); + + TEST_ASSERT_NOT_NULL(ch.callback); + TEST_ASSERT_EQUAL_PTR(ch.callback, callback1); +} + +TEST_CASE("channel_register", "[channel]") +{ + Channel ch0, ch1, ch2, ch3, ch4, ch5; + char identifier0[] = "test_channel0"; + char identifier1[] = "test_channel1"; + char identifier2[] = "test_channel2"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_init(&ch0, identifier0, ctx, flags, callback); + channel_init(&ch1, identifier0, ctx, flags, callback); + channel_init(&ch2, identifier0, ctx, flags, callback); + channel_init(&ch3, identifier1, ctx, flags, callback); + channel_init(&ch4, identifier1, ctx, flags, callback); + channel_init(&ch5, identifier2, ctx, flags, callback); + + channel_register(&ch0); + TEST_ASSERT_LIST_EMPTY(&ch0.same); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 2); + + channel_register(&ch1); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 2); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 2); + + channel_register(&ch2); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 2); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + + channel_register(&ch3); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 3); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch3.unique, 3); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + TEST_ASSERT_LIST_EMPTY (&ch3.same); + + channel_register(&ch4); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 3); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch3.unique, 3); + TEST_ASSERT_LIST_EMPTY (&ch4.unique); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch3.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch4.same, 2); + + channel_register(&ch5); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch3.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch4.unique); + TEST_ASSERT_LIST_LENGTH(&ch5.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch3.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch4.same, 2); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + TEST_ASSERT(channel_debug_resetRoot(&ch0)); +} + +TEST_CASE("channel_unregister", "[channel]") +{ + Channel ch0, ch1, ch2, ch3, ch4, ch5; + char identifier0[] = "test_channel0"; + char identifier1[] = "test_channel1"; + char identifier2[] = "test_channel2"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_init(&ch0, identifier0, ctx, flags, callback); + channel_init(&ch1, identifier0, ctx, flags, callback); + channel_init(&ch2, identifier0, ctx, flags, callback); + channel_init(&ch3, identifier1, ctx, flags, callback); + channel_init(&ch4, identifier1, ctx, flags, callback); + channel_init(&ch5, identifier2, ctx, flags, callback); + + channel_register(&ch0); + channel_register(&ch1); + channel_register(&ch2); + channel_register(&ch3); + channel_register(&ch4); + channel_register(&ch5); + + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch3.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch4.unique); + TEST_ASSERT_LIST_LENGTH(&ch5.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch3.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch4.same, 2); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + channel_unregister(&ch3); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + //ch3.unique should be empty + TEST_ASSERT_LIST_EMPTY (&ch3.unique); + //ch4 should now be unique + TEST_ASSERT_LIST_LENGTH(&ch4.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch5.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + //ch3.same should be empty + TEST_ASSERT_LIST_EMPTY (&ch3.same); + //ch4.same should be empty + TEST_ASSERT_LIST_EMPTY (&ch4.same); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + TEST_ASSERT(channel_debug_resetRoot(&ch0)); +} + +TEST_CASE("channel_send", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_init(&ch, identifier, ctx, flags, callback); + + callback_ctx = NULL; + callback_data = NULL; + callback_timeout = 0; + callback_flags = 0; + + void *data = (void*)0x3; + TickType_t timeout = 0x4; + channel_send(&ch, data, timeout); + + TEST_ASSERT_EQUAL_PTR(callback_ctx, ctx); + TEST_ASSERT_EQUAL_PTR(callback_data, data); + TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout); + TEST_ASSERT_EQUAL_UINT(callback_flags, flags); +} + +TEST_CASE("channel_broadcast_init", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*) 0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_init(&ch, identifier, ctx, flags, callback); + + Broadcast handle; + void *data = (void*) 0x3; + TickType_t timeout = 0x4; + channel_broadcast_init(&handle, &ch, data, timeout); + + TEST_ASSERT_EQUAL_PTR(handle.ch,&ch); + TEST_ASSERT_EQUAL_PTR(handle.pos,&ch); + TEST_ASSERT_EQUAL_PTR(handle.data,data); + TEST_ASSERT_EQUAL_UINT(handle.timeout,timeout); +} + +TEST_CASE("channel_broadcast_finished", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*) 0x1; + BaseType_t flags = 0x2; + Channel_callback callback = NULL; + channel_init(&ch, identifier, ctx, flags, callback); + + Broadcast handle; + void *data = (void*) 0x3; + TickType_t timeout = 0x4; + channel_broadcast_init(&handle, &ch, data, timeout); + + TEST_ASSERT_FALSE(channel_broadcast_finished(&handle)); + channel_broadcast(&handle); + TEST_ASSERT_TRUE(channel_broadcast_finished(&handle)); +} + +TEST_CASE("channel_broadcast", "[channel]") +{ + Channel ch0, ch1, ch2; + char identifier0[] = "test_channel0"; + char identifier1[] = "test_channel1"; + void *ctx0 = (void*)0x1; + void *ctx1 = (void*)0x2; + void *ctx2 = (void*)0x3; + BaseType_t flags0 = 0x4; + BaseType_t flags1 = 0x5; + BaseType_t flags2 = 0x6; + channel_init(&ch0, identifier0, ctx0, flags0, &returnErrQUEUE_FULL); + channel_init(&ch1, identifier1, ctx1, flags1, &returnPdPASS); + channel_init(&ch2, identifier1, ctx2, flags2, &returnErrQUEUE_FULL); + + channel_register(&ch0); + channel_register(&ch1); + channel_register(&ch2); + + callback_ctx = NULL; + callback_data = NULL; + callback_timeout = 0; + callback_flags = 0; + + void *data0 = (void*)0x7; + void *data1 = (void*)0x8; + void *data2 = (void*)0x9; + + TickType_t timeout0 = 0xA; + TickType_t timeout1 = 0xB; + TickType_t timeout2 = 0xC; + + BaseType_t result; + Broadcast handle; + channel_broadcast_init(&handle, &ch0, data0, timeout0); + result = channel_broadcast(&handle); + + TEST_ASSERT(channel_broadcast_finished(&handle)); + TEST_ASSERT_EQUAL_INT(result, errQUEUE_FULL); + TEST_ASSERT_EQUAL_PTR(handle.pos, NULL); + TEST_ASSERT_EQUAL_PTR(callback_ctx, ctx0); + TEST_ASSERT_EQUAL_PTR(callback_data, data0); + TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout0); + TEST_ASSERT_EQUAL_UINT(callback_flags, flags0); + + channel_broadcast_init(&handle, &ch1, data2, timeout2); + result = channel_broadcast(&handle); + + TEST_ASSERT_FALSE(channel_broadcast_finished(&handle)); + TEST_ASSERT_EQUAL_INT(result, errQUEUE_FULL); + TEST_ASSERT_EQUAL_PTR(handle.pos, &ch2); + TEST_ASSERT_EQUAL_PTR(callback_ctx, ctx2); + TEST_ASSERT_EQUAL_PTR(callback_data, data2); + TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout2); + TEST_ASSERT_EQUAL_UINT(callback_flags, flags2); + + handle.data = data1; + handle.timeout = timeout1; + result = channel_broadcast(&handle); + + TEST_ASSERT(channel_broadcast_finished(&handle)); + TEST_ASSERT_EQUAL_INT(result, pdPASS); + TEST_ASSERT_EQUAL_PTR(handle.pos, NULL); + TEST_ASSERT_EQUAL_PTR(callback_ctx, ctx1); + TEST_ASSERT_EQUAL_PTR(callback_data, data1); + TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout1); + TEST_ASSERT_EQUAL_UINT(callback_flags, flags1); + + TEST_ASSERT(channel_debug_resetRoot(&ch0)); +} \ No newline at end of file From 3175bba0ff5cb3db42538b962d800d5b45ad5ab5 Mon Sep 17 00:00:00 2001 From: drechsler Date: Sun, 29 Mar 2020 01:59:28 +0100 Subject: [PATCH 03/17] channel:refactored added Kconfig, changed interface, refactored internals --- components/channel/CMakeLists.txt | 4 +- components/channel/Kconfig | 38 ++++++ components/channel/README.md | 105 ++++++++++++++ components/channel/include/channel.h | 117 +++++++--------- components/channel/include/channel_debug.h | 38 +----- components/channel/include/channel_internal.h | 58 ++++++++ components/channel/{ => src}/channel.c | 57 +++++++- components/channel/test/channel_test.c | 128 ++++++++++++------ 8 files changed, 398 insertions(+), 147 deletions(-) create mode 100644 components/channel/Kconfig create mode 100644 components/channel/README.md create mode 100644 components/channel/include/channel_internal.h rename components/channel/{ => src}/channel.c (64%) diff --git a/components/channel/CMakeLists.txt b/components/channel/CMakeLists.txt index c8f03d5..585b309 100644 --- a/components/channel/CMakeLists.txt +++ b/components/channel/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register( - SRCS - channel.c + SRC_DIRS + src INCLUDE_DIRS include REQUIRES diff --git a/components/channel/Kconfig b/components/channel/Kconfig new file mode 100644 index 0000000..21a0cbd --- /dev/null +++ b/components/channel/Kconfig @@ -0,0 +1,38 @@ +menu "Channel" + menuconfig CHANNEL_TEST_ENABLE + bool "Enable unit test" + default y + select CHANNEL_INTERNAL_EXPORT_SYMBOLS + if CHANNEL_TEST_ENABLE + config CHANNEL_TEST_ENABLE_INIT + bool "init" + default y + config CHANNEL_TEST_ENABLE_SETCONTEXT + bool "setContext" + default y + config CHANNEL_TEST_ENABLE_SETCALLBACK + bool "setCallback" + default y + config CHANNEL_TEST_ENABLE_REGISTER + bool "register" + default y + config CHANNEL_TEST_ENABLE_UNREGISTER + bool "unregister" + default y + config CHANNEL_TEST_ENABLE_SEND + bool "send" + default y + config CHANNEL_TEST_ENABLE_BROADCAST_INIT + bool "broadcast_init" + default y + config CHANNEL_TEST_ENABLE_BROADCAST_FINISHED + bool "broadcast_finished" + default y + config CHANNEL_TEST_ENABLE_RESETROOT + bool "resetRoot" + default y + endif + config CHANNEL_INTERNAL_EXPORT_SYMBOLS + bool "Export internal functions" + default n +endmenu \ No newline at end of file diff --git a/components/channel/README.md b/components/channel/README.md new file mode 100644 index 0000000..e88881c --- /dev/null +++ b/components/channel/README.md @@ -0,0 +1,105 @@ +# Channel component + +Channels are objects which enable iteration over other Channel objects with the same name/identifier. + +Each Channel object contains a context pointer, a callback function and +an additional flags field. +The callback is issued with a data pointer and a timeout value by the caller and the callee specific context and flags. +This interface is intended to enable dynamic management of FreeRTOS queues, +where a queue can be registered as a context +of a Channel and therefore fed by the Channels callback. + +## Concept + +This implementation resembles a map, mapping identifiers to lists. +Channels are implemented as an item of two separate lists. +The "unique" list holds items with different identifiers, +the "same" list holds items that share the same identifier. +Each list item itself is a valid list containing only itself; +multiple items can be combined to a larger list. + +This implementation constructs a single valid "unique" list, +starting with a globally defined root node. +All "same" lists of all items are considered valid. +Adding to this structure implies iterating the "unique" list, +until either the same identifier is found (and the new item is added to the "same" list of the found item) or the end of the "unique" list reached (and it's appended as new "unique" list item). +Each added Channel object is now part of a "same" list which can be directly iterated to find other items. + +exemplary structure: +``` + root node---. O O + v | | +unique identifier list-> O--O--O--O--O + | | + O O + ^ + same identifier list---' +``` +## Examples + + +### Simple Input channel +```c +#include "channel.h" + +QueueHandle_t queue; +Channel_in input; + +void app_main() { + queue = xQueueCreateStatic(/*...*/); + channel_init_input(&input, "input", queue); +} +``` + +### Custom Input channel +```c +#include "channel.h" + +static void *latest_data; + +BaseType_t callback (void *ctx, + const void *data, + const TickType_t timeout, + const BaseType_t flags) { + latest_data = data; +} + +Channel_in input; + +void app_main() { + channel_init(&input, "input", data, 0, &callback); +} +``` + +### Output channel +```c +#include "channel.h" + +Channel_out output; + +void app_main() { + channel_init_output(&output, "output"); +} +``` + +### Simple broadcast without error handling +```c +#include "channel.h" + +Channel_out output; +char data[] = "test"; + +void app_main() { + channel_init_output(&output, "output"); + + Channel_broadcast broadcast; + channel_broadcast_init(&broadcast, &output, data, 0); + while(!channel_broadcast_finished(&broadcast)) { + channel_broadcast(&broadcast); + } +} +``` + + + + diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h index 31e257a..53d333b 100644 --- a/components/channel/include/channel.h +++ b/components/channel/include/channel.h @@ -45,23 +45,27 @@ #include "list.h" -typedef BaseType_t (*Channel_callback) (void *ctx, const void * const data, TickType_t timeout, const BaseType_t flags); +typedef BaseType_t (*Channel_callback) (void *ctx, const void *data, const TickType_t timeout, const BaseType_t flags); -typedef struct channel { +struct channel { const char *identifier; struct list_head same; struct list_head unique; void *ctx; BaseType_t flags; Channel_callback callback; -} Channel; +}; + +typedef struct channel Channel; +typedef struct channel Channel_in; +typedef struct channel Channel_out; typedef struct broadcast { Channel *ch; Channel *pos; void *data; TickType_t timeout; -} Broadcast; +} Channel_broadcast; #ifndef STR #define STR(X) _STR(X) @@ -69,49 +73,44 @@ typedef struct broadcast { #endif /** - * CHANNEL_INIT - Macro to provide initialization for channel object - */ -#define CHANNEL_INIT(name, identifier, ctx, flags, callback) { "" identifier "", LIST_HEAD_INIT(name.same), LIST_HEAD_INIT(name.unique), ctx, flags, callback } + * channel_init - initialize & register channel object + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + * @ctx: pointer to context of the callback + * @flags: additional flags for the callback to modify behaviour + * @callback: function to call on new data + */ +void +channel_init +(Channel *ch, const char *identifier, void *ctx, const BaseType_t flags, Channel_callback callback); /** - * CHANNEL - Macro to create and initialize channel object - */ -#define CHANNEL(name, identifier, ctx, flags, callback) Channel name = CHANNEL_INIT(name, "" identifier "", ctx, flags, callback) - -/* - * OUTPUT - Macro to create and initialize channel object used by producer - */ -#define OUTPUT(name) CHANNEL(name, STR(name), NULL, 0, NULL) - -/* - * INPUT - Macro to create and initialize channel object used by consumer - */ -#define INPUT(name, ctx, flags, callback) CHANNEL(name, STR(name), ctx, flags, callback) - -/* - * INPUT_QUEUE - Macro to create and initialize channel object used by consumer for queues + * channel_init_input - helper function to initialize channel input object + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + * @queue: queue handle to send input to */ -#define INPUT_QUEUE(name, queue) CHANNEL(name, STR(name), queue, queueSEND_TO_BACK, &xQueueGenericSend) +static +void +inline __attribute__((always_inline)) +channel_init_input +(Channel_in *ch, const char *identifier, QueueHandle_t queue) +{ + return channel_init(ch, identifier, queue, queueSEND_TO_BACK, &xQueueGenericSend); +} /** - * channel_init - helper function to initialize channel object + * channel_init_output - helper function to initialize channel output object * @ch: pointer to channel object to initialize * @identifier: identifier string of the channel - * @ctx: pointer to context of the callback - * @flags: additional flags for the callback to modify behaviour - * @callback: function to call on new data - */ + */ static void inline __attribute__((always_inline)) -channel_init -(Channel * const ch, const char * const identifier, void * const ctx, const BaseType_t flags, Channel_callback callback) { - ch->identifier = identifier; - INIT_LIST_HEAD(&ch->unique); - INIT_LIST_HEAD(&ch->same); - ch->ctx = ctx; - ch->flags = flags; - ch->callback = callback; +channel_init_output +(Channel_out *ch, const char *identifier) +{ + return channel_init(ch, identifier, NULL, 0, NULL); } /** @@ -123,7 +122,8 @@ static void inline __attribute__((always_inline)) channel_setContext -(Channel * ch, void * const ctx) { +(Channel *ch, void *ctx) +{ ch->ctx = ctx; } @@ -136,29 +136,17 @@ static void inline __attribute__((always_inline)) channel_setCallback -(Channel * ch, const Channel_callback callback) { +(Channel *ch, const Channel_callback callback) +{ ch->callback = callback; } /** - * channel_register - register a channel - * @ch: channel to register - * - * This function adds channel to the message passing structure. - * It doesn't make a difference whether this is a message - * provider or consumer. The difference is only made by the channels - * callback implementation. - */ -void -channel_register -(Channel * const ch); - -/** - * unregister_channel - removes a channel input or output from the message structure - * @ch: channel element to remove + * reset_channel - removes a channel input or output from the message structure + * @ch: channel element to reset */ void -channel_unregister +channel_reset (Channel *ch); /** @@ -172,7 +160,7 @@ channel_unregister */ BaseType_t channel_send -(const Channel *ch, const void *data, const TickType_t timeout); +(const Channel_in *ch, const void *data, const TickType_t timeout); /** * channel_broadcast_init - helper function to initialize broadcast handler @@ -185,7 +173,7 @@ static void inline __attribute__((always_inline)) channel_broadcast_init -(Broadcast *handle, const Channel * ch, const void *data, const TickType_t timeout) +(Channel_broadcast *handle, const Channel_out *ch, const void *data, const TickType_t timeout) { handle->ch = (void*) ch; handle->pos = (void*) ch; @@ -202,7 +190,7 @@ static bool inline __attribute__((always_inline)) channel_broadcast_finished -(const Broadcast *handle) +(const Channel_broadcast *handle) { return handle->pos == NULL; } @@ -213,18 +201,11 @@ channel_broadcast_finished * @return: returns callback return value each time iteration was stopped * * Iterates over all elements in the same list, that registered on this channels identifier - * and calls the callback function on it. May return with error_code before all elements were notified, allowing for custom error handling - * Example for skipping failing callbacks: - * Channel ch; - * Channel *pos; - * Broadcast handle; - * channel_broadcast_init(&handle,&ch,data,timeout); - * while(!channel_broadcast_finished(&handle)) { - * channel_broadcast(&handle); - * } + * and calls the callback function on it. May return with error_code before all elements were notified, + * allowing for custom error handling. */ BaseType_t channel_broadcast -(Broadcast *handle); +(Channel_broadcast *handle); #endif diff --git a/components/channel/include/channel_debug.h b/components/channel/include/channel_debug.h index 3abdcbc..f00b3b0 100644 --- a/components/channel/include/channel_debug.h +++ b/components/channel/include/channel_debug.h @@ -1,3 +1,6 @@ +#ifndef CHANNEL_DEBUG_H_ +#define CHANNEL_DEBUG_H_ + #include "channel.h" #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE @@ -38,7 +41,7 @@ __attribute__((unused)) channel_debug_print_dot (const char *prefix, const Channel *ch) { - if (strcmp("",ch->identifier)==0) { + if (strcmp("",ch->identifier) == 0) { ESP_LOGD(prefix, "x%p [label=\"%p\\n%s\", shape=box];", ch, ch, ch->identifier); } else { @@ -127,35 +130,4 @@ channel_debug_printAll_dot ESP_LOGD(prefix,"}"); } -/** - * channel_debug_resetRoot - search global channel root and reset it - * ATTENTION: THIS ONLY RESETS THE GLOBAL ROOT CHANNEL! - * ALL OTHER CHANNELS OBJECTS WILL BE BROKEN! - * @ch: channel object to start search from - * @return: true if root channel was found, false if not - */ -static -bool -__attribute__((unused)) -channel_debug_resetRoot -(const Channel *ch) -{ - Channel *chUniq; - list_for_each_entry(chUniq, &ch->unique, unique) { - if (strcmp("",chUniq->identifier)==0) { - channel_init(chUniq, "", NULL, 0, NULL); - return true; - } - } - Channel *chSame; - list_for_each_entry(chSame, &ch->same, same) { - Channel *currUniq; - list_for_each_entry(currUniq, &chSame->unique, unique) { - if (strcmp("",currUniq->identifier)==0) { - channel_init(currUniq, "", NULL, 0, NULL); - return true; - } - } - } - return false; -} \ No newline at end of file +#endif diff --git a/components/channel/include/channel_internal.h b/components/channel/include/channel_internal.h new file mode 100644 index 0000000..360d8d7 --- /dev/null +++ b/components/channel/include/channel_internal.h @@ -0,0 +1,58 @@ +#ifndef CHANNEL_INTERNAL_H_ +#define CHANNEL_INTERNAL_H_ + +#include "channel.h" + +#ifndef CONFIG_CHANNEL_INTERNAL_EXPORT_SYMBOLS +#define EXPORT static +#else +#define EXPORT +#endif + +/** + * channel_internal_init - initialize channel object + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + * @ctx: pointer to context of the callback + * @flags: additional flags for the callback to modify behaviour + * @callback: function to call on new data + */ +EXPORT +void +channel_internal_init +(Channel *ch, const char *identifier, void *ctx, const BaseType_t flags, Channel_callback callback); + +/** + * channel_internal_register - register a channel + * @ch: channel to register + * + * This function adds channel to the message passing structure. + * It doesn't make a difference whether this is a message + * provider or consumer. The difference is only made by the channels + * callback implementation. + */ +EXPORT +void +channel_internal_register +(Channel *ch); + +/** + * channel_debug_unregister - removes a channel input or output from the message structure + * @ch: channel element to unregister + */ +EXPORT +void +channel_internal_unregister +(Channel *ch); + +/** + * channel_internal_resetRoot - reset global root node + * ATTENTION: THIS ONLY RESETS THE GLOBAL ROOT NODE! + * ALL CHANNELS OBJECTS WILL BE BROKEN! + */ +EXPORT +void +channel_internal_resetRoot +(void); + +#endif \ No newline at end of file diff --git a/components/channel/channel.c b/components/channel/src/channel.c similarity index 64% rename from components/channel/channel.c rename to components/channel/src/channel.c index 8698505..b0c2ca7 100644 --- a/components/channel/channel.c +++ b/components/channel/src/channel.c @@ -1,12 +1,52 @@ #include "channel.h" +#include "channel_internal.h" #include #include +/** + * CHANNEL_INIT - Macro to provide initialization for channel object + */ +#define CHANNEL_INIT(name, identifier, ctx, flags, callback) { "" identifier "", LIST_HEAD_INIT(name.same), LIST_HEAD_INIT(name.unique), ctx, flags, callback } + +/** + * CHANNEL - Macro to create and initialize channel object + */ +#define CHANNEL(name, identifier, ctx, flags, callback) Channel name = CHANNEL_INIT(name, "" identifier "", ctx, flags, callback) + static CHANNEL(root_channel, "", NULL, 0, NULL); +void +channel_init +(Channel *ch, const char *identifier, void *ctx, const BaseType_t flags, Channel_callback callback) +{ + channel_internal_init(ch, identifier, ctx, flags, callback); + channel_internal_register(ch); +} + +EXPORT +void +channel_internal_init +(Channel *ch, const char *identifier, void *ctx, const BaseType_t flags, Channel_callback callback) +{ + ch->identifier = identifier; + INIT_LIST_HEAD(&ch->unique); + INIT_LIST_HEAD(&ch->same); + ch->ctx = ctx; + ch->flags = flags; + ch->callback = callback; +} + +void +channel_reset +(Channel *ch) +{ + channel_internal_unregister(ch); +} + +EXPORT void -channel_register +channel_internal_register (Channel *ch) { Channel *curr; @@ -20,8 +60,9 @@ channel_register list_add(&ch->unique, &root_channel.unique); } +EXPORT void -channel_unregister +channel_internal_unregister (Channel *ch) { int isUnique = ! list_empty(&ch->unique); @@ -68,7 +109,7 @@ channel_send BaseType_t channel_broadcast -(Broadcast *handle) +(Channel_broadcast *handle) { Channel *ch = handle->ch; Channel *pos = handle->pos; @@ -90,3 +131,13 @@ channel_broadcast handle->pos = NULL; return channel_send(handle->ch, handle->data, handle->timeout); } + +EXPORT +void +__attribute__((unused)) +channel_internal_resetRoot +(void) +{ + INIT_LIST_HEAD(&root_channel.unique); + INIT_LIST_HEAD(&root_channel.same); +} \ No newline at end of file diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c index 1ece0b5..e936bd6 100644 --- a/components/channel/test/channel_test.c +++ b/components/channel/test/channel_test.c @@ -1,7 +1,7 @@ #include "unity.h" #include #include "channel.h" -#include "channel_debug.h" +#include "channel_internal.h" static void *callback_ctx; static void *callback_data; @@ -77,6 +77,7 @@ returnErrQUEUE_FULL TEST_ASSERT_EQUAL_PTR((head)->next, (head)->prev); \ } +#ifdef CONFIG_CHANNEL_TEST_ENABLE_INIT TEST_CASE("channel_init", "[channel]") { Channel ch; @@ -84,7 +85,7 @@ TEST_CASE("channel_init", "[channel]") void *ctx = (void*)0x1; BaseType_t flags = 0x2; Channel_callback callback = &returnPdPASS; - channel_init(&ch, identifier, ctx, flags, callback); + channel_internal_init(&ch, identifier, ctx, flags, callback); TEST_ASSERT_NOT_NULL(ch.identifier); TEST_ASSERT_EQUAL_STRING(identifier, ch.identifier); @@ -100,7 +101,9 @@ TEST_CASE("channel_init", "[channel]") TEST_ASSERT_NOT_NULL(ch.callback); TEST_ASSERT_EQUAL_PTR(ch.callback, callback); } +#endif +#ifdef CONFIG_CHANNEL_TEST_ENABLE_SETCONTEXT TEST_CASE("channel_setContext", "[channel]") { Channel ch; @@ -109,14 +112,16 @@ TEST_CASE("channel_setContext", "[channel]") void *ctx1 = (void*)0x2; BaseType_t flags = 0x3; Channel_callback callback = &returnPdPASS; - channel_init(&ch, identifier, ctx0, flags, callback); + channel_internal_init(&ch, identifier, ctx0, flags, callback); channel_setContext(&ch, ctx1); TEST_ASSERT_NOT_NULL(ch.ctx); TEST_ASSERT_EQUAL_PTR(ch.ctx, ctx1); } +#endif +#ifdef CONFIG_CHANNEL_TEST_ENABLE_SETCONTEXT TEST_CASE("channel_setCallback", "[channel]") { Channel ch; @@ -125,16 +130,19 @@ TEST_CASE("channel_setCallback", "[channel]") BaseType_t flags = 0x2; Channel_callback callback0 = &returnPdPASS; Channel_callback callback1 = &returnErrQUEUE_FULL; - channel_init(&ch, identifier, ctx, flags, callback0); + channel_internal_init(&ch, identifier, ctx, flags, callback0); channel_setCallback(&ch, callback1); TEST_ASSERT_NOT_NULL(ch.callback); TEST_ASSERT_EQUAL_PTR(ch.callback, callback1); } +#endif -TEST_CASE("channel_register", "[channel]") +#ifdef CONFIG_CHANNEL_TEST_ENABLE_INTERNAL_REGISTER +TEST_CASE(INCREMENT"channel_internal_register", "[channel]") { + channel_internal_resetRoot(); Channel ch0, ch1, ch2, ch3, ch4, ch5; char identifier0[] = "test_channel0"; char identifier1[] = "test_channel1"; @@ -142,24 +150,24 @@ TEST_CASE("channel_register", "[channel]") void *ctx = (void*)0x1; BaseType_t flags = 0x2; Channel_callback callback = &returnPdPASS; - channel_init(&ch0, identifier0, ctx, flags, callback); - channel_init(&ch1, identifier0, ctx, flags, callback); - channel_init(&ch2, identifier0, ctx, flags, callback); - channel_init(&ch3, identifier1, ctx, flags, callback); - channel_init(&ch4, identifier1, ctx, flags, callback); - channel_init(&ch5, identifier2, ctx, flags, callback); - - channel_register(&ch0); + channel_internal_init(&ch0, identifier0, ctx, flags, callback); + channel_internal_init(&ch1, identifier0, ctx, flags, callback); + channel_internal_init(&ch2, identifier0, ctx, flags, callback); + channel_internal_init(&ch3, identifier1, ctx, flags, callback); + channel_internal_init(&ch4, identifier1, ctx, flags, callback); + channel_internal_init(&ch5, identifier2, ctx, flags, callback); + + channel_internal_register(&ch0); TEST_ASSERT_LIST_EMPTY(&ch0.same); TEST_ASSERT_LIST_LENGTH(&ch0.unique, 2); - channel_register(&ch1); + channel_internal_register(&ch1); TEST_ASSERT_LIST_LENGTH(&ch0.unique, 2); TEST_ASSERT_LIST_EMPTY (&ch1.unique); TEST_ASSERT_LIST_LENGTH(&ch0.same, 2); TEST_ASSERT_LIST_LENGTH(&ch1.same, 2); - channel_register(&ch2); + channel_internal_register(&ch2); TEST_ASSERT_LIST_LENGTH(&ch0.unique, 2); TEST_ASSERT_LIST_EMPTY (&ch1.unique); TEST_ASSERT_LIST_EMPTY (&ch2.unique); @@ -167,7 +175,7 @@ TEST_CASE("channel_register", "[channel]") TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); - channel_register(&ch3); + channel_internal_register(&ch3); TEST_ASSERT_LIST_LENGTH(&ch0.unique, 3); TEST_ASSERT_LIST_EMPTY (&ch1.unique); TEST_ASSERT_LIST_EMPTY (&ch2.unique); @@ -177,7 +185,7 @@ TEST_CASE("channel_register", "[channel]") TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); TEST_ASSERT_LIST_EMPTY (&ch3.same); - channel_register(&ch4); + channel_internal_register(&ch4); TEST_ASSERT_LIST_LENGTH(&ch0.unique, 3); TEST_ASSERT_LIST_EMPTY (&ch1.unique); TEST_ASSERT_LIST_EMPTY (&ch2.unique); @@ -189,7 +197,7 @@ TEST_CASE("channel_register", "[channel]") TEST_ASSERT_LIST_LENGTH(&ch3.same, 2); TEST_ASSERT_LIST_LENGTH(&ch4.same, 2); - channel_register(&ch5); + channel_internal_register(&ch5); TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); TEST_ASSERT_LIST_EMPTY (&ch1.unique); TEST_ASSERT_LIST_EMPTY (&ch2.unique); @@ -203,11 +211,14 @@ TEST_CASE("channel_register", "[channel]") TEST_ASSERT_LIST_LENGTH(&ch4.same, 2); TEST_ASSERT_LIST_EMPTY (&ch5.same); - TEST_ASSERT(channel_debug_resetRoot(&ch0)); + channel_internal_resetRoot(); } +#endif -TEST_CASE("channel_unregister", "[channel]") +#ifdef CONFIG_CHANNEL_TEST_ENABLE_INTERNAL_UNREGISTER +TEST_CASE("channel_internal_unregister", "[channel]") { + channel_internal_resetRoot(); Channel ch0, ch1, ch2, ch3, ch4, ch5; char identifier0[] = "test_channel0"; char identifier1[] = "test_channel1"; @@ -222,13 +233,6 @@ TEST_CASE("channel_unregister", "[channel]") channel_init(&ch4, identifier1, ctx, flags, callback); channel_init(&ch5, identifier2, ctx, flags, callback); - channel_register(&ch0); - channel_register(&ch1); - channel_register(&ch2); - channel_register(&ch3); - channel_register(&ch4); - channel_register(&ch5); - TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); TEST_ASSERT_LIST_EMPTY (&ch1.unique); TEST_ASSERT_LIST_EMPTY (&ch2.unique); @@ -242,7 +246,8 @@ TEST_CASE("channel_unregister", "[channel]") TEST_ASSERT_LIST_LENGTH(&ch4.same, 2); TEST_ASSERT_LIST_EMPTY (&ch5.same); - channel_unregister(&ch3); + // unregister unique& same channel + channel_internal_unregister(&ch3); TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); TEST_ASSERT_LIST_EMPTY (&ch1.unique); TEST_ASSERT_LIST_EMPTY (&ch2.unique); @@ -260,9 +265,46 @@ TEST_CASE("channel_unregister", "[channel]") TEST_ASSERT_LIST_EMPTY (&ch4.same); TEST_ASSERT_LIST_EMPTY (&ch5.same); - TEST_ASSERT(channel_debug_resetRoot(&ch0)); + // unregister same channel + channel_internal_unregister(&ch2); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_EMPTY (&ch3.unique); + TEST_ASSERT_LIST_LENGTH(&ch4.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch5.unique, 4); + //ch0 & ch1 same should decrement + TEST_ASSERT_LIST_LENGTH(&ch0.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 2); + //ch2 same should be empty + TEST_ASSERT_LIST_EMPTY (&ch2.same); + TEST_ASSERT_LIST_EMPTY (&ch3.same); + TEST_ASSERT_LIST_EMPTY (&ch4.same); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + // unregister unique channel + channel_internal_unregister(&ch5); + //ch0 unique should decrement + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 3); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_EMPTY (&ch3.unique); + //ch4 unique should decrement + TEST_ASSERT_LIST_LENGTH(&ch4.unique, 3); + //ch5 uniq should be empty + TEST_ASSERT_LIST_EMPTY (&ch5.unique); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 2); + TEST_ASSERT_LIST_EMPTY (&ch2.same); + TEST_ASSERT_LIST_EMPTY (&ch3.same); + TEST_ASSERT_LIST_EMPTY (&ch4.same); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + channel_internal_resetRoot(); } +#endif +#ifdef CONFIG_CHANNEL_TEST_ENABLE_SEND TEST_CASE("channel_send", "[channel]") { Channel ch; @@ -270,7 +312,7 @@ TEST_CASE("channel_send", "[channel]") void *ctx = (void*)0x1; BaseType_t flags = 0x2; Channel_callback callback = &returnPdPASS; - channel_init(&ch, identifier, ctx, flags, callback); + channel_internal_init(&ch, identifier, ctx, flags, callback); callback_ctx = NULL; callback_data = NULL; @@ -286,7 +328,9 @@ TEST_CASE("channel_send", "[channel]") TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout); TEST_ASSERT_EQUAL_UINT(callback_flags, flags); } +#endif +#ifdef CONFIG_CHANNEL_TEST_ENABLE_BROADCAST_INIT TEST_CASE("channel_broadcast_init", "[channel]") { Channel ch; @@ -294,9 +338,9 @@ TEST_CASE("channel_broadcast_init", "[channel]") void *ctx = (void*) 0x1; BaseType_t flags = 0x2; Channel_callback callback = &returnPdPASS; - channel_init(&ch, identifier, ctx, flags, callback); + channel_internal_init(&ch, identifier, ctx, flags, callback); - Broadcast handle; + Channel_broadcast handle; void *data = (void*) 0x3; TickType_t timeout = 0x4; channel_broadcast_init(&handle, &ch, data, timeout); @@ -306,7 +350,9 @@ TEST_CASE("channel_broadcast_init", "[channel]") TEST_ASSERT_EQUAL_PTR(handle.data,data); TEST_ASSERT_EQUAL_UINT(handle.timeout,timeout); } +#endif +#ifdef CONFIG_CHANNEL_TEST_ENABLE_BROADCAST_FINISHED TEST_CASE("channel_broadcast_finished", "[channel]") { Channel ch; @@ -314,9 +360,9 @@ TEST_CASE("channel_broadcast_finished", "[channel]") void *ctx = (void*) 0x1; BaseType_t flags = 0x2; Channel_callback callback = NULL; - channel_init(&ch, identifier, ctx, flags, callback); + channel_internal_init(&ch, identifier, ctx, flags, callback); - Broadcast handle; + Channel_broadcast handle; void *data = (void*) 0x3; TickType_t timeout = 0x4; channel_broadcast_init(&handle, &ch, data, timeout); @@ -325,9 +371,12 @@ TEST_CASE("channel_broadcast_finished", "[channel]") channel_broadcast(&handle); TEST_ASSERT_TRUE(channel_broadcast_finished(&handle)); } +#endif +#ifdef CONFIG_CHANNEL_TEST_ENABLE_BROADCAST TEST_CASE("channel_broadcast", "[channel]") { + channel_internal_resetRoot(); Channel ch0, ch1, ch2; char identifier0[] = "test_channel0"; char identifier1[] = "test_channel1"; @@ -341,10 +390,6 @@ TEST_CASE("channel_broadcast", "[channel]") channel_init(&ch1, identifier1, ctx1, flags1, &returnPdPASS); channel_init(&ch2, identifier1, ctx2, flags2, &returnErrQUEUE_FULL); - channel_register(&ch0); - channel_register(&ch1); - channel_register(&ch2); - callback_ctx = NULL; callback_data = NULL; callback_timeout = 0; @@ -359,7 +404,7 @@ TEST_CASE("channel_broadcast", "[channel]") TickType_t timeout2 = 0xC; BaseType_t result; - Broadcast handle; + Channel_broadcast handle; channel_broadcast_init(&handle, &ch0, data0, timeout0); result = channel_broadcast(&handle); @@ -394,5 +439,6 @@ TEST_CASE("channel_broadcast", "[channel]") TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout1); TEST_ASSERT_EQUAL_UINT(callback_flags, flags1); - TEST_ASSERT(channel_debug_resetRoot(&ch0)); -} \ No newline at end of file + channel_internal_resetRoot(); +} +#endif \ No newline at end of file From 118863291ca837efe5a65446932105b10ec4a578 Mon Sep 17 00:00:00 2001 From: drechsler Date: Wed, 1 Apr 2020 10:56:22 +0200 Subject: [PATCH 04/17] channel:refatored renamed channel_{in,out} to {consumer,producer} --- components/channel/include/channel.h | 41 ++++++++++++++++++++++++---- components/channel/src/channel.c | 20 ++++++++------ 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h index 53d333b..42ce96b 100644 --- a/components/channel/include/channel.h +++ b/components/channel/include/channel.h @@ -57,8 +57,8 @@ struct channel { }; typedef struct channel Channel; -typedef struct channel Channel_in; -typedef struct channel Channel_out; +typedef struct channel Channel_consumer; +typedef struct channel Channel_producer; typedef struct broadcast { Channel *ch; @@ -85,7 +85,7 @@ channel_init (Channel *ch, const char *identifier, void *ctx, const BaseType_t flags, Channel_callback callback); /** - * channel_init_input - helper function to initialize channel input object + * channel_init_consumer - helper function to initialize channel object as consumer * @ch: pointer to channel object to initialize * @identifier: identifier string of the channel * @queue: queue handle to send input to @@ -93,6 +93,35 @@ channel_init static void inline __attribute__((always_inline)) +channel_init_consumer +(Channel_consumer *ch, const char *identifier, QueueHandle_t queue) +{ + return channel_init(ch, identifier, queue, queueSEND_TO_BACK, &xQueueGenericSend); +} + +/** + * channel_init_producer - helper function to initialize channel object as producer + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + */ +static +void +inline __attribute__((always_inline)) +channel_init_producer +(Channel_producer *ch, const char *identifier) +{ + return channel_init(ch, identifier, NULL, 0, NULL); +} + +/** + * channel_init_input - helper function to initialize channel input object + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + * @queue: queue handle to send input to + */ +static +void +inline __attribute__((always_inline)) __attribute__((deprecated)) channel_init_input (Channel_in *ch, const char *identifier, QueueHandle_t queue) { @@ -106,7 +135,7 @@ channel_init_input */ static void -inline __attribute__((always_inline)) +inline __attribute__((always_inline)) __attribute__((deprecated)) channel_init_output (Channel_out *ch, const char *identifier) { @@ -160,7 +189,7 @@ channel_reset */ BaseType_t channel_send -(const Channel_in *ch, const void *data, const TickType_t timeout); +(const Channel_consumer *ch, const void *data, const TickType_t timeout); /** * channel_broadcast_init - helper function to initialize broadcast handler @@ -173,7 +202,7 @@ static void inline __attribute__((always_inline)) channel_broadcast_init -(Channel_broadcast *handle, const Channel_out *ch, const void *data, const TickType_t timeout) +(Channel_broadcast *handle, const Channel_producer *ch, const void *data, const TickType_t timeout) { handle->ch = (void*) ch; handle->pos = (void*) ch; diff --git a/components/channel/src/channel.c b/components/channel/src/channel.c index b0c2ca7..25d86f1 100644 --- a/components/channel/src/channel.c +++ b/components/channel/src/channel.c @@ -65,34 +65,36 @@ void channel_internal_unregister (Channel *ch) { - int isUnique = ! list_empty(&ch->unique); - int isSame = ! list_empty(&ch->same); + int is_unique = ! list_empty(&ch->unique); + int is_same = ! list_empty(&ch->same); //channel is not registered - if (!isUnique && !isSame) { + if (!is_unique && !is_same) { return; } //channel is not in the unique chain so just delete it - if (!isUnique && isSame) { + if (!is_unique && is_same) { list_del(&ch->same); INIT_LIST_HEAD(&ch->same); return; } + //channel is in the unique list, but single element so also just delete it - if (isUnique && !isSame) { + if (is_unique && !is_same) { list_del(&ch->unique); INIT_LIST_HEAD(&ch->unique); return; } + //channel is in unique list and there are others in same list, so replace channel - if (isUnique && isSame) { - Channel *nextSame = list_entry(ch->same.next, struct channel, same); - Channel *nextUniq = list_entry(ch->unique.next, struct channel, unique); + if (is_unique && is_same) { + Channel *next_same = list_entry(ch->same.next, struct channel, same); + Channel *next_uniq = list_entry(ch->unique.next, struct channel, unique); list_del(&ch->same); INIT_LIST_HEAD(&ch->same); list_del(&ch->unique); INIT_LIST_HEAD(&ch->unique); - list_add(&nextSame->unique, &nextUniq->unique); + list_add(&next_same->unique, &next_uniq->unique); return; } } From c12d66ed61aae17cbddb1e8bac98f881d48e75ab Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Thu, 2 Apr 2020 21:11:36 +0200 Subject: [PATCH 05/17] esp32:feature Added testcase with queue --- components/channel/test/channel_test.c | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c index e936bd6..42a5651 100644 --- a/components/channel/test/channel_test.c +++ b/components/channel/test/channel_test.c @@ -441,4 +441,68 @@ TEST_CASE("channel_broadcast", "[channel]") channel_internal_resetRoot(); } +#endif + +#define MP_TEST_STACK_SIZE 0x1000 +#define CHANNEL_QUEUE_TEST_QUEUE_SIZE 2 + +#ifdef CONFIG_TEST_CHANNEL_QUEUE_CONSUMER + +#define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" +static Channel_consumer cc; +static StaticQueue_t cc_queue; +static QueueHandle_t cc_queue_handle; +static uint8_t cc_queue_buffer[CHANNEL_QUEUE_TEST_QUEUE_SIZE*sizeof(float)]; + +static float data; +static uint8_t timeout; +static void channel_consumer_task(void *pvParameters) +{ + data = 0; + for (;;) { + xQueueReceive((QueueHandle_t)cc.ctx, &data, 100/portTICK_PERIOD_MS); + timeout = 1; + } +} + +TEST_CASE("channel_queue", "[channel]") +{ + + cc_queue_handle = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_SIZE, + sizeof(float), + cc_queue_buffer, + &cc_queue + ); + + channel_init_consumer(&cc, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_handle); + + static StackType_t cc_task_stack[MP_TEST_STACK_SIZE]; + static StaticTask_t cc_task; + static TaskHandle_t cc_task_handle; + + static Channel_producer cp; + + channel_init_producer(&cp, CHANNEL_QUEUE_TEST_IDENTIFIER_C1); + + timeout = 0; + cc_task_handle = xTaskCreateStatic( + channel_consumer_task, + "channel_consumer_task", + MP_TEST_STACK_SIZE, + NULL, + 1, + cc_task_stack, + &cc_task + ); + configASSERT(cc_task); + + Channel_broadcast br; + float test_data = 3.14; + channel_broadcast_init(&br, &cp, &test_data, 0); + channel_broadcast(&br); + + while(!timeout){} + TEST_ASSERT_EQUAL_FLOAT(data, test_data); +} #endif \ No newline at end of file From 7d5be4872eb0f5860536074dff55278cf5aceb68 Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Thu, 2 Apr 2020 22:30:20 +0200 Subject: [PATCH 06/17] esp32:feature Cleanup queue and task after test --- components/channel/Kconfig | 3 +++ components/channel/test/channel_test.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/components/channel/Kconfig b/components/channel/Kconfig index 21a0cbd..c3a18b0 100644 --- a/components/channel/Kconfig +++ b/components/channel/Kconfig @@ -31,6 +31,9 @@ menu "Channel" config CHANNEL_TEST_ENABLE_RESETROOT bool "resetRoot" default y + config CHANNEL_TEST_QUEUE_CONSUMER + bool "Channel queue" + default y endif config CHANNEL_INTERNAL_EXPORT_SYMBOLS bool "Export internal functions" diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c index 42a5651..cd07ad8 100644 --- a/components/channel/test/channel_test.c +++ b/components/channel/test/channel_test.c @@ -1,5 +1,6 @@ #include "unity.h" #include +#include #include "channel.h" #include "channel_internal.h" @@ -446,7 +447,7 @@ TEST_CASE("channel_broadcast", "[channel]") #define MP_TEST_STACK_SIZE 0x1000 #define CHANNEL_QUEUE_TEST_QUEUE_SIZE 2 -#ifdef CONFIG_TEST_CHANNEL_QUEUE_CONSUMER +#ifdef CONFIG_CHANNEL_TEST_QUEUE_CONSUMER #define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" static Channel_consumer cc; @@ -495,7 +496,7 @@ TEST_CASE("channel_queue", "[channel]") cc_task_stack, &cc_task ); - configASSERT(cc_task); + configASSERT(cc_task_handle); Channel_broadcast br; float test_data = 3.14; @@ -504,5 +505,8 @@ TEST_CASE("channel_queue", "[channel]") while(!timeout){} TEST_ASSERT_EQUAL_FLOAT(data, test_data); + vTaskDelete(cc_task_handle); + vQueueDelete(cc_queue_handle); + channel_internal_resetRoot(); } #endif \ No newline at end of file From 2db4f8dc3a294472752a7ed0f62ac6afb16787be Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Thu, 2 Apr 2020 23:08:35 +0200 Subject: [PATCH 07/17] esp32:fix Pin test task to core0 Fixes crashes of rerunning tests --- components/channel/test/channel_test.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c index cd07ad8..853e0ae 100644 --- a/components/channel/test/channel_test.c +++ b/components/channel/test/channel_test.c @@ -487,14 +487,15 @@ TEST_CASE("channel_queue", "[channel]") channel_init_producer(&cp, CHANNEL_QUEUE_TEST_IDENTIFIER_C1); timeout = 0; - cc_task_handle = xTaskCreateStatic( + cc_task_handle = xTaskCreateStaticPinnedToCore( channel_consumer_task, "channel_consumer_task", MP_TEST_STACK_SIZE, NULL, 1, cc_task_stack, - &cc_task + &cc_task, + 0 ); configASSERT(cc_task_handle); From f0b2f2bb8c230d9e0f0cbe9153a86631f918d4e6 Mon Sep 17 00:00:00 2001 From: drechsler Date: Fri, 3 Apr 2020 00:50:27 +0200 Subject: [PATCH 08/17] documentation:fix old function documentation --- components/channel/include/channel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h index 42ce96b..dc27ab6 100644 --- a/components/channel/include/channel.h +++ b/components/channel/include/channel.h @@ -212,8 +212,8 @@ channel_broadcast_init /** * channel_broadcast_finished - helper function to determine if broadcast finished + * Does not provide information if broadcast was successful! * @ch: pointer to channel object - * @pos: location of pointer where broadcast stopped */ static bool From 0200a67dd3c192d19ad71db7afca8b7ddc01f78c Mon Sep 17 00:00:00 2001 From: drechsler Date: Fri, 3 Apr 2020 01:07:29 +0200 Subject: [PATCH 09/17] channel:feature timeout per broadcast not callback - broadcast accumulates elapsed timeslices in new 'elapsed' counter - if broadcast takes more time than specified it errors with pdFAIL - added helper to determine if timeout happend --- components/channel/include/channel.h | 21 +++++++++++++++++++++ components/channel/src/channel.c | 28 +++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h index dc27ab6..04c8db2 100644 --- a/components/channel/include/channel.h +++ b/components/channel/include/channel.h @@ -65,6 +65,7 @@ typedef struct broadcast { Channel *pos; void *data; TickType_t timeout; + TickType_t elapsed; } Channel_broadcast; #ifndef STR @@ -208,6 +209,26 @@ channel_broadcast_init handle->pos = (void*) ch; handle->data = (void*) data; handle->timeout = timeout; + handle->elapsed = 0; +} + +/** + * channel_broadcast_timeout - helper function to determine if broadcast timed out + * @handle: pointer to broadcast object + */ +static +bool +inline __attribute__((always_inline)) +channel_broadcast_timeout +(Channel_broadcast *handle) +{ + if (handle->timeout != portMAX_DELAY) { + return false; + } + if (handle->elapsed >= handle->timeout) { + return true; + } + return false; } /** diff --git a/components/channel/src/channel.c b/components/channel/src/channel.c index 25d86f1..584d89d 100644 --- a/components/channel/src/channel.c +++ b/components/channel/src/channel.c @@ -2,7 +2,7 @@ #include "channel_internal.h" #include -#include +#include /** * CHANNEL_INIT - Macro to provide initialization for channel object @@ -116,22 +116,40 @@ channel_broadcast Channel *ch = handle->ch; Channel *pos = handle->pos; void *data = handle->data; - TickType_t timeout = handle->timeout; + TickType_t start = xTaskGetTickCount(); + TickType_t timeout = handle->timeout; + BaseType_t status; list_for_each_entry_continue(pos, (&ch->same), same) { - BaseType_t status; if (pos->callback) { + if (channel_broadcast_timeout(handle)) { + handle->pos = pos; + return pdFAIL; + } + if (timeout != portMAX_DELAY) { + timeout = handle->timeout - handle->elapsed; + } status = pos->callback(pos->ctx, data, timeout, pos->flags); + handle->elapsed += xTaskGetTickCount() - start; if (!status) { handle->pos = pos; return status; }; } } + if (channel_broadcast_timeout(handle)) { + handle->pos = pos; + return pdFAIL; + } + if (timeout != portMAX_DELAY) { + timeout = handle->timeout - handle->elapsed; + } handle->pos = NULL; - return channel_send(handle->ch, handle->data, handle->timeout); + status = channel_send(handle->ch, handle->data, timeout); + handle->elapsed += xTaskGetTickCount() - start; + return status; } EXPORT @@ -142,4 +160,4 @@ channel_internal_resetRoot { INIT_LIST_HEAD(&root_channel.unique); INIT_LIST_HEAD(&root_channel.same); -} \ No newline at end of file +} From 52648855e49eeb72315505869f078fcc68ee2d4e Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Fri, 3 Apr 2020 00:35:15 +0200 Subject: [PATCH 10/17] esp32:fix Remove pin to core. Add task delay instead of busy wait --- components/channel/test/channel_test.c | 101 +++++++++++++++++++++---- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c index 853e0ae..605e1b9 100644 --- a/components/channel/test/channel_test.c +++ b/components/channel/test/channel_test.c @@ -450,16 +450,13 @@ TEST_CASE("channel_broadcast", "[channel]") #ifdef CONFIG_CHANNEL_TEST_QUEUE_CONSUMER #define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" -static Channel_consumer cc; -static StaticQueue_t cc_queue; -static QueueHandle_t cc_queue_handle; -static uint8_t cc_queue_buffer[CHANNEL_QUEUE_TEST_QUEUE_SIZE*sizeof(float)]; static float data; static uint8_t timeout; + + static void channel_consumer_task(void *pvParameters) { - data = 0; for (;;) { xQueueReceive((QueueHandle_t)cc.ctx, &data, 100/portTICK_PERIOD_MS); timeout = 1; @@ -468,6 +465,16 @@ static void channel_consumer_task(void *pvParameters) TEST_CASE("channel_queue", "[channel]") { + static Channel_consumer cc; + static StaticQueue_t cc_queue; + static QueueHandle_t cc_queue_handle; + static uint8_t cc_queue_buffer[CHANNEL_QUEUE_TEST_QUEUE_SIZE*sizeof(float)]; + + static StackType_t cc_task_stack[MP_TEST_STACK_SIZE]; + static StaticTask_t cc_task; + static TaskHandle_t cc_task_handle; + + data = 0; cc_queue_handle = xQueueCreateStatic( CHANNEL_QUEUE_TEST_QUEUE_SIZE, @@ -475,36 +482,104 @@ TEST_CASE("channel_queue", "[channel]") cc_queue_buffer, &cc_queue ); + configASSERT(cc_queue_handle); channel_init_consumer(&cc, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_handle); + + static Channel_producer cp; + + channel_init_producer(&cp, CHANNEL_QUEUE_TEST_IDENTIFIER_C1); + + timeout = 0; + cc_task_handle = xTaskCreateStatic( + channel_consumer_task, + "channel_consumer_task", + MP_TEST_STACK_SIZE, + NULL, + 1, + cc_task_stack, + &cc_task + ); + configASSERT(cc_task_handle); + + Channel_broadcast br; + float test_data = 3.14; + channel_broadcast_init(&br, &cp, &test_data, 0); + + TEST_ASSERT(broadcast_finished(&br)); + TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); + + vTaskDelay(100/portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_FLOAT(data, test_data); + vTaskDelete(cc_task_handle); + vQueueDelete(cc_queue_handle); + channel_internal_resetRoot(); +} +#endif - static StackType_t cc_task_stack[MP_TEST_STACK_SIZE]; - static StaticTask_t cc_task; - static TaskHandle_t cc_task_handle; +#ifdef CONFIG_CHANNEL_TEST_QUEUE_TIMEOUT + +#define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" + +static float data; +static uint8_t timeout; + + +static void channel_consumer_task(void *pvParameters) +{ + for (;;) { + xQueueReceive((QueueHandle_t)cc.ctx, &data, 100/portTICK_PERIOD_MS); + timeout = 1; + } +} + +TEST_CASE("channel_queue", "[channel]") +{ + static Channel_consumer cc; + static StaticQueue_t cc_queue; + static QueueHandle_t cc_queue_handle; + static uint8_t cc_queue_buffer[CHANNEL_QUEUE_TEST_QUEUE_SIZE*sizeof(float)]; + + static StackType_t cc_task_stack[MP_TEST_STACK_SIZE]; + static StaticTask_t cc_task; + static TaskHandle_t cc_task_handle; + + data = 0; + + cc_queue_handle = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_SIZE, + sizeof(float), + cc_queue_buffer, + &cc_queue + ); + configASSERT(cc_queue_handle); + + channel_init_consumer(&cc, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_handle); static Channel_producer cp; channel_init_producer(&cp, CHANNEL_QUEUE_TEST_IDENTIFIER_C1); timeout = 0; - cc_task_handle = xTaskCreateStaticPinnedToCore( + cc_task_handle = xTaskCreateStatic( channel_consumer_task, "channel_consumer_task", MP_TEST_STACK_SIZE, NULL, 1, cc_task_stack, - &cc_task, - 0 + &cc_task ); configASSERT(cc_task_handle); Channel_broadcast br; float test_data = 3.14; channel_broadcast_init(&br, &cp, &test_data, 0); - channel_broadcast(&br); + + TEST_ASSERT(broadcast_finished(&br)); + TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); - while(!timeout){} + vTaskDelay(100/portTICK_PERIOD_MS); TEST_ASSERT_EQUAL_FLOAT(data, test_data); vTaskDelete(cc_task_handle); vQueueDelete(cc_queue_handle); From 03b0704fda4a7c644ba98756941c22bd9f2035ee Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Fri, 3 Apr 2020 04:15:25 +0200 Subject: [PATCH 11/17] esp32:fix Fixed Testcases for timout and a bug in channel_broadcast_timout --- components/channel/Kconfig | 3 + components/channel/include/channel.h | 2 +- components/channel/test/channel_test.c | 114 +++++++++++-------------- 3 files changed, 56 insertions(+), 63 deletions(-) diff --git a/components/channel/Kconfig b/components/channel/Kconfig index c3a18b0..99d6cfb 100644 --- a/components/channel/Kconfig +++ b/components/channel/Kconfig @@ -34,6 +34,9 @@ menu "Channel" config CHANNEL_TEST_QUEUE_CONSUMER bool "Channel queue" default y + config CHANNEL_TEST_QUEUE_TIMEOUT + bool "Channel queue timeout" + default y endif config CHANNEL_INTERNAL_EXPORT_SYMBOLS bool "Export internal functions" diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h index 04c8db2..8478096 100644 --- a/components/channel/include/channel.h +++ b/components/channel/include/channel.h @@ -222,7 +222,7 @@ inline __attribute__((always_inline)) channel_broadcast_timeout (Channel_broadcast *handle) { - if (handle->timeout != portMAX_DELAY) { + if (handle->timeout == portMAX_DELAY) { return false; } if (handle->elapsed >= handle->timeout) { diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c index 605e1b9..9ab8f6d 100644 --- a/components/channel/test/channel_test.c +++ b/components/channel/test/channel_test.c @@ -350,6 +350,7 @@ TEST_CASE("channel_broadcast_init", "[channel]") TEST_ASSERT_EQUAL_PTR(handle.pos,&ch); TEST_ASSERT_EQUAL_PTR(handle.data,data); TEST_ASSERT_EQUAL_UINT(handle.timeout,timeout); + TEST_ASSERT_EQUAL_UINT(handle.elapsed,0); } #endif @@ -444,28 +445,26 @@ TEST_CASE("channel_broadcast", "[channel]") } #endif -#define MP_TEST_STACK_SIZE 0x1000 -#define CHANNEL_QUEUE_TEST_QUEUE_SIZE 2 #ifdef CONFIG_CHANNEL_TEST_QUEUE_CONSUMER +#define MP_TEST_STACK_SIZE 0x1000 +#define CHANNEL_QUEUE_TEST_QUEUE_SIZE 2 #define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" static float data; -static uint8_t timeout; +static Channel_consumer cc; static void channel_consumer_task(void *pvParameters) { for (;;) { - xQueueReceive((QueueHandle_t)cc.ctx, &data, 100/portTICK_PERIOD_MS); - timeout = 1; + xQueueReceive((QueueHandle_t)cc.ctx, &data, portMAX_DELAY); } } TEST_CASE("channel_queue", "[channel]") { - static Channel_consumer cc; static StaticQueue_t cc_queue; static QueueHandle_t cc_queue_handle; static uint8_t cc_queue_buffer[CHANNEL_QUEUE_TEST_QUEUE_SIZE*sizeof(float)]; @@ -487,10 +486,8 @@ TEST_CASE("channel_queue", "[channel]") channel_init_consumer(&cc, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_handle); static Channel_producer cp; - channel_init_producer(&cp, CHANNEL_QUEUE_TEST_IDENTIFIER_C1); - timeout = 0; cc_task_handle = xTaskCreateStatic( channel_consumer_task, "channel_consumer_task", @@ -504,12 +501,12 @@ TEST_CASE("channel_queue", "[channel]") Channel_broadcast br; float test_data = 3.14; - channel_broadcast_init(&br, &cp, &test_data, 0); - - TEST_ASSERT(broadcast_finished(&br)); - TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); + channel_broadcast_init(&br, &cp, &test_data, 1); + + TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); + vTaskDelay(5); + TEST_ASSERT(channel_broadcast_finished(&br)); - vTaskDelay(100/portTICK_PERIOD_MS); TEST_ASSERT_EQUAL_FLOAT(data, test_data); vTaskDelete(cc_task_handle); vQueueDelete(cc_queue_handle); @@ -519,70 +516,63 @@ TEST_CASE("channel_queue", "[channel]") #ifdef CONFIG_CHANNEL_TEST_QUEUE_TIMEOUT +#define MP_TEST_STACK_SIZE 0x1000 +#define CHANNEL_QUEUE_TEST_QUEUE_1_SIZE 4 +#define CHANNEL_QUEUE_TEST_QUEUE_2_SIZE 1 #define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" static float data; -static uint8_t timeout; - +static Channel_consumer cc_1, cc_2; -static void channel_consumer_task(void *pvParameters) -{ - for (;;) { - xQueueReceive((QueueHandle_t)cc.ctx, &data, 100/portTICK_PERIOD_MS); - timeout = 1; - } -} -TEST_CASE("channel_queue", "[channel]") +TEST_CASE("channel_queue timout", "[channel]") { - static Channel_consumer cc; - static StaticQueue_t cc_queue; - static QueueHandle_t cc_queue_handle; - static uint8_t cc_queue_buffer[CHANNEL_QUEUE_TEST_QUEUE_SIZE*sizeof(float)]; + static StaticQueue_t cc_queue_1; + static QueueHandle_t cc_queue_1_handle; + static uint8_t cc_queue_1_buffer[CHANNEL_QUEUE_TEST_QUEUE_1_SIZE*sizeof(float)]; - static StackType_t cc_task_stack[MP_TEST_STACK_SIZE]; - static StaticTask_t cc_task; - static TaskHandle_t cc_task_handle; - - data = 0; - - cc_queue_handle = xQueueCreateStatic( - CHANNEL_QUEUE_TEST_QUEUE_SIZE, + cc_queue_1_handle = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_1_SIZE, sizeof(float), - cc_queue_buffer, - &cc_queue + cc_queue_1_buffer, + &cc_queue_1 ); - configASSERT(cc_queue_handle); + configASSERT(cc_queue_1_handle); + channel_init_consumer(&cc_1, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_1_handle); + + static StaticQueue_t cc_queue_2; + static QueueHandle_t cc_queue_2_handle; + static uint8_t cc_queue_2_buffer[CHANNEL_QUEUE_TEST_QUEUE_2_SIZE*sizeof(float)]; - channel_init_consumer(&cc, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_handle); + cc_queue_2_handle = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_2_SIZE, + sizeof(float), + cc_queue_2_buffer, + &cc_queue_2 + ); + configASSERT(cc_queue_2_handle); + channel_init_consumer(&cc_2, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_2_handle); static Channel_producer cp; - channel_init_producer(&cp, CHANNEL_QUEUE_TEST_IDENTIFIER_C1); - timeout = 0; - cc_task_handle = xTaskCreateStatic( - channel_consumer_task, - "channel_consumer_task", - MP_TEST_STACK_SIZE, - NULL, - 1, - cc_task_stack, - &cc_task - ); - configASSERT(cc_task_handle); - Channel_broadcast br; - float test_data = 3.14; - channel_broadcast_init(&br, &cp, &test_data, 0); - - TEST_ASSERT(broadcast_finished(&br)); - TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); - - vTaskDelay(100/portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_FLOAT(data, test_data); - vTaskDelete(cc_task_handle); - vQueueDelete(cc_queue_handle); + float test_data = 0.5; + channel_broadcast_init(&br, &cp, &test_data, 1); + TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); + + unsigned int timeout = 10; + channel_broadcast_init(&br, &cp, &test_data, timeout); + TickType_t start = xTaskGetTickCount(); + TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdFAIL); + TEST_ASSERT_FALSE(channel_broadcast_finished(&br)); + TickType_t stop = xTaskGetTickCount(); + TEST_ASSERT_EQUAL_UINT(start + timeout, stop); + TEST_ASSERT_EQUAL_UINT(timeout, br.elapsed); + TEST_ASSERT_EQUAL_PTR(&cc_2, br.pos); + TEST_ASSERT(channel_broadcast_timeout(&br)); + vQueueDelete(cc_queue_1_handle); + vQueueDelete(cc_queue_2_handle); channel_internal_resetRoot(); } #endif \ No newline at end of file From bd12e563922df186fccb87c17e547b5774e64dca Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Thu, 16 Apr 2020 20:54:50 +0200 Subject: [PATCH 12/17] esp32:test Added test for channel component with two channels/tasks in a loop --- components/channel/Kconfig | 3 + components/channel/test/channel_test.c | 179 ++++++++++++++++++++++++- 2 files changed, 181 insertions(+), 1 deletion(-) diff --git a/components/channel/Kconfig b/components/channel/Kconfig index 99d6cfb..b5cb114 100644 --- a/components/channel/Kconfig +++ b/components/channel/Kconfig @@ -37,6 +37,9 @@ menu "Channel" config CHANNEL_TEST_QUEUE_TIMEOUT bool "Channel queue timeout" default y + config CHANNEL_TEST_QUEUE_LOOP + bool "Channel queue timeout" + default y endif config CHANNEL_INTERNAL_EXPORT_SYMBOLS bool "Export internal functions" diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c index 9ab8f6d..4b7b8a3 100644 --- a/components/channel/test/channel_test.c +++ b/components/channel/test/channel_test.c @@ -575,4 +575,181 @@ TEST_CASE("channel_queue timout", "[channel]") vQueueDelete(cc_queue_2_handle); channel_internal_resetRoot(); } -#endif \ No newline at end of file +#endif + +#ifdef CONFIG_CHANNEL_TEST_QUEUE_LOOP + +#define MP_TEST_STACK_SIZE 0x1000 +#define CHANNEL_LOOP_TEST_QUEUE_SIZE 10 +#define CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE 10 +#define CHANNEL_LOOP_TEST_IDENTIFIER_C1 "c_loop1" +#define CHANNEL_LOOP_TEST_IDENTIFIER_C2 "c_loop2" +#define CHANNEL_LOOP_TEST_LOOP_COUNT 10 + +typedef struct { + Channel_consumer *in; + Channel_producer *out; + unsigned int id; +} task_data; + +static Channel_consumer t1_ch1_co; +static Channel_producer t1_ch2_pr; + +static task_data t1_data = { + .in = &t1_ch1_co, + .out = &t1_ch2_pr, + .id = 1 +}; + +static Channel_consumer t2_ch2_co; +static Channel_producer t2_ch1_pr; + +static task_data t2_data = { + .in = &t2_ch2_co, + .out = &t2_ch1_pr, + .id = 2 +}; + +static void channel_loop_task(void *pvParameters) +{ + task_data *tdata = (task_data *)pvParameters; + static Channel_broadcast bc; + float data = -1; + + for (;;) { + xQueueReceive((QueueHandle_t)tdata->in->ctx, &data, portMAX_DELAY); + + data +=1; + channel_broadcast_init(&bc, tdata->out, &data, portMAX_DELAY); + BaseType_t res = channel_broadcast(&bc); + TEST_ASSERT_EQUAL(pdPASS, res); + + if (data > CHANNEL_LOOP_TEST_LOOP_COUNT) + { + while (1) {} + } + } +} + +TEST_CASE("channel_queue loop", "[channel]") +{ + static StaticQueue_t queue_1; + static QueueHandle_t queue_handle_1; + static uint8_t queue_buffer_1[CHANNEL_LOOP_TEST_QUEUE_SIZE*sizeof(float)]; + + queue_handle_1 = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_SIZE, + sizeof(float), + queue_buffer_1, + &queue_1 + ); + configASSERT(queue_handle_1); + channel_init_consumer(t1_data.in, CHANNEL_LOOP_TEST_IDENTIFIER_C1, queue_handle_1); + channel_init_producer(t1_data.out, CHANNEL_LOOP_TEST_IDENTIFIER_C2); + + static StaticQueue_t queue_2; + static QueueHandle_t queue_handle_2; + static uint8_t queue_buffer_2[CHANNEL_LOOP_TEST_QUEUE_SIZE*sizeof(float)]; + + queue_handle_2 = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_SIZE, + sizeof(float), + queue_buffer_2, + &queue_2 + ); + configASSERT(queue_handle_2); + channel_init_consumer(t2_data.in, CHANNEL_LOOP_TEST_IDENTIFIER_C2, queue_handle_2); + channel_init_producer(t2_data.out, CHANNEL_LOOP_TEST_IDENTIFIER_C1); + + static StaticQueue_t queue_11; + static QueueHandle_t queue_11_handle; + static uint8_t queue_11_buffer[CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE*sizeof(float)]; + + + queue_11_handle = xQueueCreateStatic( + CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE, + sizeof(float), + queue_11_buffer, + &queue_11 + ); + configASSERT(queue_11_handle); + + static StaticQueue_t queue_22; + static QueueHandle_t queue_22_handle; + static uint8_t queue_22_buffer[CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE*sizeof(float)]; + + + queue_22_handle = xQueueCreateStatic( + CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE, + sizeof(float), + queue_22_buffer, + &queue_22 + ); + configASSERT(queue_22_handle); + + static Channel_consumer ch1_co; + static Channel_consumer ch2_co; + channel_init_consumer(&ch1_co, CHANNEL_LOOP_TEST_IDENTIFIER_C1, queue_11_handle); + channel_init_consumer(&ch2_co, CHANNEL_LOOP_TEST_IDENTIFIER_C2, queue_22_handle); + + static Channel_producer ch1_pr; + static Channel_producer ch2_pr; + channel_init_producer(&ch1_pr, CHANNEL_LOOP_TEST_IDENTIFIER_C1); + channel_init_producer(&ch2_pr, CHANNEL_LOOP_TEST_IDENTIFIER_C2); + + static StackType_t t1_stack[MP_TEST_STACK_SIZE]; + static StaticTask_t t1; + static TaskHandle_t t1_handle; + + t1_handle = xTaskCreateStatic( + channel_loop_task, + "channel_loop_task_1", + MP_TEST_STACK_SIZE, + &t1_data, + 1, + t1_stack, + &t1 + ); + configASSERT(t1_handle); + + static StackType_t t2_stack[MP_TEST_STACK_SIZE]; + static StaticTask_t t2; + static TaskHandle_t t2_handle; + + t2_handle = xTaskCreateStatic( + channel_loop_task, + "channel_loop_task_2", + MP_TEST_STACK_SIZE, + &t2_data, + 1, + t2_stack, + &t2 + ); + configASSERT(t2_handle); + + Channel_broadcast br; + float test_data = 0.0; + channel_broadcast_init(&br, &ch1_pr, &test_data, 10); + TEST_ASSERT_EQUAL(pdPASS, channel_broadcast(&br)); + TEST_ASSERT(channel_broadcast_finished(&br)); + + float d; + for (float i = 0.0; i < CHANNEL_LOOP_TEST_LOOP_COUNT + 1; i += 2.0) + { + xQueueReceive(queue_11_handle, &d, portMAX_DELAY); + + TEST_ASSERT_EQUAL_FLOAT(i, d); + xQueueReceive(queue_22_handle, &d, portMAX_DELAY); + + TEST_ASSERT_EQUAL_FLOAT(i+1, d); + } + + vTaskDelete(t1_handle); + vTaskDelete(t2_handle); + vQueueDelete(queue_11_handle); + vQueueDelete(queue_22_handle); + vQueueDelete(queue_handle_1); + vQueueDelete(queue_handle_2); + channel_internal_resetRoot(); +} +#endif From 2485f2a9ae8b21be35db7f551c2317a6ba68d85b Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Thu, 16 Apr 2020 22:36:55 +0200 Subject: [PATCH 13/17] esp32:fix Removed depricated init_channel_input functions --- components/channel/include/channel.h | 29 ---------------------------- 1 file changed, 29 deletions(-) diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h index 8478096..2b5e67b 100644 --- a/components/channel/include/channel.h +++ b/components/channel/include/channel.h @@ -114,35 +114,6 @@ channel_init_producer return channel_init(ch, identifier, NULL, 0, NULL); } -/** - * channel_init_input - helper function to initialize channel input object - * @ch: pointer to channel object to initialize - * @identifier: identifier string of the channel - * @queue: queue handle to send input to - */ -static -void -inline __attribute__((always_inline)) __attribute__((deprecated)) -channel_init_input -(Channel_in *ch, const char *identifier, QueueHandle_t queue) -{ - return channel_init(ch, identifier, queue, queueSEND_TO_BACK, &xQueueGenericSend); -} - -/** - * channel_init_output - helper function to initialize channel output object - * @ch: pointer to channel object to initialize - * @identifier: identifier string of the channel - */ -static -void -inline __attribute__((always_inline)) __attribute__((deprecated)) -channel_init_output -(Channel_out *ch, const char *identifier) -{ - return channel_init(ch, identifier, NULL, 0, NULL); -} - /** * channel_setContext - helper function to change the context the callback acts on * @ch: pointer to channel object to modify From bb3079f1879bcf159e209060c67a2829d96aeb91 Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Thu, 16 Apr 2020 22:39:11 +0200 Subject: [PATCH 14/17] esp32:fix Changed shape and label to differenciate between producer and consumer --- components/channel/include/channel_debug.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/components/channel/include/channel_debug.h b/components/channel/include/channel_debug.h index f00b3b0..32ef523 100644 --- a/components/channel/include/channel_debug.h +++ b/components/channel/include/channel_debug.h @@ -45,16 +45,21 @@ channel_debug_print_dot ESP_LOGD(prefix, "x%p [label=\"%p\\n%s\", shape=box];", ch, ch, ch->identifier); } else { - ESP_LOGD(prefix, "x%p [label=\"%p\\n%s\"];", - ch, ch, ch->identifier); + if (ch->ctx == NULL) { + ESP_LOGD(prefix, "x%p [label=\"producer\\n%p\\n%s\" shape=house];", + ch, ch, ch->identifier); + } else { + ESP_LOGD(prefix, "x%p [label=\"consumer\\n%p\\n%s\" shape=invhouse];", + ch, ch, ch->identifier); + } } - ESP_LOGD(prefix, "x%p -> x%p [label=same, color=red];", + ESP_LOGD(prefix, "x%p -> x%p [label=sn, color=red];", ch, list_entry(ch->same.next, struct channel, same)); - ESP_LOGD(prefix, "x%p -> x%p [label=same, color=red];", + ESP_LOGD(prefix, "x%p -> x%p [label=sp, color=red];", ch, list_entry(ch->same.prev, struct channel, same)); - ESP_LOGD(prefix, "x%p -> x%p [label=unique, color=blue];", + ESP_LOGD(prefix, "x%p -> x%p [label=un, color=blue];", ch, list_entry(ch->unique.next, struct channel, unique)); - ESP_LOGD(prefix, "x%p -> x%p [label=unique, color=blue];", + ESP_LOGD(prefix, "x%p -> x%p [label=up, color=blue];", ch, list_entry(ch->unique.prev, struct channel, unique)); } From 28ef8201f2c4033450749d87193fbdbb6a5873f8 Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Mon, 4 May 2020 21:00:09 +0200 Subject: [PATCH 15/17] esp32:fix Changed testcases to use ints instead of floats as test values QEMUs support of floats is somewhat broken and leeds to unrialiable test turnouts --- components/channel/test/channel_test.c | 50 +++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c index 4b7b8a3..0cef8ca 100644 --- a/components/channel/test/channel_test.c +++ b/components/channel/test/channel_test.c @@ -452,7 +452,7 @@ TEST_CASE("channel_broadcast", "[channel]") #define CHANNEL_QUEUE_TEST_QUEUE_SIZE 2 #define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" -static float data; +static int data; static Channel_consumer cc; @@ -467,7 +467,7 @@ TEST_CASE("channel_queue", "[channel]") { static StaticQueue_t cc_queue; static QueueHandle_t cc_queue_handle; - static uint8_t cc_queue_buffer[CHANNEL_QUEUE_TEST_QUEUE_SIZE*sizeof(float)]; + static uint8_t cc_queue_buffer[CHANNEL_QUEUE_TEST_QUEUE_SIZE*sizeof(int)]; static StackType_t cc_task_stack[MP_TEST_STACK_SIZE]; static StaticTask_t cc_task; @@ -477,7 +477,7 @@ TEST_CASE("channel_queue", "[channel]") cc_queue_handle = xQueueCreateStatic( CHANNEL_QUEUE_TEST_QUEUE_SIZE, - sizeof(float), + sizeof(int), cc_queue_buffer, &cc_queue ); @@ -500,14 +500,14 @@ TEST_CASE("channel_queue", "[channel]") configASSERT(cc_task_handle); Channel_broadcast br; - float test_data = 3.14; + int test_data = 3; channel_broadcast_init(&br, &cp, &test_data, 1); TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); vTaskDelay(5); TEST_ASSERT(channel_broadcast_finished(&br)); - TEST_ASSERT_EQUAL_FLOAT(data, test_data); + TEST_ASSERT_EQUAL_INT(data, test_data); vTaskDelete(cc_task_handle); vQueueDelete(cc_queue_handle); channel_internal_resetRoot(); @@ -521,7 +521,7 @@ TEST_CASE("channel_queue", "[channel]") #define CHANNEL_QUEUE_TEST_QUEUE_2_SIZE 1 #define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" -static float data; +static int data; static Channel_consumer cc_1, cc_2; @@ -529,11 +529,11 @@ TEST_CASE("channel_queue timout", "[channel]") { static StaticQueue_t cc_queue_1; static QueueHandle_t cc_queue_1_handle; - static uint8_t cc_queue_1_buffer[CHANNEL_QUEUE_TEST_QUEUE_1_SIZE*sizeof(float)]; + static uint8_t cc_queue_1_buffer[CHANNEL_QUEUE_TEST_QUEUE_1_SIZE*sizeof(int)]; cc_queue_1_handle = xQueueCreateStatic( CHANNEL_QUEUE_TEST_QUEUE_1_SIZE, - sizeof(float), + sizeof(int), cc_queue_1_buffer, &cc_queue_1 ); @@ -542,11 +542,11 @@ TEST_CASE("channel_queue timout", "[channel]") static StaticQueue_t cc_queue_2; static QueueHandle_t cc_queue_2_handle; - static uint8_t cc_queue_2_buffer[CHANNEL_QUEUE_TEST_QUEUE_2_SIZE*sizeof(float)]; + static uint8_t cc_queue_2_buffer[CHANNEL_QUEUE_TEST_QUEUE_2_SIZE*sizeof(int)]; cc_queue_2_handle = xQueueCreateStatic( CHANNEL_QUEUE_TEST_QUEUE_2_SIZE, - sizeof(float), + sizeof(int), cc_queue_2_buffer, &cc_queue_2 ); @@ -557,7 +557,7 @@ TEST_CASE("channel_queue timout", "[channel]") channel_init_producer(&cp, CHANNEL_QUEUE_TEST_IDENTIFIER_C1); Channel_broadcast br; - float test_data = 0.5; + int test_data = 5; channel_broadcast_init(&br, &cp, &test_data, 1); TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); @@ -614,7 +614,7 @@ static void channel_loop_task(void *pvParameters) { task_data *tdata = (task_data *)pvParameters; static Channel_broadcast bc; - float data = -1; + int data = -1; for (;;) { xQueueReceive((QueueHandle_t)tdata->in->ctx, &data, portMAX_DELAY); @@ -635,11 +635,11 @@ TEST_CASE("channel_queue loop", "[channel]") { static StaticQueue_t queue_1; static QueueHandle_t queue_handle_1; - static uint8_t queue_buffer_1[CHANNEL_LOOP_TEST_QUEUE_SIZE*sizeof(float)]; + static uint8_t queue_buffer_1[CHANNEL_LOOP_TEST_QUEUE_SIZE*sizeof(int)]; queue_handle_1 = xQueueCreateStatic( CHANNEL_QUEUE_TEST_QUEUE_SIZE, - sizeof(float), + sizeof(int), queue_buffer_1, &queue_1 ); @@ -649,11 +649,11 @@ TEST_CASE("channel_queue loop", "[channel]") static StaticQueue_t queue_2; static QueueHandle_t queue_handle_2; - static uint8_t queue_buffer_2[CHANNEL_LOOP_TEST_QUEUE_SIZE*sizeof(float)]; + static uint8_t queue_buffer_2[CHANNEL_LOOP_TEST_QUEUE_SIZE*sizeof(int)]; queue_handle_2 = xQueueCreateStatic( CHANNEL_QUEUE_TEST_QUEUE_SIZE, - sizeof(float), + sizeof(int), queue_buffer_2, &queue_2 ); @@ -663,12 +663,12 @@ TEST_CASE("channel_queue loop", "[channel]") static StaticQueue_t queue_11; static QueueHandle_t queue_11_handle; - static uint8_t queue_11_buffer[CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE*sizeof(float)]; + static uint8_t queue_11_buffer[CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE*sizeof(int)]; queue_11_handle = xQueueCreateStatic( CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE, - sizeof(float), + sizeof(int), queue_11_buffer, &queue_11 ); @@ -676,12 +676,12 @@ TEST_CASE("channel_queue loop", "[channel]") static StaticQueue_t queue_22; static QueueHandle_t queue_22_handle; - static uint8_t queue_22_buffer[CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE*sizeof(float)]; + static uint8_t queue_22_buffer[CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE*sizeof(int)]; queue_22_handle = xQueueCreateStatic( CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE, - sizeof(float), + sizeof(int), queue_22_buffer, &queue_22 ); @@ -728,20 +728,20 @@ TEST_CASE("channel_queue loop", "[channel]") configASSERT(t2_handle); Channel_broadcast br; - float test_data = 0.0; + int test_data = 0; channel_broadcast_init(&br, &ch1_pr, &test_data, 10); TEST_ASSERT_EQUAL(pdPASS, channel_broadcast(&br)); TEST_ASSERT(channel_broadcast_finished(&br)); - float d; - for (float i = 0.0; i < CHANNEL_LOOP_TEST_LOOP_COUNT + 1; i += 2.0) + int d; + for (int i = 0; i < CHANNEL_LOOP_TEST_LOOP_COUNT + 1; i += 2) { xQueueReceive(queue_11_handle, &d, portMAX_DELAY); - TEST_ASSERT_EQUAL_FLOAT(i, d); + TEST_ASSERT_EQUAL_INT(i, d); xQueueReceive(queue_22_handle, &d, portMAX_DELAY); - TEST_ASSERT_EQUAL_FLOAT(i+1, d); + TEST_ASSERT_EQUAL_INT(i+1, d); } vTaskDelete(t1_handle); From 037895e1c0d57237eebc5132c026a1add886c72c Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Mon, 4 May 2020 21:05:27 +0200 Subject: [PATCH 16/17] esp32:refactor Removed allways_inline atrribute since it's not needed See discussion in #47 https://github.com/ShilaTu/esp32-core/pull/47#discussion_r417804028 --- components/channel/include/channel.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h index 2b5e67b..d71e773 100644 --- a/components/channel/include/channel.h +++ b/components/channel/include/channel.h @@ -93,7 +93,7 @@ channel_init */ static void -inline __attribute__((always_inline)) +inline channel_init_consumer (Channel_consumer *ch, const char *identifier, QueueHandle_t queue) { @@ -107,7 +107,7 @@ channel_init_consumer */ static void -inline __attribute__((always_inline)) +inline channel_init_producer (Channel_producer *ch, const char *identifier) { @@ -121,7 +121,7 @@ channel_init_producer */ static void -inline __attribute__((always_inline)) +inline channel_setContext (Channel *ch, void *ctx) { @@ -135,7 +135,7 @@ channel_setContext */ static void -inline __attribute__((always_inline)) +inline channel_setCallback (Channel *ch, const Channel_callback callback) { @@ -172,7 +172,7 @@ channel_send */ static void -inline __attribute__((always_inline)) +inline channel_broadcast_init (Channel_broadcast *handle, const Channel_producer *ch, const void *data, const TickType_t timeout) { @@ -189,7 +189,7 @@ channel_broadcast_init */ static bool -inline __attribute__((always_inline)) +inline channel_broadcast_timeout (Channel_broadcast *handle) { @@ -209,7 +209,7 @@ channel_broadcast_timeout */ static bool -inline __attribute__((always_inline)) +inline channel_broadcast_finished (const Channel_broadcast *handle) { From fd22ae5d3f7a9f6374e72cec43d393ac306c0d6f Mon Sep 17 00:00:00 2001 From: jonathan-may Date: Tue, 5 May 2020 12:44:32 +0200 Subject: [PATCH 17/17] debug --- components/channel/test/channel_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c index 0cef8ca..8187ce8 100644 --- a/components/channel/test/channel_test.c +++ b/components/channel/test/channel_test.c @@ -502,8 +502,8 @@ TEST_CASE("channel_queue", "[channel]") Channel_broadcast br; int test_data = 3; channel_broadcast_init(&br, &cp, &test_data, 1); - - TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); + UBaseType_t res = channel_broadcast(&br); + TEST_ASSERT_EQUAL_INT(pdPASS, res); vTaskDelay(5); TEST_ASSERT(channel_broadcast_finished(&br));