VMAllocator.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/cache/VMAllocator.cpp#12 $
00003 // Fennel is a library of data storage and processing components.
00004 // Copyright (C) 2005-2009 The Eigenbase Project
00005 // Copyright (C) 2005-2009 SQLstream, Inc.
00006 // Copyright (C) 2005-2009 LucidEra, Inc.
00007 // Portions Copyright (C) 1999-2009 John V. Sichi
00008 //
00009 // This program is free software; you can redistribute it and/or modify it
00010 // under the terms of the GNU General Public License as published by the Free
00011 // Software Foundation; either version 2 of the License, or (at your option)
00012 // any later version approved by The Eigenbase Project.
00013 //
00014 // This program is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 // GNU General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU General Public License
00020 // along with this program; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 */
00023 
00024 #include "fennel/common/CommonPreamble.h"
00025 #include "fennel/cache/VMAllocator.h"
00026 #include "fennel/common/SysCallExcn.h"
00027 
00028 #include <stdlib.h>
00029 #include <malloc.h>
00030 
00031 #ifdef HAVE_MMAP
00032 #include <sys/resource.h>
00033 #include <sys/mman.h>
00034 #endif
00035 
00036 FENNEL_BEGIN_CPPFILE("$Id: //open/dev/fennel/cache/VMAllocator.cpp#12 $");
00037 
00038 VMAllocator::VMAllocator(size_t cbAllocInit,size_t nLocked)
00039 {
00040     cbAlloc = cbAllocInit;
00041     nAllocs = 0;
00042     if (nLocked) {
00043 #ifdef RLIMIT_MEMLOCK
00044         struct rlimit rl;
00045         if (::getrlimit(RLIMIT_MEMLOCK,&rl)) {
00046             throw SysCallExcn("getrlimit failed");
00047         }
00048         if (rl.rlim_cur != RLIM_INFINITY) {
00049             // REVIEW:  what if other stuff in the same process needs
00050             // locked pages?
00051             rl.rlim_cur = nLocked*cbAlloc;
00052             rl.rlim_max = nLocked*cbAlloc;
00053             if (::setrlimit(RLIMIT_MEMLOCK,&rl)) {
00054                 throw SysCallExcn("setrlimit failed");
00055             }
00056         }
00057 #endif
00058         bLockPages = true;
00059     } else {
00060         bLockPages = false;
00061     }
00062 }
00063 
00064 VMAllocator::~VMAllocator()
00065 {
00066     // This assertion indicates that not all allocated pages were deallocated.
00067     // In relation to LER-5976: in debug builds, each allocation is divided
00068     // into 3 sections (two guard pages surrounding the actual allocation).
00069     // The mprotect calls in allocate(int *) can fail in low memory situations
00070     // (the kernel may allocate private memory as it tracks a single mmap
00071     // region being split in three).  When this occurs, subsequent munmap calls
00072     // (even on different regions) in deallocate sometimes fail, leading to a
00073     // failure of this assertion.  This should not occur in release builds,
00074     // since no guard pages are allocated or protected. NOTE: On Ubuntu Edgy
00075     // Eft (2.6.17-12-generic SMP), the munmap calls in deallocate fails three
00076     // times, then inexplicably begin succeeding.  Retrying the failed calls
00077     // then succeeds.
00078     assert(!getBytesAllocated());
00079 }
00080 
00081 void *VMAllocator::allocate(int *pErrorCode)
00082 {
00083 #ifdef HAVE_MMAP
00084 
00085     uint cbActualAlloc = cbAlloc;
00086 #ifndef NDEBUG
00087     // For a debug build, allocate "fence" regions before and after each
00088     // allocated buffer.  This helps to catch stray pointers around buffer
00089     // boundaries.  The fence size is one OS memory page, since that's
00090     // the minimum unit of protection, and also guarantees 512-byte
00091     // alignment for O_DIRECT file access.
00092     cbActualAlloc += 2*getpagesize();
00093 #endif
00094 
00095     void *v = ::mmap(
00096         NULL,cbActualAlloc,
00097         PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS,-1,0);
00098     if (v == MAP_FAILED) {
00099         if (pErrorCode != NULL) {
00100             *pErrorCode = SysCallExcn::getCurrentErrorCode();
00101         }
00102         return NULL;
00103     }
00104 
00105 #ifndef NDEBUG
00106     PBuffer p = static_cast<PBuffer>(v);
00107     memset(p, 0xFE, getpagesize());
00108     if (::mprotect(p, getpagesize(), PROT_NONE)) {
00109         if (pErrorCode != NULL) {
00110             *pErrorCode = SysCallExcn::getCurrentErrorCode();
00111         }
00112         ::munmap(v,cbAlloc);
00113         return NULL;
00114     }
00115     p += getpagesize();
00116     memset(p, 0xFF, cbAlloc);
00117     v = p;
00118     p += cbAlloc;
00119     memset(p, 0xFE, getpagesize());
00120     if (::mprotect(p, getpagesize(), PROT_NONE)) {
00121         if (pErrorCode != NULL) {
00122             *pErrorCode = SysCallExcn::getCurrentErrorCode();
00123         }
00124         ::munmap(v,cbAlloc);
00125         return NULL;
00126     }
00127 #endif
00128 
00129     if (bLockPages) {
00130         if (::mlock(v,cbAlloc)) {
00131             if (pErrorCode != NULL) {
00132                 *pErrorCode = SysCallExcn::getCurrentErrorCode();
00133             }
00134             ::munmap(v,cbAlloc);
00135             return NULL;
00136         }
00137     }
00138 #else
00139     void *v = malloc(cbAlloc);
00140     if (v == NULL) {
00141         if (pErrorCode != NULL) {
00142             *pErrorCode = SysCallExcn::getCurrentErrorCode();
00143         }
00144         return NULL;
00145     }
00146 #endif
00147     ++nAllocs;
00148     return v;
00149 }
00150 
00151 int VMAllocator::deallocate(void *p, int *pErrorCode)
00152 {
00153 #ifdef HAVE_MMAP
00154     if (bLockPages) {
00155         if (::munlock(p,cbAlloc)) {
00156             if (pErrorCode != NULL) {
00157                 *pErrorCode = SysCallExcn::getCurrentErrorCode();
00158             }
00159             return -1;
00160         }
00161     }
00162 
00163     uint cbActualAlloc = cbAlloc;
00164 #ifndef NDEBUG
00165     PBuffer p2 = static_cast<PBuffer>(p);
00166     p2 -= getpagesize();
00167     p = p2;
00168     cbActualAlloc += 2*getpagesize();
00169 #endif
00170     if (::munmap((caddr_t)p,cbActualAlloc)) {
00171         if (pErrorCode != NULL) {
00172             *pErrorCode = SysCallExcn::getCurrentErrorCode();
00173         }
00174         return -1;
00175     }
00176 #else
00177     free(p);
00178 #endif
00179     --nAllocs;
00180 
00181     return 0;
00182 }
00183 
00184 size_t VMAllocator::getBytesAllocated() const
00185 {
00186     return nAllocs*cbAlloc;
00187 }
00188 
00189 int VMAllocator::setProtection(
00190     void *pMem, uint cb, bool readOnly, int *pErrorCode)
00191 {
00192     // FIXME jvs 7-Feb-2006:  use autoconf to get HAVE_MPROTECT instead
00193 #ifdef HAVE_MMAP
00194     int prot = PROT_READ;
00195     if (!readOnly) {
00196         prot |= PROT_WRITE;
00197     }
00198     if (::mprotect(pMem, cb, prot)) {
00199         if (pErrorCode != NULL) {
00200             *pErrorCode = SysCallExcn::getCurrentErrorCode();
00201         }
00202         return -1;
00203     }
00204 #endif
00205 
00206     return 0;
00207 }
00208 
00209 FENNEL_END_CPPFILE("$Id: //open/dev/fennel/cache/VMAllocator.cpp#12 $");
00210 
00211 // End VMAllocator.cpp

Generated on Mon Jun 22 04:00:13 2009 for Fennel by  doxygen 1.5.1