memheap.c 12.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * File      : memheap.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2012, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rt-thread.org/license/LICENSE
 *
 * Change Logs:
 * Date           Author       Notes
 * 2012-04-10     Bernard      first implementation
13
 * 2012-10-16     Bernard      add the mutex lock for heap object.
14
 */
15

16 17 18 19 20
#include <rtthread.h>

#ifdef RT_USING_MEMHEAP

/* dynamic pool magic and mask */
21 22 23 24
#define RT_MEMHEAP_MAGIC        0x1ea01ea0
#define RT_MEMHEAP_MASK         0xfffffffe
#define RT_MEMHEAP_USED         0x01
#define RT_MEMHEAP_FREED        0x00
25

26 27
#define RT_MEMHEAP_IS_USED(i)   ((i)->magic & RT_MEMHEAP_USED)
#define RT_MEMHEAP_MINIALLOC    12
28

29
#define RT_MEMHEAP_SIZE     RT_ALIGN(sizeof(struct rt_memheap_item), RT_ALIGN_SIZE)
30 31 32 33 34 35 36 37 38

/*
 * The initialized memory pool will be:
 * +-----------------------------------+--------------------------+
 * | whole freed memory block          | Used Memory Block Tailer |
 * +-----------------------------------+--------------------------+
 *
 * block_list --> whole freed memory block
 *
39 40
 * The length of Used Memory Block Tailer is 0,
 * which is prevents block merging across list
41
 */
42 43 44 45
rt_err_t rt_memheap_init(struct rt_memheap *memheap,
                         const char        *name,
                         void              *start_addr,
                         rt_uint32_t        size)
46
{
47
    struct rt_memheap_item *item;
48

49
    RT_ASSERT(memheap != RT_NULL);
50

51 52
    /* initialize pool object */
    rt_object_init(&(memheap->parent), RT_Object_Class_MemHeap, name);
53

54 55
    memheap->start_addr     = start_addr;
    memheap->pool_size      = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
56 57
    memheap->available_size = memheap->pool_size - (2 * RT_MEMHEAP_SIZE);

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
    /* 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_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_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_memheap_item *)start_addr;
    item->prev      = (struct rt_memheap_item *)start_addr;
    /* not in free list */
    item->next_free = item->prev_free = RT_NULL;

    /* initialize mutex lock */
    rt_mutex_init(&(memheap->lock), name, RT_IPC_FLAG_FIFO);

    RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
108 109
                 ("memory heap: start addr 0x%08x, size %d, free list header 0x%08x",
                  start_addr, size, &(memheap->free_header)));
110

111
    return RT_EOK;
112
}
113
RTM_EXPORT(rt_memheap_init);
114

115
rt_err_t rt_memheap_detach(struct rt_memheap *heap)
116
{
117
    RT_ASSERT(heap);
118

119 120
    rt_object_detach(&(heap->lock.parent.parent));
    rt_object_detach(&(heap->parent));
121

122 123
    /* Return a successful completion. */
    return RT_EOK;
124
}
125
RTM_EXPORT(rt_memheap_detach);
126

127
void *rt_memheap_alloc(struct rt_memheap *heap, rt_uint32_t size)
128
{
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    rt_err_t result;
    rt_uint32_t free_size;
    struct rt_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", size));

    if (size < heap->available_size)
    {
        /* search on free list */
        free_size = 0;

        /* lock memheap */
        result = rt_mutex_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 = (rt_uint32_t)(header_ptr->next) -
                        (rt_uint32_t)header_ptr -
                        RT_MEMHEAP_SIZE;

            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_memheap_item *new_ptr;

                /* split the block. */
                new_ptr = (struct rt_memheap_item *)
                          (((rt_uint8_t *)header_ptr) + size + RT_MEMHEAP_SIZE);

                RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
187 188 189 190 191
                             ("split: h[0x%08x] nm[0x%08x] pm[0x%08x] to n[0x%08x]",
                              header_ptr,
                              header_ptr->next,
                              header_ptr->prev,
                              new_ptr));
192

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
                /* 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: nf 0x%08x, pf 0x%08x",
217 218
                                                new_ptr->next_free,
                                                new_ptr->prev_free));
219

220 221 222 223 224 225 226 227 228 229 230 231
                /* decrement the available byte count.  */
                heap->available_size = heap->available_size -
                                       size -
                                       RT_MEMHEAP_SIZE;
            }
            else
            {
                /* decrement the entire free size from the available bytes count. */
                heap->available_size = heap->available_size - free_size;

                /* remove header_ptr from free list */
                RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
232 233 234 235
                             ("one block: h[0x%08x], nf 0x%08x, pf 0x%08x",
                              header_ptr,
                              header_ptr->next_free,
                              header_ptr->prev_free));
