/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#include "folly/MemoryMapping.h"
-#include "folly/Format.h"
-#include "folly/Portability.h"
+#include <folly/MemoryMapping.h>
+
+#include <algorithm>
+#include <functional>
+#include <utility>
+
+#include <folly/Format.h>
+#include <folly/portability/GFlags.h>
+#include <folly/portability/SysMman.h>
#ifdef __linux__
-#include "folly/experimental/io/HugePages.h"
+#include <folly/experimental/io/HugePages.h>
#endif
#include <fcntl.h>
-#include <sys/mman.h>
#include <sys/types.h>
#include <system_error>
-#include <gflags/gflags.h>
-DEFINE_int64(mlock_chunk_size, 1 << 20, // 1MB
+static constexpr ssize_t kDefaultMlockChunkSize =
+#ifndef _MSC_VER
+ // Linux implementations of unmap/mlock/munlock take a kernel
+ // semaphore and block other threads from doing other memory
+ // operations. Split the operations in chunks.
+ (1 << 20) // 1MB
+#else // _MSC_VER
+ // MSVC doesn't have this problem, and calling munmap many times
+ // with the same address is a bad idea with the windows implementation.
+ (-1)
+#endif // _MSC_VER
+ ;
+
+DEFINE_int64(mlock_chunk_size, kDefaultMlockChunkSize,
"Maximum bytes to mlock/munlock/munmap at once "
- "(will be rounded up to PAGESIZE)");
+ "(will be rounded up to PAGESIZE). Ignored if negative.");
#ifndef MAP_POPULATE
#define MAP_POPULATE 0
namespace folly {
-MemoryMapping::MemoryMapping(MemoryMapping&& other) {
+MemoryMapping::MemoryMapping(MemoryMapping&& other) noexcept {
swap(other);
}
MemoryMapping::MemoryMapping(const char* name, off_t offset, off_t length,
Options options)
- : MemoryMapping(File(name), offset, length, options) { }
+ : MemoryMapping(File(name, options.writable ? O_RDWR : O_RDONLY),
+ offset,
+ length,
+ options) { }
MemoryMapping::MemoryMapping(int fd, off_t offset, off_t length,
Options options)
bool memOpInChunks(std::function<int(void*, size_t)> op,
void* mem, size_t bufSize, off_t pageSize,
size_t& amountSucceeded) {
- // unmap/mlock/munlock take a kernel semaphore and block other threads from
- // doing other memory operations. If the size of the buffer is big the
+ // Linux' unmap/mlock/munlock take a kernel semaphore and block other threads
+ // from doing other memory operations. If the size of the buffer is big the
// semaphore can be down for seconds (for benchmarks see
// http://kostja-osipov.livejournal.com/42963.html). Doing the operations in
// chunks breaks the locking into intervals and lets other threads do memory
return true;
}
- auto msg(folly::format(
- "mlock({}) failed at {}",
- mapLength_, amountSucceeded).str());
-
- if (lock == LockMode::TRY_LOCK && (errno == EPERM || errno == ENOMEM)) {
+ auto msg =
+ folly::format("mlock({}) failed at {}", mapLength_, amountSucceeded);
+ if (lock == LockMode::TRY_LOCK && errno == EPERM) {
PLOG(WARNING) << msg;
+ } else if (lock == LockMode::TRY_LOCK && errno == ENOMEM) {
+ VLOG(1) << msg;
} else {
PLOG(FATAL) << msg;
}
size_t amountSucceeded = 0;
if (!memOpInChunks(::munmap, mapStart_, mapLength_, options_.pageSize,
amountSucceeded)) {
- PLOG(FATAL) << folly::format(
- "munmap({}) failed at {}",
- mapLength_, amountSucceeded).str();
+ PLOG(FATAL) << folly::format("munmap({}) failed at {}",
+ mapLength_, amountSucceeded);
}
}
}
-void MemoryMapping::advise(int advice) const {
- if (mapLength_ && ::madvise(mapStart_, mapLength_, advice)) {
- PLOG(WARNING) << "madvise()";
+void MemoryMapping::advise(int advice) const { advise(advice, 0, mapLength_); }
+
+void MemoryMapping::advise(int advice, size_t offset, size_t length) const {
+ CHECK_LE(offset + length, mapLength_)
+ << " offset: " << offset
+ << " length: " << length
+ << " mapLength_: " << mapLength_;
+
+ // Include the entire start page: round down to page boundary.
+ const auto offMisalign = offset % options_.pageSize;
+ offset -= offMisalign;
+ length += offMisalign;
+
+ // Round the last page down to page boundary.
+ if (offset + length != size_t(mapLength_)) {
+ length -= length % options_.pageSize;
}
+
+ if (length == 0) {
+ return;
+ }
+
+ char* mapStart = static_cast<char*>(mapStart_) + offset;
+ PLOG_IF(WARNING, ::madvise(mapStart, length, advice)) << "madvise";
}
MemoryMapping& MemoryMapping::operator=(MemoryMapping other) {
return *this;
}
-void MemoryMapping::swap(MemoryMapping& other) {
+void MemoryMapping::swap(MemoryMapping& other) noexcept {
using std::swap;
swap(this->file_, other.file_);
swap(this->mapStart_, other.mapStart_);
swap(this->data_, other.data_);
}
-void swap(MemoryMapping& a, MemoryMapping& b) { a.swap(b); }
+void swap(MemoryMapping& a, MemoryMapping& b) noexcept { a.swap(b); }
void alignedForwardMemcpy(void* dst, const void* src, size_t size) {
assert(reinterpret_cast<uintptr_t>(src) % alignof(unsigned long) == 0);