usermode/library/pmalloc/src/vistaheap.c

00001 /*
00002  * Copyright (c) 1997, 1998, 1999, 2000, David E. Lowell
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
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  *
00014  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00015  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00016  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00017  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00018  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00019  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00020  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00021  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00022  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00023  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00024  * SUCH DAMAGE.
00025  *
00026  * Vista library, version 0.6.1, September 2000
00027  */
00028 
00029 /*
00030  * vistaheap.c 
00031  *
00032  * This module contains the code for a fast vistaheap allocator that can take 
00033  * its vistaheap management structure as an argument, letting the user keep
00034  * the vistaheap struct anywhere the user likes. This vistaheap system gets its 
00035  * memory from an mmapped file that is handed to its initialization routine. 
00036  * This code is poorly documented. 
00037  */
00038 
00039 #include <stdio.h>
00040 #include <unistd.h>
00041 #include <sys/stat.h>
00042 #include <itm.h>
00043 #include "vistaheap.h"
00044 
00045 /*
00046  * Overview of this vistaheap management system
00047  *
00048  *      This vistaheap is designed to be fast, small, and to let vistaheap metadata
00049  *      live anywhere the user likes. The memory it provides the user comes
00050  *      from a file mapped into the process' address space with mmap().
00051  *      This vistaheap automatically extends the end of the file (and vistaheap) as
00052  *      user needs grow. It makes no attempt to shorten the file as user
00053  *      needs shrink however. It also never unmaps any portion of the vistaheap
00054  *      to return memory to the system.
00055  *      
00056  *      All memory requests are quantized to the nearest power of two size.
00057  *      The smallest allocatable size is eight bytes. For example, a request 
00058  *      for 150k results in 256k being returned to the user. All allocations 
00059  *      are at least quadword (8 bytes) aligned. Those allocations that are 
00060  *      8k or more in size are page aligned. 
00061  *
00062  *      This vistaheap's routines are extremely fast, at a cost of being somewhat
00063  *      extravagant in its space usage. This vistaheap does not keep track of 
00064  *      the size of each allocation as most vistaheaps do (for example, by 
00065  *      storing the size of the allocation just before the start of the 
00066  *      returned chunk of memory). Therefore the size of each allocation 
00067  *      needs to be passed to the vistaheap_free() routine when the allocation
00068  *      is returned to the vistaheap. Although this is a little more work than
00069  *      one usually has to do with a vistaheap, the size of each allocation is 
00070  *      usually known statically. We impose this constraint because keeping
00071  *      the size of each allocation with the users chunk of memory 
00072  *      essentially puts vistaheap metadata in a location in which it could be
00073  *      readily corrupted by the user. The alternative might be to keep 
00074  *      some sort of hash table for this information, but that might add
00075  *      significantly to the overhead of this extremely lightweight package.
00076  */
00077 
00078 /*
00079  * vistaheap_init()
00080  *
00081  *      h -     pointer to vistaheap structure that will manage this vistaheap
00082  *      base -  pointer to base of mapped region where vistaheap will live
00083  *      limit - pointer to end of mapped region
00084  *      allocator - vistaheap from which this vistaheap's internal data will be 
00085  *                  allocated. Can be the same as h.
00086  *
00087  *      This function initializes a vistaheap management structure so that future
00088  *      mallocs and frees can be made with this vistaheap. It returns nothing. 
00089  *      'limit' can point to the same location as 'base', indicating that
00090  *      no portion of the region has yet been . It will be mapped 
00091  *      automatically the first time memory is allocated from this vistaheap.
00092  *      Alternately, 'base' could point somewhere into a file already mapped.
00093  *      In this case, 'limit' should point to the end of the mapped region.
00094  */
00095 __attribute__((tm_callable))
00096 void* vistaheap_init(vistaheap* h, void* base, void *hardlimit, vistaheap* allocator)
00097 {
00098         int     i;
00099 
00100         for (i = 0; i < NBUCKETS; i++) {
00101                 h->bucketlists[i] = NULL;
00102         }       
00103         h->base = base;
00104         h->limit = base;
00105         h->hardlimit = hardlimit;
00106         h->key = NULL;
00107         h->nlist = NULL;
00108         h->allocator = allocator;
00109 
00110         return (void *) h;
00111 }
00112 
00113 /*
00114  * log2()
00115  *
00116  *      Compute the integer log base two of 'val'.
00117  */
00118 __attribute__((tm_pure))
00119 static int log2(int val)
00120 {
00121         int     x, i;
00122 
00123         for (x = 1, i = 0; val > x; x <<= 1, i++);
00124         return i;
00125 }
00126 
00127 /*
00128  * pow2()
00129  *
00130  *      Compute 2 to the power 'pow'.
00131  */
00132 __attribute__((tm_pure))
00133 static int pow2(int pow)
00134 {
00135         return (1 << pow);
00136 }
00137 
00138 /*
00139  * morecore() - add memory to this vistaheap
00140  *
00141  *      h -     vistaheap to which to add memory from the mapped file
00142  *      pages - number of pages to add to the vistaheap
00143  *
00144  *      This function adds 'pages' pages of memory to the vistaheap 'h'. 
00145  *      It returns a pointer to the new pages, or NULL on failure.
00146  */
00147 __attribute__((tm_callable))
00148 void* morecore(vistaheap* h, int pages)
00149 {
00150         long    len;
00151         void*   result;
00152 
00153         len = pages * EXTENDSIZE;
00154         result = h->limit;
00155 
00156         h->limit += len;
00157         if (h->limit > h->hardlimit) {
00158                 h->limit = h->hardlimit;
00159                 result = NULL;
00160         }
00161 
00162         return result;
00163 }
00164 
00165 
00166 /*
00167  * nalloc() -   allocate a nugget from this vistaheap
00168  *
00169  *      h -     vistaheap from which to grab a nugget
00170  *
00171  *      This function allocates and returns one nugget from vistaheap 'h'.
00172  *
00173  *      The free lists for this vistaheap consist of linked lists of 'nuggets'.
00174  *      Each nugget (node) in the linked list points to one free chunk of
00175  *      memory in the vistaheap that could be returned to the user during a 
00176  *      subsequent vistaheap_malloc() call. The vistaheap also maintains a free list
00177  *      of nuggets that can be allocated and freed internally as needed
00178  *      to maintain the free lists. The memory for these nuggets comes
00179  *      from this vistaheap's 'allocator' which is specified when the vistaheap is
00180  *      initialized. The allocator for this vistaheap is another vistaheap from
00181  *      which all internal data (the nuggets) will be allocated. A vistaheap
00182  *      can be the allocator for itself. That just means that nuggets will
00183  *      be interspersed with user data. Should a nugget be requested with 
00184  *      none waiting on the free list, the allocator's mapped region will
00185  *      automatically be extended to accomodate the new nugget(s).
00186  */
00187 __attribute__((tm_callable))
00188 static nugget* nalloc(vistaheap* h)
00189 {
00190         nugget* result;
00191 
00192         if (h->nlist != NULL) {
00193                 /* Get nugget off the free list */
00194                 result = h->nlist;
00195                 h->nlist = result->next;
00196         }
00197         else {
00198                 /* 
00199                  * Extend allocator by one page, and break it up
00200                  * new page into nuggets. 
00201                  */
00202                 nugget  *new, *mem, *local_mem;
00203                 int     i, chunks;
00204 
00205                 mem = (nugget*) morecore(h->allocator, 1);
00206                 if (mem == NULL) {
00207                         __tm_waiver fprintf(stderr, "nalloc: morecore failed.\n");
00208                         return NULL;
00209                 }
00210                 /* 
00211                  * HACK: Here we have a nice or ugly hack depending on the optical angle 
00212                  * you view it. The call to 'morecore' above extends the pregion limits
00213                  * which is done inside a transaction to ensure the atomicity of the
00214                  * operation in case of failure (we are always called from the 
00215                  * transactional path). Then the following block creates new nuggets
00216                  * in the newly allocated region. Since the update of the region limits 
00217                  * is done transactionally, there is no reason to include the following
00218                  * inside the transaction. However there is a slight issue when running
00219                  * the following code non-transactionally: variable 'mem' was written
00220                  * transactionally, so we need to read it using the TM system to ensure
00221                  * we read the correct version independently of the version management
00222                  * done by the TM system.
00223                  */
00224                 __tm_waiver 
00225                 {
00226                         _ITM_transaction *tx = _ITM_getTransaction();
00227                         _ITM_memcpyRtWn(tx, &local_mem, &mem, sizeof(nugget *));
00228                         chunks = EXTENDSIZE / sizeof(nugget);
00229                         for (i = 0; i < chunks; i++) {
00230                                 new = &local_mem[i];
00231                                 new->addr = NULL;
00232                                 if (i == chunks - 1)
00233                                         new->next = NULL;
00234                                 else 
00235                                         new->next = &mem[i+1];
00236                         }
00237                 }
00238                 h->nlist = mem;
00239                 
00240                 result = h->nlist;
00241                 h->nlist = result->next;
00242         }
00243 
00244         return result;
00245 }
00246 
00247 /*
00248  * nfree() 
00249  *
00250  *      Return nugget pointed to by 'n' to the nugget free list for 'h'.
00251  */
00252 __attribute__((tm_callable))
00253 static void nfree(vistaheap *h, nugget *n)
00254 {
00255         n->next = h->nlist;
00256         h->nlist = n;
00257 }
00258 
00259 
00260 /*
00261  * vistaheap_malloc() 
00262  *
00263  *      Allocated at least 'size' bytes from vistaheap 'h'.
00264  */
00265 __attribute__((tm_callable))
00266 void* vistaheap_malloc(vistaheap* h, int size)
00267 {
00268         int     bucket;
00269         nugget* n;
00270         void*   result;
00271 
00272         if (size == 0)
00273                 return NULL;
00274         
00275         bucket = log2(size);
00276         bucket = (bucket < BUCKET_MIN ? BUCKET_MIN : bucket);
00277         n = h->bucketlists[bucket];
00278         if (n != NULL) {
00279                 h->bucketlists[bucket] = n->next;
00280                 result =  n->addr;
00281                 nfree(h, n);
00282         }
00283         else {
00284                 int     bsize, space, chunks, i;
00285                 nugget  *last_nugget, *newn;
00286                 char*   mem;
00287 
00288                 /* We need more storage */
00289                 if (size <= EXTENDSIZE)
00290                         space = EXTENDSIZE;
00291                 else
00292                         space = pow2(bucket);
00293                 mem = morecore(h, space / EXTENDSIZE);
00294                 if (mem == NULL)
00295                         return NULL;
00296 
00297                 bsize = pow2(bucket);
00298                 chunks = space / bsize;
00299 
00300                 newn = nalloc(h);
00301                 if (newn == NULL)
00302                         return NULL;
00303                 newn->addr = mem;
00304                 newn->next = NULL;
00305                 h->bucketlists[bucket] = newn;
00306 
00307                 last_nugget = newn;
00308                 for (i = 1; i < chunks; i++) {
00309                         newn = nalloc(h);
00310                         if (newn == NULL)
00311                                 break;
00312                         newn->addr = &mem[i * bsize];
00313                         newn->next = NULL; 
00314                         last_nugget->next = newn;
00315                         last_nugget = newn;
00316                 }
00317 
00318                 n = h->bucketlists[bucket];
00319                 if (n == NULL)
00320                         result = NULL;
00321                 else {
00322                         result = n->addr;
00323                         h->bucketlists[bucket] = n->next;
00324                         nfree(h, n);
00325                 }
00326         }
00327 
00328         return result;
00329 }
00330 
00331 
00332 /*
00333  * vistaheap_free()
00334  *
00335  *      Return 'size' bytes of memory pointed to by 'p' to vistaheap 'h'.
00336  */
00337 __attribute__((tm_callable))
00338 void vistaheap_free(vistaheap* h, void* p, int size)
00339 {
00340         nugget* n;
00341         int     bucket;
00342 
00343         n = nalloc(h);
00344         if (n == NULL) {
00345                 __tm_waiver fprintf(stderr, "vistaheap_free: nalloc failed.\n");
00346                 return;
00347         }
00348         bucket = log2(size);
00349         bucket = (bucket < BUCKET_MIN ? BUCKET_MIN : bucket);
00350         n->addr = p;
00351         n->next = h->bucketlists[bucket];
00352         h->bucketlists[bucket] = n;
00353 }
00354 

Generated on Sat Apr 23 11:43:36 2011 for Mnemosyne by  doxygen 1.4.7