/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2006-05-27 Bernard implement memory pool * 2006-06-03 Bernard fix the thread timer init bug * 2006-06-30 Bernard fix the allocate/free block bug * 2006-08-04 Bernard add hook support * 2006-08-10 Bernard fix interrupt bug in rt_mp_alloc * 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca * 2010-10-26 yi.qiu add module support in rt_mp_delete * 2011-01-24 Bernard add object allocation check. * 2012-03-22 Bernard fix align issue in rt_mp_init and rt_mp_create. * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to mempool.c */ #include #include #ifdef RT_USING_MEMPOOL #ifndef __on_rt_mp_alloc_hook #define __on_rt_mp_alloc_hook(mp, block) __ON_HOOK_ARGS(rt_mp_alloc_hook, (mp, block)) #endif #ifndef __on_rt_mp_free_hook #define __on_rt_mp_free_hook(mp, block) __ON_HOOK_ARGS(rt_mp_free_hook, (mp, block)) #endif #if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR) static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block); static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block); /** * @addtogroup Hook */ /**@{*/ /** * @brief This function will set a hook function, which will be invoked when a memory * block is allocated from the memory pool. * * @param hook the hook function */ void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block)) { rt_mp_alloc_hook = hook; } /** * @brief This function will set a hook function, which will be invoked when a memory * block is released to the memory pool. * * @param hook the hook function */ void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block)) { rt_mp_free_hook = hook; } /**@}*/ #endif /* RT_USING_HOOK */ /** * @addtogroup MM */ /**@{*/ /** * @brief This function will initialize a memory pool object, normally which is used * for static object. * * @param mp is the memory pool object. * * @param name is the name of the memory pool. * * @param start is the start address of the memory pool. * * @param size is the total size of the memory pool. * * @param block_size is the size for each block.. * * @return RT_EOK */ rt_err_t rt_mp_init(struct rt_mempool *mp, const char *name, void *start, rt_size_t size, rt_size_t block_size) { rt_uint8_t *block_ptr; register rt_size_t offset; /* parameter check */ RT_ASSERT(mp != RT_NULL); RT_ASSERT(name != RT_NULL); RT_ASSERT(start != RT_NULL); RT_ASSERT(size > 0 && block_size > 0); /* initialize object */ rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name); /* initialize memory pool */ mp->start_address = start; mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE); /* align the block size */ block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE); mp->block_size = block_size; /* align to align size byte */ mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *)); mp->block_free_count = mp->block_total_count; /* initialize suspended thread list */ rt_list_init(&(mp->suspend_thread)); /* initialize free block list */ block_ptr = (rt_uint8_t *)mp->start_address; for (offset = 0; offset < mp->block_total_count; offset ++) { *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) = (rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *))); } *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) = RT_NULL; mp->block_list = block_ptr; return RT_EOK; } RTM_EXPORT(rt_mp_init); /** * @brief This function will detach a memory pool from system object management. * * @param mp is the memory pool object. * * @return RT_EOK */ rt_err_t rt_mp_detach(struct rt_mempool *mp) { struct rt_thread *thread; rt_base_t level; /* parameter check */ RT_ASSERT(mp != RT_NULL); RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool); RT_ASSERT(rt_object_is_systemobject(&mp->parent)); /* wake up all suspended threads */ while (!rt_list_isempty(&(mp->suspend_thread))) { /* disable interrupt */ level = rt_hw_interrupt_disable(); /* get next suspend thread */ thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); /* set error code to RT_ERROR */ thread->error = -RT_ERROR; /* * resume thread * In rt_thread_resume function, it will remove current thread from * suspend list */ rt_thread_resume(thread); /* enable interrupt */ rt_hw_interrupt_enable(level); } /* detach object */ rt_object_detach(&(mp->parent)); return RT_EOK; } RTM_EXPORT(rt_mp_detach); #ifdef RT_USING_HEAP /** * @brief This function will create a mempool object and allocate the memory pool from * heap. * * @param name is the name of memory pool. * * @param block_count is the count of blocks in memory pool. * * @param block_size is the size for each block. * * @return the created mempool object */ rt_mp_t rt_mp_create(const char *name, rt_size_t block_count, rt_size_t block_size) { rt_uint8_t *block_ptr; struct rt_mempool *mp; register rt_size_t offset; RT_DEBUG_NOT_IN_INTERRUPT; /* parameter check */ RT_ASSERT(name != RT_NULL); RT_ASSERT(block_count > 0 && block_size > 0); /* allocate object */ mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name); /* allocate object failed */ if (mp == RT_NULL) return RT_NULL; /* initialize memory pool */ block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE); mp->block_size = block_size; mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count; /* allocate memory */ mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) * block_count); if (mp->start_address == RT_NULL) { /* no memory, delete memory pool object */ rt_object_delete(&(mp->parent)); return RT_NULL; } mp->block_total_count = block_count; mp->block_free_count = mp->block_total_count; /* initialize suspended thread list */ rt_list_init(&(mp->suspend_thread)); /* initialize free block list */ block_ptr = (rt_uint8_t *)mp->start_address; for (offset = 0; offset < mp->block_total_count; offset ++) { *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) = block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)); } *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) = RT_NULL; mp->block_list = block_ptr; return mp; } RTM_EXPORT(rt_mp_create); /** * @brief This function will delete a memory pool and release the object memory. * * @param mp is the memory pool object. * * @return RT_EOK */ rt_err_t rt_mp_delete(rt_mp_t mp) { struct rt_thread *thread; rt_base_t level; RT_DEBUG_NOT_IN_INTERRUPT; /* parameter check */ RT_ASSERT(mp != RT_NULL); RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool); RT_ASSERT(rt_object_is_systemobject(&mp->parent) == RT_FALSE); /* wake up all suspended threads */ while (!rt_list_isempty(&(mp->suspend_thread))) { /* disable interrupt */ level = rt_hw_interrupt_disable(); /* get next suspend thread */ thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); /* set error code to RT_ERROR */ thread->error = -RT_ERROR; /* * resume thread * In rt_thread_resume function, it will remove current thread from * suspend list */ rt_thread_resume(thread); /* enable interrupt */ rt_hw_interrupt_enable(level); } /* release allocated room */ rt_free(mp->start_address); /* detach object */ rt_object_delete(&(mp->parent)); return RT_EOK; } RTM_EXPORT(rt_mp_delete); #endif /* RT_USING_HEAP */ /** * @brief This function will allocate a block from memory pool. * * @param mp is the memory pool object. * * @param time is the maximum waiting time for allocating memory. * - 0 for not waiting, allocating memory immediately. * * @return the allocated memory block or RT_NULL on allocated failed. */ void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time) { rt_uint8_t *block_ptr; rt_base_t level; struct rt_thread *thread; rt_uint32_t before_sleep = 0; /* parameter check */ RT_ASSERT(mp != RT_NULL); /* get current thread */ thread = rt_thread_self(); /* disable interrupt */ level = rt_hw_interrupt_disable(); while (mp->block_free_count == 0) { /* memory block is unavailable. */ if (time == 0) { /* enable interrupt */ rt_hw_interrupt_enable(level); rt_set_errno(-RT_ETIMEOUT); return RT_NULL; } RT_DEBUG_NOT_IN_INTERRUPT; thread->error = RT_EOK; /* need suspend thread */ rt_thread_suspend(thread); rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist)); if (time > 0) { /* get the start tick of timer */ before_sleep = rt_tick_get(); /* init thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &time); rt_timer_start(&(thread->thread_timer)); } /* enable interrupt */ rt_hw_interrupt_enable(level); /* do a schedule */ rt_schedule(); if (thread->error != RT_EOK) return RT_NULL; if (time > 0) { time -= rt_tick_get() - before_sleep; if (time < 0) time = 0; } /* disable interrupt */ level = rt_hw_interrupt_disable(); } /* memory block is available. decrease the free block counter */ mp->block_free_count--; /* get block from block list */ block_ptr = mp->block_list; RT_ASSERT(block_ptr != RT_NULL); /* Setup the next free node. */ mp->block_list = *(rt_uint8_t **)block_ptr; /* point to memory pool */ *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp; /* enable interrupt */ rt_hw_interrupt_enable(level); RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook, (mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *)))); return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *)); } RTM_EXPORT(rt_mp_alloc); /** * @brief This function will release a memory block. * * @param block the address of memory block to be released. */ void rt_mp_free(void *block) { rt_uint8_t **block_ptr; struct rt_mempool *mp; struct rt_thread *thread; rt_base_t level; /* parameter check */ if (block == RT_NULL) return; /* get the control block of pool which the block belongs to */ block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *)); mp = (struct rt_mempool *)*block_ptr; RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block)); /* disable interrupt */ level = rt_hw_interrupt_disable(); /* increase the free block count */ mp->block_free_count ++; /* link the block into the block list */ *block_ptr = mp->block_list; mp->block_list = (rt_uint8_t *)block_ptr; if (!rt_list_isempty(&(mp->suspend_thread))) { /* get the suspended thread */ thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); /* set error */ thread->error = RT_EOK; /* resume thread */ rt_thread_resume(thread); /* enable interrupt */ rt_hw_interrupt_enable(level); /* do a schedule */ rt_schedule(); return; } /* enable interrupt */ rt_hw_interrupt_enable(level); } RTM_EXPORT(rt_mp_free); /**@}*/ #endif /* RT_USING_MEMPOOL */