mirror of https://gitee.com/bigwinds/arangodb
320 lines
9.2 KiB
C++
320 lines
9.2 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Simon Grätzer
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef ARANGODB_PREGEL_BUFFER_H
|
|
#define ARANGODB_PREGEL_BUFFER_H 1
|
|
|
|
#include "Basics/Common.h"
|
|
#include "Basics/FileUtils.h"
|
|
#include "Basics/OpenFilesTracker.h"
|
|
#include "Basics/files.h"
|
|
#include "Basics/memory-map.h"
|
|
#include "Logger/Logger.h"
|
|
|
|
#include <cstddef>
|
|
|
|
#ifdef __linux__
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
namespace arangodb {
|
|
namespace pregel {
|
|
|
|
template <typename T>
|
|
struct TypedBuffer {
|
|
/// close file (see close() )
|
|
virtual ~TypedBuffer(){};
|
|
TypedBuffer() : _ptr(nullptr) {}
|
|
|
|
/// @brief return whether the datafile is a physical file (true) or an
|
|
/// anonymous mapped region (false)
|
|
// inline bool isPhysical() const { return !_filename.empty(); }
|
|
|
|
/// close datafile
|
|
virtual void close() = 0;
|
|
|
|
/// raw access
|
|
T* data() const { return _ptr; }
|
|
|
|
virtual T& back() = 0;
|
|
|
|
/// get file size
|
|
// uint64_t size() const;
|
|
/// get number of actually mapped bytes
|
|
virtual size_t size() const = 0;
|
|
|
|
/// replace mapping by a new one of the same file, offset MUST be a multiple
|
|
/// of the page size
|
|
virtual void resize(size_t newSize) = 0;
|
|
|
|
private:
|
|
/// don't copy object
|
|
TypedBuffer(const TypedBuffer&) = delete;
|
|
/// don't copy object
|
|
TypedBuffer& operator=(const TypedBuffer&) = delete;
|
|
|
|
protected:
|
|
T* _ptr;
|
|
};
|
|
|
|
template <typename T>
|
|
class VectorTypedBuffer : public TypedBuffer<T> {
|
|
std::vector<T> _vector;
|
|
|
|
public:
|
|
VectorTypedBuffer(size_t entries) : _vector(entries) {
|
|
this->_ptr = _vector.data();
|
|
}
|
|
|
|
void close() override {
|
|
//_data.clear();
|
|
}
|
|
|
|
T& back() override { return _vector.back(); }
|
|
|
|
/// get file size
|
|
// uint64_t size() const;
|
|
/// get number of actually mapped bytes
|
|
size_t size() const override { return _vector.size(); }
|
|
|
|
/// replace mapping by a new one of the same file, offset MUST be a multiple
|
|
/// of the page size
|
|
virtual void resize(size_t newSize) override { _vector.resize(newSize); }
|
|
|
|
void appendEmptyElement() {
|
|
_vector.push_back(T());
|
|
this->_ptr = _vector.data(); // might change adress
|
|
}
|
|
};
|
|
|
|
/// Portable read-only memory mapping (Windows and Linux)
|
|
/** Filesize limited by size_t, usually 2^32 or 2^64 */
|
|
template <typename T>
|
|
class MappedFileBuffer : public TypedBuffer<T> {
|
|
public:
|
|
#ifdef TRI_HAVE_ANONYMOUS_MMAP
|
|
MappedFileBuffer(size_t entries) : _size(entries) {
|
|
#ifdef TRI_MMAP_ANONYMOUS
|
|
// fd -1 is required for "real" anonymous regions
|
|
_fd = -1;
|
|
int flags = TRI_MMAP_ANONYMOUS | MAP_SHARED;
|
|
#else
|
|
// ugly workaround if MAP_ANONYMOUS is not available
|
|
_fd = TRI_TRACKED_OPEN_FILE("/dev/zero", O_RDWR | TRI_O_CLOEXEC);
|
|
if (_fd == -1) {
|
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
|
|
}
|
|
|
|
int flags = MAP_PRIVATE;
|
|
#endif
|
|
|
|
// memory map the data
|
|
_mappedSize = sizeof(T) * entries;
|
|
void* ptr;
|
|
int res = TRI_MMFile(nullptr, _mappedSize, PROT_WRITE | PROT_READ, flags,
|
|
_fd, &_mmHandle, 0, &ptr);
|
|
#ifdef MAP_ANONYMOUS
|
|
// nothing to do
|
|
#else
|
|
// close auxilliary file
|
|
TRI_TRACKED_CLOSE_FILE(_fd);
|
|
_fd = -1;
|
|
#endif
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_set_errno(res);
|
|
|
|
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
|
<< "cannot memory map anonymous region: " << TRI_last_error();
|
|
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
|
<< "The database directory might reside on a shared folder "
|
|
"(VirtualBox, VMWare) or an NFS "
|
|
"mounted volume which does not allow memory mapped files.";
|
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
|
|
}
|
|
this->_ptr = (T*)ptr;
|
|
// return new TypedBuffer(StaticStrings::Empty, fd, mmHandle, initialSize,
|
|
// ptr);
|
|
}
|
|
#else
|
|
MappedFileBuffer(size_t entries) : _size(entries) {
|
|
double tt = TRI_microtime();
|
|
std::string file = "pregel_" + std::to_string((uint64_t)tt) + ".mmap";
|
|
std::string filename = FileUtils::buildFilename(TRI_GetTempPath(), file);
|
|
|
|
_mappedSize = sizeof(T) * _size;
|
|
_fd = TRI_CreateDatafile(filename, _mappedSize);
|
|
if (_fd < 0) {
|
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
|
|
}
|
|
|
|
// memory map the data
|
|
void* data;
|
|
int flags = MAP_SHARED;
|
|
#ifdef __linux__
|
|
// try populating the mapping already
|
|
flags |= MAP_POPULATE;
|
|
#endif
|
|
int res = TRI_MMFile(0, _mappedSize, PROT_WRITE | PROT_READ, flags, _fd,
|
|
&_mmHandle, 0, &data);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_set_errno(res);
|
|
TRI_TRACKED_CLOSE_FILE(fd);
|
|
|
|
// remove empty file
|
|
TRI_UnlinkFile(filename.c_str());
|
|
|
|
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "cannot memory map file '"
|
|
<< filename << "': '"
|
|
<< TRI_errno_string(res) << "'";
|
|
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
|
<< "The database directory might reside on a shared folder "
|
|
"(VirtualBox, VMWare) or an NFS-mounted volume which does not "
|
|
"allow memory mapped files.";
|
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
|
|
}
|
|
|
|
this->_ptr = (T*)data;
|
|
}
|
|
#endif
|
|
|
|
/// close file (see close() )
|
|
~MappedFileBuffer() { close(); }
|
|
|
|
/// @brief return whether the datafile is a physical file (true) or an
|
|
/// anonymous mapped region (false)
|
|
inline bool isPhysical() const { return !_filename.empty(); }
|
|
|
|
void sequentialAccess() {
|
|
TRI_MMFileAdvise(this->_ptr, _mappedSize, TRI_MADVISE_SEQUENTIAL);
|
|
}
|
|
|
|
void randomAccess() {
|
|
TRI_MMFileAdvise(this->_ptr, _mappedSize, TRI_MADVISE_RANDOM);
|
|
}
|
|
|
|
void willNeed() {
|
|
TRI_MMFileAdvise(this->_ptr, _mappedSize, TRI_MADVISE_WILLNEED);
|
|
}
|
|
|
|
void dontNeed() {
|
|
TRI_MMFileAdvise(this->_ptr, _mappedSize, TRI_MADVISE_DONTNEED);
|
|
}
|
|
|
|
/// close file
|
|
void close() override {
|
|
int res = TRI_UNMMFile(this->_ptr, _mappedSize, _fd, &_mmHandle);
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
// leave file open here as it will still be memory-mapped
|
|
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "munmap failed with: " << res;
|
|
}
|
|
if (isPhysical()) {
|
|
TRI_ASSERT(_fd >= 0);
|
|
int res = TRI_TRACKED_CLOSE_FILE(_fd);
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
|
<< "unable to close pregel mapped file '" << _filename
|
|
<< "': " << res;
|
|
}
|
|
}
|
|
|
|
this->_ptr = nullptr;
|
|
_fd = -1;
|
|
}
|
|
|
|
T& back() override { return *(this->_ptr + _size - 1); }
|
|
|
|
/// true, if file successfully opened
|
|
bool isValid() const { return this->_ptr != nullptr; }
|
|
|
|
/// get file size
|
|
// uint64_t size() const;
|
|
/// get number of actually mapped bytes
|
|
size_t size() const override { return _size; }
|
|
|
|
/// replace mapping by a new one of the same file, offset MUST be a multiple
|
|
/// of the page size
|
|
void resize(size_t newSize) override {
|
|
if (this->_ptr == nullptr) {
|
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
|
|
} else if (newSize == _size) {
|
|
return;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
size_t newMappedSize = sizeof(T) * newSize;
|
|
void* newPtr =
|
|
mremap((void*)this->_ptr, _mappedSize, newMappedSize, MREMAP_MAYMOVE);
|
|
if (newPtr != MAP_FAILED) { // success
|
|
TRI_ASSERT(this->_ptr != nullptr);
|
|
this->_ptr = (T*)newPtr;
|
|
_mappedSize = newMappedSize;
|
|
_size = newSize;
|
|
return;
|
|
}
|
|
if (errno == ENOMEM) {
|
|
LOG_TOPIC(DEBUG, Logger::MMAP) << "out of memory in mmap";
|
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY_MMAP);
|
|
} else {
|
|
// preserve errno value while we're logging
|
|
LOG_TOPIC(WARN, Logger::MMAP) << "memory-mapping failed";
|
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_SYS_ERROR);
|
|
}
|
|
#else
|
|
// resizing mappings doesn't exist on other systems
|
|
if (_size < newSize || newSize * sizeof(T) <= _mappedSize) {
|
|
_size = newSize;
|
|
} else {
|
|
LOG_TOPIC(ERR, Logger::MMAP)
|
|
<< "Resizing mmap not supported on this platform";
|
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_FAILED);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// get OS page size (for remap)
|
|
/*int TypedBuffer::getpagesize() {
|
|
#ifdef _MSC_VER
|
|
SYSTEM_INFO sysInfo;
|
|
GetSystemInfo(&sysInfo);
|
|
return sysInfo.dwAllocationGranularity;
|
|
#else
|
|
return sysconf(_SC_PAGESIZE); //::getpagesize();
|
|
#endif
|
|
}*/
|
|
|
|
private:
|
|
/// get OS page size (for remap)
|
|
// static int getpagesize();
|
|
|
|
std::string _filename; // underlying filename
|
|
int _fd = -1; // underlying file descriptor
|
|
void* _mmHandle; // underlying memory map object handle (windows only)
|
|
size_t _size = 0;
|
|
size_t _mappedSize;
|
|
};
|
|
}
|
|
}
|
|
|
|
#endif
|