1
0
Fork 0
arangodb/arangod/Pregel/MemoryMapped.cpp

269 lines
5.8 KiB
C++

// //////////////////////////////////////////////////////////
// MemoryMapped.cpp
// Copyright (c) 2013 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#include "Pregel/MemoryMapped.h"
#include "ApplicationFeatures/PageSizeFeature.h"
#include "Basics/FileUtils.h"
#include "Basics/StaticStrings.h"
#include "Basics/StringUtils.h"
#include "Basics/files.h"
#include "Basics/memory-map.h"
#include "Basics/tri-strings.h"
#include "Logger/Logger.h"
#include <iomanip>
#include <sstream>
#include <cstdio>
#include <stdexcept>
using namespace arangodb;
using namespace arangodb::basics;
/// open file, mappedBytes = 0 maps the whole file
MemoryMapped::MemoryMapped(const std::string& filename, size_t mappedBytes,
CacheHint hint)
: _filename(filename),
_filesize(0),
_hint(hint),
_mappedBytes(mappedBytes),
_file(0),
#ifdef _MSC_VER
_mappedFile(NULL),
#endif
_mappedView(NULL) {
open(filename, mappedBytes, hint);
}
/// close file (see close() )
MemoryMapped::~MemoryMapped() { close(); }
/// open file
bool MemoryMapped::open(const std::string& filename, size_t mappedBytes,
CacheHint hint) {
// already open ?
if (isValid()) return false;
_file = 0;
_filesize = 0;
_hint = hint;
#ifdef _MSC_VER
_mappedFile = NULL;
#endif
_mappedView = NULL;
#ifdef _MSC_VER
// Windows
DWORD winHint = 0;
switch (_hint) {
case Normal:
winHint = FILE_ATTRIBUTE_NORMAL;
break;
case SequentialScan:
winHint = FILE_FLAG_SEQUENTIAL_SCAN;
break;
case RandomAccess:
winHint = FILE_FLAG_RANDOM_ACCESS;
break;
default:
break;
}
// open file
_file = ::CreateFileA(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, winHint, NULL);
if (!_file) return false;
// file size
LARGE_INTEGER result;
if (!GetFileSizeEx(_file, &result)) return false;
_filesize = static_cast<uint64_t>(result.QuadPart);
// convert to mapped mode
_mappedFile = ::CreateFileMapping(_file, NULL, PAGE_READONLY, 0, 0, NULL);
if (!_mappedFile) return false;
#else
// Linux
// open file
_file = ::open(filename.c_str(), O_RDONLY | O_LARGEFILE);
if (_file == -1) {
_file = 0;
return false;
}
// file size
struct stat64 statInfo;
if (fstat64(_file, &statInfo) < 0) return false;
_filesize = statInfo.st_size;
#endif
// initial mapping
remap(0, mappedBytes);
if (!_mappedView) return false;
// everything's fine
return true;
}
/// close file
void MemoryMapped::close() {
// kill pointer
if (_mappedView) {
#ifdef _MSC_VER
::UnmapViewOfFile(_mappedView);
#else
::munmap(_mappedView, _filesize);
#endif
_mappedView = NULL;
}
#ifdef _MSC_VER
if (_mappedFile) {
::CloseHandle(_mappedFile);
_mappedFile = NULL;
}
#endif
// close underlying file
if (_file) {
#ifdef _MSC_VER
::CloseHandle(_file);
#else
::close(_file);
#endif
_file = 0;
}
_filesize = 0;
}
/// access position, no range checking (faster)
unsigned char MemoryMapped::operator[](size_t offset) const {
return ((unsigned char*)_mappedView)[offset];
}
/// access position, including range checking
unsigned char MemoryMapped::at(size_t offset) const {
// checks
if (!_mappedView) throw std::invalid_argument("No view mapped");
if (offset >= _filesize) throw std::out_of_range("View is not large enough");
return operator[](offset);
}
/// raw access
const unsigned char* MemoryMapped::getData() const {
return (const unsigned char*)_mappedView;
}
/// true, if file successfully opened
bool MemoryMapped::isValid() const { return _mappedView != NULL; }
/// get file size
uint64_t MemoryMapped::size() const { return _filesize; }
/// get number of actually mapped bytes
size_t MemoryMapped::mappedSize() const { return _mappedBytes; }
/// replace mapping by a new one of the same file, offset MUST be a multiple of
/// the page size
bool MemoryMapped::remap(uint64_t offset, size_t mappedBytes) {
if (!_file) return false;
if (mappedBytes == WholeFile) mappedBytes = _filesize;
// close old mapping
if (_mappedView) {
#ifdef _MSC_VER
::UnmapViewOfFile(_mappedView);
#else
::munmap(_mappedView, _mappedBytes);
#endif
_mappedView = NULL;
}
// don't go further than end of file
if (offset > _filesize) return false;
if (offset + mappedBytes > _filesize)
mappedBytes = size_t(_filesize - offset);
#ifdef _MSC_VER
// Windows
DWORD offsetLow = DWORD(offset & 0xFFFFFFFF);
DWORD offsetHigh = DWORD(offset >> 32);
_mappedBytes = mappedBytes;
// get memory address
_mappedView = ::MapViewOfFile(_mappedFile, FILE_MAP_READ, offsetHigh,
offsetLow, mappedBytes);
if (_mappedView == NULL) {
_mappedBytes = 0;
_mappedView = NULL;
return false;
}
return true;
#else
// Linux
// new mapping
_mappedView =
::mmap64(NULL, mappedBytes, PROT_READ, MAP_SHARED, _file, offset);
if (_mappedView == MAP_FAILED) {
_mappedBytes = 0;
_mappedView = NULL;
return false;
}
_mappedBytes = mappedBytes;
// tweak performance
int linuxHint = 0;
switch (_hint) {
case Normal:
linuxHint = MADV_NORMAL;
break;
case SequentialScan:
linuxHint = MADV_SEQUENTIAL;
break;
case RandomAccess:
linuxHint = MADV_RANDOM;
break;
default:
break;
}
// assume that file will be accessed soon
// linuxHint |= MADV_WILLNEED;
// assume that file will be large
// linuxHint |= MADV_HUGEPAGE;
::madvise(_mappedView, _mappedBytes, linuxHint);
return true;
#endif
}
/// get OS page size (for remap)
int MemoryMapped::getpagesize() {
#ifdef _MSC_VER
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.dwAllocationGranularity;
#else
return sysconf(_SC_PAGESIZE); //::getpagesize();
#endif
}