258 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			258 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * COPYRIGHT (C) 2018, Real-Thread Information Technology Ltd
							 | 
						||
| 
								 | 
							
								 * 
							 | 
						||
| 
								 | 
							
								 * SPDX-License-Identifier: Apache-2.0
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * 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");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 |