new implementation of gnuk_malloc/free

This commit is contained in:
NIIBE Yutaka
2013-07-19 11:45:18 +09:00
parent 760fd947ca
commit 596a307219

View File

@@ -452,7 +452,20 @@ fatal (uint8_t code)
_write ("fatal\r\n", 7); _write ("fatal\r\n", 7);
for (;;); for (;;);
} }
/*
* Malloc for Gnuk.
*
* Each memory chunk has header with size information.
* The size of chunk is at least 16.
*
* Free memory is managed by FREE_LIST.
*
* When it is managed in FREE_LIST, three pointers, ->NEXT, ->PREV,
* and ->NEIGHBOR is used. NEXT and PREV is to implement doubly
* linked list. NEIGHBOR is to link adjacent memory chunk to be
* reclaimed to system.
*/
extern uint8_t __heap_base__[]; extern uint8_t __heap_base__[];
extern uint8_t __heap_end__[]; extern uint8_t __heap_end__[];
@@ -464,27 +477,25 @@ extern uint8_t __heap_end__[];
static uint8_t *heap_p; static uint8_t *heap_p;
static chopstx_mutex_t malloc_mtx; static chopstx_mutex_t malloc_mtx;
/*
* Assume the size of heap is less than 64KiB - 1.
*/
struct mem_head { struct mem_head {
uint16_t next_mem_offset; /* pointer to next. */ uint32_t size;
uint16_t size; /**/
} __attribute__((packed)); struct mem_head *next, *prev; /* free list chain */
struct mem_head *neighbor; /* backlink to neighbor */
};
static uint16_t free_mem_offset; static struct mem_head *free_list;
#define FREE_MEM_NONE (0xffff)
static void static void
gnuk_malloc_init (void) gnuk_malloc_init (void)
{ {
chopstx_mutex_init (&malloc_mtx); chopstx_mutex_init (&malloc_mtx);
heap_p = __heap_base__; heap_p = __heap_base__;
free_mem_offset = FREE_MEM_NONE; free_list = NULL;
} }
static static void *
void *sbrk (size_t size) sbrk (size_t size)
{ {
void *p = (void *)heap_p; void *p = (void *)heap_p;
@@ -495,44 +506,47 @@ void *sbrk (size_t size)
return p; return p;
} }
static void
remove_from_free_list (struct mem_head *m)
{
if (m->prev)
m->prev->next = m->next;
else
free_list = m->next;
if (m->next)
m->next->prev = m->prev;
}
void * void *
gnuk_malloc (size_t size) gnuk_malloc (size_t size)
{ {
struct mem_head *m; struct mem_head *m;
uint16_t mem_offset;
uint16_t *mem_offset_prev;
size = MEMORY_ALIGN (size + sizeof (struct mem_head)); size = MEMORY_ALIGN (size + sizeof (uint32_t));
chopstx_mutex_lock (&malloc_mtx); chopstx_mutex_lock (&malloc_mtx);
DEBUG_INFO ("malloc: "); DEBUG_INFO ("malloc: ");
DEBUG_SHORT (size); DEBUG_SHORT (size);
mem_offset_prev = &free_mem_offset; m = free_list;
mem_offset = free_mem_offset;
while (1) while (1)
{ {
if (mem_offset == FREE_MEM_NONE) if (m == NULL)
{ {
m = sbrk (size); m = (struct mem_head *)sbrk (size);
if (m) if (m)
{
m->next_mem_offset = FREE_MEM_NONE;
m->size = size; m->size = size;
}
break; break;
} }
m = (struct mem_head *)(__heap_base__ + mem_offset);
if (m->size == size) if (m->size == size)
{ {
*mem_offset_prev = m->next_mem_offset; remove_from_free_list (m);
m->next_mem_offset = FREE_MEM_NONE;
break; break;
} }
mem_offset_prev = &m->next_mem_offset; m = m->next;
mem_offset = m->next_mem_offset;
} }
chopstx_mutex_unlock (&malloc_mtx); chopstx_mutex_unlock (&malloc_mtx);
@@ -543,21 +557,54 @@ gnuk_malloc (size_t size)
} }
else else
{ {
DEBUG_WORD ((uint32_t)(m+1)); DEBUG_WORD ((uint32_t)m + sizeof (uint32_t));
return (void *)(m + 1); return (void *)m + sizeof (uint32_t);
} }
} }
void void
gnuk_free (void *p) gnuk_free (void *p)
{ {
struct mem_head *m = ((struct mem_head *)p) - 1; struct mem_head *m = (struct mem_head *)((void *)p - sizeof (uint32_t));
struct mem_head *m0 = free_list;
chopstx_mutex_lock (&malloc_mtx); chopstx_mutex_lock (&malloc_mtx);
DEBUG_INFO ("free: "); DEBUG_INFO ("free: ");
DEBUG_SHORT (m->size); DEBUG_SHORT (m->size);
DEBUG_WORD ((uint32_t)p); DEBUG_WORD ((uint32_t)p);
m->next_mem_offset = free_mem_offset;
free_mem_offset = (uint16_t)((uint8_t *)m - __heap_base__); m->neighbor = NULL;
while (m0)
{
if ((void *)m + m->size == (void *)m0)
m0->neighbor = m;
else if ((void *)m0 + m0->size == (void *)m)
m->neighbor = m0;
m0 = m0->next;
}
if ((void *)m + m->size == heap_p)
{
struct mem_head *mn = m->neighbor;
heap_p -= m->size;
while (mn)
{
heap_p -= mn->size;
remove_from_free_list (mn);
mn = mn->neighbor;
}
}
else
{
m->next = free_list;
m->prev = NULL;
if (free_list)
free_list->prev = m;
free_list = m;
}
chopstx_mutex_unlock (&malloc_mtx); chopstx_mutex_unlock (&malloc_mtx);
} }