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"); | ||
|  |     } | ||
|  | } | ||
|  | 
 |