aboutsummaryrefslogtreecommitdiff
path: root/icing/file/memory-mapped-file.cc
diff options
context:
space:
mode:
authorCassie Wang <cassiewang@google.com>2019-12-20 15:11:45 -0800
committerCassie Wang <cassiewang@google.com>2019-12-20 16:18:05 -0800
commit128c9db88925c8425f2ad81e1d8985461d7ba21a (patch)
treef97ee47cc99d2c162eb30a5e051c606823dfd1ec /icing/file/memory-mapped-file.cc
parent1897505cb34f3d53e848da13fafe7691c17417ea (diff)
downloadicing-128c9db88925c8425f2ad81e1d8985461d7ba21a.tar.gz
Port over Icing c++ code from upstream
Change-Id: Ia3981fed7e0e70589efc027d4123f306cdfbe990
Diffstat (limited to 'icing/file/memory-mapped-file.cc')
-rw-r--r--icing/file/memory-mapped-file.cc171
1 files changed, 171 insertions, 0 deletions
diff --git a/icing/file/memory-mapped-file.cc b/icing/file/memory-mapped-file.cc
new file mode 100644
index 0000000..ebd419b
--- /dev/null
+++ b/icing/file/memory-mapped-file.cc
@@ -0,0 +1,171 @@
+// Copyright (C) 2019 Google LLC
+//
+// 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.
+
+// TODO(cassiewang) Add unit-tests to this class.
+
+#include "icing/file/memory-mapped-file.h"
+
+#include <sys/mman.h>
+
+#include <cerrno>
+
+#include "utils/base/status.h"
+#include "icing/absl_ports/canonical_errors.h"
+#include "icing/absl_ports/str_cat.h"
+#include "icing/file/filesystem.h"
+#include "icing/legacy/core/icing-string-util.h"
+#include "icing/util/math-util.h"
+
+namespace icing {
+namespace lib {
+
+MemoryMappedFile::MemoryMappedFile(const Filesystem& filesystem,
+ const std::string_view file_path,
+ Strategy mmap_strategy)
+ : filesystem_(&filesystem),
+ file_path_(file_path),
+ strategy_(mmap_strategy) {}
+
+MemoryMappedFile::~MemoryMappedFile() { Unmap(); }
+
+void MemoryMappedFile::MemoryMappedFile::Unmap() {
+ if (mmap_result_ != nullptr) {
+ munmap(mmap_result_, region_size_);
+ mmap_result_ = nullptr;
+ }
+
+ file_offset_ = 0;
+ region_ = nullptr;
+ region_size_ = 0;
+ adjusted_mmap_size_ = 0;
+}
+
+libtextclassifier3::Status MemoryMappedFile::Remap(size_t file_offset,
+ size_t mmap_size) {
+ // First unmap any previously mmapped region.
+ Unmap();
+
+ if (mmap_size == 0) {
+ // Nothing more to do.
+ return libtextclassifier3::Status::OK;
+ }
+
+ size_t aligned_offset =
+ math_util::RoundDownTo(file_offset, system_page_size());
+ size_t alignment_adjustment = file_offset - aligned_offset;
+ size_t adjusted_mmap_size = alignment_adjustment + mmap_size;
+
+ int mmap_flags = 0;
+ // Determines if the mapped region should just be readable or also writable.
+ int protection_flags = 0;
+ ScopedFd fd;
+ switch (strategy_) {
+ case Strategy::READ_ONLY: {
+ mmap_flags = MAP_PRIVATE;
+ protection_flags = PROT_READ;
+ fd.reset(filesystem_->OpenForRead(file_path_.c_str()));
+ break;
+ }
+ case Strategy::READ_WRITE_AUTO_SYNC: {
+ mmap_flags = MAP_SHARED;
+ protection_flags = PROT_READ | PROT_WRITE;
+ fd.reset(filesystem_->OpenForWrite(file_path_.c_str()));
+ break;
+ }
+ case Strategy::READ_WRITE_MANUAL_SYNC: {
+ mmap_flags = MAP_PRIVATE;
+ protection_flags = PROT_READ | PROT_WRITE;
+ // TODO(cassiewang) MAP_PRIVATE effectively makes it a read-only file.
+ // figure out if we can open this file in read-only mode.
+ fd.reset(filesystem_->OpenForWrite(file_path_.c_str()));
+ break;
+ }
+ default:
+ return absl_ports::UnknownError(IcingStringUtil::StringPrintf(
+ "Invalid value in switch statement: %d", strategy_));
+ }
+
+ if (!fd.is_valid()) {
+ return absl_ports::InternalError(absl_ports::StrCat(
+ "Unable to open file meant to be mmapped: ", file_path_));
+ }
+
+ mmap_result_ = mmap(nullptr, adjusted_mmap_size, protection_flags, mmap_flags,
+ fd.get(), aligned_offset);
+
+ if (mmap_result_ == MAP_FAILED) {
+ mmap_result_ = nullptr;
+ return absl_ports::InternalError(absl_ports::StrCat(
+ "Failed to mmap region due to error: ", strerror(errno)));
+ }
+
+ file_offset_ = file_offset;
+ region_ = reinterpret_cast<char*>(mmap_result_) + alignment_adjustment;
+ region_size_ = mmap_size;
+ adjusted_mmap_size_ = adjusted_mmap_size;
+ return libtextclassifier3::Status::OK;
+}
+
+libtextclassifier3::Status MemoryMappedFile::PersistToDisk() {
+ if (strategy_ == Strategy::READ_ONLY) {
+ return absl_ports::FailedPreconditionError(absl_ports::StrCat(
+ "Attempting to PersistToDisk on a read-only file: ", file_path_));
+ }
+
+ if (region_ == nullptr) {
+ // Nothing mapped to sync.
+ return libtextclassifier3::Status::OK;
+ }
+
+ if (strategy_ == Strategy::READ_WRITE_AUTO_SYNC &&
+ msync(mmap_result_, adjusted_mmap_size_, MS_SYNC) != 0) {
+ return absl_ports::InternalError(
+ absl_ports::StrCat("Unable to sync file using msync(): ", file_path_));
+ }
+
+ // In order to prevent automatic syncing of changes, files that use the
+ // READ_WRITE_MANUAL_SYNC strategy are mmapped using MAP_PRIVATE. Such files
+ // can't be synced using msync(). So, we have to directly write to the
+ // underlying file to update it.
+ if (strategy_ == Strategy::READ_WRITE_MANUAL_SYNC &&
+ !filesystem_->PWrite(file_path_.c_str(), 0, region(), region_size())) {
+ return absl_ports::InternalError(
+ absl_ports::StrCat("Unable to sync file using PWrite(): ", file_path_));
+ }
+
+ return libtextclassifier3::Status::OK;
+}
+
+libtextclassifier3::Status MemoryMappedFile::OptimizeFor(
+ AccessPattern access_pattern) {
+ int madvise_flag = 0;
+ if (access_pattern == AccessPattern::ACCESS_ALL) {
+ madvise_flag = MADV_WILLNEED;
+ } else if (access_pattern == AccessPattern::ACCESS_NONE) {
+ madvise_flag = MADV_DONTNEED;
+ } else if (access_pattern == AccessPattern::ACCESS_RANDOM) {
+ madvise_flag = MADV_RANDOM;
+ } else if (access_pattern == AccessPattern::ACCESS_SEQUENTIAL) {
+ madvise_flag = MADV_SEQUENTIAL;
+ }
+
+ if (madvise(mmap_result_, adjusted_mmap_size_, madvise_flag) != 0) {
+ return absl_ports::InternalError(absl_ports::StrCat(
+ "Unable to madvise file ", file_path_, "; Error: ", strerror(errno)));
+ }
+ return libtextclassifier3::Status::OK;
+}
+
+} // namespace lib
+} // namespace icing