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.11  2006/10/05 17:26:15  haraldkipp
00052  * Fixes bug #1567729. Thanks to Ashley Duncan.
00053  *
00054  * Revision 1.10  2006/09/29 12:26:14  haraldkipp
00055  * All code should use dedicated stack allocation routines. For targets
00056  * allocating stack from the normal heap the API calls are remapped by
00057  * preprocessor macros.
00058  * Stack allocation code moved from thread module to heap module.
00059  * Adding static attribute to variable 'available' will avoid interference
00060  * with application code. The ugly format macros had been replaced by
00061  * converting all platform specific sizes to unsigned integers.
00062  *
00063  * Revision 1.9  2005/08/02 17:47:04  haraldkipp
00064  * Major API documentation update.
00065  *
00066  * Revision 1.8  2005/07/26 15:49:59  haraldkipp
00067  * Cygwin support added.
00068  *
00069  * Revision 1.7  2005/04/30 16:42:42  chaac
00070  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00071  * is defined in NutConf, it will make effect where it is used.
00072  *
00073  * Revision 1.6  2004/11/08 18:15:02  haraldkipp
00074  * Very bad hack to support 32-bit boundaries.
00075  *
00076  * Revision 1.5  2004/04/07 12:13:58  haraldkipp
00077  * Matthias Ringwald's *nix emulation added
00078  *
00079  * Revision 1.4  2004/03/19 09:05:12  jdubiec
00080  * Fixed format strings declarations for AVR.
00081  *
00082  * Revision 1.3  2004/03/16 16:48:45  haraldkipp
00083  * Added Jan Dubiec's H8/300 port.
00084  *
00085  * Revision 1.2  2003/07/20 16:04:36  haraldkipp
00086  * Nicer debug output
00087  *
00088  * Revision 1.1.1.1  2003/05/09 14:41:49  haraldkipp
00089  * Initial using 3.2.1
00090  *
00091  * Revision 1.18  2003/05/06 18:53:10  harald
00092  * Use trace flag
00093  *
00094  * Revision 1.17  2003/03/31 14:53:30  harald
00095  * Prepare release 3.1
00096  *
00097  * Revision 1.16  2003/02/04 18:15:56  harald
00098  * Version 3 released
00099  *
00100  * Revision 1.15  2002/06/26 17:29:44  harald
00101  * First pre-release with 2.4 stack
00102  *
00103  */
00104 
00110 
00111 #include <cfg/os.h>
00112 #include <compiler.h>
00113 #include <string.h>
00114 
00115 #include <sys/atom.h>
00116 #include <sys/heap.h>
00117 
00118 #ifdef NUTDEBUG
00119 #include <sys/osdebug.h>
00120 #endif
00121 
00122 #if defined(__arm__) || defined(__m68k__) || defined(__H8300H__) || defined(__H8300S__) || defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00123 #define ARCH_32BIT
00124 #endif
00125 
00129 HEAPNODE *volatile heapFreeList;
00130 
00134 static size_t available;
00135 
00140 /* MEMOVHD = sizeof(HEAPNODE:hn_size) + sizeof(0xDEADBEEF) */
00141 #define MEMOVHD (sizeof(size_t) + sizeof(0xDEADBEEF))
00142 
00174 void *NutHeapAlloc(size_t size)
00175 {
00176     HEAPNODE *node;
00177     HEAPNODE **npp;
00178     HEAPNODE *fit = 0;
00179     HEAPNODE **fpp = 0;
00180 
00181 #if defined(__arm__) || defined(__m68k__) || defined(__H8300H__) || defined(__H8300S__) || defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00182     /*
00183      * Allign to the word boundary
00184      */
00185     while ((size & 0x03) != 0)
00186         size++;
00187 #endif
00188 
00189     if (size >= available) {
00190 #ifdef NUTDEBUG
00191         if (__heap_trf)
00192             fputs("MEMOVR\n", __heap_trs);
00193 #endif
00194         return 0;
00195     }
00196 
00197     /*
00198      * We need additional space in front of the allocated memory
00199      * block to store its size. If this is still less than the
00200      * space required by a free node, increase it.
00201      */
00202     if ((size += MEMOVHD) < sizeof(HEAPNODE))
00203         size = sizeof(HEAPNODE);
00204 
00205     /*
00206      * Walk through the linked list of free nodes and find the best fit.
00207      */
00208     node = heapFreeList;
00209     npp = (HEAPNODE **) & heapFreeList;
00210     while (node) {
00211 
00212         /*
00213          * Found a note that fits?
00214          */
00215         if (node->hn_size >= size) {
00216             /*
00217              * If it's an exact match, we don't
00218              * search any further.
00219              */
00220             if (node->hn_size == size) {
00221                 fit = node;
00222                 fpp = npp;
00223                 break;
00224             }
00225 
00226             /*
00227              * Is it the first one we found
00228              * or was the previous one larger?
00229              */
00230             if (fit == 0 || (fit->hn_size > node->hn_size)) {
00231                 fit = node;
00232                 fpp = npp;
00233             }
00234         }
00235         npp = &node->hn_next;
00236         node = node->hn_next;
00237     }
00238 
00239     if (fit) {
00240         /*
00241          * If the node we found is larger than the
00242          * required space plus the space needed for
00243          * a new node plus a defined threshold, then
00244          * we split it.
00245          */
00246         if (fit->hn_size > size + sizeof(HEAPNODE) + ALLOC_THRESHOLD) {
00247             node = (HEAPNODE *) ((uptr_t) fit + size);
00248             node->hn_size = fit->hn_size - size;
00249             node->hn_next = fit->hn_next;
00250             fit->hn_size = size;
00251             *fpp = node;
00252         } else
00253             *fpp = fit->hn_next;
00254 
00255         available -= fit->hn_size;
00256         *((u_long *) (((char *) fit) + (fit->hn_size - 4))) = 0xDEADBEEF;
00257         fit = (HEAPNODE *) & fit->hn_next;
00258     }
00259 #ifdef NUTDEBUG
00260     if (__heap_trf) {
00261         fprintf(__heap_trs, "\n[H%x,A%d/%d] ", (u_int)(uptr_t) fit, (int)(((HEAPNODE *) (((uptr_t *) fit) - 1))->hn_size), (int)size);
00262     }
00263 #endif
00264     return fit;
00265 }
00266 
00280 void *NutHeapAllocClear(size_t size)
00281 {
00282     void *ptr;
00283 
00284     if ((ptr = NutHeapAlloc(size)) != 0)
00285         memset(ptr, 0, size);
00286 
00287     return ptr;
00288 }
00289 
00308 int NutHeapFree(void *block)
00309 {
00310     HEAPNODE *node;
00311     HEAPNODE **npp;
00312     HEAPNODE *fnode;
00313     size_t size;
00314 
00315 #ifdef NUTDEBUG
00316     if (__heap_trf) {
00317         if (block) {
00318             size_t size;
00319             size = *(((uptr_t *) block) - 1);
00320             if (*((u_long *) (((char *) block) + (size - MEMOVHD))) != 0xDEADBEEF)
00321                 fputs("\nMEMCORR-", __heap_trs);
00322 
00323         } else
00324             fputs("\nMEMNULL", __heap_trs);
00325     }
00326 #endif
00327 
00328     /*
00329      * Convert our block into a node.
00330      */
00331     fnode = (HEAPNODE *) (((uptr_t *) block) - 1);
00332 
00333 #ifdef NUTDEBUG
00334     if (__heap_trf)
00335         fprintf(__heap_trs, "\n[H%x,F%d] ", (u_int)(uptr_t) block, (int)fnode->hn_size);
00336 #endif
00337     size = fnode->hn_size;
00338 
00339     /*
00340      * Walk through the linked list of free nodes and try
00341      * to link us in.
00342      */
00343     node = heapFreeList;
00344     npp = (HEAPNODE **) & heapFreeList;
00345     while (node) {
00346         /*
00347          * If there' s a free node in front of us, merge it.
00348          */
00349         if (((uptr_t) node + node->hn_size) == (uptr_t) fnode) {
00350             node->hn_size += fnode->hn_size;
00351 
00352             /*
00353              * If a free node is following us, merge it.
00354              */
00355             if (((uptr_t) node + node->hn_size) == (uptr_t) node->hn_next) {
00356                 node->hn_size += node->hn_next->hn_size;
00357                 node->hn_next = node->hn_next->hn_next;
00358             }
00359             break;
00360         }
00361 
00362         /*
00363          * If we walked past our address, link us to the list.
00364          */
00365         if ((uptr_t) node > (uptr_t) fnode) {
00366             *npp = fnode;
00367 
00368             /*
00369              * If a free node is following us, merge it.
00370              */
00371             if (((uptr_t) fnode + fnode->hn_size) == (uptr_t) node) {
00372                 fnode->hn_size += node->hn_size;
00373                 fnode->hn_next = node->hn_next;
00374             } else
00375                 fnode->hn_next = node;
00376             break;
00377         }
00378 
00379         /*
00380          * If we are within a free node, somebody tried
00381          * to free a block twice.
00382          */
00383         if (((uptr_t) node + node->hn_size) > (uptr_t) fnode) {
00384 #ifdef NUTDEBUG
00385             if (__heap_trf)
00386                 fputs("\nTWICE\n", __heap_trs);
00387 #endif
00388             //NutExitCritical();
00389             return -1;
00390         }
00391 
00392         npp = &node->hn_next;
00393         node = node->hn_next;
00394     }
00395 
00396     /*
00397      * If no link was found, put us at the end of the list
00398      */
00399     if (!node) {
00400         fnode->hn_next = node;
00401         *npp = fnode;
00402     }
00403     available += size;
00404 
00405     return 0;
00406 }
00407 
00420 void NutHeapAdd(void *addr, size_t size)
00421 {
00422     *((uptr_t *) addr) = size;
00423     NutHeapFree(((uptr_t *) addr) + 1);
00424 }
00425 
00431 size_t NutHeapAvailable(void)
00432 {
00433     return available;
00434 }
00435 
00436 
00437 #if defined (NUTMEM_STACKHEAP) /* Stack resides in internal memory */
00438 /*
00439  * The following routines are wrappers around the standard heap
00440  * allocation routines.  These wrappers tweak the free heap pointer to point
00441  * to a second heap which is kept in internal memory and used only for a
00442  * thread's stack.
00443  */
00444 
00445 static HEAPNODE* volatile stackHeapFreeList; /* for special stack heap */
00446 static u_short stackHeapAvailable;
00447 
00448 void *NutStackAlloc(size_t size)
00449 {
00450     void * result;
00451     HEAPNODE* savedHeapNode;
00452     u_short savedAvailable;
00453 
00454     // Save current real heap context
00455     savedHeapNode = heapFreeList;
00456     savedAvailable = available;
00457     // Restore stack-heap context
00458     heapFreeList = stackHeapFreeList;
00459     available = stackHeapAvailable;
00460 
00461     result = NutHeapAlloc(size);
00462 
00463     // Save stack-heap context
00464     stackHeapFreeList = heapFreeList;
00465     stackHeapAvailable = available;
00466     // Restore real heap context
00467     heapFreeList = savedHeapNode;
00468     available = savedAvailable;
00469 
00470     return result;
00471 }
00472 
00473 int NutStackFree(void *block)
00474 {
00475     int result;
00476     HEAPNODE* savedHeapNode;
00477     u_short savedAvailable;
00478 
00479     // Save current real heap context
00480     savedHeapNode = heapFreeList;
00481     savedAvailable = available;
00482     // Restore stack-heap context
00483     heapFreeList = stackHeapFreeList;
00484     available = stackHeapAvailable;
00485 
00486     result = NutHeapFree(block);
00487 
00488     // Save stack-heap context
00489     stackHeapFreeList = heapFreeList;
00490     stackHeapAvailable = available;
00491     // Restore real heap context
00492     heapFreeList = savedHeapNode;
00493     available = savedAvailable;
00494 
00495     return result;
00496 }
00497 
00498 void NutStackAdd(void *addr, size_t size)
00499 {
00500    HEAPNODE* savedHeapNode;
00501    u_short savedAvailable;
00502 
00503    // Save current real heap context
00504    savedHeapNode = heapFreeList;
00505    savedAvailable = available;
00506    // Restore stack-heap context
00507    heapFreeList = stackHeapFreeList;
00508    available = stackHeapAvailable;
00509 
00510    NutHeapAdd(addr, size);
00511 
00512    // Save stack-heap context
00513    stackHeapFreeList = heapFreeList;
00514    stackHeapAvailable = available;
00515    // Restore real heap context
00516    heapFreeList = savedHeapNode;
00517    available = savedAvailable;
00518 }
00519 
00520 #endif /* defined(NUTMEM_STACKHEAP) */
00521 

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