dynarray.cpp

00001 /* -*- mode:C++; c-basic-offset:4 -*-
00002      Shore-MT -- Multi-threaded port of the SHORE storage manager
00003    
00004                        Copyright (c) 2007-2009
00005       Data Intensive Applications and Systems Labaratory (DIAS)
00006                Ecole Polytechnique Federale de Lausanne
00007    
00008                          All Rights Reserved.
00009    
00010    Permission to use, copy, modify and distribute this software and
00011    its documentation is hereby granted, provided that both the
00012    copyright notice and this permission notice appear in all copies of
00013    the software, derivative works or modified versions, and any
00014    portions thereof, and that both notices appear in supporting
00015    documentation.
00016    
00017    This code is distributed in the hope that it will be useful, but
00018    WITHOUT ANY WARRANTY; without even the implied warranty of
00019    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS
00020    DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
00021    RESULTING FROM THE USE OF THIS SOFTWARE.
00022 */
00023 /*<std-header orig-src='shore' incl-file-exclusion='DYNARRAY_CPP'>
00024 
00025  $Id: dynarray.cpp,v 1.5 2012/01/02 21:52:21 nhall Exp $
00026 
00027 SHORE -- Scalable Heterogeneous Object REpository
00028 
00029 Copyright (c) 1994-99 Computer Sciences Department, University of
00030                       Wisconsin -- Madison
00031 All Rights Reserved.
00032 
00033 Permission to use, copy, modify and distribute this software and its
00034 documentation is hereby granted, provided that both the copyright
00035 notice and this permission notice appear in all copies of the
00036 software, derivative works or modified versions, and any portions
00037 thereof, and that both notices appear in supporting documentation.
00038 
00039 THE AUTHORS AND THE COMPUTER SCIENCES DEPARTMENT OF THE UNIVERSITY
00040 OF WISCONSIN - MADISON ALLOW FREE USE OF THIS SOFTWARE IN ITS
00041 "AS IS" CONDITION, AND THEY DISCLAIM ANY LIABILITY OF ANY KIND
00042 FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
00043 
00044 This software was developed with support by the Advanced Research
00045 Project Agency, ARPA order number 018 (formerly 8230), monitored by
00046 the U.S. Army Research Laboratory under contract DAAB07-91-C-Q518.
00047 Further funding for this work was provided by DARPA through
00048 Rome Research Laboratory Contract No. F30602-97-2-0247.
00049 
00050 */
00051 
00052 /**\cond skip */
00053 #include "dynarray.h"
00054 #include "shore-config.h"
00055 #include <errno.h>
00056 #include <sys/mman.h>
00057 #include <algorithm>
00058 #include <cstdlib>
00059 #include <cassert>
00060 #include <cstring>
00061 
00062 // no system I know of *requires* larger pages than this
00063 static size_t const MM_PAGE_SIZE = 8192;
00064 // most systems can't handle bigger than this, and we need a sanity check
00065 static size_t const MM_MAX_CAPACITY = MM_PAGE_SIZE*1024*1024*1024;
00066 
00067 static size_t align_up(size_t bytes, size_t align) {
00068     size_t mask = align - 1;
00069     return (bytes+mask) &~ mask;
00070 }
00071 
00072 #if HAVE_DECL_MAP_ALIGN 
00073 #define USE_MAP_ALIGN 1
00074 #endif
00075 
00076 #undef TEST_ME
00077 
00078 int dynarray::init(size_t max_size, size_t align) {
00079     // round up to the nearest page boundary
00080     max_size = align_up(max_size, MM_PAGE_SIZE);
00081     
00082     // validate inputs
00083     if(max_size > MM_MAX_CAPACITY)
00084         return EFBIG;
00085     if(MM_PAGE_SIZE > max_size)
00086         return EINVAL;
00087     if((align & -align) != align)
00088         return EINVAL;
00089 
00090     /*
00091       The magical incantation below tells mmap to reserve address
00092       space within the process without actually allocating any
00093       memory. We are then free to re-map any subset of that
00094       reservation using MAP_FIXED (using MAP_FIXED without a
00095       reservation always fails).
00096 
00097       Note that MAP_FIXED is smart enough to mix and match different
00098       sets of permissions, so we can extend the array simply by
00099       remapping 0..new_size with R/W permissions, and can blow
00100       everything away by unmapping 0..reserved_size.
00101 
00102       Tested on both Linux-2.6.18/x86 and Solaris-10/Sparc.
00103     */
00104 
00105     static int const PROTS = PROT_NONE;
00106     int flags = MAP_NORESERVE | MAP_ANON | MAP_PRIVATE;
00107 
00108     // Allocate extra for forced alignment (no effect for align=0)
00109     align = std::max(align, MM_PAGE_SIZE);
00110 #if USE_MAP_ALIGN
00111     char* align_arg = (char*) align;
00112     size_t align_extra = 0;
00113     flags |= MAP_ALIGN;
00114 #else
00115     char* align_arg = 0;
00116     size_t align_extra = align - MM_PAGE_SIZE;
00117 #endif
00118 #ifdef TEST_ME
00119     std::fprintf(stderr, 
00120     "thread %p dynarray::init(%p): mmap:%p %u\n", 
00121     (void *)pthread_self(),
00122     this,
00123     align_arg,  
00124     unsigned(max_size + align_extra));
00125 #endif
00126     union { void* v; uintptr_t n; char* c; }
00127         u={mmap(align_arg, max_size+align_extra, PROTS, flags, -1, 0)};
00128 
00129     if(u.v == MAP_FAILED)
00130         return errno;
00131 
00132 #if !USE_MAP_ALIGN
00133     /* Verify alignment...
00134 
00135        This is incredibly annoying: in all probability the system will
00136        give us far stronger alignment than we request, but Linux
00137        doesn't actually promise anything more strict than
00138        page-aligned. Solaris does promise to always do 1MB or 4MB
00139        alignment, but it also provides a MAP_ALIGN which moots the
00140        whole issue.
00141 
00142        Unfortunately Linux insists on not being helpful, so we have to
00143        request more than needed, then chop off the extra in a way that
00144        gives us the desired alignment. That extra could be the
00145        little bit that pushes the system over the edge and gives us
00146        ENOMEM, but we're kind of stuck.
00147      */
00148     long aligned_base = align_up(u.n, align);
00149     if(long extra=aligned_base-u.n) {
00150 #ifdef TEST_ME
00151         std::fprintf(stderr, "chopping off %zx bytes of prefix for start: %zx\n",
00152             extra, aligned_base);
00153         std::fprintf(stderr, "dynarray::init: munmap:%p %u\n", u.c, unsigned( extra));
00154 #endif
00155         munmap(u.c, extra);
00156         u.n = aligned_base;
00157         align_extra -= extra;
00158     }
00159     if(align_extra > 0) {
00160 #ifdef TEST_ME
00161         std::fprintf(stderr, "chopping %zx bytes of postfix for end: %p\n", align_extra, u.c+max_size);
00162         std::fprintf(stderr, "dynarray::init: munmap:%p %u\n", u.c + max_size, unsigned(align_extra));
00163 #endif
00164         munmap(u.c+max_size, align_extra);
00165     }
00166 #endif
00167 
00168     _base = u.c;
00169     _capacity = max_size;
00170     _size = 0;
00171 
00172 #ifdef TEST_ME
00173     std::fprintf(stderr, 
00174         "thread %p dynarray::init(%p) _base: %p size requested %u (%u KB)    end:%p capacity %u _size %u\n", 
00175             (void *)pthread_self(), this,
00176             _base,
00177             unsigned(max_size + align_extra),
00178             (unsigned(max_size + align_extra)/1024),
00179             _base+max_size+align_extra,
00180             unsigned(_capacity),
00181             unsigned(_size)
00182             );
00183 #endif
00184     return 0;
00185 }
00186 
00187 
00188 int dynarray::fini() 
00189 {
00190 #ifdef TEST_ME
00191     std::fprintf(stderr, "dynarray::fini: munmap:%p %u\n", _base,unsigned(_capacity));
00192 #endif
00193     if(int err=munmap(_base, _capacity))
00194     return err;
00195         
00196     _base = 0;
00197     _size = 0;
00198     _capacity = 0;
00199     return 0;
00200 }
00201 
00202 
00203 int dynarray::resize(size_t new_size) {
00204     // round up to the nearest page boundary
00205     new_size = align_up(new_size, MM_PAGE_SIZE);
00206 
00207     // validate
00208     if(_size > new_size)
00209         return EINVAL;
00210 
00211     if(new_size > _capacity) {
00212         // GNATS 174:
00213         // For now, just print a bunch of stuff and return an error.
00214         // This will ultimately result in a throw.
00215         return EFBIG;
00216     }
00217 
00218     static int const PROTS = PROT_READ | PROT_WRITE;
00219     static int const FLAGS = MAP_FIXED | MAP_ANON | MAP_PRIVATE;
00220 
00221     // remap the new range as RW. Don't mess w/ the existing region!!
00222     void* result = mmap(_base+_size, new_size-_size, PROTS, FLAGS, -1, 0);
00223     if(result == MAP_FAILED) return errno;
00224     if(result != _base+_size) {
00225         // This will result in a throw
00226         return EFBIG;
00227     }
00228 
00229     _size = new_size;
00230     return 0;
00231 }
00232 
00233 int dynarray::ensure_capacity(size_t min_size) {
00234 #ifdef TEST_ME
00235     std::fprintf(stderr, 
00236     "thread %p dynarray(%p)::ensure_capacity(min_size %u) from size()=%u\n",
00237     (void *)pthread_self(), this, 
00238     unsigned(min_size), unsigned(size())
00239     );
00240 #endif
00241 
00242     min_size  = align_up(min_size, MM_PAGE_SIZE);
00243 
00244 #ifdef TEST_ME
00245     std::fprintf(stderr, 
00246     "thread %p dynarray(%p)::ensure_capacity(aligned-up min_size %u)\n",
00247     (void *)pthread_self(), this, 
00248     unsigned(min_size) 
00249     );
00250 #endif
00251     int err = 0;
00252     if(size() < min_size) {
00253         size_t next_size = std::max(min_size, 2*size());
00254         err = resize(next_size);
00255     
00256         if(err == EFBIG)  {
00257             // Try a smaller size. We cannot resize down.
00258             next_size = std::max(min_size, size());
00259             err = resize(next_size);
00260         }
00261     }
00262     return err;
00263 }
00264 /**\endcond skip */
00265 

Generated on Mon Jan 2 15:13:56 2012 for Shore Storage Manager by  doxygen 1.4.7