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  2008/07/29 07:27:46  haraldkipp
00052  * Removed setBeef from NutHeapAdd (crashes EIR).
00053  *
00054  * Revision 1.18  2008/07/07 07:39:10  haraldkipp
00055  * Fixes data abort exception on ARM.
00056  *
00057  * Revision 1.17  2008/06/28 08:35:38  haraldkipp
00058  * Replaced inline by INLINE.
00059  *
00060  * Revision 1.16  2008/06/25 08:28:39  freckle
00061  * added new function NutHeapRealloc
00062  *
00063  * Revision 1.15  2008/06/25 07:59:41  freckle
00064  * more detailed error msgs for NutHeapFree
00065  *
00066  * Revision 1.13  2008/06/15 17:07:15  haraldkipp
00067  * Rolled back to version 1.11.
00068  *
00069  * Revision 1.11  2006/10/05 17:26:15  haraldkipp
00070  * Fixes bug #1567729. Thanks to Ashley Duncan.
00071  *
00072  * Revision 1.10  2006/09/29 12:26:14  haraldkipp
00073  * All code should use dedicated stack allocation routines. For targets
00074  * allocating stack from the normal heap the API calls are remapped by
00075  * preprocessor macros.
00076  * Stack allocation code moved from thread module to heap module.
00077  * Adding static attribute to variable 'available' will avoid interference
00078  * with application code. The ugly format macros had been replaced by
00079  * converting all platform specific sizes to unsigned integers.
00080  *
00081  * Revision 1.9  2005/08/02 17:47:04  haraldkipp
00082  * Major API documentation update.
00083  *
00084  * Revision 1.8  2005/07/26 15:49:59  haraldkipp
00085  * Cygwin support added.
00086  *
00087  * Revision 1.7  2005/04/30 16:42:42  chaac
00088  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00089  * is defined in NutConf, it will make effect where it is used.
00090  *
00091  * Revision 1.6  2004/11/08 18:15:02  haraldkipp
00092  * Very bad hack to support 32-bit boundaries.
00093  *
00094  * Revision 1.5  2004/04/07 12:13:58  haraldkipp
00095  * Matthias Ringwald's *nix emulation added
00096  *
00097  * Revision 1.4  2004/03/19 09:05:12  jdubiec
00098  * Fixed format strings declarations for AVR.
00099  *
00100  * Revision 1.3  2004/03/16 16:48:45  haraldkipp
00101  * Added Jan Dubiec's H8/300 port.
00102  *
00103  * Revision 1.2  2003/07/20 16:04:36  haraldkipp
00104  * Nicer debug output
00105  *
00106  * Revision 1.1.1.1  2003/05/09 14:41:49  haraldkipp
00107  * Initial using 3.2.1
00108  *
00109  * Revision 1.18  2003/05/06 18:53:10  harald
00110  * Use trace flag
00111  *
00112  * Revision 1.17  2003/03/31 14:53:30  harald
00113  * Prepare release 3.1
00114  *
00115  * Revision 1.16  2003/02/04 18:15:56  harald
00116  * Version 3 released
00117  *
00118  * Revision 1.15  2002/06/26 17:29:44  harald
00119  * First pre-release with 2.4 stack
00120  *
00121  */
00122 
00128 
00129 #include <cfg/os.h>
00130 #include <compiler.h>
00131 #include <string.h>
00132 
00133 #include <sys/atom.h>
00134 #include <sys/heap.h>
00135 
00136 #ifdef NUTDEBUG
00137 #include <sys/osdebug.h>
00138 #endif
00139 
00140 #if defined(__arm__) || defined(__m68k__) || defined(__H8300H__) || defined(__H8300S__) || defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00141 #define ARCH_32BIT
00142 #endif
00143 
00147 HEAPNODE *volatile heapFreeList;
00148 
00152 static size_t available;
00153 
00158 /* MEMOVHD = sizeof(HEAPNODE:hn_size) + sizeof(0xDEADBEEF) */
00159 #define MEMOVHD (sizeof(size_t) + sizeof(0xDEADBEEF))
00160 
00165 static INLINE void setBeef(HEAPNODE * node){
00166     // Shouldn't it be sizeof(u_long) instead of sizeof(0xDEADBEEF)? 
00167     *((u_long *) ((uptr_t) node + node->hn_size - sizeof(0xDEADBEEF))) = 0xDEADBEEF;
00168 }
00169 
00170 #if !defined(ARCH_32BIT)
00171 // This crashed on AT91R40008 (Ethernut 3)
00179 static INLINE char checkBeef(HEAPNODE * node){
00180     return (*((u_long *) ((uptr_t) node + node->hn_size - sizeof(0xDEADBEEF))) == 0xDEADBEEF);
00181 }
00182 #endif
00183 
00215 void *NutHeapAlloc(size_t size)
00216 {
00217     HEAPNODE *node;
00218     HEAPNODE **npp;
00219     HEAPNODE *fit = 0;
00220     HEAPNODE **fpp = 0;
00221 
00222 #if defined(ARCH_32BIT)
00223     /*
00224      * Allign to the word boundary
00225      */
00226     while ((size & 0x03) != 0)
00227         size++;
00228 #endif
00229 
00230     if (size >= available) {
00231 #ifdef NUTDEBUG
00232         if (__heap_trf)
00233             fputs("MEMOVR\n", __heap_trs);
00234 #endif
00235         return 0;
00236     }
00237 
00238     /*
00239      * We need additional space in front of the allocated memory
00240      * block to store its size. If this is still less than the
00241      * space required by a free node, increase it.
00242      */
00243     if ((size += MEMOVHD) < sizeof(HEAPNODE))
00244         size = sizeof(HEAPNODE);
00245 
00246     /*
00247      * Walk through the linked list of free nodes and find the best fit.
00248      */
00249     node = heapFreeList;
00250     npp = (HEAPNODE **) & heapFreeList;
00251     while (node) {
00252 
00253         /*
00254          * Found a note that fits?
00255          */
00256         if (node->hn_size >= size) {
00257             /*
00258              * If it's an exact match, we don't
00259              * search any further.
00260              */
00261             if (node->hn_size == size) {
00262                 fit = node;
00263                 fpp = npp;
00264                 break;
00265             }
00266 
00267             /*
00268              * Is it the first one we found
00269              * or was the previous one larger?
00270              */
00271             if (fit == 0 || (fit->hn_size > node->hn_size)) {
00272                 fit = node;
00273                 fpp = npp;
00274             }
00275         }
00276         npp = &node->hn_next;
00277         node = node->hn_next;
00278     }
00279 
00280     if (fit) {
00281         /*
00282          * If the node we found is larger than the
00283          * required space plus the space needed for
00284          * a new node plus a defined threshold, then
00285          * we split it.
00286          */
00287         if (fit->hn_size > size + sizeof(HEAPNODE) + ALLOC_THRESHOLD) {
00288             node = (HEAPNODE *) ((uptr_t) fit + size);
00289             node->hn_size = fit->hn_size - size;
00290             node->hn_next = fit->hn_next;
00291             fit->hn_size = size;
00292             *fpp = node;
00293         } else
00294             *fpp = fit->hn_next;
00295 
00296         available -= fit->hn_size;
00297         setBeef(fit);
00298         fit = (HEAPNODE *) & fit->hn_next;
00299     }
00300 #ifdef NUTDEBUG
00301     if (__heap_trf) {
00302         fprintf(__heap_trs, "\n[H%x,A%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) (((uptr_t *) fit) - 1))->hn_size), (int)size);
00303     }
00304 #endif
00305     return fit;
00306 }
00307 
00321 void *NutHeapAllocClear(size_t size)
00322 {
00323     void *ptr;
00324 
00325     if ((ptr = NutHeapAlloc(size)) != 0)
00326         memset(ptr, 0, size);
00327 
00328     return ptr;
00329 }
00330 
00347 void * NutHeapRealloc( void * block, u_short size){
00348     HEAPNODE *node;
00349     HEAPNODE **npp;
00350     HEAPNODE *fnode;
00351     HEAPNODE *newNode;
00352     size_t size_miss;
00353     void * newmem;
00354     
00355     //basic forwarding
00356     if(size == 0){
00357         if( NutHeapFree(block) == 0){
00358             return block;
00359         } else {
00360             return NULL;
00361         } 
00362     } else if(block == NULL){
00363         return  NutHeapAlloc(size);
00364     }
00365     
00366     /*
00367      * Convert our block into a node.
00368      */
00369     fnode = (HEAPNODE *) (((uptr_t *) block) - 1);
00370     
00371 #ifdef NUTDEBUG
00372     if (__heap_trf) {
00373         if (!checkBeef(fnode)){
00374             fputs("\nMEMCORR-", __heap_trs);    
00375         }
00376     }
00377 #endif
00378     
00379     // Calculate minimum size
00380     if ((size += MEMOVHD) < sizeof(HEAPNODE))
00381         size = sizeof(HEAPNODE);
00382     
00383 #ifdef NUTDEBUG
00384     if (__heap_trf)
00385         fprintf(__heap_trs, "\n[H%x,R%d] ", (u_int)(uptr_t) block, (int)fnode->hn_size);
00386 #endif
00387     
00388     if (size > fnode->hn_size){  //More ram is needed
00389         //Check whether there is a free node behind.
00390         node = heapFreeList;
00391         npp = (HEAPNODE **) & heapFreeList;
00392         size_miss = size - fnode->hn_size;
00393         //Find the first node behind the node to realloc
00394         while(node != NULL && fnode < node){             
00395             npp = &node->hn_next;
00396             node = node->hn_next;
00397         } 
00398         
00399         if(node != NULL){ // There is a node behind the node
00400             /*
00401              * If a free node is following us _and_ is big enaugh: use it!
00402              */
00403             if (((uptr_t) fnode + fnode->hn_size) == (uptr_t) node &&  node->hn_size >= size_miss) {
00404                 if(node->hn_size + ALLOC_THRESHOLD >= size_miss){ //split next block
00405                     newNode = (HEAPNODE *) ((uptr_t) node + size_miss); //create new node;
00406                     //Memove seves difficulties when allocating less then HEAPNODE bytes
00407                     memmove(newNode, node, sizeof(HEAPNODE)); 
00408                     newNode->hn_size -= size_miss;
00409                     //newNode->hn_next is already ok.
00410                     *npp = newNode; //link previous node to new node.
00411                     fnode->hn_size = size; //Adjust size of current node
00412                     available -= size_miss;
00413                 } else { //Fits nicely
00414                     *npp = node->hn_next;   //Link previus node 
00415                     fnode->hn_size += node->hn_size;
00416                     available -= node->hn_size;
00417                 }
00418                 setBeef(fnode);
00419 #ifdef NUTDEBUG
00420                 if (__heap_trf) {
00421                     fprintf(__heap_trs, "\n[H%x,R%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) ((size_t *) fit - 1))->hn_size), (int)size);
00422                 }
00423 #endif
00424                 return block; 
00425             }
00426         }
00427         
00428         //Failed to resize -> move
00429         newmem = NutHeapAlloc(size);
00430         if(newmem == NULL) return NULL; //Failed to allocate a big enaugh block.
00431         memcpy(newmem, block, fnode->hn_size - MEMOVHD); //MWMOVHD must not to be moved!!!
00432         NutHeapFree(block);
00433         return newmem;
00434     } 
00435     
00436     //The new size is smaller. 
00437     if(size + REALLOC_THRESHOLD + MEMOVHD < fnode->hn_size){    
00438         newNode = (HEAPNODE *) ((uptr_t) fnode + size); //behind realloc node
00439         newNode->hn_size = fnode->hn_size - size; //set size of freed mem
00440         fnode->hn_size = size; //Adjust the size of the realloc node
00441         setBeef(fnode); //Add new beef to current node
00442         NutHeapFree((void *)((size_t *) newNode + 1)); //Free the block             
00443     }
00444 #ifdef NUTDEBUG
00445     if (__heap_trf) {
00446         fprintf(__heap_trs, "\n[H%x,R%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) ((size_t *) fit - 1))->hn_size), (int)size);
00447     }
00448 #endif
00449     return block;
00450 }
00451 
00452 
00474 int NutHeapFree(void *block)
00475 {
00476     HEAPNODE *node;
00477     HEAPNODE **npp;
00478     HEAPNODE *fnode;
00479     size_t size;
00480 
00481     if(block == NULL) return -3;
00482 
00483     /*
00484      * Convert our block into a node.
00485      */
00486     fnode = (HEAPNODE *) (((uptr_t *) block) - 1);
00487 
00488 #ifdef NUTDEBUG
00489     if (__heap_trf) {
00490         if (block) {
00491             if (!checkBeef(fnode)) {
00492                 fputs("\nMEMCORR-", __heap_trs);
00493             }
00494         } else {
00495             fputs("\nMEMNULL", __heap_trs);
00496         } 
00497     }
00498 #endif
00499 #if !defined(ARCH_32BIT)
00500     if(!checkBeef(fnode)) {
00501         return -2;
00502     }
00503 #endif
00504 
00505 #ifdef NUTDEBUG
00506     if (__heap_trf)
00507         fprintf(__heap_trs, "\n[H%x,F%d] ", (u_int)(uptr_t) block, (int)fnode->hn_size);
00508 #endif
00509     size = fnode->hn_size;
00510 
00511     /*
00512      * Walk through the linked list of free nodes and try
00513      * to link us in.
00514      */
00515     node = heapFreeList;
00516     npp = (HEAPNODE **) & heapFreeList;
00517     while (node) {
00518         /*
00519          * If there' s a free node in front of us, merge it.
00520          */
00521         if (((uptr_t) node + node->hn_size) == (uptr_t) fnode) {
00522             node->hn_size += fnode->hn_size;
00523 
00524             /*
00525              * If a free node is following us, merge it.
00526              */
00527             if (((uptr_t) node + node->hn_size) == (uptr_t) node->hn_next) {
00528                 node->hn_size += node->hn_next->hn_size;
00529                 node->hn_next = node->hn_next->hn_next;
00530             }
00531             break;
00532         }
00533 
00534         /*
00535          * If we walked past our address, link us to the list.
00536          */
00537         if ((uptr_t) node > (uptr_t) fnode) {
00538             *npp = fnode;
00539 
00540             /*
00541              * If a free node is following us, merge it.
00542              */
00543             if (((uptr_t) fnode + fnode->hn_size) == (uptr_t) node) {
00544                 fnode->hn_size += node->hn_size;
00545                 fnode->hn_next = node->hn_next;
00546             } else
00547                 fnode->hn_next = node;
00548             break;
00549         }
00550 
00551         /*
00552          * If we are within a free node, somebody tried
00553          * to free a block twice.
00554          */
00555         if (((uptr_t) node + node->hn_size) > (uptr_t) fnode) {
00556 #ifdef NUTDEBUG
00557             if (__heap_trf)
00558                 fputs("\nTWICE\n", __heap_trs);
00559 #endif
00560             return -1;
00561         }
00562 
00563         npp = &node->hn_next;
00564         node = node->hn_next;
00565     }
00566 
00567     /*
00568      * If no link was found, put us at the end of the list
00569      */
00570     if (!node) {
00571         fnode->hn_next = node;
00572         *npp = fnode;
00573     }
00574     available += size;
00575 
00576     return 0;
00577 }
00578 
00591 void NutHeapAdd(void *addr, size_t size)
00592 {
00593     *((uptr_t *) addr) = size;
00594 #if !defined(ARCH_32BIT)
00595     // Bug #2030525
00596     // This crashes on the EIR and may harm other 32-bit targets.
00597     // What is this for?
00598     setBeef((HEAPNODE *)addr);
00599 #endif
00600     NutHeapFree(((uptr_t *) addr) + 1);
00601 }
00602 
00608 size_t NutHeapAvailable(void)
00609 {
00610     return available;
00611 }
00612 
00613 
00614 #if defined (NUTMEM_STACKHEAP) /* Stack resides in internal memory */
00615 /*
00616  * The following routines are wrappers around the standard heap
00617  * allocation routines.  These wrappers tweak the free heap pointer to point
00618  * to a second heap which is kept in internal memory and used only for a
00619  * thread's stack.
00620  */
00621 
00622 static HEAPNODE* volatile stackHeapFreeList; /* for special stack heap */
00623 static u_short stackHeapAvailable;
00624 
00625 void *NutStackAlloc(size_t size)
00626 {
00627     void * result;
00628     HEAPNODE* savedHeapNode;
00629     u_short savedAvailable;
00630 
00631     // Save current real heap context
00632     savedHeapNode = heapFreeList;
00633     savedAvailable = available;
00634     // Restore stack-heap context
00635     heapFreeList = stackHeapFreeList;
00636     available = stackHeapAvailable;
00637 
00638     result = NutHeapAlloc(size);
00639 
00640     // Save stack-heap context
00641     stackHeapFreeList = heapFreeList;
00642     stackHeapAvailable = available;
00643     // Restore real heap context
00644     heapFreeList = savedHeapNode;
00645     available = savedAvailable;
00646 
00647     return result;
00648 }
00649 
00650 int NutStackFree(void *block)
00651 {
00652     int result;
00653     HEAPNODE* savedHeapNode;
00654     u_short savedAvailable;
00655 
00656     // Save current real heap context
00657     savedHeapNode = heapFreeList;
00658     savedAvailable = available;
00659     // Restore stack-heap context
00660     heapFreeList = stackHeapFreeList;
00661     available = stackHeapAvailable;
00662 
00663     result = NutHeapFree(block);
00664 
00665     // Save stack-heap context
00666     stackHeapFreeList = heapFreeList;
00667     stackHeapAvailable = available;
00668     // Restore real heap context
00669     heapFreeList = savedHeapNode;
00670     available = savedAvailable;
00671 
00672     return result;
00673 }
00674 
00675 void NutStackAdd(void *addr, size_t size)
00676 {
00677    HEAPNODE* savedHeapNode;
00678    u_short savedAvailable;
00679 
00680    // Save current real heap context
00681    savedHeapNode = heapFreeList;
00682    savedAvailable = available;
00683    // Restore stack-heap context
00684    heapFreeList = stackHeapFreeList;
00685    available = stackHeapAvailable;
00686 
00687    NutHeapAdd(addr, size);
00688 
00689    // Save stack-heap context
00690    stackHeapFreeList = heapFreeList;
00691    stackHeapAvailable = available;
00692    // Restore real heap context
00693    heapFreeList = savedHeapNode;
00694    available = savedAvailable;
00695 }
00696 
00697 #endif /* defined(NUTMEM_STACKHEAP) */
00698 

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