db

db_salloc.c

Go to the documentation of this file.
00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996, 1997, 1998, 1999, 2000
00005  * Sleepycat Software. All rights reserved.
00006  */
00007 
00008 #include "config.h"
00009 
00010 #ifndef lint
00011 static const char revid[] = "$Id: db__salloc_8c-source.html,v 1.1 2008年06月08日 10:18:16 sebdiaz Exp $";
00012 #endif /* not lint */
00013 
00014 #ifndef NO_SYSTEM_INCLUDES
00015 #include <sys/types.h>
00016 
00017 #include <errno.h>
00018 #include <stdlib.h>
00019 #include <string.h>
00020 #endif
00021 
00022 #include "db_int.h"
00023 
00024 /*
00025  * Implement shared memory region allocation, using simple first-fit algorithm.
00026  * The model is that we take a "chunk" of shared memory store and begin carving
00027  * it up into areas, similarly to how malloc works. We do coalescing on free.
00028  *
00029  * The "len" field in the __data struct contains the length of the free region
00030  * (less the size_t bytes that holds the length). We use the address provided
00031  * by the caller to find this length, which allows us to free a chunk without
00032  * requiring that the caller pass in the length of the chunk they're freeing.
00033  */
00034 SH_LIST_HEAD(__head);
00035 struct __data {
00036 size_t len;
00037 SH_LIST_ENTRY links;
00038 };
00039 
00040 /*
00041  * CDB___db_shalloc_init --
00042  * Initialize the area as one large chunk.
00043  *
00044  * PUBLIC: void CDB___db_shalloc_init __P((void *, size_t));
00045  */
00046 void
00047 CDB___db_shalloc_init(area, size)
00048 void *area;
00049 size_t size;
00050 {
00051 struct __data *elp;
00052 struct __head *hp;
00053 
00054 hp = area;
00055 SH_LIST_INIT(hp);
00056 
00057 elp = (struct __data *)(hp + 1);
00058 elp->len = size - sizeof(struct __head) - sizeof(elp->len);
00059 SH_LIST_INSERT_HEAD(hp, elp, links, __data);
00060 }
00061 
00062 /*
00063  * CDB___db_shalloc --
00064  * Allocate some space from the shared region.
00065  *
00066  * PUBLIC: int CDB___db_shalloc __P((void *, size_t, size_t, void *));
00067  */
00068 int
00069 CDB___db_shalloc(p, len, align, retp)
00070 void *p, *retp;
00071 size_t len, align;
00072 {
00073 struct __data *elp;
00074 size_t *sp;
00075 void *rp;
00076 
00077 /* Never allocate less than the size of a struct __data. */
00078 if (len < sizeof(struct __data))
00079 len = sizeof(struct __data);
00080 
00081 #ifdef DIAGNOSTIC
00082 /* Add room for a guard byte. */
00083 ++len;
00084 #endif
00085 
00086 /* Never align to less than a db_align_t boundary. */
00087 if (align <= sizeof(db_align_t))
00088 align = sizeof(db_align_t);
00089 
00090 /* Walk the list, looking for a slot. */
00091 for (elp = SH_LIST_FIRST((struct __head *)p, __data);
00092 elp != NULL;
00093 elp = SH_LIST_NEXT(elp, links, __data)) {
00094 /*
00095  * Calculate the value of the returned pointer if we were to
00096  * use this chunk.
00097  * + Find the end of the chunk.
00098  * + Subtract the memory the user wants.
00099  * + Find the closest previous correctly-aligned address.
00100  */
00101 rp = (u_int8_t *)elp + sizeof(size_t) + elp->len;
00102 rp = (u_int8_t *)rp - len;
00103 rp = (u_int8_t *)((db_alignp_t)rp & ~(align - 1));
00104 
00105 /*
00106  * Rp may now point before elp->links, in which case the chunk
00107  * was too small, and we have to try again.
00108  */
00109 if ((u_int8_t *)rp < (u_int8_t *)&elp->links)
00110 continue;
00111 
00112 *(void **)retp = rp;
00113 #ifdef DIAGNOSTIC
00114 /*
00115  * At this point, whether or not we still need to split up a
00116  * chunk, retp is the address of the region we are returning,
00117  * and (u_int8_t *)elp + sizeof(size_t) + elp->len gives us
00118  * the address of the first byte after the end of the chunk.
00119  * Make the byte immediately before that the guard byte.
00120  */
00121 *((u_int8_t *)elp + sizeof(size_t) + elp->len - 1) = GUARD_BYTE;
00122 #endif
00123 
00124 #define SHALLOC_FRAGMENT 32
00125 /*
00126  * If there are at least SHALLOC_FRAGMENT additional bytes of
00127  * memory, divide the chunk into two chunks.
00128  */
00129 if ((u_int8_t *)rp >=
00130 (u_int8_t *)&elp->links + SHALLOC_FRAGMENT) {
00131 sp = rp;
00132 *--sp = elp->len -
00133 ((u_int8_t *)rp - (u_int8_t *)&elp->links);
00134 elp->len -= *sp + sizeof(size_t);
00135 return (0);
00136 }
00137 
00138 /*
00139  * Otherwise, we return the entire chunk, wasting some amount
00140  * of space to keep the list compact. However, because the
00141  * address we're returning to the user may not be the address
00142  * of the start of the region for alignment reasons, set the
00143  * size_t length fields back to the "real" length field to a
00144  * flag value, so that we can find the real length during free.
00145  */
00146 #define ILLEGAL_SIZE 1
00147 SH_LIST_REMOVE(elp, links, __data);
00148 for (sp = rp; (u_int8_t *)--sp >= (u_int8_t *)&elp->links;)
00149 *sp = ILLEGAL_SIZE;
00150 return (0);
00151 }
00152 
00153 return (ENOMEM);
00154 }
00155 
00156 /*
00157  * CDB___db_shalloc_free --
00158  * Free a shared memory allocation.
00159  *
00160  * PUBLIC: void CDB___db_shalloc_free __P((void *, void *));
00161  */
00162 void
00163 CDB___db_shalloc_free(regionp, ptr)
00164 void *regionp, *ptr;
00165 {
00166 struct __data *elp, *lastp, *newp;
00167 struct __head *hp;
00168 size_t free_size, *sp;
00169 int merged;
00170 
00171 /*
00172  * Step back over flagged length fields to find the beginning of
00173  * the object and its real size.
00174  */
00175 for (sp = (size_t *)ptr; sp[-1] == ILLEGAL_SIZE; --sp)
00176 ;
00177 ptr = sp;
00178 
00179 newp = (struct __data *)((u_int8_t *)ptr - sizeof(size_t));
00180 free_size = newp->len;
00181 
00182 #ifdef DIAGNOSTIC
00183 /*
00184  * The "real size" includes the guard byte; it's just the last
00185  * byte in the chunk, and the caller never knew it existed.
00186  *
00187  * Check it to make sure it hasn't been stomped.
00188  */
00189 if (*((u_int8_t *)ptr + free_size - 1) != GUARD_BYTE) {
00190 /*
00191  * Eventually, once we push a DB_ENV handle down to these
00192  * routines, we should use the standard output channels.
00193  */
00194 fprintf(stderr,
00195 "Guard byte incorrect during shared memory free.\n");
00196 abort();
00197 /* NOTREACHED */
00198 }
00199 
00200 /* Trash the returned memory (including guard byte). */
00201 memset(ptr, CLEAR_BYTE, free_size);
00202 #endif
00203 
00204 /*
00205  * Walk the list, looking for where this entry goes.
00206  *
00207  * We keep the free list sorted by address so that coalescing is
00208  * trivial.
00209  *
00210  * XXX
00211  * Probably worth profiling this to see how expensive it is.
00212  */
00213 hp = (struct __head *)regionp;
00214 for (elp = SH_LIST_FIRST(hp, __data), lastp = NULL;
00215 elp != NULL && (void *)elp < (void *)ptr;
00216 lastp = elp, elp = SH_LIST_NEXT(elp, links, __data))
00217 ;
00218 
00219 /*
00220  * Elp is either NULL (we reached the end of the list), or the slot
00221  * after the one that's being returned. Lastp is either NULL (we're
00222  * returning the first element of the list) or the element before the
00223  * one being returned.
00224  *
00225  * Check for coalescing with the next element.
00226  */
00227 merged = 0;
00228 if ((u_int8_t *)ptr + free_size == (u_int8_t *)elp) {
00229 newp->len += elp->len + sizeof(size_t);
00230 SH_LIST_REMOVE(elp, links, __data);
00231 if (lastp != NULL)
00232 SH_LIST_INSERT_AFTER(lastp, newp, links, __data);
00233 else
00234 SH_LIST_INSERT_HEAD(hp, newp, links, __data);
00235 merged = 1;
00236 }
00237 
00238 /* Check for coalescing with the previous element. */
00239 if (lastp != NULL && (u_int8_t *)lastp +
00240 lastp->len + sizeof(size_t) == (u_int8_t *)newp) {
00241 lastp->len += newp->len + sizeof(size_t);
00242 
00243 /*
00244  * If we have already put the new element into the list take
00245  * it back off again because it's just been merged with the
00246  * previous element.
00247  */
00248 if (merged)
00249 SH_LIST_REMOVE(newp, links, __data);
00250 merged = 1;
00251 }
00252 
00253 if (!merged) {
00254 if (lastp == NULL)
00255 SH_LIST_INSERT_HEAD(hp, newp, links, __data);
00256 else
00257 SH_LIST_INSERT_AFTER(lastp, newp, links, __data);
00258 }
00259 }
00260 
00261 /*
00262  * CDB___db_shalloc_count --
00263  * Return the amount of memory on the free list.
00264  *
00265  * PUBLIC: size_t CDB___db_shalloc_count __P((void *));
00266  */
00267 size_t
00268 CDB___db_shalloc_count(addr)
00269 void *addr;
00270 {
00271 struct __data *elp;
00272 size_t count;
00273 
00274 count = 0;
00275 for (elp = SH_LIST_FIRST((struct __head *)addr, __data);
00276 elp != NULL;
00277 elp = SH_LIST_NEXT(elp, links, __data))
00278 count += elp->len;
00279 
00280 return (count);
00281 }
00282 
00283 /*
00284  * CDB___db_shsizeof --
00285  * Return the size of a shalloc'd piece of memory.
00286  *
00287  * !!!
00288  * Note that this is from an internal standpoint -- it includes not only
00289  * the size of the memory being used, but also the extra alignment bytes
00290  * in front and, #ifdef DIAGNOSTIC, the guard byte at the end.
00291  *
00292  * PUBLIC: size_t CDB___db_shsizeof __P((void *));
00293  */
00294 size_t
00295 CDB___db_shsizeof(ptr)
00296 void *ptr;
00297 {
00298 struct __data *elp;
00299 size_t *sp;
00300 
00301 /*
00302  * Step back over flagged length fields to find the beginning of
00303  * the object and its real size.
00304  */
00305 for (sp = (size_t *)ptr; sp[-1] == ILLEGAL_SIZE; --sp)
00306 ;
00307 
00308 elp = (struct __data *)((u_int8_t *)sp - sizeof(size_t));
00309 return (elp->len);
00310 }
00311 
00312 /*
00313  * CDB___db_shalloc_dump --
00314  *
00315  * PUBLIC: void CDB___db_shalloc_dump __P((void *, FILE *));
00316  */
00317 void
00318 CDB___db_shalloc_dump(addr, fp)
00319 void *addr;
00320 FILE *fp;
00321 {
00322 struct __data *elp;
00323 
00324 /* Make it easy to call from the debugger. */
00325 if (fp == NULL)
00326 fp = stderr;
00327 
00328 fprintf(fp, "%s\nMemory free list\n", DB_LINE);
00329 
00330 for (elp = SH_LIST_FIRST((struct __head *)addr, __data);
00331 elp != NULL;
00332 elp = SH_LIST_NEXT(elp, links, __data))
00333 fprintf(fp, "%#lx: %lu\t", (u_long)elp, (u_long)elp->len);
00334 fprintf(fp, "\n");
00335 }

Generated on Sun Jun 8 10:56:36 2008 for GNUmifluz by doxygen 1.5.5

AltStyle によって変換されたページ (->オリジナル) /