diff options
author | Cassie Wang <cassiewang@google.com> | 2019-12-20 15:11:45 -0800 |
---|---|---|
committer | Cassie Wang <cassiewang@google.com> | 2019-12-20 16:18:05 -0800 |
commit | 128c9db88925c8425f2ad81e1d8985461d7ba21a (patch) | |
tree | f97ee47cc99d2c162eb30a5e051c606823dfd1ec /icing/file/memory-mapped-file.cc | |
parent | 1897505cb34f3d53e848da13fafe7691c17417ea (diff) | |
download | icing-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.cc | 171 |
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 |