chris@163: /** chris@163: * Copyright (c) 2014, 2015, Enzien Audio Ltd. chris@163: * chris@163: * Permission to use, copy, modify, and/or distribute this software for any chris@163: * purpose with or without fee is hereby granted, provided that the above chris@163: * copyright notice and this permission notice appear in all copies. chris@163: * chris@163: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH chris@163: * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY chris@163: * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, chris@163: * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM chris@163: * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR chris@163: * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR chris@163: * PERFORMANCE OF THIS SOFTWARE. chris@163: */ chris@163: chris@163: #include "MessageQueue.h" chris@163: #include "Utils.h" chris@163: chris@163: hv_size_t mq_init(MessageQueue *q) { chris@163: q->head = NULL; chris@163: q->tail = NULL; chris@163: q->pool = NULL; chris@163: return mp_init(&q->mp, 1); chris@163: } chris@163: chris@163: void mq_initWithPoolSize(MessageQueue *q, hv_size_t poolSizeKB) { chris@163: q->head = NULL; chris@163: q->tail = NULL; chris@163: q->pool = NULL; chris@163: mp_init(&q->mp, poolSizeKB); chris@163: } chris@163: chris@163: void mq_free(MessageQueue *q) { chris@163: mq_clear(q); chris@163: while (q->pool != NULL) { chris@163: MessageNode *n = q->pool; chris@163: q->pool = q->pool->next; chris@163: hv_free(n); chris@163: } chris@163: mp_free(&q->mp); chris@163: } chris@163: chris@163: static MessageNode *mq_getOrCreateNodeFromPool(MessageQueue *q) { chris@163: if (q->pool == NULL) { chris@163: // if necessary, create a new empty node chris@163: q->pool = (MessageNode *) hv_malloc(sizeof(MessageNode)); chris@163: q->pool->next = NULL; chris@163: } chris@163: MessageNode *node = q->pool; chris@163: q->pool = q->pool->next; chris@163: return node; chris@163: } chris@163: chris@163: int mq_size(MessageQueue *q) { chris@163: int size = 0; chris@163: MessageNode *n = q->head; chris@163: while (n != NULL) { chris@163: ++size; chris@163: n = n->next; chris@163: } chris@163: return size; chris@163: } chris@163: chris@163: HvMessage *mq_addMessage(MessageQueue *q, const HvMessage *m, int let, chris@163: void (*sendMessage)(struct HvBase *, int, const HvMessage *)) { chris@163: MessageNode *node = mq_getOrCreateNodeFromPool(q); chris@163: node->m = mp_addMessage(&q->mp, m); chris@163: node->let = let; chris@163: node->sendMessage = sendMessage; chris@163: node->prev = NULL; chris@163: node->next = NULL; chris@163: chris@163: if (q->tail != NULL) { chris@163: // the list already contains elements chris@163: q->tail->next = node; chris@163: node->prev = q->tail; chris@163: q->tail = node; chris@163: } else { chris@163: // the list is empty chris@163: node->prev = NULL; chris@163: q->head = node; chris@163: q->tail = node; chris@163: } chris@163: return mq_node_getMessage(node); chris@163: } chris@163: chris@163: HvMessage *mq_addMessageByTimestamp(MessageQueue *q, HvMessage *m, int let, chris@163: void (*sendMessage)(struct HvBase *, int, const HvMessage *)) { chris@163: if (mq_hasMessage(q)) { chris@163: MessageNode *n = mq_getOrCreateNodeFromPool(q); chris@163: n->m = mp_addMessage(&q->mp, m); chris@163: n->let = let; chris@163: n->sendMessage = sendMessage; chris@163: chris@163: if (msg_getTimestamp(m) < msg_getTimestamp(q->head->m)) { chris@163: // the message occurs before the current head chris@163: n->next = q->head; chris@163: q->head->prev = n; chris@163: n->prev = NULL; chris@163: q->head = n; chris@163: } else if (msg_getTimestamp(m) >= msg_getTimestamp(q->tail->m)) { chris@163: // the message occurs after the current tail chris@163: n->next = NULL; chris@163: n->prev = q->tail; chris@163: q->tail->next = n; chris@163: q->tail = n; chris@163: } else { chris@163: // the message occurs somewhere between the head and tail chris@163: MessageNode *node = q->head; chris@163: while (node != NULL) { chris@163: if (m->timestamp < msg_getTimestamp(node->next->m)) { chris@163: MessageNode *r = node->next; chris@163: node->next = n; chris@163: n->next = r; chris@163: n->prev = node; chris@163: r->prev = n; chris@163: break; chris@163: } chris@163: node = node->next; chris@163: } chris@163: } chris@163: return n->m; chris@163: } else { chris@163: // add a message to the head chris@163: return mq_addMessage(q, m, let, sendMessage); chris@163: } chris@163: } chris@163: chris@163: void mq_pop(MessageQueue *q) { chris@163: if (mq_hasMessage(q)) { chris@163: MessageNode *n = q->head; chris@163: chris@163: mp_freeMessage(&q->mp, n->m); chris@163: n->m = NULL; chris@163: chris@163: n->let = 0; chris@163: n->sendMessage = NULL; chris@163: chris@163: q->head = n->next; chris@163: if (q->head == NULL) { chris@163: q->tail = NULL; chris@163: } else { chris@163: q->head->prev = NULL; chris@163: } chris@163: n->next = q->pool; chris@163: n->prev = NULL; chris@163: q->pool = n; chris@163: } chris@163: } chris@163: chris@163: void mq_removeMessage(MessageQueue *q, HvMessage *m, void (*sendMessage)(struct HvBase *, int, const HvMessage *)) { chris@163: if (mq_hasMessage(q)) { chris@163: if (mq_node_getMessage(q->head) == m) { // msg in head node chris@163: // only remove the message if sendMessage is the same as the stored one, chris@163: // if the sendMessage argument is NULL, it is not checked and will remove any matching message pointer chris@163: if (sendMessage == NULL || q->head->sendMessage == sendMessage) { chris@163: mq_pop(q); chris@163: } chris@163: } else { chris@163: MessageNode *prevNode = q->head; chris@163: MessageNode *currNode = q->head->next; chris@163: while ((currNode != NULL) && (currNode->m != m)) { chris@163: prevNode = currNode; chris@163: currNode = currNode->next; chris@163: } chris@163: if (currNode != NULL) { chris@163: if (sendMessage == NULL || currNode->sendMessage == sendMessage) { chris@163: mp_freeMessage(&q->mp, m); chris@163: currNode->m = NULL; chris@163: currNode->let = 0; chris@163: currNode->sendMessage = NULL; chris@163: if (currNode == q->tail) { // msg in tail node chris@163: prevNode->next = NULL; chris@163: q->tail = prevNode; chris@163: } else { // msg in middle node chris@163: prevNode->next = currNode->next; chris@163: currNode->next->prev = prevNode; chris@163: } chris@163: currNode->next = (q->pool == NULL) ? NULL : q->pool; chris@163: currNode->prev = NULL; chris@163: q->pool = currNode; chris@163: } chris@163: } chris@163: } chris@163: } chris@163: } chris@163: chris@163: void mq_clear(MessageQueue *q) { chris@163: while (mq_hasMessage(q)) { chris@163: mq_pop(q); chris@163: } chris@163: } chris@163: chris@163: void mq_clearAfter(MessageQueue *q, const double timestamp) { chris@163: MessageNode *n = q->tail; chris@163: while (n != NULL && timestamp <= msg_getTimestamp(n->m)) { chris@163: // free the node's message chris@163: mp_freeMessage(&q->mp, n->m); chris@163: n->m = NULL; chris@163: n->let = 0; chris@163: n->sendMessage = NULL; chris@163: chris@163: // the tail points at the previous node chris@163: q->tail = n->prev; chris@163: chris@163: // put the node back in the pool chris@163: n->next = q->pool; chris@163: n->prev = NULL; chris@163: if (q->pool != NULL) q->pool->prev = n; chris@163: q->pool = n; chris@163: chris@163: // update the tail node chris@163: n = q->tail; chris@163: } chris@163: chris@163: if (q->tail == NULL) q->head = NULL; chris@163: }