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.3 2010/07/07 21:43:45 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 int dynarray::init(size_t max_size, size_t align) {
00077     // round up to the nearest page boundary
00078     max_size = align_up(max_size, MM_PAGE_SIZE);
00079     
00080     // validate inputs
00081     if(max_size > MM_MAX_CAPACITY)
00082     return EFBIG;
00083     if(MM_PAGE_SIZE > max_size)
00084     return EINVAL;
00085     if((align & -align) != align)
00086     return EINVAL;
00087 
00088     /*
00089       The magical incantation below tells mmap to reserve address
00090       space within the process without actually allocating any
00091       memory. We are then free to re-map any subset of that
00092       reservation using MAP_FIXED (using MAP_FIXED without a
00093       reservation always fails).
00094 
00095       Note that MAP_FIXED is smart enough to mix and match different
00096       sets of permissions, so we can extend the array simply by
00097       remapping 0..new_size with R/W permissions, and can blow
00098       everything away by unmapping 0..reserved_size.
00099 
00100       Tested on both Linux-2.6.18/x86 and Solaris-10/Sparc.
00101     */
00102 
00103     static int const PROTS = PROT_NONE;
00104     int flags = MAP_NORESERVE | MAP_ANON | MAP_PRIVATE;
00105 
00106     // Allocate extra for forced alignment (no effect for align=0)
00107     align = std::max(align, MM_PAGE_SIZE);
00108 #if USE_MAP_ALIGN
00109     char* align_arg = (char*) align;
00110     size_t align_extra = 0;
00111     flags |= MAP_ALIGN;
00112 #else
00113     char* align_arg = 0;
00114     size_t align_extra = align - MM_PAGE_SIZE;
00115 #endif
00116     union { void* v; uintptr_t n; char* c; }
00117         u={mmap(align_arg, max_size+align_extra, PROTS, flags, -1, 0)};
00118 
00119     if(u.v == MAP_FAILED)
00120     return errno;
00121 
00122 #if !USE_MAP_ALIGN
00123     /* Verify alignment...
00124 
00125        This is incredibly annoying: in all probability the system will
00126        give us far stronger alignment than we request, but Linux
00127        doesn't actually promise anything more strict than
00128        page-aligned. Solaris does promise to always do 1MB or 4MB
00129        alignment, but it also provides a MAP_ALIGN which moots the
00130        whole issue.
00131 
00132        Unfortunately Linux insists on not being helpful, so we have to
00133        request more than needed, then chop off the extra in a way that
00134        gives us the desired alignment. That extra could be the
00135        little bit that pushes the system over the edge and gives us
00136        ENOMEM, but we're kind of stuck.
00137      */
00138 #ifdef TEST_ME
00139     std::fprintf(stderr, "start: %p end:%p\n", u.c, u.c+max_size+align_extra);
00140 #endif
00141     long aligned_base = align_up(u.n, align);
00142     if(long extra=aligned_base-u.n) {
00143 #ifdef TEST_ME
00144     std::fprintf(stderr, "chopping off %zx bytes of prefix for start: %zx\n",
00145         extra, aligned_base);
00146 #endif
00147     munmap(u.c, extra);
00148     u.n = aligned_base;
00149     align_extra -= extra;
00150     }
00151     if(align_extra > 0) {
00152 #ifdef TEST_ME
00153     std::fprintf(stderr, "chopping %zx bytes of postfix for end: %p\n", align_extra, u.c+max_size);
00154 #endif
00155     munmap(u.c+max_size, align_extra);
00156     }
00157 #endif
00158 
00159     _base = u.c;
00160     _capacity = max_size;
00161     _size = 0;
00162     return 0;
00163 }
00164 
00165 int dynarray::init(dynarray const &to_copy, size_t max_size) {
00166     max_size = std::max(max_size, to_copy.capacity());
00167     if(int err=init(max_size))
00168     return err;
00169 
00170     std::memmove(_base, to_copy._base, to_copy.size());
00171     return 0;
00172 }
00173 
00174 int dynarray::fini() {
00175     if(int err=munmap(_base, _capacity))
00176     return err;
00177         
00178     _base = 0;
00179     _size = 0;
00180     _capacity = 0;
00181     return 0;
00182 }
00183 
00184 int dynarray::resize(size_t new_size) {
00185     // round up to the nearest page boundary
00186     new_size = align_up(new_size, MM_PAGE_SIZE);
00187 
00188     // validate
00189     if(_size > new_size)
00190     return EINVAL;
00191 
00192     static int const PROTS = PROT_READ | PROT_WRITE;
00193     static int const FLAGS = MAP_FIXED | MAP_ANON | MAP_PRIVATE;
00194 
00195     // remap the new range as RW. Don't mess w/ the existing region!!
00196     void* result = mmap(_base+_size, new_size-_size, PROTS, FLAGS, -1, 0);
00197     if(result == MAP_FAILED)
00198     return errno;
00199 
00200     _size = new_size;
00201     return 0;
00202 }
00203 
00204 int dynarray::ensure_capacity(size_t min_size) {
00205     min_size  = align_up(min_size, MM_PAGE_SIZE);
00206     int err = 0;
00207     if(size() < min_size) {
00208     size_t next_size = std::max(min_size, 2*size());
00209     err = resize(next_size);
00210     
00211     // did we reach a limit?
00212     if(err == EFBIG) 
00213         err = resize(min_size);
00214     }
00215     return err;
00216 }
00217 /**\endcond skip */
00218 

Generated on Wed Jul 7 17:22:32 2010 for Shore Storage Manager by  doxygen 1.4.7