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.20  2008/08/11 06:56:27  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 #include <stdint.h>
00140 
00141 #ifdef NUTDEBUG
00142 #include <sys/osdebug.h>
00143 #endif
00144 
00145 #if defined(__arm__) || defined(__m68k__) || defined(__H8300H__) || defined(__H8300S__) || defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00146 #define ARCH_32BIT
00147 #endif
00148 
00152 HEAPNODE *volatile heapFreeList;
00153 
00157 static size_t available;
00158 
00163 /* MEMOVHD = sizeof(HEAPNODE:hn_size) + sizeof(0xDEADBEEF) */
00164 #define MEMOVHD (sizeof(size_t) + sizeof(0xDEADBEEF))
00165 
00170 static INLINE void setBeef(HEAPNODE * node){
00171     // Shouldn't it be sizeof(uint32_t) instead of sizeof(0xDEADBEEF)? 
00172     *((uint32_t *) ((uptr_t) node + node->hn_size - sizeof(0xDEADBEEF))) = 0xDEADBEEF;
00173 }
00174 
00175 #if !defined(ARCH_32BIT)
00176 // This crashed on AT91R40008 (Ethernut 3)
00184 static INLINE char checkBeef(HEAPNODE * node){
00185     return (*((uint32_t *) ((uptr_t) node + node->hn_size - sizeof(0xDEADBEEF))) == 0xDEADBEEF);
00186 }
00187 #endif
00188 
00220 void *NutHeapAlloc(size_t size)
00221 {
00222     HEAPNODE *node;
00223     HEAPNODE **npp;
00224     HEAPNODE *fit = 0;
00225     HEAPNODE **fpp = 0;
00226 
00227 #if defined(ARCH_32BIT)
00228     /*
00229      * Allign to the word boundary
00230      */
00231     while ((size & 0x03) != 0)
00232         size++;
00233 #endif
00234 
00235     if (size >= available) {
00236 #ifdef NUTDEBUG
00237         if (__heap_trf)
00238             fputs("MEMOVR\n", __heap_trs);
00239 #endif
00240         return 0;
00241     }
00242 
00243     /*
00244      * We need additional space in front of the allocated memory
00245      * block to store its size. If this is still less than the
00246      * space required by a free node, increase it.
00247      */
00248     if ((size += MEMOVHD) < sizeof(HEAPNODE))
00249         size = sizeof(HEAPNODE);
00250 
00251     /*
00252      * Walk through the linked list of free nodes and find the best fit.
00253      */
00254     node = heapFreeList;
00255     npp = (HEAPNODE **) & heapFreeList;
00256     while (node) {
00257 
00258         /*
00259          * Found a note that fits?
00260          */
00261         if (node->hn_size >= size) {
00262             /*
00263              * If it's an exact match, we don't
00264              * search any further.
00265              */
00266             if (node->hn_size == size) {
00267                 fit = node;
00268                 fpp = npp;
00269                 break;
00270             }
00271 
00272             /*
00273              * Is it the first one we found
00274              * or was the previous one larger?
00275              */
00276             if (fit == 0 || (fit->hn_size > node->hn_size)) {
00277                 fit = node;
00278                 fpp = npp;
00279             }
00280         }
00281         npp = &node->hn_next;
00282         node = node->hn_next;
00283     }
00284 
00285     if (fit) {
00286         /*
00287          * If the node we found is larger than the
00288          * required space plus the space needed for
00289          * a new node plus a defined threshold, then
00290          * we split it.
00291          */
00292         if (fit->hn_size > size + sizeof(HEAPNODE) + ALLOC_THRESHOLD) {
00293             node = (HEAPNODE *) ((uptr_t) fit + size);
00294             node->hn_size = fit->hn_size - size;
00295             node->hn_next = fit->hn_next;
00296             fit->hn_size = size;
00297             *fpp = node;
00298         } else
00299             *fpp = fit->hn_next;
00300 
00301         available -= fit->hn_size;
00302         setBeef(fit);
00303         fit = (HEAPNODE *) & fit->hn_next;
00304     }
00305 #ifdef NUTDEBUG
00306     if (__heap_trf) {
00307         fprintf(__heap_trs, "\n[H%x,A%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) (((uptr_t *) fit) - 1))->hn_size), (int)size);
00308     }
00309 #endif
00310     return fit;
00311 }
00312 
00326 void *NutHeapAllocClear(size_t size)
00327 {
00328     void *ptr;
00329 
00330     if ((ptr = NutHeapAlloc(size)) != 0)
00331         memset(ptr, 0, size);
00332 
00333     return ptr;
00334 }
00335 
00352 void * NutHeapRealloc( void * block, size_t size){
00353     HEAPNODE *node;
00354     HEAPNODE **npp;
00355     HEAPNODE *fnode;
00356     HEAPNODE *newNode;
00357     size_t size_miss;
00358     void * newmem;
00359     
00360     //basic forwarding
00361     if(size == 0){
00362         if( NutHeapFree(block) == 0){
00363             return block;
00364         } else {
00365             return NULL;
00366         } 
00367     } else if(block == NULL){
00368         return  NutHeapAlloc(size);
00369     }
00370     
00371     /*
00372      * Convert our block into a node.
00373      */
00374     fnode = (HEAPNODE *) (((uptr_t *) block) - 1);
00375     
00376 #ifdef NUTDEBUG
00377     if (__heap_trf) {
00378         if (!checkBeef(fnode)){
00379             fputs("\nMEMCORR-", __heap_trs);    
00380         }
00381     }
00382 #endif
00383     
00384     // Calculate minimum size
00385     if ((size += MEMOVHD) < sizeof(HEAPNODE))
00386         size = sizeof(HEAPNODE);
00387     
00388 #ifdef NUTDEBUG
00389     if (__heap_trf)
00390         fprintf(__heap_trs, "\n[H%x,R%d] ", (u_int)(uptr_t) block, (int)fnode->hn_size);
00391 #endif
00392     
00393     if (size > fnode->hn_size){  //More ram is needed
00394         //Check whether there is a free node behind.
00395         node = heapFreeList;
00396         npp = (HEAPNODE **) & heapFreeList;
00397         size_miss = size - fnode->hn_size;
00398         //Find the first node behind the node to realloc
00399         while(node != NULL && fnode < node){             
00400             npp = &node->hn_next;
00401             node = node->hn_next;
00402         } 
00403         
00404         if(node != NULL){ // There is a node behind the node
00405             /*
00406              * If a free node is following us _and_ is big enaugh: use it!
00407              */
00408             if (((uptr_t) fnode + fnode->hn_size) == (uptr_t) node &&  node->hn_size >= size_miss) {
00409                 if(node->hn_size + ALLOC_THRESHOLD >= size_miss){ //split next block
00410                     newNode = (HEAPNODE *) ((uptr_t) node + size_miss); //create new node;
00411                     //Memove seves difficulties when allocating less then HEAPNODE bytes
00412                     memmove(newNode, node, sizeof(HEAPNODE)); 
00413                     newNode->hn_size -= size_miss;
00414                     //newNode->hn_next is already ok.
00415                     *npp = newNode; //link previous node to new node.
00416                     fnode->hn_size = size; //Adjust size of current node
00417                     available -= size_miss;
00418                 } else { //Fits nicely
00419                     *npp = node->hn_next;   //Link previus node 
00420                     fnode->hn_size += node->hn_size;
00421                     available -= node->hn_size;
00422                 }
00423                 setBeef(fnode);
00424 #ifdef NUTDEBUG
00425                 if (__heap_trf) {
00426                     fprintf(__heap_trs, "\n[H%x,R%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) ((size_t *) fit - 1))->hn_size), (int)size);
00427                 }
00428 #endif
00429                 return block; 
00430             }
00431         }
00432         
00433         //Failed to resize -> move
00434         newmem = NutHeapAlloc(size);
00435         if(newmem == NULL) return NULL; //Failed to allocate a big enaugh block.
00436         memcpy(newmem, block, fnode->hn_size - MEMOVHD); //MWMOVHD must not to be moved!!!
00437         NutHeapFree(block);
00438         return newmem;
00439     } 
00440     
00441     //The new size is smaller. 
00442     if(size + REALLOC_THRESHOLD + MEMOVHD < fnode->hn_size){    
00443         newNode = (HEAPNODE *) ((uptr_t) fnode + size); //behind realloc node
00444         newNode->hn_size = fnode->hn_size - size; //set size of freed mem
00445         fnode->hn_size = size; //Adjust the size of the realloc node
00446         setBeef(fnode); //Add new beef to current node
00447         NutHeapFree((void *)((size_t *) newNode + 1)); //Free the block             
00448     }
00449 #ifdef NUTDEBUG
00450     if (__heap_trf) {
00451         fprintf(__heap_trs, "\n[H%x,R%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) ((size_t *) fit - 1))->hn_size), (int)size);
00452     }
00453 #endif
00454     return block;
00455 }
00456 
00457 
00479 int NutHeapFree(void *block)
00480 {
00481     HEAPNODE *node;
00482     HEAPNODE **npp;
00483     HEAPNODE *fnode;
00484     size_t size;
00485 
00486     if(block == NULL) return -3;
00487 
00488     /*
00489      * Convert our block into a node.
00490      */
00491     fnode = (HEAPNODE *) (((uptr_t *) block) - 1);
00492 
00493 #ifdef NUTDEBUG
00494     if (__heap_trf) {
00495         if (block) {
00496             if (!checkBeef(fnode)) {
00497                 fputs("\nMEMCORR-", __heap_trs);
00498             }
00499         } else {
00500             fputs("\nMEMNULL", __heap_trs);
00501         } 
00502     }
00503 #endif
00504 #if !defined(ARCH_32BIT)
00505     if(!checkBeef(fnode)) {
00506         return -2;
00507     }
00508 #endif
00509 
00510 #ifdef NUTDEBUG
00511     if (__heap_trf)
00512         fprintf(__heap_trs, "\n[H%x,F%d] ", (u_int)(uptr_t) block, (int)fnode->hn_size);
00513 #endif
00514     size = fnode->hn_size;
00515 
00516     /*
00517      * Walk through the linked list of free nodes and try
00518      * to link us in.
00519      */
00520     node = heapFreeList;
00521     npp = (HEAPNODE **) & heapFreeList;
00522     while (node) {
00523         /*
00524          * If there' s a free node in front of us, merge it.
00525          */
00526         if (((uptr_t) node + node->hn_size) == (uptr_t) fnode) {
00527             node->hn_size += fnode->hn_size;
00528 
00529             /*
00530              * If a free node is following us, merge it.
00531              */
00532             if (((uptr_t) node + node->hn_size) == (uptr_t) node->hn_next) {
00533                 node->hn_size += node->hn_next->hn_size;
00534                 node->hn_next = node->hn_next->hn_next;
00535             }
00536             break;
00537         }
00538 
00539         /*
00540          * If we walked past our address, link us to the list.
00541          */
00542         if ((uptr_t) node > (uptr_t) fnode) {
00543             *npp = fnode;
00544 
00545             /*
00546              * If a free node is following us, merge it.
00547              */
00548             if (((uptr_t) fnode + fnode->hn_size) == (uptr_t) node) {
00549                 fnode->hn_size += node->hn_size;
00550                 fnode->hn_next = node->hn_next;
00551             } else
00552                 fnode->hn_next = node;
00553             break;
00554         }
00555 
00556         /*
00557          * If we are within a free node, somebody tried
00558          * to free a block twice.
00559          */
00560         if (((uptr_t) node + node->hn_size) > (uptr_t) fnode) {
00561 #ifdef NUTDEBUG
00562             if (__heap_trf)
00563                 fputs("\nTWICE\n", __heap_trs);
00564 #endif
00565             return -1;
00566         }
00567 
00568         npp = &node->hn_next;
00569         node = node->hn_next;
00570     }
00571 
00572     /*
00573      * If no link was found, put us at the end of the list
00574      */
00575     if (!node) {
00576         fnode->hn_next = node;
00577         *npp = fnode;
00578     }
00579     available += size;
00580 
00581     return 0;
00582 }
00583 
00596 void NutHeapAdd(void *addr, size_t size)
00597 {
00598     *((uptr_t *) addr) = size;
00599 #if !defined(ARCH_32BIT)
00600     // Bug #2030525
00601     // This crashes on the EIR and may harm other 32-bit targets.
00602     // What is this for?
00603     setBeef((HEAPNODE *)addr);
00604 #endif
00605     NutHeapFree(((uptr_t *) addr) + 1);
00606 }
00607 
00613 size_t NutHeapAvailable(void)
00614 {
00615     return available;
00616 }
00617 
00618 
00619 #if defined (NUTMEM_STACKHEAP) /* Stack resides in internal memory */
00620 /*
00621  * The following routines are wrappers around the standard heap
00622  * allocation routines.  These wrappers tweak the free heap pointer to point
00623  * to a second heap which is kept in internal memory and used only for a
00624  * thread's stack.
00625  */
00626 
00627 static HEAPNODE* volatile stackHeapFreeList; /* for special stack heap */
00628 static uint16_t stackHeapAvailable;
00629 
00630 void *NutStackAlloc(size_t size)
00631 {
00632     void * result;
00633     HEAPNODE* savedHeapNode;
00634     uint16_t savedAvailable;
00635 
00636     // Save current real heap context
00637     savedHeapNode = heapFreeList;
00638     savedAvailable = available;
00639     // Restore stack-heap context
00640     heapFreeList = stackHeapFreeList;
00641     available = stackHeapAvailable;
00642 
00643     result = NutHeapAlloc(size);
00644 
00645     // Save stack-heap context
00646     stackHeapFreeList = heapFreeList;
00647     stackHeapAvailable = available;
00648     // Restore real heap context
00649     heapFreeList = savedHeapNode;
00650     available = savedAvailable;
00651 
00652     return result;
00653 }
00654 
00655 int NutStackFree(void *block)
00656 {
00657     int result;
00658     HEAPNODE* savedHeapNode;
00659     uint16_t savedAvailable;
00660 
00661     // Save current real heap context
00662     savedHeapNode = heapFreeList;
00663     savedAvailable = available;
00664     // Restore stack-heap context
00665     heapFreeList = stackHeapFreeList;
00666     available = stackHeapAvailable;
00667 
00668     result = NutHeapFree(block);
00669 
00670     // Save stack-heap context
00671     stackHeapFreeList = heapFreeList;
00672     stackHeapAvailable = available;
00673     // Restore real heap context
00674     heapFreeList = savedHeapNode;
00675     available = savedAvailable;
00676 
00677     return result;
00678 }
00679 
00680 void NutStackAdd(void *addr, size_t size)
00681 {
00682    HEAPNODE* savedHeapNode;
00683    uint16_t savedAvailable;
00684 
00685    // Save current real heap context
00686    savedHeapNode = heapFreeList;
00687    savedAvailable = available;
00688    // Restore stack-heap context
00689    heapFreeList = stackHeapFreeList;
00690    available = stackHeapAvailable;
00691 
00692    NutHeapAdd(addr, size);
00693 
00694    // Save stack-heap context
00695    stackHeapFreeList = heapFreeList;
00696    stackHeapAvailable = available;
00697    // Restore real heap context
00698    heapFreeList = savedHeapNode;
00699    available = savedAvailable;
00700 }
00701 
00702 #endif /* defined(NUTMEM_STACKHEAP) */
00703 

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