summaryrefslogtreecommitdiff
path: root/components/vbus/prio_queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'components/vbus/prio_queue.c')
-rw-r--r--components/vbus/prio_queue.c275
1 files changed, 275 insertions, 0 deletions
diff --git a/components/vbus/prio_queue.c b/components/vbus/prio_queue.c
new file mode 100644
index 0000000000..e0f57d1e05
--- /dev/null
+++ b/components/vbus/prio_queue.c
@@ -0,0 +1,275 @@
+/*
+ * Priority Queue
+ *
+ * COPYRIGHT (C) 2013, Shanghai Real-Thread Technology Co., Ltd
+ *
+ * This file is part of RT-Thread (http://www.rt-thread.org)
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2013-11-04 Grissiom add comment
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+
+#include "prio_queue.h"
+
+struct rt_prio_queue_item {
+ struct rt_prio_queue_item *next;
+ /* data follows */
+};
+
+static void _do_push(struct rt_prio_queue *que,
+ rt_uint8_t prio,
+ struct rt_prio_queue_item *item)
+{
+ if (que->head[prio] == RT_NULL)
+ {
+ que->head[prio] = item;
+ que->bitmap |= 1 << prio;
+ }
+ else
+ {
+ RT_ASSERT(que->tail[prio]);
+ que->tail[prio]->next = item;
+ }
+ que->tail[prio] = item;
+}
+
+static struct rt_prio_queue_item* _do_pop(struct rt_prio_queue *que)
+{
+ int ffs;
+ struct rt_prio_queue_item *item;
+
+ ffs = __rt_ffs(que->bitmap);
+ if (ffs == 0)
+ return RT_NULL;
+ ffs--;
+
+ item = que->head[ffs];
+ RT_ASSERT(item);
+
+ que->head[ffs] = item->next;
+ if (que->head[ffs] == RT_NULL)
+ {
+ que->bitmap &= ~(1 << ffs);
+ }
+
+ return item;
+}
+
+rt_err_t rt_prio_queue_init(struct rt_prio_queue *que,
+ const char *name,
+ void *buf,
+ rt_size_t bufsz,
+ rt_size_t itemsz)
+{
+ RT_ASSERT(que);
+
+ rt_memset(que, 0, sizeof(*que));
+
+ rt_list_init(&(que->suspended_pop_list));
+
+ rt_mp_init(&que->pool, name, buf, bufsz,
+ sizeof(struct rt_prio_queue_item) + itemsz);
+
+ que->item_sz = itemsz;
+
+ return RT_EOK;
+}
+
+void rt_prio_queue_detach(struct rt_prio_queue *que)
+{
+ /* wake up all suspended pop threads, push thread is suspended on mempool.
+ */
+ while (!rt_list_isempty(&(que->suspended_pop_list)))
+ {
+ rt_thread_t thread;
+
+ /* disable interrupt */
+ rt_ubase_t temp = rt_hw_interrupt_disable();
+
+ /* get next suspend thread */
+ thread = rt_list_entry(que->suspended_pop_list.next, struct rt_thread, tlist);
+ /* set error code to RT_ERROR */
+ thread->error = -RT_ERROR;
+
+ rt_thread_resume(thread);
+
+ /* enable interrupt */
+ rt_hw_interrupt_enable(temp);
+ }
+ rt_mp_detach(&que->pool);
+}
+
+#ifdef RT_USING_HEAP
+struct rt_prio_queue* rt_prio_queue_create(const char *name,
+ rt_size_t item_nr,
+ rt_size_t item_sz)
+{
+ struct rt_prio_queue *que;
+ rt_size_t bufsz;
+
+ bufsz = item_nr * (sizeof(struct rt_prio_queue_item)
+ + item_sz
+ + sizeof(void*));
+
+ RT_ASSERT(item_nr);
+
+ que = rt_malloc(sizeof(*que) + bufsz);
+ if (!que)
+ return RT_NULL;
+
+ rt_prio_queue_init(que, name, que+1, bufsz, item_sz);
+
+ return que;
+}
+
+void rt_prio_queue_delete(struct rt_prio_queue *que)
+{
+ rt_prio_queue_detach(que);
+ rt_free(que);
+}
+#endif
+
+rt_err_t rt_prio_queue_push(struct rt_prio_queue *que,
+ rt_uint8_t prio,
+ void *data,
+ rt_int32_t timeout)
+{
+ rt_ubase_t level;
+ struct rt_prio_queue_item *item;
+
+ RT_ASSERT(que);
+
+ if (prio >= RT_PRIO_QUEUE_PRIO_MAX)
+ return -RT_ERROR;
+
+ item = rt_mp_alloc(&que->pool, timeout);
+ if (item == RT_NULL)
+ return -RT_ENOMEM;
+
+ rt_memcpy(item+1, data, que->item_sz);
+ item->next = RT_NULL;
+
+ level = rt_hw_interrupt_disable();
+
+ _do_push(que, prio, item);
+
+ if (!rt_list_isempty(&(que->suspended_pop_list)))
+ {
+ rt_thread_t thread;
+
+ /* get thread entry */
+ thread = rt_list_entry(que->suspended_pop_list.next,
+ struct rt_thread,
+ tlist);
+ /* resume it */
+ rt_thread_resume(thread);
+ rt_hw_interrupt_enable(level);
+
+ /* perform a schedule */
+ rt_schedule();
+
+ return RT_EOK;
+ }
+
+ rt_hw_interrupt_enable(level);
+
+ return RT_EOK;
+}
+
+rt_err_t rt_prio_queue_pop(struct rt_prio_queue *que,
+ void *data,
+ rt_int32_t timeout)
+{
+ rt_ubase_t level;
+ struct rt_prio_queue_item *item;
+
+ RT_ASSERT(que);
+ RT_ASSERT(data);
+
+ level = rt_hw_interrupt_disable();
+ for (item = _do_pop(que);
+ item == RT_NULL;
+ item = _do_pop(que))
+ {
+ rt_thread_t thread;
+
+ if (timeout == 0)
+ {
+ rt_hw_interrupt_enable(level);
+ return -RT_ETIMEOUT;
+ }
+
+ RT_DEBUG_NOT_IN_INTERRUPT;
+
+ thread = rt_thread_self();
+ thread->error = RT_EOK;
+ rt_thread_suspend(thread);
+
+ rt_list_insert_before(&(que->suspended_pop_list), &(thread->tlist));
+
+ if (timeout > 0)
+ {
+ rt_timer_control(&(thread->thread_timer),
+ RT_TIMER_CTRL_SET_TIME,
+ &timeout);
+ rt_timer_start(&(thread->thread_timer));
+ }
+
+ rt_hw_interrupt_enable(level);
+
+ rt_schedule();
+
+ /* thread is waked up */
+ if (thread->error != RT_EOK)
+ return thread->error;
+ level = rt_hw_interrupt_disable();
+ }
+
+ rt_hw_interrupt_enable(level);
+
+ rt_memcpy(data, item+1, que->item_sz);
+ rt_mp_free(item);
+
+ return RT_EOK;
+}
+
+void rt_prio_queue_dump(struct rt_prio_queue *que)
+{
+ int level = 0;
+
+ rt_kprintf("bitmap: %08x\n", que->bitmap);
+ for (level = 0; level < RT_PRIO_QUEUE_PRIO_MAX; level++)
+ {
+ struct rt_prio_queue_item *item;
+
+ rt_kprintf("%2d: ", level);
+ for (item = que->head[level];
+ item;
+ item = item->next)
+ {
+ rt_kprintf("%p, ", item);
+ }
+ rt_kprintf("\n");
+ }
+}
+