ChibiOS/RT Architecture - Reference Manual - Guides |
00001 /* 00002 ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010 Giovanni Di Sirio. 00003 00004 This file is part of ChibiOS/RT. 00005 00006 ChibiOS/RT is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 3 of the License, or 00009 (at your option) any later version. 00010 00011 ChibiOS/RT is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program. If not, see <http://www.gnu.org/licenses/>. 00018 00019 --- 00020 00021 A special exception to the GPL can be applied should you wish to distribute 00022 a combined work that includes ChibiOS/RT, without being obliged to provide 00023 the source code for any proprietary components. See the file exception.txt 00024 for full details of how and when the exception can be applied. 00025 */ 00026 00027 /** 00028 * @file chheap.c 00029 * @brief Heaps code. 00030 * 00031 * @addtogroup heaps 00032 * @details Heap Allocator related APIs. 00033 * <h2>Operation mode</h2> 00034 * The heap allocator implements a first-fit strategy and its APIs 00035 * are functionally equivalent to the usual @p malloc() and @p free() 00036 * library functions. The main difference is that the OS heap APIs 00037 * are guaranteed to be thread safe.<br> 00038 * By enabling the @p CH_USE_MALLOC_HEAP option the heap manager 00039 * will use the runtime-provided @p malloc() and @p free() as 00040 * backend for the heap APIs instead of the system provided 00041 * allocator.<br> 00042 * In order to use the heap APIs the @p CH_USE_HEAP option must 00043 * be enabled in @p chconf.h. 00044 * @{ 00045 */ 00046 00047 #include "ch.h" 00048 00049 #if CH_USE_HEAP 00050 00051 #if !CH_USE_MALLOC_HEAP 00052 00053 /* 00054 * Defaults on the best synchronization mechanism available. 00055 */ 00056 #if CH_USE_MUTEXES 00057 #define H_LOCK(h) chMtxLock(&(h)->h_mtx) 00058 #define H_UNLOCK(h) chMtxUnlock() 00059 #else 00060 #define H_LOCK(h) chSemWait(&(h)->h_sem) 00061 #define H_UNLOCK(h) chSemSignal(&(h)->h_sem) 00062 #endif 00063 00064 /** 00065 * @brief Default heap descriptor. 00066 */ 00067 static MemoryHeap default_heap; 00068 00069 /** 00070 * @brief Initializes the default heap. 00071 * @note Internal use only. 00072 */ 00073 void heap_init(void) { 00074 default_heap.h_provider = chCoreAlloc; 00075 default_heap.h_free.h.u.next = (union heap_header *)NULL; 00076 default_heap.h_free.h.size = 0; 00077 #if CH_USE_MUTEXES 00078 chMtxInit(&default_heap.h_mtx); 00079 #else 00080 chSemInit(&default_heap.h_sem, 1); 00081 #endif 00082 } 00083 00084 /** 00085 * @brief Initializes a memory heap from a static memory area. 00086 * @note Both the heap buffer base and the heap size must be aligned to 00087 * the @p align_t type size. 00088 * 00089 * @param[out] heapp pointer to the memory heap descriptor to be initialized 00090 * @param[in] buf heap buffer base 00091 * @param[in] size heap size 00092 */ 00093 void chHeapInit(MemoryHeap *heapp, void *buf, size_t size) { 00094 union heap_header *hp; 00095 00096 chDbgCheck(MEM_IS_ALIGNED(buf) && MEM_IS_ALIGNED(size), "chHeapInit"); 00097 00098 heapp->h_provider = (memgetfunc_t)NULL; 00099 heapp->h_free.h.u.next = hp = buf; 00100 heapp->h_free.h.size = 0; 00101 hp->h.u.next = NULL; 00102 hp->h.size = size - sizeof(union heap_header); 00103 #if CH_USE_MUTEXES 00104 chMtxInit(&heapp->h_mtx); 00105 #else 00106 chSemInit(&heapp->h_sem, 1); 00107 #endif 00108 } 00109 00110 /** 00111 * @brief Allocates a block of memory from the heap by using the first-fit 00112 * algorithm. 00113 * @details The allocated block is guaranteed to be properly aligned for a 00114 * pointer data type (@p align_t). 00115 * 00116 * @param[in] heapp pointer to a heap descriptor or @p NULL in order to 00117 * access the default heap. 00118 * @param[in] size the size of the block to be allocated. Note that the 00119 * allocated block may be a bit bigger than the requested 00120 * size for alignment and fragmentation reasons. 00121 * @return A pointer to the allocated block. 00122 * @retval NULL if the block cannot be allocated. 00123 */ 00124 void *chHeapAlloc(MemoryHeap *heapp, size_t size) { 00125 union heap_header *qp, *hp, *fp; 00126 00127 if (heapp == NULL) 00128 heapp = &default_heap; 00129 00130 size = MEM_ALIGN_SIZE(size); 00131 qp = &heapp->h_free; 00132 H_LOCK(heapp); 00133 00134 while (qp->h.u.next != NULL) { 00135 hp = qp->h.u.next; 00136 if (hp->h.size >= size) { 00137 if (hp->h.size < size + sizeof(union heap_header)) { 00138 /* Gets the whole block even if it is slightly bigger than the 00139 requested size because the fragment would be too small to be 00140 useful.*/ 00141 qp->h.u.next = hp->h.u.next; 00142 } 00143 else { 00144 /* Block bigger enough, must split it.*/ 00145 fp = (void *)((uint8_t *)(hp) + sizeof(union heap_header) + size); 00146 fp->h.u.next = hp->h.u.next; 00147 fp->h.size = hp->h.size - sizeof(union heap_header) - size; 00148 qp->h.u.next = fp; 00149 hp->h.size = size; 00150 } 00151 hp->h.u.heap = heapp; 00152 00153 H_UNLOCK(heapp); 00154 return (void *)(hp + 1); 00155 } 00156 qp = hp; 00157 } 00158 00159 H_UNLOCK(heapp); 00160 00161 /* More memory is required, tries to get it from the associated provider 00162 else fails.*/ 00163 if (heapp->h_provider) { 00164 hp = heapp->h_provider(size + sizeof(union heap_header)); 00165 if (hp != NULL) { 00166 hp->h.u.heap = heapp; 00167 hp->h.size = size; 00168 hp++; 00169 return (void *)hp; 00170 } 00171 } 00172 return NULL; 00173 } 00174 00175 #define LIMIT(p) (union heap_header *)((uint8_t *)(p) + \ 00176 sizeof(union heap_header) + \ 00177 (p)->h.size) 00178 00179 /** 00180 * @brief Frees a previously allocated memory block. 00181 * 00182 * @param[in] p pointer to the memory block to be freed 00183 */ 00184 void chHeapFree(void *p) { 00185 union heap_header *qp, *hp; 00186 MemoryHeap *heapp; 00187 00188 chDbgCheck(p != NULL, "chHeapFree"); 00189 00190 hp = (union heap_header *)p - 1; 00191 heapp = hp->h.u.heap; 00192 qp = &heapp->h_free; 00193 H_LOCK(heapp); 00194 00195 while (TRUE) { 00196 chDbgAssert((hp < qp) || (hp >= LIMIT(qp)), 00197 "chHeapFree(), #1", 00198 "within free block"); 00199 00200 if (((qp == &heapp->h_free) || (hp > qp)) && 00201 ((qp->h.u.next == NULL) || (hp < qp->h.u.next))) { 00202 /* Insertion after qp.*/ 00203 hp->h.u.next = qp->h.u.next; 00204 qp->h.u.next = hp; 00205 /* Verifies if the newly inserted block should be merged.*/ 00206 if (LIMIT(hp) == hp->h.u.next) { 00207 /* Merge with the next block.*/ 00208 hp->h.size += hp->h.u.next->h.size + sizeof(union heap_header); 00209 hp->h.u.next = hp->h.u.next->h.u.next; 00210 } 00211 if ((LIMIT(qp) == hp)) { 00212 /* Merge with the previous block.*/ 00213 qp->h.size += hp->h.size + sizeof(union heap_header); 00214 qp->h.u.next = hp->h.u.next; 00215 } 00216 break; 00217 } 00218 qp = qp->h.u.next; 00219 } 00220 00221 H_UNLOCK(heapp); 00222 return; 00223 } 00224 00225 /** 00226 * @brief Reports the heap status. 00227 * @note This function is meant to be used in the test suite, it should 00228 * not be really useful for the application code. 00229 * @note This function is not implemented when the @p CH_USE_MALLOC_HEAP 00230 * configuration option is used (it always returns zero). 00231 * 00232 * @param[in] heapp pointer to a heap descriptor or @p NULL in order to 00233 * access the default heap. 00234 * @param[in] sizep pointer to a variable that will receive the total 00235 * fragmented free space 00236 * @return The number of fragments in the heap. 00237 */ 00238 size_t chHeapStatus(MemoryHeap *heapp, size_t *sizep) { 00239 union heap_header *qp; 00240 size_t n, sz; 00241 00242 if (heapp == NULL) 00243 heapp = &default_heap; 00244 00245 H_LOCK(heapp); 00246 00247 sz = 0; 00248 for (n = 0, qp = &heapp->h_free; qp->h.u.next; n++, qp = qp->h.u.next) 00249 sz += qp->h.u.next->h.size; 00250 if (sizep) 00251 *sizep = sz; 00252 00253 H_UNLOCK(heapp); 00254 return n; 00255 } 00256 00257 #else /* CH_USE_MALLOC_HEAP */ 00258 00259 #include <stdlib.h> 00260 00261 #if CH_USE_MUTEXES 00262 #define H_LOCK() chMtxLock(&hmtx) 00263 #define H_UNLOCK() chMtxUnlock() 00264 static Mutex hmtx; 00265 #elif CH_USE_SEMAPHORES 00266 #define H_LOCK() chSemWait(&hsem) 00267 #define H_UNLOCK() chSemSignal(&hsem) 00268 static Semaphore hsem; 00269 #endif 00270 00271 void heap_init(void) { 00272 00273 #if CH_USE_MUTEXES 00274 chMtxInit(&hmtx); 00275 #else 00276 chSemInit(&hsem, 1); 00277 #endif 00278 } 00279 00280 void *chHeapAlloc(MemoryHeap *heapp, size_t size) { 00281 void *p; 00282 00283 chDbgCheck(heapp == NULL, "chHeapAlloc"); 00284 00285 H_LOCK(); 00286 p = malloc(size); 00287 H_UNLOCK(); 00288 return p; 00289 } 00290 00291 void chHeapFree(void *p) { 00292 00293 chDbgCheck(p != NULL, "chHeapFree"); 00294 00295 H_LOCK(); 00296 free(p); 00297 H_UNLOCK(); 00298 } 00299 00300 size_t chHeapStatus(MemoryHeap *heapp, size_t *sizep) { 00301 00302 chDbgCheck(heapp == NULL, "chHeapStatus"); 00303 00304 if (sizep) 00305 *sizep = 0; 00306 return 0; 00307 } 00308 00309 #endif /* CH_USE_MALLOC_HEAP */ 00310 00311 #endif /* CH_USE_HEAP */ 00312 00313 /** @} */