| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 | /* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date           Author       Notes * 2012-04-10     Bernard      first implementation * 2012-10-16     Bernard      add the mutex lock for heap object. * 2012-12-29     Bernard      memheap can be used as system heap. *                             change mutex lock to semaphore lock. * 2013-04-10     Bernard      add rt_lwp_memheap_realloc function. * 2013-05-24     Bernard      fix the rt_lwp_memheap_realloc issue. * 2013-07-11     Grissiom     fix the memory block splitting issue. * 2013-07-15     Grissiom     optimize rt_lwp_memheap_realloc */#include <rthw.h>#include <rtthread.h>#include <lwp.h>/* dynamic pool magic and mask */#define RT_MEMHEAP_MAGIC        0x1ea01ea0#define RT_MEMHEAP_MASK         0xfffffffe#define RT_MEMHEAP_USED         0x01#define RT_MEMHEAP_FREED        0x00#define RT_MEMHEAP_IS_USED(i)   ((i)->magic & RT_MEMHEAP_USED)#define RT_MEMHEAP_MINIALLOC    12#define RT_MEMHEAP_SIZE         RT_ALIGN(sizeof(struct rt_lwp_memheap_item), RT_ALIGN_SIZE)#define MEMITEM_SIZE(item)      ((rt_uint32_t)item->next - (rt_uint32_t)item - RT_MEMHEAP_SIZE)/* * The initialized memory pool will be: * +-----------------------------------+--------------------------+ * | whole freed memory block          | Used Memory Block Tailer | * +-----------------------------------+--------------------------+ * * block_list --> whole freed memory block * * The length of Used Memory Block Tailer is 0, * which is prevents block merging across list */rt_err_t rt_lwp_memheap_init(struct rt_lwp_memheap *memheap,                         const char        *name,                         void              *start_addr,                         rt_uint32_t        size){    struct rt_lwp_memheap_item *item;    RT_ASSERT(memheap != RT_NULL);    /* initialize pool object */    memheap->start_addr     = start_addr;    memheap->pool_size      = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);    memheap->available_size = memheap->pool_size - (2 * RT_MEMHEAP_SIZE);    memheap->max_used_size  = memheap->pool_size - memheap->available_size;    /* initialize the free list header */    item            = &(memheap->free_header);    item->magic     = RT_MEMHEAP_MAGIC;    item->pool_ptr  = memheap;    item->next      = RT_NULL;    item->prev      = RT_NULL;    item->next_free = item;    item->prev_free = item;    /* set the free list to free list header */    memheap->free_list = item;    /* initialize the first big memory block */    item            = (struct rt_lwp_memheap_item *)start_addr;    item->magic     = RT_MEMHEAP_MAGIC;    item->pool_ptr  = memheap;    item->next      = RT_NULL;    item->prev      = RT_NULL;    item->next_free = item;    item->prev_free = item;    item->next = (struct rt_lwp_memheap_item *)                 ((rt_uint8_t *)item + memheap->available_size + RT_MEMHEAP_SIZE);    item->prev = item->next;    /* block list header */    memheap->block_list = item;    /* place the big memory block to free list */    item->next_free = memheap->free_list->next_free;    item->prev_free = memheap->free_list;    memheap->free_list->next_free->prev_free = item;    memheap->free_list->next_free            = item;    /* move to the end of memory pool to build a small tailer block,     * which prevents block merging     */    item = item->next;    /* it's a used memory block */    item->magic     = RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED;    item->pool_ptr  = memheap;    item->next      = (struct rt_lwp_memheap_item *)start_addr;    item->prev      = (struct rt_lwp_memheap_item *)start_addr;    /* not in free list */    item->next_free = item->prev_free = RT_NULL;    /* initialize semaphore lock */    rt_sem_init(&(memheap->lock), name, 1, RT_IPC_FLAG_FIFO);    RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                 ("memory heap: start addr 0x%08x, size %d, free list header 0x%08x\n",                  start_addr, size, &(memheap->free_header)));    return RT_EOK;}void *rt_lwp_memheap_alloc(struct rt_lwp_memheap *heap, rt_uint32_t size){    rt_err_t result;    rt_uint32_t free_size;    struct rt_lwp_memheap_item *header_ptr;    RT_ASSERT(heap != RT_NULL);    /* align allocated size */    size = RT_ALIGN(size, RT_ALIGN_SIZE);    if (size < RT_MEMHEAP_MINIALLOC)        size = RT_MEMHEAP_MINIALLOC;    RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate %d on heap:%8.*s",                                    size, RT_NAME_MAX, heap->parent.name));    if (size < heap->available_size)    {        /* search on free list */        free_size = 0;        /* lock memheap */        result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);        if (result != RT_EOK)        {            rt_set_errno(result);            return RT_NULL;        }        /* get the first free memory block */        header_ptr = heap->free_list->next_free;        while (header_ptr != heap->free_list && free_size < size)        {            /* get current freed memory block size */            free_size = MEMITEM_SIZE(header_ptr);            if (free_size < size)            {                /* move to next free memory block */                header_ptr = header_ptr->next_free;            }        }        /* determine if the memory is available. */        if (free_size >= size)        {            /* a block that satisfies the request has been found. */            /* determine if the block needs to be split. */            if (free_size >= (size + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC))            {                struct rt_lwp_memheap_item *new_ptr;                /* split the block. */                new_ptr = (struct rt_lwp_memheap_item *)                          (((rt_uint8_t *)header_ptr) + size + RT_MEMHEAP_SIZE);                RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                             ("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]\n",                              header_ptr,                              header_ptr->next,                              header_ptr->prev,                              new_ptr));                /* mark the new block as a memory block and freed. */                new_ptr->magic = RT_MEMHEAP_MAGIC;                /* put the pool pointer into the new block. */                new_ptr->pool_ptr = heap;                /* break down the block list */                new_ptr->prev          = header_ptr;                new_ptr->next          = header_ptr->next;                header_ptr->next->prev = new_ptr;                header_ptr->next       = new_ptr;                /* remove header ptr from free list */                header_ptr->next_free->prev_free = header_ptr->prev_free;                header_ptr->prev_free->next_free = header_ptr->next_free;                header_ptr->next_free = RT_NULL;                header_ptr->prev_free = RT_NULL;                /* insert new_ptr to free list */                new_ptr->next_free = heap->free_list->next_free;                new_ptr->prev_free = heap->free_list;                heap->free_list->next_free->prev_free = new_ptr;                heap->free_list->next_free            = new_ptr;                RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: next_free 0x%08x, prev_free 0x%08x\n",                                                new_ptr->next_free,                                                new_ptr->prev_free));                /* decrement the available byte count.  */                heap->available_size = heap->available_size -                                       size -                                       RT_MEMHEAP_SIZE;                if (heap->pool_size - heap->available_size > heap->max_used_size)                    heap->max_used_size = heap->pool_size - heap->available_size;            }            else            {                /* decrement the entire free size from the available bytes count. */                heap->available_size = heap->available_size - free_size;                if (heap->pool_size - heap->available_size > heap->max_used_size)                    heap->max_used_size = heap->pool_size - heap->available_size;                /* remove header_ptr from free list */                RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                             ("one block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x\n",                              header_ptr,                              header_ptr->next_free,                              header_ptr->prev_free));                header_ptr->next_free->prev_free = header_ptr->prev_free;                header_ptr->prev_free->next_free = header_ptr->next_free;                header_ptr->next_free = RT_NULL;                header_ptr->prev_free = RT_NULL;            }            /* Mark the allocated block as not available. */            header_ptr->magic |= RT_MEMHEAP_USED;            /* release lock */            rt_sem_release(&(heap->lock));            /* Return a memory address to the caller.  */            RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                         ("alloc mem: memory[0x%08x], heap[0x%08x], size: %d\n",                          (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE),                          header_ptr,                          size));            return (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE);        }        /* release lock */        rt_sem_release(&(heap->lock));    }    RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate memory: failed\n"));    /* Return the completion status.  */    return RT_NULL;}void *rt_lwp_memheap_realloc(struct rt_lwp_memheap *heap, void *ptr, rt_size_t newsize){    rt_err_t result;    rt_size_t oldsize;    struct rt_lwp_memheap_item *header_ptr;    struct rt_lwp_memheap_item *new_ptr;    if (newsize == 0)    {        rt_lwp_memheap_free(ptr);        return RT_NULL;    }    /* align allocated size */    newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);    if (newsize < RT_MEMHEAP_MINIALLOC)        newsize = RT_MEMHEAP_MINIALLOC;    if (ptr == RT_NULL)    {        return rt_lwp_memheap_alloc(heap, newsize);    }    /* get memory block header and get the size of memory block */    header_ptr = (struct rt_lwp_memheap_item *)                 ((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE);    oldsize = MEMITEM_SIZE(header_ptr);    /* re-allocate memory */    if (newsize > oldsize)    {        void *new_ptr;        struct rt_lwp_memheap_item *next_ptr;        /* lock memheap */        result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);        if (result != RT_EOK)        {            rt_set_errno(result);            return RT_NULL;        }        next_ptr = header_ptr->next;        /* header_ptr should not be the tail */        RT_ASSERT(next_ptr > header_ptr);        /* check whether the following free space is enough to expand */        if (!RT_MEMHEAP_IS_USED(next_ptr))        {            rt_int32_t nextsize;            nextsize = MEMITEM_SIZE(next_ptr);            RT_ASSERT(next_ptr > 0);            /* Here is the ASCII art of the situation that we can make use of             * the next free node without alloc/memcpy, |*| is the control             * block:             *             *      oldsize           free node             * |*|-----------|*|----------------------|*|             *         newsize          >= minialloc             * |*|----------------|*|-----------------|*|             */            if (nextsize + oldsize > newsize + RT_MEMHEAP_MINIALLOC)            {                /* decrement the entire free size from the available bytes count. */                heap->available_size = heap->available_size - (newsize - oldsize);                if (heap->pool_size - heap->available_size > heap->max_used_size)                    heap->max_used_size = heap->pool_size - heap->available_size;                /* remove next_ptr from free list */                RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                             ("remove block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x",                              next_ptr,                              next_ptr->next_free,                              next_ptr->prev_free));                next_ptr->next_free->prev_free = next_ptr->prev_free;                next_ptr->prev_free->next_free = next_ptr->next_free;                next_ptr->next->prev = next_ptr->prev;                next_ptr->prev->next = next_ptr->next;                /* build a new one on the right place */                next_ptr = (struct rt_lwp_memheap_item *)((char *)ptr + newsize);                RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                             ("new free block: block[0x%08x] nextm[0x%08x] prevm[0x%08x]",                              next_ptr,                              next_ptr->next,                              next_ptr->prev));                /* mark the new block as a memory block and freed. */                next_ptr->magic = RT_MEMHEAP_MAGIC;                /* put the pool pointer into the new block. */                next_ptr->pool_ptr = heap;                next_ptr->prev          = header_ptr;                next_ptr->next          = header_ptr->next;                header_ptr->next->prev = next_ptr;                header_ptr->next       = next_ptr;                /* insert next_ptr to free list */                next_ptr->next_free = heap->free_list->next_free;                next_ptr->prev_free = heap->free_list;                heap->free_list->next_free->prev_free = next_ptr;                heap->free_list->next_free            = next_ptr;                RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: next_free 0x%08x, prev_free 0x%08x",                                                next_ptr->next_free,                                                next_ptr->prev_free));                /* release lock */                rt_sem_release(&(heap->lock));                return ptr;            }        }        /* release lock */        rt_sem_release(&(heap->lock));        /* re-allocate a memory block */        new_ptr = (void *)rt_lwp_memheap_alloc(heap, newsize);        if (new_ptr != RT_NULL)        {            rt_memcpy(new_ptr, ptr, oldsize < newsize ? oldsize : newsize);            rt_lwp_memheap_free(ptr);        }        return new_ptr;    }    /* don't split when there is less than one node space left */    if (newsize + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC >= oldsize)        return ptr;    /* lock memheap */    result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);    if (result != RT_EOK)    {        rt_set_errno(result);        return RT_NULL;    }    /* split the block. */    new_ptr = (struct rt_lwp_memheap_item *)              (((rt_uint8_t *)header_ptr) + newsize + RT_MEMHEAP_SIZE);    RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                 ("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]\n",                  header_ptr,                  header_ptr->next,                  header_ptr->prev,                  new_ptr));    /* mark the new block as a memory block and freed. */    new_ptr->magic = RT_MEMHEAP_MAGIC;    /* put the pool pointer into the new block. */    new_ptr->pool_ptr = heap;    /* break down the block list */    new_ptr->prev          = header_ptr;    new_ptr->next          = header_ptr->next;    header_ptr->next->prev = new_ptr;    header_ptr->next       = new_ptr;    /* determine if the block can be merged with the next neighbor. */    if (!RT_MEMHEAP_IS_USED(new_ptr->next))    {        struct rt_lwp_memheap_item *free_ptr;        /* merge block with next neighbor. */        free_ptr = new_ptr->next;        heap->available_size = heap->available_size - MEMITEM_SIZE(free_ptr);        RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                     ("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x\n",                      header_ptr, header_ptr->next_free, header_ptr->prev_free));        free_ptr->next->prev = new_ptr;        new_ptr->next   = free_ptr->next;        /* remove free ptr from free list */        free_ptr->next_free->prev_free = free_ptr->prev_free;        free_ptr->prev_free->next_free = free_ptr->next_free;    }    /* insert the split block to free list */    new_ptr->next_free = heap->free_list->next_free;    new_ptr->prev_free = heap->free_list;    heap->free_list->next_free->prev_free = new_ptr;    heap->free_list->next_free            = new_ptr;    RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new free ptr: next_free 0x%08x, prev_free 0x%08x\n",                                    new_ptr->next_free,                                    new_ptr->prev_free));    /* increment the available byte count.  */    heap->available_size = heap->available_size + MEMITEM_SIZE(new_ptr);    /* release lock */    rt_sem_release(&(heap->lock));    /* return the old memory block */    return ptr;}void rt_lwp_memheap_free(void *ptr){    rt_err_t result;    struct rt_lwp_memheap *heap;    struct rt_lwp_memheap_item *header_ptr, *new_ptr;    rt_uint32_t insert_header;    /* NULL check */    if (ptr == RT_NULL) return;    /* set initial status as OK */    insert_header = 1;    new_ptr       = RT_NULL;    header_ptr    = (struct rt_lwp_memheap_item *)                    ((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE);    RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("free memory: memory[0x%08x], block[0x%08x]\n",                                    ptr, header_ptr));    /* check magic */    RT_ASSERT((header_ptr->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC);    RT_ASSERT(header_ptr->magic & RT_MEMHEAP_USED);    /* check whether this block of memory has been over-written. */    RT_ASSERT((header_ptr->next->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC);    /* get pool ptr */    heap = header_ptr->pool_ptr;    /* lock memheap */    result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);    if (result != RT_EOK)    {        rt_set_errno(result);        return ;    }    /* Mark the memory as available. */    header_ptr->magic &= ~RT_MEMHEAP_USED;    /* Adjust the available number of bytes. */    heap->available_size = heap->available_size + MEMITEM_SIZE(header_ptr);    /* Determine if the block can be merged with the previous neighbor. */    if (!RT_MEMHEAP_IS_USED(header_ptr->prev))    {        RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("merge: left node 0x%08x\n",                                        header_ptr->prev));        /* adjust the available number of bytes. */        heap->available_size = heap->available_size + RT_MEMHEAP_SIZE;        /* yes, merge block with previous neighbor. */        (header_ptr->prev)->next = header_ptr->next;        (header_ptr->next)->prev = header_ptr->prev;        /* move header pointer to previous. */        header_ptr = header_ptr->prev;        /* don't insert header to free list */        insert_header = 0;    }    /* determine if the block can be merged with the next neighbor. */    if (!RT_MEMHEAP_IS_USED(header_ptr->next))    {        /* adjust the available number of bytes. */        heap->available_size = heap->available_size + RT_MEMHEAP_SIZE;        /* merge block with next neighbor. */        new_ptr = header_ptr->next;        RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                     ("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x\n",                      new_ptr, new_ptr->next_free, new_ptr->prev_free));        new_ptr->next->prev = header_ptr;        header_ptr->next    = new_ptr->next;        /* remove new ptr from free list */        new_ptr->next_free->prev_free = new_ptr->prev_free;        new_ptr->prev_free->next_free = new_ptr->next_free;    }    if (insert_header)    {        /* no left merge, insert to free list */        header_ptr->next_free = heap->free_list->next_free;        header_ptr->prev_free = heap->free_list;        heap->free_list->next_free->prev_free = header_ptr;        heap->free_list->next_free            = header_ptr;        RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,                     ("insert to free list: next_free 0x%08x, prev_free 0x%08x\n",                      header_ptr->next_free, header_ptr->prev_free));    }    /* release lock */    rt_sem_release(&(heap->lock));}rt_bool_t rt_lwp_memheap_is_empty(struct rt_lwp_memheap *memheap){    RT_ASSERT(memheap != RT_NULL);    return (memheap->available_size + 2 * sizeof(struct rt_lwp_memheap_item)) == memheap->pool_size;}rt_bool_t rt_lwp_memheap_unavailable_size_get(void){    return 2 * RT_MEMHEAP_SIZE + 3;}
 |