MemoryMap.cpp

00001 /*
00002  *  Copyright (C) 2010  Regents of the University of Michigan
00003  *
00004  *   This program is free software: you can redistribute it and/or modify
00005  *   it under the terms of the GNU General Public License as published by
00006  *   the Free Software Foundation, either version 3 of the License, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details.
00013  *
00014  *   You should have received a copy of the GNU General Public License
00015  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  */
00017 
00018 #include <sys/stat.h>
00019 #include <sys/types.h>
00020 #include <assert.h>
00021 #include <errno.h>
00022 #include <fcntl.h>
00023 #include <iostream>
00024 #include <stdio.h>
00025 #include <stdexcept>
00026 #include <stdlib.h>
00027 #include <string>
00028 #include <unistd.h>
00029 
00030 #include "MemoryMap.h"
00031 
00032 #if defined(WIN32)
00033 SYSTEM_INFO MemoryMap::system_info;
00034 static bool need_system_info = 1;
00035 #else
00036 #include <sys/mman.h>
00037 #endif
00038 
00039 #ifndef MAP_POPULATE
00040 #define MAP_POPULATE 0x0000
00041 #endif
00042 #ifndef MAP_NONBLOCK
00043 #define MAP_NONBLOCK 0x0000
00044 #endif
00045 
00046 MemoryMap::MemoryMap()
00047 {
00048     constructor_clear();
00049 #if defined(WIN32)
00050     if (need_system_info)
00051     {
00052         GetSystemInfo(&system_info);
00053         need_system_info = false;
00054     }
00055     page_size = system_info.dwAllocationGranularity;
00056 #else
00057     page_size = sysconf(_SC_PAGE_SIZE);
00058 #endif
00059 };
00060 
00061 MemoryMap::~MemoryMap()
00062 {
00063     destructor_clear();
00064 };
00065 
00066 void MemoryMap::debug_print()
00067 {
00068 #if defined(WIN32)
00069     std::cout << "fd = " << file_handle << std::endl;
00070 #else
00071     std::cout << "fd = " << fd << std::endl;
00072 #endif
00073     std::cout << "data = 0x" << std::hex << data << std::endl;
00074     std::cout << "offset = 0x" << std::hex << offset << std::endl;
00075     std::cout << "mapped_length = 0x" << std::hex << mapped_length << std::endl;
00076     std::cout << "total_length = 0x" << std::hex << total_length << std::endl;
00077     std::cout << "page_size = 0x" << std::hex << page_size << std::endl;
00078 };
00079 
00080 void MemoryMap::constructor_clear()
00081 {
00082 #if defined(WIN32)
00083     file_handle = 0;
00084 #else
00085     fd = -1;
00086 #endif
00087     data = (void *) NULL;
00088     offset = 0;
00089     mapped_length = 0;
00090     total_length = 0;
00091     useMemoryMapFlag = true;
00092 };
00093 
00094 void MemoryMap::destructor_clear()
00095 {
00096     if (data!=NULL)
00097     {
00098 #if defined(WIN32)
00099         UnmapViewOfFile((LPVOID) data);
00100 #else
00101         munmap(data, mapped_length);
00102 #endif
00103     }
00104 #if !defined(WIN32)
00105     if (fd!=-1)
00106     {
00107         ::close(fd);
00108     }
00109 #endif
00110     constructor_clear();
00111 };
00112 
00113 #if defined(WIN32)
00114 // XXX
00115 // TODO implement win32 ::open and ::create
00116 #else
00117 bool MemoryMap::open(const char * file, int flags)
00118 {
00119 
00120     struct stat buf;
00121 
00122     int mmap_prot_flag = PROT_READ;
00123     if (flags != O_RDONLY) mmap_prot_flag = PROT_WRITE;
00124 
00125     fd = ::open(file, flags);
00126     if (fd==-1)
00127     {
00128         fprintf(stderr, "MemoryMap::open: file %s not found\n", (const char *) file);
00129         constructor_clear();
00130         return true;
00131     }
00132     if (fstat(fd, &buf))
00133     {
00134         perror("MemoryMap::open");
00135         constructor_clear();
00136         return true;
00137     }
00138     mapped_length = buf.st_size;
00139     total_length = mapped_length;
00140 
00141     if (useMemoryMapFlag)
00142     {
00143 
00144         int additionalFlags = 0;
00145 
00146         // try this for amusement ... not portable:
00147 //        additionalFlags |= MAP_HUGETLB;
00148 // #define USE_LOCKED_MMAP
00149 #if defined(USE_LOCKED_MMAP)
00150         // MAP_POPULATE only makes sense if we are reading
00151         // the data
00152         if (flags != O_RDONLY)
00153         {
00154             // furthermore, according to Linux mmap page, populate only
00155             // works if the map is private.
00156             additionalFlags |= MAP_POPULATE;
00157             additionalFlags |= MAP_PRIVATE;
00158         }
00159         else
00160         {
00161             additionalFlags |= MAP_SHARED;
00162         }
00163 #else
00164 additionalFlags |= MAP_SHARED;
00165 #endif
00166 
00167         data = ::mmap(
00168                    NULL,           // start
00169                    mapped_length,
00170                    mmap_prot_flag, // protection flags
00171                    additionalFlags,
00172                    fd,
00173                    offset
00174                );
00175         if (data == MAP_FAILED)
00176         {
00177             ::close(fd);
00178             std::cerr << "Error: Attempting to map " << mapped_length << " bytes of file "
00179                       << file << ":" << std::endl;
00180             perror("MemoryMap::open");
00181             constructor_clear();
00182             return true;
00183         }
00184 
00185 #if defined(USE_LOCKED_MMAP)
00186         //
00187         // non-POSIX, so non portable.
00188         // This call is limited by the RLIMIT_MEMLOCK resource.
00189         //
00190         // In bash, "ulimit -l" shows the limit.
00191         //
00192         if (mlock(data, mapped_length))
00193         {
00194             std::cerr << "Warning: Attempting to lock " << mapped_length << " bytes of file " << file << ":" << std::endl;
00195             perror("unable to lock memory");
00196             // not a fatal error, so continue
00197         }
00198 #endif
00199 
00200         // these really don't appear to have any greatly useful effect on
00201         // Linux.  Last time I checked, the effect on Solaris and AIX was
00202         // exactly what was documented and was significant.
00203         //
00204         madvise(data, mapped_length, MADV_WILLNEED);   // warning, this could hose the system
00205 
00206     }
00207     else
00208     {
00209         data = (void *) malloc(mapped_length);
00210         if (data==NULL)
00211         {
00212             ::close(fd);
00213             perror("MemoryMap::open");
00214             constructor_clear();
00215             return true;
00216         }
00217         ssize_t resultSize = read(fd, data, mapped_length);
00218         if (resultSize!=(ssize_t) mapped_length)
00219         {
00220             ::close(fd);
00221             perror("MemoryMap::open");
00222             constructor_clear();
00223             return true;
00224         }
00225     }
00226     return false;
00227 }
00228 
00229 bool MemoryMap::create(const char *file, size_t size)
00230 {
00231 
00232     if (file==NULL)
00233     {
00234         data = calloc(size, 1);
00235         if (data==NULL) return true;
00236     }
00237     else
00238     {
00239         int mmap_prot_flag = PROT_READ | PROT_WRITE;
00240 
00241         fd = ::open(file, O_RDWR|O_CREAT|O_TRUNC, 0666);
00242         if (fd==-1)
00243         {
00244             fprintf(stderr, "MemoryMap::open: can't create file '%s'\n",(const char *) file);
00245             constructor_clear();
00246             return true;
00247         }
00248 
00249         lseek(fd, (off_t) size - 1, SEEK_SET);
00250         char ch = 0;
00251         if(write(fd, &ch, 1)!=1) {
00252             perror("MemoryMap::create:");
00253             throw std::logic_error("unable to write at end of file");
00254         }
00255 
00256         data = ::mmap(
00257                    NULL,           // start
00258                    size,
00259                    mmap_prot_flag, // protection flags
00260                    MAP_SHARED,     // share/execute/etc flags
00261                    fd,
00262                    offset
00263                );
00264         if (data == MAP_FAILED)
00265         {
00266             ::close(fd);
00267             unlink(file);
00268             perror("MemoryMap::open");
00269             constructor_clear();
00270             return true;
00271         }
00272         mapped_length = size;
00273         total_length = size;
00274     }
00275     return false;
00276 }
00277 #endif
00278 
00279 bool MemoryMap::create(size_t size)
00280 {
00281     return create(NULL, size);
00282 }
00283 
00284 bool MemoryMap::close()
00285 {
00286     destructor_clear();
00287     return false;
00288 };
00289 
00290 void MemoryMap::test()
00291 {
00292     int result;
00293 
00294     result = this->open("test/test_memmap_data.txt");
00295     assert(result == 0);
00296     assert(data!=NULL);
00297     assert(mapped_length == 183);   // length of the above file
00298     close();
00299 
00300     // now try non memory mapped (direct slow file I/O)
00301     useMemoryMap(false);
00302     result = this->open("test/test_memmap_data.txt");
00303     assert(result == 0);
00304     assert(data!=NULL);
00305     assert(mapped_length == 183);   // length of the above file
00306     close();
00307 }
00308 
00309 int MemoryMap::prefetch()
00310 {
00311     int sum = 0;
00312     size_t i;
00313 
00314     for (i=0; i<mapped_length; i += page_size) sum += *(i + (char *) data);
00315 
00316     return sum;
00317 }
00318 
00319 #if defined(TEST)
00320 //
00321 // compile test using:
00322 // g++ -DTEST -o testMemoryMap MemoryMap.cpp Error.o -lz
00323 //
00324 
00325 int main(int argc, const char *argv)
00326 {
00327     MemoryMap map;
00328 
00329     map.test();
00330 
00331 //  map.debug_print();
00332 
00333     exit(0);
00334 }
00335 #endif
00336 
Generated on Wed Nov 17 15:38:29 2010 for StatGen Software by  doxygen 1.6.3