236

237 238 239 240 241
                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;
            }
242

243 244 245 246 247
            /* release lock */
            rt_mutex_release(&(heap->lock));
            
            /* Mark the allocated block as not available. */
            header_ptr->magic |= RT_MEMHEAP_USED;
248

249 250
            /* Return a memory address to the caller.  */
            RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
251 252 253 254
                         ("am: m[0x%08x], h[0x%08x], size: %d",
                          (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE),
                          header_ptr,
                          size);
255

256 257
            return (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE));
        }
258

259 260 261
        /* release lock */
        rt_mutex_release(&(heap->lock));
    }
262

263
    RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate memory: failed\n"));
264 265 266 267

    /* Return the completion status.  */
    return RT_NULL;
}
268
RTM_EXPORT(rt_memheap_alloc);
269

270
void rt_memheap_free(void *ptr)
271
{
272 273 274 275 276 277 278 279 280 281 282 283
    rt_err_t result;
    struct rt_memheap *heap;
    struct rt_memheap_item *header_ptr, *new_ptr;
    rt_uint32_t insert_header;

    /* set initial status as OK */
    insert_header = 1;
    new_ptr       = RT_NULL;
    header_ptr    = (struct rt_memheap_item *)((rt_uint8_t *)ptr -
                                               RT_MEMHEAP_SIZE);

    RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("free memory: m[0x%08x], h[0x%08x]",
284
                                    ptr, header_ptr));
285

286 287 288 289 290
    /* check magic */
    RT_ASSERT((header_ptr->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC);

    /* get pool ptr */
    heap = header_ptr->pool_ptr;
291

292 293 294 295 296
    /* lock memheap */
    result = rt_mutex_take(&(heap->lock), RT_WAITING_FOREVER);
    if (result != RT_EOK)
    {
        rt_set_errno(result);
297

298 299
        return ;
    }
300

301 302
    /* Mark the memory as available. */
    header_ptr->magic &= ~RT_MEMHEAP_USED;
303

304 305 306 307 308
    /* Adjust the available number of bytes. */
    heap->available_size =
        heap->available_size +
        ((rt_uint32_t)(header_ptr->next) - (rt_uint32_t)header_ptr) -
        RT_MEMHEAP_SIZE;
309

310 311 312 313
    /* 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",
314
                                        header_ptr->prev));
315

316 317
        /* adjust the available number of bytes. */
        heap->available_size = heap->available_size + RT_MEMHEAP_SIZE;
318

319 320 321
        /* yes, merge block with previous neighbor. */
        (header_ptr->prev)->next = header_ptr->next;
        (header_ptr->next)->prev = header_ptr->prev;
322

323 324 325 326 327
        /* move header pointer to previous. */
        header_ptr = header_ptr->prev;
        /* don't insert header to free list */
        insert_header = 0;
    }
328

329 330 331 332 333
    /* 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;
334

335 336
        /* merge block with next neighbor. */
        new_ptr = header_ptr->next;
337

338
        RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
339 340
                     ("merge: right node 0x%08x, nf 0x%08x, pf 0x%08x",
                      new_ptr, new_ptr->next_free, new_ptr->prev_free));
341

342 343
        new_ptr->next->prev = header_ptr;
        header_ptr->next    = new_ptr->next;
344

345 346 347 348
        /* 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;
    }
349

350 351 352 353 354 355 356
    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;
357

358
        RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
359 360
                     ("insert to free list: nf 0x%08x, pf 0x%08x",
                      header_ptr->next_free, header_ptr->prev_free));
361
    }
362

363 364
    /* release lock */
    rt_mutex_release(&(heap->lock));
365
}
366
RTM_EXPORT(rt_memheap_free);
367 368

#endif