heap.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  * -
00033  * Portions Copyright (C) 2000 David J. Hudson <dave@humbug.demon.co.uk>
00034  *
00035  * This file is distributed in the hope that it will be useful, but WITHOUT
00036  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00037  * FITNESS FOR A PARTICULAR PURPOSE.
00038  *
00039  * You can redistribute this file and/or modify it under the terms of the GNU
00040  * General Public License (GPL) as published by the Free Software Foundation;
00041  * either version 2 of the License, or (at your discretion) any later version.
00042  * See the accompanying file "copying-gpl.txt" for more details.
00043  *
00044  * As a special exception to the GPL, permission is granted for additional
00045  * uses of the text contained in this file.  See the accompanying file
00046  * "copying-liquorice.txt" for details.
00047  */
00048 
00049 /*
00050  * $Log: heap.c,v $
00051  * Revision 1.19.2.1  2008/08/25 15:10:57  haraldkipp
00052  * Corrected size type of NutHeapRealloc().
00053  *
00054  * Revision 1.19  2008/07/29 07:27:46  haraldkipp
00055  * Removed setBeef from NutHeapAdd (crashes EIR).
00056  *
00057  * Revision 1.18  2008/07/07 07:39:10  haraldkipp
00058  * Fixes data abort exception on ARM.
00059  *
00060  * Revision 1.17  2008/06/28 08:35:38  haraldkipp
00061  * Replaced inline by INLINE.
00062  *
00063  * Revision 1.16  2008/06/25 08:28:39  freckle
00064  * added new function NutHeapRealloc
00065  *
00066  * Revision 1.15  2008/06/25 07:59:41  freckle
00067  * more detailed error msgs for NutHeapFree
00068  *
00069  * Revision 1.13  2008/06/15 17:07:15  haraldkipp
00070  * Rolled back to version 1.11.
00071  *
00072  * Revision 1.11  2006/10/05 17:26:15  haraldkipp
00073  * Fixes bug #1567729. Thanks to Ashley Duncan.
00074  *
00075  * Revision 1.10  2006/09/29 12:26:14  haraldkipp
00076  * All code should use dedicated stack allocation routines. For targets
00077  * allocating stack from the normal heap the API calls are remapped by
00078  * preprocessor macros.
00079  * Stack allocation code moved from thread module to heap module.
00080  * Adding static attribute to variable 'available' will avoid interference
00081  * with application code. The ugly format macros had been replaced by
00082  * converting all platform specific sizes to unsigned integers.
00083  *
00084  * Revision 1.9  2005/08/02 17:47:04  haraldkipp
00085  * Major API documentation update.
00086  *
00087  * Revision 1.8  2005/07/26 15:49:59  haraldkipp
00088  * Cygwin support added.
00089  *
00090  * Revision 1.7  2005/04/30 16:42:42  chaac
00091  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00092  * is defined in NutConf, it will make effect where it is used.
00093  *
00094  * Revision 1.6  2004/11/08 18:15:02  haraldkipp
00095  * Very bad hack to support 32-bit boundaries.
00096  *
00097  * Revision 1.5  2004/04/07 12:13:58  haraldkipp
00098  * Matthias Ringwald's *nix emulation added
00099  *
00100  * Revision 1.4  2004/03/19 09:05:12  jdubiec
00101  * Fixed format strings declarations for AVR.
00102  *
00103  * Revision 1.3  2004/03/16 16:48:45  haraldkipp
00104  * Added Jan Dubiec's H8/300 port.
00105  *
00106  * Revision 1.2  2003/07/20 16:04:36  haraldkipp
00107  * Nicer debug output
00108  *
00109  * Revision 1.1.1.1  2003/05/09 14:41:49  haraldkipp
00110  * Initial using 3.2.1
00111  *
00112  * Revision 1.18  2003/05/06 18:53:10  harald
00113  * Use trace flag
00114  *
00115  * Revision 1.17  2003/03/31 14:53:30  harald
00116  * Prepare release 3.1
00117  *
00118  * Revision 1.16  2003/02/04 18:15:56  harald
00119  * Version 3 released
00120  *
00121  * Revision 1.15  2002/06/26 17:29:44  harald
00122  * First pre-release with 2.4 stack
00123  *
00124  */
00125 
00131 
00132 #include <cfg/os.h>
00133 #include <compiler.h>
00134 #include <string.h>
00135 
00136 #include <sys/atom.h>
00137 #include <sys/heap.h>
00138 
00139 #ifdef NUTDEBUG
00140 #include <sys/osdebug.h>
00141 #endif
00142 
00143 #if defined(__arm__) || defined(__m68k__) || defined(__H8300H__) || defined(__H8300S__) || defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00144 #define ARCH_32BIT
00145 #endif
00146 
00150 HEAPNODE *volatile heapFreeList;
00151 
00155 static size_t available;
00156 
00161 /* MEMOVHD = sizeof(HEAPNODE:hn_size) + sizeof(0xDEADBEEF) */
00162 #define MEMOVHD (sizeof(size_t) + sizeof(0xDEADBEEF))
00163 
00168 static INLINE void setBeef(HEAPNODE * node){
00169     // Shouldn't it be sizeof(u_long) instead of sizeof(0xDEADBEEF)? 
00170     *((u_long *) ((uptr_t) node + node->hn_size - sizeof(0xDEADBEEF))) = 0xDEADBEEF;
00171 }
00172 
00173 #if !defined(ARCH_32BIT)
00174 // This crashed on AT91R40008 (Ethernut 3)
00182 static INLINE char checkBeef(HEAPNODE * node){
00183     return (*((u_long *) ((uptr_t) node + node->hn_size - sizeof(0xDEADBEEF))) == 0xDEADBEEF);
00184 }
00185 #endif
00186 
00218 void *NutHeapAlloc(size_t size)
00219 {
00220     HEAPNODE *node;
00221     HEAPNODE **npp;
00222     HEAPNODE *fit = 0;
00223     HEAPNODE **fpp = 0;
00224 
00225 #if defined(ARCH_32BIT)
00226     /*
00227      * Allign to the word boundary
00228      */
00229     while ((size & 0x03) != 0)
00230         size++;
00231 #endif
00232 
00233     if (size >= available) {
00234 #ifdef NUTDEBUG
00235         if (__heap_trf)
00236             fputs("MEMOVR\n", __heap_trs);
00237 #endif
00238         return 0;
00239     }
00240 
00241     /*
00242      * We need additional space in front of the allocated memory
00243      * block to store its size. If this is still less than the
00244      * space required by a free node, increase it.
00245      */
00246     if ((size += MEMOVHD) < sizeof(HEAPNODE))
00247         size = sizeof(HEAPNODE);
00248 
00249     /*
00250      * Walk through the linked list of free nodes and find the best fit.
00251      */
00252     node = heapFreeList;
00253     npp = (HEAPNODE **) & heapFreeList;
00254     while (node) {
00255 
00256         /*
00257          * Found a note that fits?
00258          */
00259         if (node->hn_size >= size) {
00260             /*
00261              * If it's an exact match, we don't
00262              * search any further.
00263              */
00264             if (node->hn_size == size) {
00265                 fit = node;
00266                 fpp = npp;
00267                 break;
00268             }
00269 
00270             /*
00271              * Is it the first one we found
00272              * or was the previous one larger?
00273              */
00274             if (fit == 0 || (fit->hn_size > node->hn_size)) {
00275                 fit = node;
00276                 fpp = npp;
00277             }
00278         }
00279         npp = &node->hn_next;
00280         node = node->hn_next;
00281     }
00282 
00283     if (fit) {
00284         /*
00285          * If the node we found is larger than the
00286          * required space plus the space needed for
00287          * a new node plus a defined threshold, then
00288          * we split it.
00289          */
00290         if (fit->hn_size > size + sizeof(HEAPNODE) + ALLOC_THRESHOLD) {
00291             node = (HEAPNODE *) ((uptr_t) fit + size);
00292             node->hn_size = fit->hn_size - size;
00293             node->hn_next = fit->hn_next;
00294             fit->hn_size = size;
00295             *fpp = node;
00296         } else
00297             *fpp = fit->hn_next;
00298 
00299         available -= fit->hn_size;
00300         setBeef(fit);
00301         fit = (HEAPNODE *) & fit->hn_next;
00302     }
00303 #ifdef NUTDEBUG
00304     if (__heap_trf) {
00305         fprintf(__heap_trs, "\n[H%x,A%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) (((uptr_t *) fit) - 1))->hn_size), (int)size);
00306     }
00307 #endif
00308     return fit;
00309 }
00310 
00324 void *NutHeapAllocClear(size_t size)
00325 {
00326     void *ptr;
00327 
00328     if ((ptr = NutHeapAlloc(size)) != 0)
00329         memset(ptr, 0, size);
00330 
00331     return ptr;
00332 }
00333 
00350 void * NutHeapRealloc( void * block, size_t size){
00351     HEAPNODE *node;
00352     HEAPNODE **npp;
00353     HEAPNODE *fnode;
00354     HEAPNODE *newNode;
00355     size_t size_miss;
00356     void * newmem;
00357     
00358     //basic forwarding
00359     if(size == 0){
00360         if( NutHeapFree(block) == 0){
00361             return block;
00362         } else {
00363             return NULL;
00364         } 
00365     } else if(block == NULL){
00366         return  NutHeapAlloc(size);
00367     }
00368     
00369     /*
00370      * Convert our block into a node.
00371      */
00372     fnode = (HEAPNODE *) (((uptr_t *) block) - 1);
00373     
00374 #ifdef NUTDEBUG
00375     if (__heap_trf) {
00376         if (!checkBeef(fnode)){
00377             fputs("\nMEMCORR-", __heap_trs);    
00378         }
00379     }
00380 #endif
00381     
00382     // Calculate minimum size
00383     if ((size += MEMOVHD) < sizeof(HEAPNODE))
00384         size = sizeof(HEAPNODE);
00385     
00386 #ifdef NUTDEBUG
00387     if (__heap_trf)
00388         fprintf(__heap_trs, "\n[H%x,R%d] ", (u_int)(uptr_t) block, (int)fnode->hn_size);
00389 #endif
00390     
00391     if (size > fnode->hn_size){  //More ram is needed
00392         //Check whether there is a free node behind.
00393         node = heapFreeList;
00394         npp = (HEAPNODE **) & heapFreeList;
00395         size_miss = size - fnode->hn_size;
00396         //Find the first node behind the node to realloc
00397         while(node != NULL && fnode < node){             
00398             npp = &node->hn_next;
00399             node = node->hn_next;
00400         } 
00401         
00402         if(node != NULL){ // There is a node behind the node
00403             /*
00404              * If a free node is following us _and_ is big enaugh: use it!
00405              */
00406             if (((uptr_t) fnode + fnode->hn_size) == (uptr_t) node &&  node->hn_size >= size_miss) {
00407                 if(node->hn_size + ALLOC_THRESHOLD >= size_miss){ //split next block
00408                     newNode = (HEAPNODE *) ((uptr_t) node + size_miss); //create new node;
00409                     //Memove seves difficulties when allocating less then HEAPNODE bytes
00410                     memmove(newNode, node, sizeof(HEAPNODE)); 
00411                     newNode->hn_size -= size_miss;
00412                     //newNode->hn_next is already ok.
00413                     *npp = newNode; //link previous node to new node.
00414                     fnode->hn_size = size; //Adjust size of current node
00415                     available -= size_miss;
00416                 } else { //Fits nicely
00417                     *npp = node->hn_next;   //Link previus node 
00418                     fnode->hn_size += node->hn_size;
00419                     available -= node->hn_size;
00420                 }
00421                 setBeef(fnode);
00422 #ifdef NUTDEBUG
00423                 if (__heap_trf) {
00424                     fprintf(__heap_trs, "\n[H%x,R%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) ((size_t *) fit - 1))->hn_size), (int)size);
00425                 }
00426 #endif
00427                 return block; 
00428             }
00429         }
00430         
00431         //Failed to resize -> move
00432         newmem = NutHeapAlloc(size);
00433         if(newmem == NULL) return NULL; //Failed to allocate a big enaugh block.
00434         memcpy(newmem, block, fnode->hn_size - MEMOVHD); //MWMOVHD must not to be moved!!!
00435         NutHeapFree(block);
00436         return newmem;
00437     } 
00438     
00439     //The new size is smaller. 
00440     if(size + REALLOC_THRESHOLD + MEMOVHD < fnode->hn_size){    
00441         newNode = (HEAPNODE *) ((uptr_t) fnode + size); //behind realloc node
00442         newNode->hn_size = fnode->hn_size - size; //set size of freed mem
00443         fnode->hn_size = size; //Adjust the size of the realloc node
00444         setBeef(fnode); //Add new beef to current node
00445         NutHeapFree((void *)((size_t *) newNode + 1)); //Free the block             
00446     }
00447 #ifdef NUTDEBUG
00448     if (__heap_trf) {
00449         fprintf(__heap_trs, "\n[H%x,R%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) ((size_t *) fit - 1))->hn_size), (int)size);
00450     }
00451 #endif
00452     return block;
00453 }
00454 
00455 
00477 int NutHeapFree(void *block)
00478 {
00479     HEAPNODE *node;
00480     HEAPNODE **npp;
00481     HEAPNODE *fnode;
00482     size_t size;
00483 
00484     if(block == NULL) return -3;
00485 
00486     /*
00487      * Convert our block into a node.
00488      */
00489     fnode = (HEAPNODE *) (((uptr_t *) block) - 1);
00490 
00491 #ifdef NUTDEBUG
00492     if (__heap_trf) {
00493         if (block) {
00494             if (!checkBeef(fnode)) {
00495                 fputs("\nMEMCORR-", __heap_trs);
00496             }
00497         } else {
00498             fputs("\nMEMNULL", __heap_trs);
00499         } 
00500     }
00501 #endif
00502 #if !defined(ARCH_32BIT)
00503     if(!checkBeef(fnode)) {
00504         return -2;
00505     }
00506 #endif
00507 
00508 #ifdef NUTDEBUG
00509     if (__heap_trf)
00510         fprintf(__heap_trs, "\n[H%x,F%d] ", (u_int)(uptr_t) block, (int)fnode->hn_size);
00511 #endif
00512     size = fnode->hn_size;
00513 
00514     /*
00515      * Walk through the linked list of free nodes and try
00516      * to link us in.
00517      */
00518     node = heapFreeList;
00519     npp = (HEAPNODE **) & heapFreeList;
00520     while (node) {
00521         /*
00522          * If there' s a free node in front of us, merge it.
00523          */
00524         if (((uptr_t) node + node->hn_size) == (uptr_t) fnode) {
00525             node->hn_size += fnode->hn_size;
00526 
00527             /*
00528              * If a free node is following us, merge it.
00529              */
00530             if (((uptr_t) node + node->hn_size) == (uptr_t) node->hn_next) {
00531                 node->hn_size += node->hn_next->hn_size;
00532                 node->hn_next = node->hn_next->hn_next;
00533             }
00534             break;
00535         }
00536 
00537         /*
00538          * If we walked past our address, link us to the list.
00539          */
00540         if ((uptr_t) node > (uptr_t) fnode) {
00541             *npp = fnode;
00542 
00543             /*
00544              * If a free node is following us, merge it.
00545              */
00546             if (((uptr_t) fnode + fnode->hn_size) == (uptr_t) node) {
00547                 fnode->hn_size += node->hn_size;
00548                 fnode->hn_next = node->hn_next;
00549             } else
00550                 fnode->hn_next = node;
00551             break;
00552         }
00553 
00554         /*
00555          * If we are within a free node, somebody tried
00556          * to free a block twice.
00557          */
00558         if (((uptr_t) node + node->hn_size) > (uptr_t) fnode) {
00559 #ifdef NUTDEBUG
00560             if (__heap_trf)
00561                 fputs("\nTWICE\n", __heap_trs);
00562 #endif
00563             return -1;
00564         }
00565 
00566         npp = &node->hn_next;
00567         node = node->hn_next;
00568     }
00569 
00570     /*
00571      * If no link was found, put us at the end of the list
00572      */
00573     if (!node) {
00574         fnode->hn_next = node;
00575         *npp = fnode;
00576     }
00577     available += size;
00578 
00579     return 0;
00580 }
00581 
00594 void NutHeapAdd(void *addr, size_t size)
00595 {
00596     *((uptr_t *) addr) = size;
00597 #if !defined(ARCH_32BIT)
00598     // Bug #2030525
00599     // This crashes on the EIR and may harm other 32-bit targets.
00600     // What is this for?
00601     setBeef((HEAPNODE *)addr);
00602 #endif
00603     NutHeapFree(((uptr_t *) addr) + 1);
00604 }
00605 
00611 size_t NutHeapAvailable(void)
00612 {
00613     return available;
00614 }
00615 
00616 
00617 #if defined (NUTMEM_STACKHEAP) /* Stack resides in internal memory */
00618 /*
00619  * The following routines are wrappers around the standard heap
00620  * allocation routines.  These wrappers tweak the free heap pointer to point
00621  * to a second heap which is kept in internal memory and used only for a
00622  * thread's stack.
00623  */
00624 
00625 static HEAPNODE* volatile stackHeapFreeList; /* for special stack heap */
00626 static u_short stackHeapAvailable;
00627 
00628 void *NutStackAlloc(size_t size)
00629 {
00630     void * result;
00631     HEAPNODE* savedHeapNode;
00632     u_short savedAvailable;
00633 
00634     // Save current real heap context
00635     savedHeapNode = heapFreeList;
00636     savedAvailable = available;
00637     // Restore stack-heap context
00638     heapFreeList = stackHeapFreeList;
00639     available = stackHeapAvailable;
00640 
00641     result = NutHeapAlloc(size);
00642 
00643     // Save stack-heap context
00644     stackHeapFreeList = heapFreeList;
00645     stackHeapAvailable = available;
00646     // Restore real heap context
00647     heapFreeList = savedHeapNode;
00648     available = savedAvailable;
00649 
00650     return result;
00651 }
00652 
00653 int NutStackFree(void *block)
00654 {
00655     int result;
00656     HEAPNODE* savedHeapNode;
00657     u_short savedAvailable;
00658 
00659     // Save current real heap context
00660     savedHeapNode = heapFreeList;
00661     savedAvailable = available;
00662     // Restore stack-heap context
00663     heapFreeList = stackHeapFreeList;
00664     available = stackHeapAvailable;
00665 
00666     result = NutHeapFree(block);
00667 
00668     // Save stack-heap context
00669     stackHeapFreeList = heapFreeList;
00670     stackHeapAvailable = available;
00671     // Restore real heap context
00672     heapFreeList = savedHeapNode;
00673     available = savedAvailable;
00674 
00675     return result;
00676 }
00677 
00678 void NutStackAdd(void *addr, size_t size)
00679 {
00680    HEAPNODE* savedHeapNode;
00681    u_short savedAvailable;
00682 
00683    // Save current real heap context
00684    savedHeapNode = heapFreeList;
00685    savedAvailable = available;
00686    // Restore stack-heap context
00687    heapFreeList = stackHeapFreeList;
00688    available = stackHeapAvailable;
00689 
00690    NutHeapAdd(addr, size);
00691 
00692    // Save stack-heap context
00693    stackHeapFreeList = heapFreeList;
00694    stackHeapAvailable = available;
00695    // Restore real heap context
00696    heapFreeList = savedHeapNode;
00697    available = savedAvailable;
00698 }
00699 
00700 #endif /* defined(NUTMEM_STACKHEAP) */
00701 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/