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 
00034 SYSTEM_INFO MemoryMap::system_info;
00035 static bool need_system_info = true;
00036 #include <windows.h>
00037 
00038 #if !defined(__CYGWIN__)
00039 typedef char *  caddr_t;
00040 #endif
00041 
00042 #else
00043 #include <sys/mman.h>
00044 #endif
00045 
00046 #ifndef MAP_POPULATE
00047 #define MAP_POPULATE 0x0000
00048 #endif
00049 #ifndef MAP_NONBLOCK
00050 #define MAP_NONBLOCK 0x0000
00051 #endif
00052 
00053 MemoryMap::MemoryMap()
00054 {
00055     constructor_clear();
00056 #if defined(WIN32)
00057     if (need_system_info)
00058     {
00059         GetSystemInfo(&system_info);
00060         need_system_info = false;
00061     }
00062     page_size = system_info.dwAllocationGranularity;
00063 #else
00064     page_size = sysconf(_SC_PAGE_SIZE);
00065 #endif
00066 };
00067 
00068 MemoryMap::~MemoryMap()
00069 {
00070     destructor_clear();
00071 };
00072 
00073 void MemoryMap::debug_print()
00074 {
00075 #if defined(WIN32)
00076     std::cout << "fd = " << file_handle << std::endl;
00077 #else
00078     std::cout << "fd = " << fd << std::endl;
00079 #endif
00080     std::cout << "data = 0x" << std::hex << data << std::endl;
00081     std::cout << "offset = 0x" << std::hex << offset << std::endl;
00082     std::cout << "mapped_length = 0x" << std::hex << mapped_length << std::endl;
00083     std::cout << "total_length = 0x" << std::hex << total_length << std::endl;
00084     std::cout << "page_size = 0x" << std::hex << page_size << std::endl;
00085 };
00086 
00087 void MemoryMap::constructor_clear()
00088 {
00089 #if defined(WIN32)
00090     file_handle = 0;
00091     map_handle = 0;
00092 #else
00093     fd = -1;
00094 #endif
00095     data = (void *) NULL;
00096     offset = 0;
00097     mapped_length = 0;
00098     total_length = 0;
00099     useMemoryMapFlag = true;
00100 };
00101 
00102 void MemoryMap::destructor_clear()
00103 {
00104     if (data!=NULL)
00105     {
00106 #if defined(WIN32)
00107         // free windows mapped object
00108         UnmapViewOfFile((LPVOID) data);
00109 #else
00110         // free unix mapped object
00111         munmap(data, mapped_length);
00112 #endif
00113     }
00114 
00115 #if !defined(WIN32)
00116     // free unix resources
00117     if (fd!=-1)
00118     {
00119         ::close(fd);
00120     }
00121 #endif
00122 
00123     constructor_clear();
00124 };
00125 
00126 #if defined(WIN32)
00127 bool MemoryMap::open(const char * file, int flags)
00128 {
00129     const off_t offset = 0;     // in old code, this was an argument to ::open
00130     const size_t size = 0;
00131 
00132 //
00133 // XXX lots of errors can happen here that aren't considered yet.
00134 // WIN32 has more common read only devices (cdroms), and I don't know
00135 // what the error path looks like trying to open a files read/write on a 
00136 // read only dataset.
00137 //
00138     BY_HANDLE_FILE_INFORMATION file_handle_information;
00139 
00140 // dwShareMode (FILE_SHARE*) specifies what subsequent CreateFile operations
00141 // are allowed to do.  To get around programming problems, we need to
00142 // allow an object to first be opened READ (for the header) then READ/WRITE
00143 // for the vector.
00144 // XXX fix the above problem - clarify READ/WRITE access to the header vs
00145 // to the vector itself
00146     file_handle = CreateFile(
00147         file,
00148         (flags==O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ,
00149         FILE_SHARE_READ | FILE_SHARE_WRITE, // subsequent opens may either read or write
00150         NULL,
00151         OPEN_ALWAYS,
00152         FILE_ATTRIBUTE_NORMAL,
00153         NULL);
00154 
00155 #ifdef DEBUG_MAP
00156 fprintf(stderr,"MemoryMap::open: file_handle=%x\n", h->file_handle);
00157 #endif
00158 
00159     if(file_handle == INVALID_HANDLE_VALUE) {
00160         std::cerr << "MemoryMap::open: CreateFile(" <<  file << ") failed." << std::endl;
00161         return true;
00162     }
00163 
00164     GetFileInformationByHandle(file_handle, &file_handle_information);
00165 
00166     mapped_length = size;
00167 
00168     if(size==0) mapped_length = file_handle_information.nFileSizeLow - offset;   // map all if size=0
00169 
00170 #if !defined(__MINGW32__)
00171     if(mapped_length+offset > file_handle_information.nFileSizeLow)
00172 #else
00173 // XXX hacked
00174     if((unsigned)mapped_length+offset > file_handle_information.nFileSizeLow)
00175 #endif
00176 #if 0
00177         // XXX free CreateFile resources... HOW?
00178         CloseHandle(file_handle);   ??
00179         FreeResource(file_handle);  ??
00180 #endif
00181     {
00182         std::cerr << "MemoryMap::open: unable to map file " <<  file << " at offset " << offset << "." << std::endl;
00183         constructor_clear();
00184         return true;
00185     }
00186 
00187     map_handle = CreateFileMapping(
00188         file_handle,    // file handle
00189         NULL,           // dunno
00190         (flags==O_RDWR) ? PAGE_READWRITE : PAGE_READONLY,    // map flags
00191         0,              // upper 32 bits of map size
00192         size,           // lower 32 bits of map size
00193         NULL            // winblows id name so others can get the same object by name
00194     );
00195 
00196 #ifdef DEBUG_MAP
00197     std::cout << "MemoryMap::open: map_handle = " << h->map_handle << std::endl;
00198 #endif
00199 
00200 
00201     if(map_handle == NULL) {
00202         std::cerr << "MemoryMap::open: unable to map file " <<  file << "." << std::endl;
00203         constructor_clear();
00204         return true;
00205     }
00206 
00207 #ifdef DEBUG_MAP
00208     std::cerr << "MemoryMap::open: calling MapViewOfFile(offset=" << offset << ", mapped_length=" << mapped_length << ")." << std::endl;
00209 #endif
00210     LPVOID map_pointer = MapViewOfFile(
00211         map_handle,         // map handle
00212         (flags==O_RDWR) ? (FILE_MAP_WRITE | FILE_MAP_READ) : FILE_MAP_READ,
00213         (DWORD) 0,          // high 32 bits of map offset
00214         (DWORD) offset,     // low 32 bits of map offset
00215         mapped_length
00216     );
00217 
00218 #ifdef DEBUG_MAP
00219     DWORD e = GetLastError();
00220     std::cerr << "MemoryMap::open: map_pointer = " << map_pointer << ", error = " << error << std::endl;
00221 #endif
00222 
00223     data = (caddr_t) map_pointer;
00224 
00225     return false;
00226 }
00227 
00228 bool MemoryMap::create(const char *file, size_t size)
00229 {
00230     //
00231     // first create the file, then open it mapped
00232     //
00233     struct stat sb;
00234 
00235     if(stat(file,&sb)!=0) {
00236         if(size==0) {
00237             std::cerr << "MemoryMap::create requires non zero size when creating a new file." << std::endl;
00238             return true;
00239         }
00240         int d = ::open(file, O_RDWR);
00241         off_t result = lseek(d, (off_t) size - 1, SEEK_SET);
00242         ::close(d);
00243     }
00244 
00245     return open(file, O_RDWR);
00246 }
00247 
00248 #else
00249 bool MemoryMap::open(const char * file, int flags)
00250 {
00251 
00252     struct stat buf;
00253 
00254     int mmap_prot_flag = PROT_READ;
00255     if (flags != O_RDONLY) mmap_prot_flag = PROT_WRITE;
00256 
00257     fd = ::open(file, flags);
00258     if (fd==-1)
00259     {
00260         fprintf(stderr, "MemoryMap::open: file %s not found\n", (const char *) file);
00261         constructor_clear();
00262         return true;
00263     }
00264     if (fstat(fd, &buf))
00265     {
00266         perror("MemoryMap::open");
00267         constructor_clear();
00268         return true;
00269     }
00270     mapped_length = buf.st_size;
00271     total_length = mapped_length;
00272 
00273     if (useMemoryMapFlag)
00274     {
00275 
00276         int additionalFlags = 0;
00277 
00278         // try this for amusement ... not portable:
00279 //        additionalFlags |= MAP_HUGETLB;
00280 // #define USE_LOCKED_MMAP
00281 #if defined(USE_LOCKED_MMAP)
00282         // MAP_POPULATE only makes sense if we are reading
00283         // the data
00284         if (flags != O_RDONLY)
00285         {
00286             // furthermore, according to Linux mmap page, populate only
00287             // works if the map is private.
00288             additionalFlags |= MAP_POPULATE;
00289             additionalFlags |= MAP_PRIVATE;
00290         }
00291         else
00292         {
00293             additionalFlags |= MAP_SHARED;
00294         }
00295 #else
00296 additionalFlags |= MAP_SHARED;
00297 #endif
00298 
00299         data = ::mmap(
00300                    NULL,           // start
00301                    mapped_length,
00302                    mmap_prot_flag, // protection flags
00303                    additionalFlags,
00304                    fd,
00305                    offset
00306                );
00307         if (data == MAP_FAILED)
00308         {
00309             ::close(fd);
00310             std::cerr << "Error: Attempting to map " << mapped_length << " bytes of file "
00311                       << file << ":" << std::endl;
00312             perror("MemoryMap::open");
00313             constructor_clear();
00314             return true;
00315         }
00316 
00317 #if defined(USE_LOCKED_MMAP)
00318         //
00319         // non-POSIX, so non portable.
00320         // This call is limited by the RLIMIT_MEMLOCK resource.
00321         //
00322         // In bash, "ulimit -l" shows the limit.
00323         //
00324         if (mlock(data, mapped_length))
00325         {
00326             std::cerr << "Warning: Attempting to lock " << mapped_length << " bytes of file " << file << ":" << std::endl;
00327             perror("unable to lock memory");
00328             // not a fatal error, so continue
00329         }
00330 #endif
00331 
00332         // these really don't appear to have any greatly useful effect on
00333         // Linux.  Last time I checked, the effect on Solaris and AIX was
00334         // exactly what was documented and was significant.
00335         //
00336         madvise(data, mapped_length, MADV_WILLNEED);   // warning, this could hose the system
00337 
00338     }
00339     else
00340     {
00341         data = (void *) malloc(mapped_length);
00342         if (data==NULL)
00343         {
00344             ::close(fd);
00345             perror("MemoryMap::open");
00346             constructor_clear();
00347             return true;
00348         }
00349         ssize_t resultSize = read(fd, data, mapped_length);
00350         if (resultSize!=(ssize_t) mapped_length)
00351         {
00352             ::close(fd);
00353             perror("MemoryMap::open");
00354             constructor_clear();
00355             return true;
00356         }
00357     }
00358     return false;
00359 }
00360 
00361 bool MemoryMap::create(const char *file, size_t size)
00362 {
00363 
00364     if (file==NULL)
00365     {
00366         data = calloc(size, 1);
00367         if (data==NULL) return true;
00368     }
00369     else
00370     {
00371         int mmap_prot_flag = PROT_READ | PROT_WRITE;
00372 
00373         fd = ::open(file, O_RDWR|O_CREAT|O_TRUNC, 0666);
00374         if (fd==-1)
00375         {
00376             fprintf(stderr, "MemoryMap::open: can't create file '%s'\n",(const char *) file);
00377             constructor_clear();
00378             return true;
00379         }
00380 
00381         lseek(fd, (off_t) size - 1, SEEK_SET);
00382         char ch = 0;
00383         if(write(fd, &ch, 1)!=1) {
00384             perror("MemoryMap::create:");
00385             throw std::logic_error("unable to write at end of file");
00386         }
00387 
00388         data = ::mmap(
00389                    NULL,           // start
00390                    size,
00391                    mmap_prot_flag, // protection flags
00392                    MAP_SHARED,     // share/execute/etc flags
00393                    fd,
00394                    offset
00395                );
00396         if (data == MAP_FAILED)
00397         {
00398             ::close(fd);
00399             unlink(file);
00400             perror("MemoryMap::open");
00401             constructor_clear();
00402             return true;
00403         }
00404         mapped_length = size;
00405         total_length = size;
00406     }
00407     return false;
00408 }
00409 #endif
00410 
00411 bool MemoryMap::create(size_t size)
00412 {
00413     return create(NULL, size);
00414 }
00415 
00416 bool MemoryMap::close()
00417 {
00418     destructor_clear();
00419     return false;
00420 };
00421 
00422 void MemoryMap::test()
00423 {
00424     int result;
00425 
00426     result = this->open("test/test_memmap_data.txt");
00427     assert(result == 0);
00428     assert(data!=NULL);
00429     assert(mapped_length == 183);   // length of the above file
00430     close();
00431 
00432     // now try non memory mapped (direct slow file I/O)
00433     useMemoryMap(false);
00434     result = this->open("test/test_memmap_data.txt");
00435     assert(result == 0);
00436     assert(data!=NULL);
00437     assert(mapped_length == 183);   // length of the above file
00438     close();
00439 }
00440 
00441 int MemoryMap::prefetch()
00442 {
00443     int sum = 0;
00444     size_t i;
00445 
00446     for (i=0; i<mapped_length; i += page_size) sum += *(i + (char *) data);
00447 
00448     return sum;
00449 }
00450 
00451 #if defined(TEST)
00452 //
00453 // compile test using:
00454 // g++ -DTEST -o testMemoryMap MemoryMap.cpp Error.o -lz
00455 //
00456 
00457 int main(int argc, const char *argv)
00458 {
00459     MemoryMap map;
00460 
00461     map.test();
00462 
00463 //  map.debug_print();
00464 
00465     exit(0);
00466 }
00467 #endif
00468 
Generated on Mon Feb 11 13:45:18 2013 for libStatGen Software by  doxygen 1.6.3