shmalloc.c

Go to the documentation of this file.
00001 /* $Id: shmalloc.c,v 1.5 2005/02/03 11:02:20 jlaako Exp $ */
00002 
00003 /*
00004 
00005     Shared memory allocator.
00006     
00007     Copyright (C) 2005-2007 Nokia Corporation.
00008 
00009     Contact: Jussi Laako <jussi.laako@nokia.com>
00010 
00011     This library is free software; you can redistribute it and/or
00012     modify it under the terms of the GNU Lesser General Public
00013     License as published by the Free Software Foundation; either
00014     version 2.1 of the License, or (at your option) any later version.
00015 
00016     This library is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019     Lesser General Public License for more details.
00020 
00021     You should have received a copy of the GNU Lesser General Public
00022     License along with this library; if not, write to the Free Software
00023     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00024 
00025 */
00026 
00027 
00028 /* specify source conformance to include necessary functions */
00029 #define _XOPEN_SOURCE 600
00030 #define _POSIX_C_SOURCE 200112L
00031 
00032 
00033 #include <stdio.h>
00034 #include <string.h>
00035 #include <errno.h>
00036 #include <limits.h>
00037 #include <sys/types.h>
00038 
00039 #include "shmalloc.h"
00040 #include "sharedmem_private.h"
00041 
00042 
00043 /* --- internal */
00044 
00046 #define BL_SZ(b) ((b)->size & ~SHAREDMEM_FLAGS_MASK)
00047 
00048 
00049 /* set error message */
00050 static void set_error (sharedmem_alloc_t *inst, int error_code,
00051     const char *error_str)
00052 {
00053     _sharedmem_set_error(SHMALLOC_SHAREDMEM(inst), error_code, error_str);
00054 }
00055 
00056 
00057 /* get size rounded to block aligned boundary */
00058 static size_t aligned_block_size (size_t size)
00059 {
00060     size_t block_size;
00061     
00062     block_size = sizeof(sharedmem_block_t) + size;
00063     block_size += SHAREDMEM_ALIGN - block_size % SHAREDMEM_ALIGN;
00064     return block_size;
00065 }
00066 
00067 
00068 /* is the block reserved? */
00069 static inline int block_is_reserved (sharedmem_block_t *block)
00070 {
00071     if (block->size & SHAREDMEM_BLOCK_INUSE)
00072         return 1;
00073     else
00074         return 0;
00075 }
00076 
00077 
00078 /* is the block free? */
00079 static inline int block_is_free (sharedmem_block_t *block)
00080 {
00081     if (block->size & SHAREDMEM_BLOCK_INUSE)
00082         return 0;
00083     else
00084         return 1;
00085 }
00086 
00087 
00088 /* get offset mapped to process' address space */
00089 static inline sharedmem_block_t * get_offs_ptr (sharedmem_alloc_t *inst,
00090     long offset)
00091 {
00092     char *base_ptr;
00093     
00094     base_ptr = (char *) SHAREDMEM_PTR(SHMALLOC_SHAREDMEM(inst));
00095     return ((sharedmem_block_t *) (base_ptr + offset));
00096 }
00097 
00098 
00099 /* get data offset for specific block offset */
00100 static inline long get_data_offs (long offset)
00101 {
00102     return (offset + sizeof(sharedmem_block_t));
00103 }
00104 
00105 
00106 /* find space for block allocation on first-fit basis */
00107 static long find_free (sharedmem_alloc_t *inst, size_t block_size)
00108 {
00109     long block = 0;
00110     sharedmem_block_t *block_ptr;
00111 
00112     while (block >= 0)
00113     {
00114         block_ptr = get_offs_ptr(inst, block);
00115         if (block_is_free(block_ptr))
00116         {
00117             if (BL_SZ(block_ptr) >= block_size)
00118                 return block;
00119         }
00120         block = block_ptr->next;
00121     }
00122 
00123     return -1;
00124 }
00125 
00126 
00127 /* if the last block is free, return it's offset, otherwise add new after it */
00128 static long get_last_free (sharedmem_alloc_t *inst)
00129 {
00130     long block = 0;
00131     long this;
00132     long next;
00133     sharedmem_block_t *block_ptr;
00134     sharedmem_block_t *next_ptr;
00135 
00136     do {
00137         this = block;
00138         block_ptr = get_offs_ptr(inst, block);
00139         block = block_ptr->next;
00140     } while (block >= 0);
00141     if (!block_is_free(block_ptr))
00142     {
00143         next = this + BL_SZ(block_ptr);
00144         next_ptr = get_offs_ptr(inst, next);
00145         next_ptr->size = 0;
00146         next_ptr->next = -1;
00147         block_ptr->next = next;
00148         return next;
00149     }
00150     return this;
00151 }
00152 
00153 
00154 /* expand the current block if there's enough space available */
00155 static long expand_block (sharedmem_alloc_t *inst, long oldblock,
00156     size_t block_size)
00157 {
00158     long block = oldblock;
00159     long next;
00160     long flags;
00161     sharedmem_block_t *block_ptr;
00162     sharedmem_block_t *next_ptr;
00163 
00164     block_ptr = get_offs_ptr(inst, block);
00165     next = block_ptr->next;
00166     if (next >= 0)
00167     {
00168         next_ptr = get_offs_ptr(inst, next);
00169         if (block_is_free(next_ptr))
00170         {
00171             /* next block has enough room to expand current */
00172             if ((BL_SZ(block_ptr) + BL_SZ(next_ptr)) >= block_size)
00173             {
00174                 flags = next_ptr->size & SHAREDMEM_FLAGS_MASK;
00175                 /* resize next */
00176                 next_ptr->size = BL_SZ(next_ptr) - 
00177                     (block_size - BL_SZ(block_ptr));
00178                 next_ptr->size |= flags;
00179                 /* next ended up being zero sized, remove */
00180                 if (BL_SZ(next_ptr) == 0)
00181                     block_ptr->next = next_ptr->next;
00182                 /* current was just resized, return current */
00183                 return block;
00184             }
00185         }
00186     }
00187 
00188     /* no free space */
00189     return -1;
00190 }
00191 
00192 
00193 /* shrink the current block by splitting out the freed space */
00194 static long shrink_block (sharedmem_alloc_t *inst, long block,
00195     size_t block_size)
00196 {
00197     long next;
00198     long flags;
00199     sharedmem_block_t *block_ptr;
00200     sharedmem_block_t *next_ptr;
00201     size_t next_size;
00202 
00203     block_ptr = get_offs_ptr(inst, block);
00204     /* create new next, zeroed flags */
00205     next_size = BL_SZ(block_ptr) - block_size;
00206     next = block + block_size;
00207     next_ptr = get_offs_ptr(inst, next);
00208     memset(next_ptr, 0x00, sizeof(sharedmem_block_t));
00209     /* move old next one ahead */
00210     next_ptr->next = block_ptr->next;
00211     next_ptr->size = next_size;
00212     /* the new size, new next, retain flags */
00213     flags = block_ptr->size & SHAREDMEM_FLAGS_MASK;
00214     block_ptr->size = block_size;
00215     block_ptr->size |= flags;
00216     block_ptr->next = next;
00217     
00218     return block;
00219 }
00220 
00221 
00222 static int _coalesce_blocks (sharedmem_alloc_t *inst)
00223 {
00224     int retval = 0;
00225     long block;
00226     long next;
00227     sharedmem_block_t *block_ptr;
00228     sharedmem_block_t *next_ptr;
00229     
00230     block = 0;
00231     while (block >= 0)
00232     {
00233         /*printf("block = %x\n", (size_t) block);*/
00234         block_ptr = get_offs_ptr(inst, block);
00235         /* block is free and next exists */
00236         if (block_is_free(block_ptr) && block_ptr->next >= 0)
00237         {
00238             next = block_ptr->next;
00239             /*printf("next = %x\n", (size_t) next);*/
00240             next_ptr = get_offs_ptr(inst, next);
00241             /* two consecutive free blocks? coalesce... */
00242             if (block_is_free(next_ptr))
00243             {
00244                 /*printf("combine %x && %x\n", (size_t) block, (size_t) next);*/
00245                 block_ptr->size = BL_SZ(block_ptr) + BL_SZ(next_ptr);
00246                 block_ptr->next = next_ptr->next;
00247                 memset(next_ptr, 0x00, sizeof(sharedmem_block_t));
00248                 retval = 1;
00249             }
00250         }
00251         block = block_ptr->next;
00252     }
00253 
00254     return retval;
00255 }
00256 
00257 
00258 /* block coalescence, is done on free and realloc */
00259 static void coalesce_blocks (sharedmem_alloc_t *inst)
00260 {
00261     while (_coalesce_blocks(inst));
00262 }
00263 
00264 
00265 static long sharedmem_alloc_nl (sharedmem_alloc_t *inst, size_t size)
00266 {
00267     long block;
00268     long next;
00269     long offset = -1;
00270     size_t block_size;
00271     sharedmem_block_t *block_ptr;
00272     sharedmem_block_t *next_ptr;
00273     
00274     block_size = aligned_block_size(size);
00275     /* find free space */
00276     block = find_free(inst, block_size);
00277     if (block >= 0)
00278     {
00279         /* allocate and split the free space if needed */
00280         block_ptr = get_offs_ptr(inst, block);
00281         /* free space is larger than allocation request, split */
00282         if ((BL_SZ(block_ptr) - block_size) > sizeof(sharedmem_block_t))
00283         {
00284             /* next block starts after this */
00285             next = block + block_size;
00286             next_ptr = get_offs_ptr(inst, next);
00287             /* was there this->next already? */
00288             if (block_ptr->next >= 0)
00289             {
00290                 /* newnext->next = this->next */
00291                 next_ptr->next = block_ptr->next;
00292                 /* this->next = newnext */
00293                 block_ptr->next = next;
00294             }
00295             else
00296             {
00297                 /* newnext->next = none (last) */
00298                 next_ptr->next = -1;
00299                 /* this->next = newnext */
00300                 block_ptr->next = next;
00301             }
00302             /* next->size = this->size - newsize */
00303             next_ptr->size = BL_SZ(block_ptr) - block_size;
00304             /* this->size = newsize */
00305             block_ptr->size = (block_size | SHAREDMEM_BLOCK_INUSE);
00306         }
00307         else
00308         {
00309             block_ptr->size |= SHAREDMEM_BLOCK_INUSE;
00310         }
00311         offset = get_data_offs(block);
00312     }
00313     else
00314     {
00315         set_error(inst, 0, "sharedmem_alloc(): no space available");
00316     }
00317 
00318     return offset;
00319 }
00320 
00321 
00322 static int sharedmem_free_nl (sharedmem_alloc_t *inst, long offset)
00323 {
00324     long block;
00325     sharedmem_block_t *block_ptr;
00326 
00327     block = offset - sizeof(sharedmem_block_t);
00328     if (block < 0)
00329         return -1;
00330 
00331     block_ptr = get_offs_ptr(inst, block);
00332     if (block_is_reserved(block_ptr))
00333     {
00334         block_ptr->size ^= SHAREDMEM_BLOCK_INUSE;
00335         coalesce_blocks(inst);
00336     }
00337     else
00338     {
00339         set_error(inst, 0, "sharedmem_free(): double free?");
00340         return -1;
00341     }
00342 
00343     return 0;
00344 }
00345 
00346 
00347 /* --- public */
00348 
00349 
00350 int sharedmem_alloc_create (sharedmem_alloc_t *inst, sharedmem_t *shminst)
00351 {
00352     sharedmem_block_t *block_ptr;
00353 
00354     if (inst == NULL || shminst == NULL)
00355         return -1;
00356     memset(inst, 0x00, sizeof(sharedmem_alloc_t));
00357 
00358     SHMALLOC_SHAREDMEM(inst) = shminst;
00359     block_ptr = (sharedmem_block_t *) SHAREDMEM_PTR(SHMALLOC_SHAREDMEM(inst));
00360     if (block_ptr == NULL)
00361     {
00362         set_error(inst, 0, "sharedmem_alloc_create(): sharedmem_t * == NULL");
00363         return -1;
00364     }
00365 
00366     if (!sharedmem_lock(SHMALLOC_SHAREDMEM(inst)))
00367         return -1;
00368 
00369     block_ptr->size = SHAREDMEM_SIZE(SHMALLOC_SHAREDMEM(inst));
00370     block_ptr->next = -1;
00371 
00372     if (!sharedmem_unlock(SHMALLOC_SHAREDMEM(inst)))
00373         return -1;
00374 
00375     return 0;
00376 }
00377 
00378 
00379 long sharedmem_alloc (sharedmem_alloc_t *inst, size_t size)
00380 {
00381     long offset = -1;
00382     
00383     if (inst == NULL)
00384         return -1;
00385     if (size == 0)
00386         return -1;
00387 
00388     if (!sharedmem_lock(SHMALLOC_SHAREDMEM(inst)))
00389         return -1;
00390 
00391     offset = sharedmem_alloc_nl(inst, size);
00392 
00393     sharedmem_unlock(SHMALLOC_SHAREDMEM(inst));
00394     
00395     return offset;
00396 }
00397 
00398 
00399 long sharedmem_realloc (sharedmem_alloc_t *inst, long old_offset, size_t size)
00400 {
00401     long old;
00402     long block = -1;
00403     long offset = -1;
00404     size_t block_size;
00405     size_t old_size;
00406     size_t copy_size;
00407     sharedmem_block_t *block_ptr;
00408     
00409     if (inst == NULL)
00410         return -1;
00411 
00412     /* new size is 0, so memory is just freed */
00413     if (size == 0)
00414         return sharedmem_free(inst, old_offset);
00415 
00416     old = old_offset - sizeof(sharedmem_block_t);
00417     if (old < 0)
00418         return -1;
00419 
00420     if (!sharedmem_lock(SHMALLOC_SHAREDMEM(inst)))
00421         return -1;
00422 
00423     block_ptr = get_offs_ptr(inst, old);
00424     old_size = BL_SZ(block_ptr);
00425 
00426     block_size = aligned_block_size(size);
00427     /* block is same size? */
00428     if (block_size == old_size)
00429     {
00430         offset = old_offset;
00431         goto realloc_out;
00432     }
00433     else if (block_size > old_size)
00434     {
00435         /* try to expand the current block */
00436         block = expand_block(inst, old, block_size);
00437     }
00438     else
00439     {
00440         /* shrink the current block */
00441         block = shrink_block(inst, old, block_size);
00442         coalesce_blocks(inst);
00443     }
00444     /* resize was successfull */
00445     if (block >= 0)
00446     {
00447         /*block_ptr = get_offs_ptr(inst, block);*/
00448         offset = get_data_offs(block);
00449     }
00450     else /* go to full allocate & copy & free */
00451     {
00452         offset = sharedmem_alloc_nl(inst, size);
00453         if (offset < 0)
00454         {
00455             set_error(inst, 0, "sharedmem_realloc(): no space available");
00456             goto realloc_out;
00457         }
00458         copy_size = (BL_SZ(block_ptr) <= old_size) ?
00459             BL_SZ(block_ptr) : old_size;
00460         copy_size -= sizeof(sharedmem_block_t);
00461         memcpy(sharedmem_alloc_get_ptr(inst, offset),
00462             sharedmem_alloc_get_ptr(inst, old_offset),
00463             copy_size);
00464         sharedmem_free_nl(inst, old_offset);
00465     }
00466 
00467 realloc_out:
00468     sharedmem_unlock(SHMALLOC_SHAREDMEM(inst));
00469     
00470     return offset;
00471 }
00472 
00473 
00474 int sharedmem_free (sharedmem_alloc_t *inst, long offset)
00475 {
00476     long res = 0;
00477 
00478     if (inst == NULL)
00479         return -1;
00480 
00481     if (!sharedmem_lock(SHMALLOC_SHAREDMEM(inst)))
00482         return -1;
00483 
00484     res = sharedmem_free_nl(inst, offset);
00485 
00486     sharedmem_unlock(SHMALLOC_SHAREDMEM(inst));
00487 
00488     return res;
00489 }
00490 
00491 
00492 void * sharedmem_alloc_get_ptr (sharedmem_alloc_t *inst, long offset)
00493 {
00494     char *base_ptr;
00495     
00496     base_ptr = (char *) SHAREDMEM_PTR(SHMALLOC_SHAREDMEM(inst));
00497     return ((void *) (base_ptr + offset));
00498 }
00499 
00500 
00501 int sharedmem_alloc_grow (sharedmem_alloc_t *inst, size_t new_size)
00502 {
00503     long last;
00504     size_t old_size;
00505     size_t size_diff;
00506     sharedmem_block_t *last_ptr;
00507 
00508     if (inst == NULL)
00509         return -1;
00510     if (new_size <= SHAREDMEM_SIZE(SHMALLOC_SHAREDMEM(inst)))
00511     {
00512         set_error(inst, 0, "sharedmem_alloc_grow(): new_size <= old_size");
00513         return -1;
00514     }
00515     old_size = SHAREDMEM_SIZE(SHMALLOC_SHAREDMEM(inst));
00516     sharedmem_resize(SHMALLOC_SHAREDMEM(inst), new_size);
00517     size_diff = SHAREDMEM_SIZE(SHMALLOC_SHAREDMEM(inst)) - old_size;
00518     if (size_diff < sizeof(sharedmem_block_t))
00519     {
00520         set_error(inst, 0,
00521             "sharedmem_alloc_grow(): size_diff < sizeof(sharedmem_block_t)");
00522         return -1;
00523     }
00524 
00525     if (!sharedmem_lock(SHMALLOC_SHAREDMEM(inst)))
00526         return -1;
00527 
00528     last = get_last_free(inst);
00529     last_ptr = get_offs_ptr(inst, last);
00530     last_ptr->size += size_diff;
00531 
00532     sharedmem_unlock(SHMALLOC_SHAREDMEM(inst));
00533 
00534     return 0;
00535 }
00536 
00537 
00538 void sharedmem_alloc_list_allocs (sharedmem_alloc_t *inst)
00539 {
00540     long block = 0;
00541     sharedmem_block_t *block_ptr;
00542 
00543     if (inst == NULL)
00544         return;
00545 
00546     fprintf(stderr, "sharedmem_alloc_list_allocs()\n");
00547     fprintf(stderr, "-----------------------------\n");
00548     while (block >= 0)
00549     {
00550         block_ptr = get_offs_ptr(inst, block);
00551         if (!block_is_free(block_ptr))
00552         {
00553             fprintf(stderr, "0x%0x: offset = 0x%0x, size = %u\n",
00554                 (size_t) block_ptr, (size_t) block, BL_SZ(block_ptr));
00555         }
00556         block = block_ptr->next;
00557     }
00558     fprintf(stderr, "-----------------------------\n");
00559 }
00560 
00561 
00562 void sharedmem_alloc_list_frees (sharedmem_alloc_t *inst)
00563 {
00564     long block = 0;
00565     sharedmem_block_t *block_ptr;
00566 
00567     if (inst == NULL)
00568         return;
00569 
00570     fprintf(stderr, "sharedmem_alloc_list_frees()\n");
00571     fprintf(stderr, "----------------------------\n");
00572     while (block >= 0)
00573     {
00574         block_ptr = get_offs_ptr(inst, block);
00575         if (block_is_free(block_ptr))
00576         {
00577             fprintf(stderr, "0x%0x: offset = 0x%0x, size = %u\n",
00578                 (size_t) block_ptr, (size_t) block, BL_SZ(block_ptr));
00579         }
00580         block = block_ptr->next;
00581     }
00582     fprintf(stderr, "----------------------------\n");
00583 }

Generated on Thu Sep 13 18:14:21 2007 for libsharedmem by  doxygen 1.5.1