| Index: net/disk_cache/mapped_file_avoid_mmap_posix.cc
|
| diff --git a/net/disk_cache/mapped_file_avoid_mmap_posix.cc b/net/disk_cache/mapped_file_avoid_mmap_posix.cc
|
| index 3d91265ff8eb6b70f2b325211926f34226b78d4e..cd514a366e2fcc7b42965f38675799c9268855e5 100644
|
| --- a/net/disk_cache/mapped_file_avoid_mmap_posix.cc
|
| +++ b/net/disk_cache/mapped_file_avoid_mmap_posix.cc
|
| @@ -5,221 +5,9 @@
|
| #include "net/disk_cache/mapped_file.h"
|
|
|
| #include <stdlib.h>
|
| -#include <sys/mman.h>
|
| -
|
| -#include <map>
|
|
|
| #include "base/files/file_path.h"
|
| -#include "base/lazy_instance.h"
|
| #include "base/logging.h"
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "base/memory/scoped_vector.h"
|
| -#include "base/threading/thread_local.h"
|
| -
|
| -// This implementation of MappedFile doesn't use a shared RW mmap for
|
| -// performance reason. Instead it will use a private RO mmap and install a SEGV
|
| -// signal handler. When the memory is modified, the handler will register the
|
| -// modified address and change the protection of the page to RW. When a flush is
|
| -// then executed, it has access to the exact pages that have been modified and
|
| -// will only write those to disk. The handler will only track half of the dirty
|
| -// pages. If more than half the pages are modified, the flush will instead write
|
| -// the full buffer to disk.
|
| -
|
| -namespace {
|
| -
|
| -// Choose 4k as a reasonable page size. As this file is used mainly on Android,
|
| -// this is the real android page size.
|
| -const size_t kPageSize = 4096;
|
| -
|
| -// Variable capacity array, optimized for capacity of 1. Most of the mapped file
|
| -// are used to map exactly 2 pages. Tracking 1 page is then optimal because if
|
| -// both pages are modified, writing the full view is the optimal behavior.
|
| -class SmallArray {
|
| - public:
|
| - SmallArray() : capacity_(0), array_(NULL) {}
|
| - ~SmallArray() { SetCapacity(0); }
|
| -
|
| - size_t capacity() { return capacity_; }
|
| - char** array() { return array_; }
|
| - void SetCapacity(size_t capacity) {
|
| - if (capacity_ > 1)
|
| - delete[] array_;
|
| - capacity_ = capacity;
|
| - if (capacity > 1)
|
| - array_ = new char*[capacity];
|
| - else
|
| - array_ = small_array_;
|
| - }
|
| -
|
| - private:
|
| - size_t capacity_;
|
| - char** array_;
|
| - char* small_array_[1];
|
| -};
|
| -
|
| -// Information about the memory mapped part of a file.
|
| -struct MappedFileInfo {
|
| - // Stat address of the memory.
|
| - char* start_address;
|
| - // Size of the memory map.
|
| - size_t size;
|
| - // Number of dirty page. A page is dirty if the memory content is different
|
| - // from the file content.
|
| - size_t num_dirty_pages;
|
| - // The dirty pages.
|
| - SmallArray dirty_pages;
|
| -};
|
| -
|
| -// The maximum number of dirty pages that can be tracked. Limit the memory
|
| -// overhead to 2kb per file.
|
| -const size_t kMaxDirtyPagesCacheSize =
|
| - kPageSize / sizeof(char*) / 2 - sizeof(MappedFileInfo);
|
| -
|
| -class ThreadLocalMappedFileInfo {
|
| - public:
|
| - ThreadLocalMappedFileInfo() {}
|
| - ~ThreadLocalMappedFileInfo() {}
|
| -
|
| - void RegisterMappedFile(disk_cache::MappedFile* mapped_file, size_t size) {
|
| - scoped_ptr<MappedFileInfo> new_info(new MappedFileInfo);
|
| - new_info->start_address = static_cast<char*>(mapped_file->buffer());
|
| - new_info->size = size;
|
| - new_info->num_dirty_pages = 0;
|
| - // Track half of the dirty pages, after this, just overwrite the full
|
| - // content.
|
| - size_t capacity = (size + kPageSize - 1) / kPageSize / 2;
|
| - if (capacity > kMaxDirtyPagesCacheSize)
|
| - capacity = kMaxDirtyPagesCacheSize;
|
| - new_info->dirty_pages.SetCapacity(capacity);
|
| - info_per_map_file_[mapped_file] = new_info.get();
|
| - infos_.push_back(new_info.release());
|
| - Update();
|
| - }
|
| -
|
| - void UnregisterMappedFile(disk_cache::MappedFile* mapped_file) {
|
| - MappedFileInfo* info = InfoForMappedFile(mapped_file);
|
| - DCHECK(info);
|
| - info_per_map_file_.erase(mapped_file);
|
| - infos_.erase(std::find(infos_.begin(), infos_.end(), info));
|
| - Update();
|
| - }
|
| -
|
| - MappedFileInfo* InfoForMappedFile(disk_cache::MappedFile* mapped_file) {
|
| - return info_per_map_file_[mapped_file];
|
| - }
|
| -
|
| - MappedFileInfo** infos_ptr() { return infos_ptr_; }
|
| - size_t infos_size() { return infos_size_; }
|
| -
|
| - private:
|
| - // Update |infos_ptr_| and |infos_size_| when |infos_| change.
|
| - void Update() {
|
| - infos_ptr_ = &infos_[0];
|
| - infos_size_ = infos_.size();
|
| - }
|
| -
|
| - // Link to the MappedFileInfo for a given MappedFile.
|
| - std::map<disk_cache::MappedFile*, MappedFileInfo*> info_per_map_file_;
|
| - // Vector of information about all current MappedFile belonging to the current
|
| - // thread.
|
| - ScopedVector<MappedFileInfo> infos_;
|
| - // Pointer to the storage part of |infos_|. This is kept as a variable to
|
| - // prevent the signal handler from calling any C++ method that might allocate
|
| - // memory.
|
| - MappedFileInfo** infos_ptr_;
|
| - // Size of |infos_|.
|
| - size_t infos_size_;
|
| -};
|
| -
|
| -class SegvHandler {
|
| - public:
|
| - // Register the signal handler.
|
| - SegvHandler();
|
| - ~SegvHandler() {}
|
| -
|
| - // SEGV signal handler. This handler will check that the address that
|
| - // generated the fault is one associated with a mapped file. If that's the
|
| - // case, it will register the address and change the protection to RW then
|
| - // return. This will cause the instruction that generated the fault to be
|
| - // re-executed. If not, it will just reinstall the old handler and return,
|
| - // which will generate the fault again and let the initial handler get called.
|
| - static void SigSegvHandler(int sig, siginfo_t* si, void* unused);
|
| -
|
| - base::ThreadLocalPointer<ThreadLocalMappedFileInfo>& thread_local_infos() {
|
| - return thread_local_infos_;
|
| - }
|
| -
|
| - private:
|
| - // Install the SEGV handler, storing the current sigaction in |old_sigaction|
|
| - // if it is not NULL.
|
| - static void InstallSigHandler(struct sigaction* old_sigaction);
|
| -
|
| - base::ThreadLocalPointer<ThreadLocalMappedFileInfo> thread_local_infos_;
|
| - struct sigaction old_sigaction_;
|
| -};
|
| -
|
| -static base::LazyInstance<SegvHandler> g_segv_handler =
|
| - LAZY_INSTANCE_INITIALIZER;
|
| -
|
| -// Initialisation method.
|
| -SegvHandler::SegvHandler() {
|
| - // Setup the SIGV signal handler.
|
| - InstallSigHandler(&old_sigaction_);
|
| -}
|
| -
|
| -// static
|
| -void SegvHandler::SigSegvHandler(int sig, siginfo_t* si, void* unused) {
|
| - // First, check if the current sighandler has the SA_SIGINFO flag. If it
|
| - // doesn't it means an external library installed temporarly a signal handler
|
| - // using signal, and so incorrectly restored the current one. The parameters
|
| - // are then useless.
|
| - struct sigaction current_action;
|
| - sigaction(SIGSEGV, NULL, ¤t_action);
|
| - if (!(current_action.sa_flags & SA_SIGINFO)) {
|
| - LOG(WARNING) << "Signal handler have been re-installed incorrectly.";
|
| - InstallSigHandler(NULL);
|
| - // Returning will re-run the signal with the correct parameters.
|
| - return;
|
| - }
|
| - ThreadLocalMappedFileInfo* thread_local_info =
|
| - g_segv_handler.Pointer()->thread_local_infos().Get();
|
| - if (thread_local_info) {
|
| - char* addr = reinterpret_cast<char*>(si->si_addr);
|
| - for (size_t i = 0; i < thread_local_info->infos_size(); ++i) {
|
| - MappedFileInfo* info = thread_local_info->infos_ptr()[i];
|
| - if (info->start_address <= addr &&
|
| - addr < info->start_address + info->size) {
|
| - // Only track new dirty pages if the array has still some capacity.
|
| - // Otherwise, the full buffer will be written to disk and it is not
|
| - // necessary to track changes until the next flush.
|
| - if (info->num_dirty_pages < info->dirty_pages.capacity()) {
|
| - char* aligned_address = reinterpret_cast<char*>(
|
| - reinterpret_cast<size_t>(addr) & ~(kPageSize - 1));
|
| - mprotect(aligned_address, kPageSize, PROT_READ | PROT_WRITE);
|
| - info->dirty_pages.array()[info->num_dirty_pages] = aligned_address;
|
| - } else {
|
| - mprotect(info->start_address, info->size, PROT_READ | PROT_WRITE);
|
| - }
|
| - info->num_dirty_pages++;
|
| - return;
|
| - }
|
| - }
|
| - }
|
| - // The address it not handled by any mapped filed. Let the default handler get
|
| - // called.
|
| - sigaction(SIGSEGV, &g_segv_handler.Pointer()->old_sigaction_, NULL);
|
| -}
|
| -
|
| -// static
|
| -void SegvHandler::InstallSigHandler(struct sigaction* old_sigaction) {
|
| - struct sigaction action;
|
| - action.sa_sigaction = SigSegvHandler;
|
| - sigemptyset(&action.sa_mask);
|
| - action.sa_flags = SA_SIGINFO | SA_RESTART;
|
| - sigaction(SIGSEGV, &action, old_sigaction);
|
| -}
|
| -
|
| -} // namespace
|
|
|
| namespace disk_cache {
|
|
|
| @@ -231,21 +19,14 @@ void* MappedFile::Init(const base::FilePath& name, size_t size) {
|
| if (!size)
|
| size = GetLength();
|
|
|
| - buffer_ = mmap(NULL, size, PROT_READ, MAP_PRIVATE, platform_file(), 0);
|
| - if (reinterpret_cast<ptrdiff_t>(buffer_) == -1) {
|
| - NOTREACHED();
|
| - buffer_ = 0;
|
| - }
|
| -
|
| - if (buffer_) {
|
| - ThreadLocalMappedFileInfo* thread_local_info =
|
| - g_segv_handler.Pointer()->thread_local_infos().Get();
|
| - if (!thread_local_info) {
|
| - thread_local_info = new ThreadLocalMappedFileInfo();
|
| - g_segv_handler.Pointer()->thread_local_infos().Set(thread_local_info);
|
| - }
|
| - DCHECK(size);
|
| - thread_local_info->RegisterMappedFile(this, size);
|
| + buffer_ = malloc(size);
|
| + snapshot_ = malloc(size);
|
| + if (buffer_ && snapshot_ && Read(buffer_, size, 0)) {
|
| + memcpy(snapshot_, buffer_, size);
|
| + } else {
|
| + free(buffer_);
|
| + free(snapshot_);
|
| + buffer_ = snapshot_ = 0;
|
| }
|
|
|
| init_ = true;
|
| @@ -265,39 +46,28 @@ bool MappedFile::Store(const FileBlock* block) {
|
|
|
| void MappedFile::Flush() {
|
| DCHECK(buffer_);
|
| - MappedFileInfo* info = g_segv_handler.Pointer()->thread_local_infos().Get()->
|
| - InfoForMappedFile(this);
|
| - DCHECK(info);
|
| - if (info->num_dirty_pages > info->dirty_pages.capacity()) {
|
| - Write(buffer_, view_size_, 0);
|
| - } else {
|
| - const char* buffer_ptr = static_cast<const char*>(buffer_);
|
| - for (size_t i = 0; i < info->num_dirty_pages; ++i) {
|
| - const char* ptr = info->dirty_pages.array()[i];
|
| - size_t size_to_write = kPageSize;
|
| - // The view_size is not a full number of page. Only write the fraction of
|
| - // the page that is in the view.
|
| - if (ptr - buffer_ptr + kPageSize > view_size_)
|
| - size_to_write = view_size_ - (ptr - buffer_ptr);
|
| - Write(ptr, size_to_write, ptr - buffer_ptr);
|
| + DCHECK(snapshot_);
|
| + const char* buffer_ptr = static_cast<const char*>(buffer_);
|
| + char* snapshot_ptr = static_cast<char*>(snapshot_);
|
| + const size_t block_size = 4096;
|
| + for (size_t offset = 0; offset < view_size_; offset += block_size) {
|
| + size_t size = std::min(view_size_ - offset, block_size);
|
| + if (memcmp(snapshot_ptr + offset, buffer_ptr + offset, size)) {
|
| + memcpy(snapshot_ptr + offset, buffer_ptr + offset, size);
|
| + Write(snapshot_ptr + offset, size, offset);
|
| }
|
| }
|
| - info->num_dirty_pages = 0;
|
| - mprotect(buffer_, view_size_, PROT_READ);
|
| }
|
|
|
| MappedFile::~MappedFile() {
|
| if (!init_)
|
| return;
|
|
|
| - if (buffer_) {
|
| + if (buffer_ && snapshot_) {
|
| Flush();
|
| - ThreadLocalMappedFileInfo* thread_local_info =
|
| - g_segv_handler.Pointer()->thread_local_infos().Get();
|
| - DCHECK(thread_local_info);
|
| - thread_local_info->UnregisterMappedFile(this);
|
| - munmap(buffer_, 0);
|
| }
|
| + free(buffer_);
|
| + free(snapshot_);
|
| }
|
|
|
| } // namespace disk_cache
|
|
|