From 2c46df1916a25b7880416aee974a518cc607717a Mon Sep 17 00:00:00 2001 From: gdisirio Date: Fri, 16 Oct 2009 17:45:19 +0000 Subject: New heap manager. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1221 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/kernel/src/chheap.c | 222 ++++++++++++++++++++++++++-------------------- os/kernel/src/chmem.c | 94 -------------------- os/kernel/src/chmemcore.c | 99 +++++++++++++++++++++ os/kernel/src/chsys.c | 3 + os/kernel/src/chthreads.c | 7 +- 5 files changed, 234 insertions(+), 191 deletions(-) delete mode 100644 os/kernel/src/chmem.c create mode 100644 os/kernel/src/chmemcore.c (limited to 'os/kernel/src') diff --git a/os/kernel/src/chheap.c b/os/kernel/src/chheap.c index b5ad50505..e7a975f3d 100644 --- a/os/kernel/src/chheap.c +++ b/os/kernel/src/chheap.c @@ -28,41 +28,23 @@ #if CH_USE_HEAP -#if !CH_USE_MALLOC_HEAP - -#define MAGIC 0xF5A0 -#define ALIGN_TYPE void * -#define ALIGN_MASK (sizeof(ALIGN_TYPE) - 1) -#define ALIGN_SIZE(p) (((size_t)(p) + ALIGN_MASK) & ~ALIGN_MASK) - -struct header { - union { - struct header *h_next; - size_t h_magic; - }; - size_t h_size; -}; - -static struct { - struct header free; /* Guaranteed to be not adjacent to the heap */ +/* + * Defaults on the best synchronization mechanism available. + */ #if CH_USE_MUTEXES -#define H_LOCK() chMtxLock(&heap.hmtx) -#define H_UNLOCK() chMtxUnlock() - Mutex hmtx; -#elif CH_USE_SEMAPHORES -#define H_LOCK() chSemWait(&heap.hsem) -#define H_UNLOCK() chSemSignal(&heap.hsem) - Semaphore hsem; +#define H_LOCK(h) chMtxLock(&(h)->h_mtx) +#define H_UNLOCK(h) chMtxUnlock() #else -#error "The heap allocator requires mutexes or semaphores to be enabled" +#define H_LOCK(h) chSemWait(&(h)->h_sem) +#define H_UNLOCK(h) chSemSignal(&(h)->h_sem) #endif -#if CH_HEAP_SIZE > 0 - union { - ALIGN_TYPE alignment; - char buffer[ALIGN_SIZE(CH_HEAP_SIZE)]; - }; -#endif -} heap; + +#if !CH_USE_MALLOC_HEAP + +/** + * @brief Default heap descriptor. + */ +static MemoryHeap default_heap; /** * @brief Initializes the allocator subsystem. @@ -70,26 +52,40 @@ static struct { * @note Internal use only. */ void heap_init(void) { - struct header *hp; - -#if CH_HEAP_SIZE == 0 - extern char __heap_base__; - extern char __heap_end__; - - hp = (void *)&__heap_base__; - hp->h_size = &__heap_end__ - &__heap_base__ - sizeof(struct header); + default_heap.h_provider = chCoreAlloc; + default_heap.h_free.h_next = NULL; + default_heap.h_free.h_size = 0; +#if CH_USE_MUTEXES + chMtxInit(&default_heap.h_mtx); #else - hp = (void *)&heap.buffer[0]; - hp->h_size = (&heap.buffer[ALIGN_SIZE(CH_HEAP_SIZE)] - &heap.buffer[0]) - - sizeof(struct header); + chSemInit(&default_heap.h_sem, 1); #endif +} + +/** + * @brief Initializes a memory heap. + * + * @param[out] heapp pointer to a memory heap descriptor to be initialized + * @param[in] buf heap buffer base + * @param[in] size heap size + * + * @note Both the heap buffer base and the heap size must be aligned to + * the @p align_t type size. + */ +void chHeapInit(MemoryHeap *heapp, void *buf, size_t size) { + struct heap_header *hp; + + chDbgCheck(MEM_IS_ALIGNED(buf) && MEM_IS_ALIGNED(size), "chHeapInit"); + + heapp->h_provider = NULL; + heapp->h_free.h_next = hp = buf; + heapp->h_free.h_size = 0; hp->h_next = NULL; - heap.free.h_next = hp; - heap.free.h_size = 0; + hp->h_size = size - sizeof(struct heap_header); #if CH_USE_MUTEXES - chMtxInit(&heap.hmtx); + chMtxInit(&heapp->h_mtx); #else - chSemInit(&heap.hsem, 1); + chSemInit(&heapp->h_sem, 1); #endif } @@ -97,53 +93,75 @@ void heap_init(void) { * @brief Allocates a block of memory from the heap by using the first-fit * algorithm. * @details The allocated block is guaranteed to be properly aligned for a - * pointer data type. + * pointer data type (@p align_t). * + * @param[in] heapp pointer to a heap descriptor or @p NULL in order to access + * the default heap. * @param[in] size the size of the block to be allocated. Note that the * allocated block may be a bit bigger than the requested * size for alignment and fragmentation reasons. * @return A pointer to the allocated block. * @retval NULL if the block cannot be allocated. */ -void *chHeapAlloc(size_t size) { - struct header *qp, *hp, *fp; +void *chHeapAlloc(MemoryHeap *heapp, size_t size) { + struct heap_header *qp, *hp, *fp; - size = ALIGN_SIZE(size); - qp = &heap.free; - H_LOCK(); + if (heapp == NULL) + heapp = &default_heap; + + size = MEM_ALIGN_SIZE(size); + qp = &heapp->h_free; + H_LOCK(heapp); while (qp->h_next != NULL) { hp = qp->h_next; if (hp->h_size >= size) { - if (hp->h_size < size + sizeof(struct header)) { - /* Gets the whole block even if it is slightly bigger than the - requested size because the fragment would be too small to be - useful */ + if (hp->h_size < size + sizeof(struct heap_header)) { + /* + * Gets the whole block even if it is slightly bigger than the + * requested size because the fragment would be too small to be + * useful. + */ qp->h_next = hp->h_next; } else { - /* Block bigger enough, must split it */ - fp = (void *)((char *)(hp) + sizeof(struct header) + size); + /* + * Block bigger enough, must split it. + */ + fp = (void *)((uint8_t *)(hp) + sizeof(struct heap_header) + size); fp->h_next = hp->h_next; - fp->h_size = hp->h_size - sizeof(struct header) - size; + fp->h_size = hp->h_size - sizeof(struct heap_header) - size; qp->h_next = fp; hp->h_size = size; } - hp->h_magic = MAGIC; + hp->h_heap = heapp; - H_UNLOCK(); + H_UNLOCK(heapp); return (void *)(hp + 1); } qp = hp; } - H_UNLOCK(); + H_UNLOCK(heapp); + + /* + * More memory is required, tries to get it from the associated provider. + */ + if (heapp->h_provider) { + hp = heapp->h_provider(size + sizeof(struct heap_header)); + if (hp != NULL) { + hp->h_heap = heapp; + hp->h_size = size; + hp++; + return (void *)hp; + } + } return NULL; } -#define LIMIT(p) (struct header *)((char *)(p) + \ - sizeof(struct header) + \ - (p)->h_size) +#define LIMIT(p) (struct heap_header *)((uint8_t *)(p) + \ + sizeof(struct heap_header) + \ + (p)->h_size) /** * @brief Frees a previously allocated memory block. @@ -151,50 +169,59 @@ void *chHeapAlloc(size_t size) { * @param[in] p the memory block pointer */ void chHeapFree(void *p) { - struct header *qp, *hp; + struct heap_header *qp, *hp; + MemoryHeap *heapp; chDbgCheck(p != NULL, "chHeapFree"); - hp = (struct header *)p - 1; - chDbgAssert(hp->h_magic == MAGIC, - "chHeapFree(), #1", - "it is not magic"); - qp = &heap.free; - H_LOCK(); + hp = (struct heap_header *)p - 1; + heapp = hp->h_heap; + qp = &heapp->h_free; + H_LOCK(heapp); while (TRUE) { - chDbgAssert((hp < qp) || (hp >= LIMIT(qp)), - "chHeapFree(), #2", + "chHeapFree(), #1", "within free block"); - if (((qp == &heap.free) || (hp > qp)) && + if (((qp == &heapp->h_free) || (hp > qp)) && ((qp->h_next == NULL) || (hp < qp->h_next))) { - /* Insertion after qp */ + /* + * Insertion after qp. + */ hp->h_next = qp->h_next; qp->h_next = hp; - /* Verifies if the newly inserted block should be merged */ + /* + * Verifies if the newly inserted block should be merged. + */ if (LIMIT(hp) == hp->h_next) { - /* Merge with the next block */ - hp->h_size += hp->h_next->h_size + sizeof(struct header); + /* + * Merge with the next block. + */ + hp->h_size += hp->h_next->h_size + sizeof(struct heap_header); hp->h_next = hp->h_next->h_next; } - if ((LIMIT(qp) == hp)) { /* Cannot happen when qp == &heap.free */ - /* Merge with the previous block */ - qp->h_size += hp->h_size + sizeof(struct header); + if ((LIMIT(qp) == hp)) { + /* + * Merge with the previous block. + */ + qp->h_size += hp->h_size + sizeof(struct heap_header); qp->h_next = hp->h_next; } - - H_UNLOCK(); - return; + break; } qp = qp->h_next; } + + H_UNLOCK(heapp); + return; } /** * @brief Reports the heap status. * + * @param[in] heapp pointer to a heap descriptor or @p NULL in order to access + * the default heap. * @param[in] sizep pointer to a variable that will receive the total * fragmented free space * @return The number of fragments in the heap. @@ -203,19 +230,22 @@ void chHeapFree(void *p) { * @note This function is not implemented when the @p CH_USE_MALLOC_HEAP * configuration option is used (it always returns zero). */ -size_t chHeapStatus(size_t *sizep) { - struct header *qp; +size_t chHeapStatus(MemoryHeap *heapp, size_t *sizep) { + struct heap_header *qp; size_t n, sz; - H_LOCK(); + if (heapp == NULL) + heapp = &default_heap; + + H_LOCK(heapp); sz = 0; - for (n = 0, qp = &heap.free; qp->h_next; n++, qp = qp->h_next) + for (n = 0, qp = &heapp->h_free; qp->h_next; n++, qp = qp->h_next) sz += qp->h_next->h_size; if (sizep) *sizep = sz; - H_UNLOCK(); + H_UNLOCK(heapp); return n; } @@ -231,8 +261,6 @@ static Mutex hmtx; #define H_LOCK() chSemWait(&hsem) #define H_UNLOCK() chSemSignal(&hsem) static Semaphore hsem; -#else -#error "The heap allocator requires mutexes or semaphores to be enabled" #endif void heap_init(void) { @@ -244,9 +272,11 @@ void heap_init(void) { #endif } -void *chHeapAlloc(size_t size) { +void *chHeapAlloc(MemoryHeap *heapp, size_t size) { void *p; + chDbgCheck(heapp == NULL, "chHeapAlloc"); + H_LOCK(); p = malloc(size); H_UNLOCK(); @@ -262,7 +292,9 @@ void chHeapFree(void *p) { H_UNLOCK(); } -size_t chHeapStatus(size_t *sizep) { +size_t chHeapStatus(MemoryHeap *heapp, size_t *sizep) { + + chDbgCheck(heapp == NULL, "chHeapStatus"); if (sizep) *sizep = 0; diff --git a/os/kernel/src/chmem.c b/os/kernel/src/chmem.c deleted file mode 100644 index 3ac45a6ae..000000000 --- a/os/kernel/src/chmem.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. - - This file is part of ChibiOS/RT. - - ChibiOS/RT is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - ChibiOS/RT is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/** - * @file chmem.c - * @brief Low level memory manager code. - * @addtogroup coremem - * @{ - */ - -#include - -#if CH_USE_COREMEM - -#if CH_COREMEM_SIZE == 0 - extern align_t __heap_base__; - extern align_t __heap_end__; -#else -align_t buffer[ALIGN_SIZE(CH_MEM_SIZE) / sizeof(align_t)]; -#endif - -static align_t *nextmem; -static align_t *endmem; - -void mem_init(void) { -#if CH_COREMEM_SIZE == 0 - nextmem = &__heap_base__; - endmem = &__heap_end__; -#else - nextmem = &buffer[0]; - endmem = &buffer[ALIGN_SIZE(CH_MEM_SIZE) / sizeof(align_t)]; -#endif -} - -/** - * @brief Allocates a block of memory. - * @details The size of the returned block is aligned to the alignment - * type @p align_t so it is not possible to allocate less than - * sizeof(align_t). - * - * - * @param[in] the size of the block to be allocated - * @return A pointer to the allocated memory block. - * @retval NULL allocation failed, core memory exhausted. - */ -void *chCoreAlloc(size_t size) { - void *p; - - chSysLock(); - p = chCoreAllocI(size); - chSysUnlock(); - return p; -} - -/** - * @brief Allocates a block of memory. - * @details The size of the returned block is aligned to the alignment - * type @p align_t so it is not possible to allocate less than - * sizeof(align_t). - * - * @param[in] the size of the block to be allocated. - * @return A pointer to the allocated memory block. - * @retval NULL allocation failed, core memory exhausted. - */ -void *chCoreAllocI(size_t size) { - void *p; - - size = ALIGN_SIZE(size); - if (nextmem + size > endmem) - return NULL; - p = nextmem; - nextmem += size; - return p; -} - -#endif /* CH_USE_COREMEM */ - -/** @} */ diff --git a/os/kernel/src/chmemcore.c b/os/kernel/src/chmemcore.c new file mode 100644 index 000000000..87ef6037a --- /dev/null +++ b/os/kernel/src/chmemcore.c @@ -0,0 +1,99 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chmemcore.c + * @brief Core memory manager code. + * @addtogroup memcore + * @{ + */ + +#include + +#if CH_USE_MEMCORE + +#if CH_MEMCORE_SIZE == 0 + extern align_t __heap_base__; + extern align_t __heap_end__; +#else +align_t buffer[ALIGN_SIZE(CH_MEMCORE_SIZE) / sizeof(align_t)]; +#endif + +static align_t *nextmem; +static align_t *endmem; + +/** + * @brief Low level memory manager initialization. + * + * @note Internal use only. + */ +void core_init(void) { +#if CH_MEMCORE_SIZE == 0 + nextmem = &__heap_base__; + endmem = &__heap_end__; +#else + nextmem = &buffer[0]; + endmem = &buffer[ALIGN_SIZE(CH_MEMCORE_SIZE) / sizeof(align_t)]; +#endif +} + +/** + * @brief Allocates a block of memory. + * @details The size of the returned block is aligned to the alignment + * type @p align_t so it is not possible to allocate less than + * sizeof(align_t). + * + * + * @param[in] the size of the block to be allocated + * @return A pointer to the allocated memory block. + * @retval NULL allocation failed, core memory exhausted. + */ +void *chCoreAlloc(size_t size) { + void *p; + + chSysLock(); + p = chCoreAllocI(size); + chSysUnlock(); + return p; +} + +/** + * @brief Allocates a block of memory. + * @details The size of the returned block is aligned to the alignment + * type @p align_t so it is not possible to allocate less than + * sizeof(align_t). + * + * @param[in] the size of the block to be allocated. + * @return A pointer to the allocated memory block. + * @retval NULL allocation failed, core memory exhausted. + */ +void *chCoreAllocI(size_t size) { + void *p; + + size = MEM_ALIGN_SIZE(size); + if (nextmem + size > endmem) + return NULL; + p = nextmem; + nextmem += size; + return p; +} + +#endif /* CH_USE_MEMCORE */ + +/** @} */ diff --git a/os/kernel/src/chsys.c b/os/kernel/src/chsys.c index 8ee7375f8..7d42eb644 100644 --- a/os/kernel/src/chsys.c +++ b/os/kernel/src/chsys.c @@ -61,6 +61,9 @@ void chSysInit(void) { port_init(); scheduler_init(); vt_init(); +#if CH_USE_MEMCORE + core_init(); +#endif #if CH_USE_HEAP heap_init(); #endif diff --git a/os/kernel/src/chthreads.c b/os/kernel/src/chthreads.c index a3028867a..89d1d6329 100644 --- a/os/kernel/src/chthreads.c +++ b/os/kernel/src/chthreads.c @@ -124,6 +124,8 @@ Thread *chThdCreateStatic(void *wsp, size_t size, /** * @brief Creates a new thread allocating the memory from the heap. * + * @param[in] heapp heap from which allocate the memory or NULL for the + * default heap * @param[in] size size of the working area to be allocated * @param[in] prio the priority level for the new thread * @param[in] pf the thread function @@ -139,11 +141,12 @@ Thread *chThdCreateStatic(void *wsp, size_t size, * @p CH_USE_HEAP and @p CH_USE_WAITEXIT options are enabled * in @p chconf.h. */ -Thread *chThdCreateFromHeap(size_t size, tprio_t prio, tfunc_t pf, void *arg) { +Thread *chThdCreateFromHeap(MemoryHeap *heapp, size_t size, + tprio_t prio, tfunc_t pf, void *arg) { void *wsp; Thread *tp; - wsp = chHeapAlloc(size); + wsp = chHeapAlloc(heapp, size); if (wsp == NULL) return NULL; tp = chThdInit(wsp, size, prio, pf, arg); -- cgit v1.2.3