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