Commit 83146ab0 authored by cmumford@chromium.org's avatar cmumford@chromium.org

Revert of Created new Windows LevelDB environment. (https://codereview.chromium.org/113373002/)

Reason for revert:
The indexeddb_perf tests on all Windows testers regressed in performance significantly. Don't yet know the exact cause, but because the performance drop is significant will revert this change first, and then proceed with the investigation.

Original issue's description:
> Created new Win32 LevelDB environment.
>     
> This change splits ChromiumEnv into two classes: ChromiumEnv/ChromiumEnvPosix.
> ChromiumEnvWin32 was also added which uses the Win32 API - except for logging.
> 
> The default is to create POSIX environments for all platforms for non-IDB
> LevelDB users. For IDB we create a Windows environment on Windows, else a
> POSIX environment like before.
> 
> BUG=222623
> 
> Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=245135

TBR=dgrogan@chromium.org,alecflett@chromium.org,jsbell@chromium.org,iannucci@chromium.org
NOTREECHECKS=true
NOTRY=true
BUG=222623

Review URL: https://codereview.chromium.org/140923005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245270 0039d316-1c4b-4281-b951-d872f2087c98
parent ec673cae
...@@ -2,19 +2,46 @@ ...@@ -2,19 +2,46 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors. // found in the LICENSE file. See the AUTHORS file for names of contributors.
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <deque>
#include "base/at_exit.h"
#include "base/debug/trace_event.h" #include "base/debug/trace_event.h"
#include "base/file_util.h" #include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "base/platform_file.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "env_chromium_stdio.h" #include "base/synchronization/lock.h"
#include "base/sys_info.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "chromium_logger.h"
#include "env_chromium.h"
#include "leveldb/env.h"
#include "leveldb/slice.h"
#include "port/port.h"
#include "third_party/re2/re2/re2.h" #include "third_party/re2/re2/re2.h"
#include "util/logging.h"
#if defined(OS_WIN) #if defined(OS_WIN)
#include <io.h> #include <io.h>
#include "base/command_line.h"
#include "base/win/win_util.h" #include "base/win/win_util.h"
#include "env_chromium_win.h" #endif
#if defined(OS_POSIX)
#include <dirent.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/time.h>
#endif #endif
using namespace leveldb; using namespace leveldb;
...@@ -27,9 +54,163 @@ const base::FilePath::CharType backup_table_extension[] = ...@@ -27,9 +54,163 @@ const base::FilePath::CharType backup_table_extension[] =
FILE_PATH_LITERAL(".bak"); FILE_PATH_LITERAL(".bak");
const base::FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb"); const base::FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
#if (defined(OS_POSIX) && !defined(OS_LINUX)) || defined(OS_WIN)
// The following are glibc-specific
size_t fread_unlocked(void *ptr, size_t size, size_t n, FILE *file) {
return fread(ptr, size, n, file);
}
size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *file) {
return fwrite(ptr, size, n, file);
}
int fflush_unlocked(FILE *file) {
return fflush(file);
}
#if !defined(OS_ANDROID)
int fdatasync(int fildes) {
#if defined(OS_WIN)
return _commit(fildes);
#else
return HANDLE_EINTR(fsync(fildes));
#endif
}
#endif
#endif
// Wide-char safe fopen wrapper.
FILE* fopen_internal(const char* fname, const char* mode) {
#if defined(OS_WIN)
return _wfopen(base::UTF8ToUTF16(fname).c_str(),
base::ASCIIToUTF16(mode).c_str());
#else
return fopen(fname, mode);
#endif
}
base::FilePath CreateFilePath(const std::string& file_path) {
#if defined(OS_WIN)
return base::FilePath(base::UTF8ToUTF16(file_path));
#else
return base::FilePath(file_path);
#endif
}
static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[] static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
= FILE_PATH_LITERAL("leveldb-test-"); = FILE_PATH_LITERAL("leveldb-test-");
const char* PlatformFileErrorString(const ::base::PlatformFileError& error) {
switch (error) {
case ::base::PLATFORM_FILE_ERROR_FAILED:
return "No further details.";
case ::base::PLATFORM_FILE_ERROR_IN_USE:
return "File currently in use.";
case ::base::PLATFORM_FILE_ERROR_EXISTS:
return "File already exists.";
case ::base::PLATFORM_FILE_ERROR_NOT_FOUND:
return "File not found.";
case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
return "Access denied.";
case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED:
return "Too many files open.";
case ::base::PLATFORM_FILE_ERROR_NO_MEMORY:
return "Out of memory.";
case ::base::PLATFORM_FILE_ERROR_NO_SPACE:
return "No space left on drive.";
case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY:
return "Not a directory.";
case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION:
return "Invalid operation.";
case ::base::PLATFORM_FILE_ERROR_SECURITY:
return "Security error.";
case ::base::PLATFORM_FILE_ERROR_ABORT:
return "File operation aborted.";
case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE:
return "The supplied path was not a file.";
case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY:
return "The file was not empty.";
case ::base::PLATFORM_FILE_ERROR_INVALID_URL:
return "Invalid URL.";
case ::base::PLATFORM_FILE_ERROR_IO:
return "OS or hardware error.";
case ::base::PLATFORM_FILE_OK:
return "OK.";
case ::base::PLATFORM_FILE_ERROR_MAX:
NOTREACHED();
}
NOTIMPLEMENTED();
return "Unknown error.";
}
class ChromiumSequentialFile: public SequentialFile {
private:
std::string filename_;
FILE* file_;
const UMALogger* uma_logger_;
public:
ChromiumSequentialFile(const std::string& fname, FILE* f,
const UMALogger* uma_logger)
: filename_(fname), file_(f), uma_logger_(uma_logger) { }
virtual ~ChromiumSequentialFile() { fclose(file_); }
virtual Status Read(size_t n, Slice* result, char* scratch) {
Status s;
size_t r = fread_unlocked(scratch, 1, n, file_);
*result = Slice(scratch, r);
if (r < n) {
if (feof(file_)) {
// We leave status as ok if we hit the end of the file
} else {
// A partial read with an error: return a non-ok status
s = MakeIOError(filename_, strerror(errno), kSequentialFileRead, errno);
uma_logger_->RecordErrorAt(kSequentialFileRead);
}
}
return s;
}
virtual Status Skip(uint64_t n) {
if (fseek(file_, n, SEEK_CUR)) {
int saved_errno = errno;
uma_logger_->RecordErrorAt(kSequentialFileSkip);
return MakeIOError(
filename_, strerror(saved_errno), kSequentialFileSkip, saved_errno);
}
return Status::OK();
}
};
class ChromiumRandomAccessFile: public RandomAccessFile {
private:
std::string filename_;
::base::PlatformFile file_;
const UMALogger* uma_logger_;
public:
ChromiumRandomAccessFile(const std::string& fname, ::base::PlatformFile file,
const UMALogger* uma_logger)
: filename_(fname), file_(file), uma_logger_(uma_logger) { }
virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_); }
virtual Status Read(uint64_t offset, size_t n, Slice* result,
char* scratch) const {
Status s;
int r = ::base::ReadPlatformFile(file_, offset, scratch, n);
*result = Slice(scratch, (r < 0) ? 0 : r);
if (r < 0) {
// An error: return a non-ok status
s = MakeIOError(
filename_, "Could not perform read", kRandomAccessFileRead);
uma_logger_->RecordErrorAt(kRandomAccessFileRead);
}
return s;
}
};
class ChromiumFileLock : public FileLock { class ChromiumFileLock : public FileLock {
public: public:
::base::PlatformFile file_; ::base::PlatformFile file_;
...@@ -80,33 +261,17 @@ class Retrier { ...@@ -80,33 +261,17 @@ class Retrier {
RetrierProvider* provider_; RetrierProvider* provider_;
}; };
class IDBEnvStdio : public ChromiumEnvStdio { class IDBEnv : public ChromiumEnv {
public: public:
IDBEnvStdio() : ChromiumEnvStdio() { IDBEnv() : ChromiumEnv() {
name_ = "LevelDBEnv.IDB"; name_ = "LevelDBEnv.IDB";
make_backup_ = true; make_backup_ = true;
} }
}; };
#if defined(OS_WIN) ::base::LazyInstance<IDBEnv>::Leaky idb_env = LAZY_INSTANCE_INITIALIZER;
class IDBEnvWin : public ChromiumEnvWin {
public:
IDBEnvWin() : ChromiumEnvWin() {
name_ = "LevelDBEnv.IDB";
make_backup_ = true;
}
};
#endif
#if defined(OS_WIN) ::base::LazyInstance<ChromiumEnv>::Leaky default_env =
::base::LazyInstance<IDBEnvWin>::Leaky idb_env =
LAZY_INSTANCE_INITIALIZER;
#else
::base::LazyInstance<IDBEnvStdio>::Leaky idb_env =
LAZY_INSTANCE_INITIALIZER;
#endif
::base::LazyInstance<ChromiumEnvStdio>::Leaky default_env =
LAZY_INSTANCE_INITIALIZER; LAZY_INSTANCE_INITIALIZER;
} // unnamed namespace } // unnamed namespace
...@@ -329,24 +494,122 @@ std::string FilePathToString(const base::FilePath& file_path) { ...@@ -329,24 +494,122 @@ std::string FilePathToString(const base::FilePath& file_path) {
#endif #endif
} }
base::FilePath ChromiumEnv::CreateFilePath(const std::string& file_path) { ChromiumWritableFile::ChromiumWritableFile(const std::string& fname,
#if defined(OS_WIN) FILE* f,
return base::FilePath(base::UTF8ToUTF16(file_path)); const UMALogger* uma_logger,
#else WriteTracker* tracker,
return base::FilePath(file_path); bool make_backup)
: filename_(fname),
file_(f),
uma_logger_(uma_logger),
tracker_(tracker),
file_type_(kOther),
make_backup_(make_backup) {
base::FilePath path = base::FilePath::FromUTF8Unsafe(fname);
if (FilePathToString(path.BaseName()).find("MANIFEST") == 0)
file_type_ = kManifest;
else if (path.MatchesExtension(table_extension))
file_type_ = kTable;
if (file_type_ != kManifest)
tracker_->DidCreateNewFile(filename_);
parent_dir_ = FilePathToString(CreateFilePath(fname).DirName());
}
ChromiumWritableFile::~ChromiumWritableFile() {
if (file_ != NULL) {
// Ignoring any potential errors
fclose(file_);
}
}
Status ChromiumWritableFile::SyncParent() {
Status s;
#if !defined(OS_WIN)
TRACE_EVENT0("leveldb", "SyncParent");
int parent_fd =
HANDLE_EINTR(open(parent_dir_.c_str(), O_RDONLY));
if (parent_fd < 0) {
int saved_errno = errno;
return MakeIOError(
parent_dir_, strerror(saved_errno), kSyncParent, saved_errno);
}
if (HANDLE_EINTR(fsync(parent_fd)) != 0) {
int saved_errno = errno;
s = MakeIOError(
parent_dir_, strerror(saved_errno), kSyncParent, saved_errno);
};
close(parent_fd);
#endif #endif
return s;
}
Status ChromiumWritableFile::Append(const Slice& data) {
if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) {
Status s = SyncParent();
if (!s.ok())
return s;
tracker_->DidSyncDir(filename_);
}
size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_);
if (r != data.size()) {
int saved_errno = errno;
uma_logger_->RecordOSError(kWritableFileAppend, saved_errno);
return MakeIOError(
filename_, strerror(saved_errno), kWritableFileAppend, saved_errno);
}
return Status::OK();
}
Status ChromiumWritableFile::Close() {
Status result;
if (fclose(file_) != 0) {
result = MakeIOError(filename_, strerror(errno), kWritableFileClose, errno);
uma_logger_->RecordErrorAt(kWritableFileClose);
}
file_ = NULL;
return result;
}
Status ChromiumWritableFile::Flush() {
Status result;
if (HANDLE_EINTR(fflush_unlocked(file_))) {
int saved_errno = errno;
result = MakeIOError(
filename_, strerror(saved_errno), kWritableFileFlush, saved_errno);
uma_logger_->RecordOSError(kWritableFileFlush, saved_errno);
}
return result;
} }
bool ChromiumEnv::MakeBackup(const std::string& fname) { static bool MakeBackup(const std::string& fname) {
base::FilePath original_table_name = CreateFilePath(fname); base::FilePath original_table_name = CreateFilePath(fname);
base::FilePath backup_table_name = base::FilePath backup_table_name =
original_table_name.ReplaceExtension(backup_table_extension); original_table_name.ReplaceExtension(backup_table_extension);
return base::CopyFile(original_table_name, backup_table_name); return base::CopyFile(original_table_name, backup_table_name);
} }
bool ChromiumEnv::HasTableExtension(const base::FilePath& path) Status ChromiumWritableFile::Sync() {
{ TRACE_EVENT0("leveldb", "ChromiumEnv::Sync");
return path.MatchesExtension(table_extension); Status result;
int error = 0;
if (HANDLE_EINTR(fflush_unlocked(file_)))
error = errno;
// Sync even if fflush gave an error; perhaps the data actually got out,
// even though something went wrong.
if (fdatasync(fileno(file_)) && !error)
error = errno;
// Report the first error we found.
if (error) {
result = MakeIOError(filename_, strerror(error), kWritableFileSync, error);
uma_logger_->RecordErrorAt(kWritableFileSync);
} else if (make_backup_ && file_type_ == kTable) {
bool success = MakeBackup(filename_);
uma_logger_->RecordBackupResult(success);
}
return result;
} }
ChromiumEnv::ChromiumEnv() ChromiumEnv::ChromiumEnv()
...@@ -363,51 +626,71 @@ ChromiumEnv::~ChromiumEnv() { ...@@ -363,51 +626,71 @@ ChromiumEnv::~ChromiumEnv() {
// a unit test that is deleted. // a unit test that is deleted.
} }
bool ChromiumEnv::FileExists(const std::string& fname) { Status ChromiumEnv::NewSequentialFile(const std::string& fname,
return ::base::PathExists(CreateFilePath(fname)); SequentialFile** result) {
FILE* f = fopen_internal(fname.c_str(), "rb");
if (f == NULL) {
*result = NULL;
int saved_errno = errno;
RecordOSError(kNewSequentialFile, saved_errno);
return MakeIOError(
fname, strerror(saved_errno), kNewSequentialFile, saved_errno);
} else {
*result = new ChromiumSequentialFile(fname, f, this);
return Status::OK();
}
} }
const char* ChromiumEnv::PlatformFileErrorString(const ::base::PlatformFileError& error) { void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
switch (error) { #if defined(OS_POSIX)
case ::base::PLATFORM_FILE_ERROR_FAILED: struct rlimit nofile;
return "No further details."; if (getrlimit(RLIMIT_NOFILE, &nofile))
case ::base::PLATFORM_FILE_ERROR_IN_USE: return;
return "File currently in use."; GetMaxFDHistogram(type)->Add(nofile.rlim_cur);
case ::base::PLATFORM_FILE_ERROR_EXISTS: #endif
return "File already exists."; }
case ::base::PLATFORM_FILE_ERROR_NOT_FOUND:
return "File not found."; Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED: RandomAccessFile** result) {
return "Access denied."; int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN;
case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED: bool created;
return "Too many files open."; ::base::PlatformFileError error_code;
case ::base::PLATFORM_FILE_ERROR_NO_MEMORY: ::base::PlatformFile file = ::base::CreatePlatformFile(
return "Out of memory."; CreateFilePath(fname), flags, &created, &error_code);
case ::base::PLATFORM_FILE_ERROR_NO_SPACE: if (error_code == ::base::PLATFORM_FILE_OK) {
return "No space left on drive."; *result = new ChromiumRandomAccessFile(fname, file, this);
case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY: RecordOpenFilesLimit("Success");
return "Not a directory."; return Status::OK();
case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION:
return "Invalid operation.";
case ::base::PLATFORM_FILE_ERROR_SECURITY:
return "Security error.";
case ::base::PLATFORM_FILE_ERROR_ABORT:
return "File operation aborted.";
case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE:
return "The supplied path was not a file.";
case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY:
return "The file was not empty.";
case ::base::PLATFORM_FILE_ERROR_INVALID_URL:
return "Invalid URL.";
case ::base::PLATFORM_FILE_ERROR_IO:
return "OS or hardware error.";
case ::base::PLATFORM_FILE_OK:
return "OK.";
case ::base::PLATFORM_FILE_ERROR_MAX:
NOTREACHED();
} }
NOTIMPLEMENTED(); if (error_code == ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED)
return "Unknown error."; RecordOpenFilesLimit("TooManyOpened");
else
RecordOpenFilesLimit("OtherError");
*result = NULL;
RecordOSError(kNewRandomAccessFile, error_code);
return MakeIOError(fname,
PlatformFileErrorString(error_code),
kNewRandomAccessFile,
error_code);
}
Status ChromiumEnv::NewWritableFile(const std::string& fname,
WritableFile** result) {
*result = NULL;
FILE* f = fopen_internal(fname.c_str(), "wb");
if (f == NULL) {
int saved_errno = errno;
RecordErrorAt(kNewWritableFile);
return MakeIOError(
fname, strerror(saved_errno), kNewWritableFile, saved_errno);
} else {
*result = new ChromiumWritableFile(fname, f, this, this, make_backup_);
return Status::OK();
}
}
bool ChromiumEnv::FileExists(const std::string& fname) {
return ::base::PathExists(CreateFilePath(fname));
} }
base::FilePath ChromiumEnv::RestoreFromBackup(const base::FilePath& base_name) { base::FilePath ChromiumEnv::RestoreFromBackup(const base::FilePath& base_name) {
...@@ -463,6 +746,62 @@ void ChromiumEnv::RestoreIfNecessary(const std::string& dir, ...@@ -463,6 +746,62 @@ void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
} }
} }
namespace {
#if defined(OS_WIN)
static base::PlatformFileError GetDirectoryEntries(
const base::FilePath& dir_param,
std::vector<base::FilePath>* result) {
result->clear();
base::FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
WIN32_FIND_DATA find_data;
HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
if (find_handle == INVALID_HANDLE_VALUE) {
DWORD last_error = GetLastError();
if (last_error == ERROR_FILE_NOT_FOUND)
return base::PLATFORM_FILE_OK;
return base::LastErrorToPlatformFileError(last_error);
}
do {
base::FilePath filepath(find_data.cFileName);
base::FilePath::StringType basename = filepath.BaseName().value();
if (basename == FILE_PATH_LITERAL(".") ||
basename == FILE_PATH_LITERAL(".."))
continue;
result->push_back(filepath.BaseName());
} while (FindNextFile(find_handle, &find_data));
DWORD last_error = GetLastError();
base::PlatformFileError return_value = base::PLATFORM_FILE_OK;
if (last_error != ERROR_NO_MORE_FILES)
return_value = base::LastErrorToPlatformFileError(last_error);
FindClose(find_handle);
return return_value;
}
#else
static base::PlatformFileError GetDirectoryEntries(
const base::FilePath& dir_filepath,
std::vector<base::FilePath>* result) {
const std::string dir_string = FilePathToString(dir_filepath);
result->clear();
DIR* dir = opendir(dir_string.c_str());
if (!dir)
return base::ErrnoToPlatformFileError(errno);
struct dirent dent_buf;
struct dirent* dent;
int readdir_result;
while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent) {
if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
continue;
result->push_back(CreateFilePath(dent->d_name));
}
int saved_errno = errno;
closedir(dir);
if (readdir_result != 0)
return base::ErrnoToPlatformFileError(saved_errno);
return base::PLATFORM_FILE_OK;
}
#endif
}
Status ChromiumEnv::GetChildren(const std::string& dir_string, Status ChromiumEnv::GetChildren(const std::string& dir_string,
std::vector<std::string>* result) { std::vector<std::string>* result) {
std::vector<base::FilePath> entries; std::vector<base::FilePath> entries;
...@@ -670,6 +1009,19 @@ Status ChromiumEnv::GetTestDirectory(std::string* path) { ...@@ -670,6 +1009,19 @@ Status ChromiumEnv::GetTestDirectory(std::string* path) {
return Status::OK(); return Status::OK();
} }
Status ChromiumEnv::NewLogger(const std::string& fname, Logger** result) {
FILE* f = fopen_internal(fname.c_str(), "w");
if (f == NULL) {
*result = NULL;
int saved_errno = errno;
RecordOSError(kNewLogger, saved_errno);
return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno);
} else {
*result = new ChromiumLogger(f);
return Status::OK();
}
}
uint64_t ChromiumEnv::NowMicros() { uint64_t ChromiumEnv::NowMicros() {
return ::base::TimeTicks::Now().ToInternalValue(); return ::base::TimeTicks::Now().ToInternalValue();
} }
......
...@@ -11,7 +11,10 @@ ...@@ -11,7 +11,10 @@
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "base/platform_file.h" #include "base/platform_file.h"
#include "base/synchronization/condition_variable.h"
#include "leveldb/env.h" #include "leveldb/env.h"
#include "leveldb/slice.h"
#include "leveldb/status.h"
#include "port/port_chromium.h" #include "port/port_chromium.h"
#include "util/mutexlock.h" #include "util/mutexlock.h"
...@@ -98,18 +101,51 @@ class WriteTracker { ...@@ -98,18 +101,51 @@ class WriteTracker {
virtual void DidSyncDir(const std::string& fname) = 0; virtual void DidSyncDir(const std::string& fname) = 0;
}; };
class ChromiumWritableFile : public leveldb::WritableFile {
public:
ChromiumWritableFile(const std::string& fname,
FILE* f,
const UMALogger* uma_logger,
WriteTracker* tracker,
bool make_backup);
virtual ~ChromiumWritableFile();
virtual leveldb::Status Append(const leveldb::Slice& data);
virtual leveldb::Status Close();
virtual leveldb::Status Flush();
virtual leveldb::Status Sync();
private:
enum Type {
kManifest,
kTable,
kOther
};
leveldb::Status SyncParent();
std::string filename_;
FILE* file_;
const UMALogger* uma_logger_;
WriteTracker* tracker_;
Type file_type_;
std::string parent_dir_;
bool make_backup_;
};
class ChromiumEnv : public leveldb::Env, class ChromiumEnv : public leveldb::Env,
public UMALogger, public UMALogger,
public RetrierProvider, public RetrierProvider,
public WriteTracker { public WriteTracker {
public: public:
static bool MakeBackup(const std::string& fname); ChromiumEnv();
static base::FilePath CreateFilePath(const std::string& file_path);
static const char* PlatformFileErrorString(
const ::base::PlatformFileError& error);
static bool HasTableExtension(const base::FilePath& path);
virtual ~ChromiumEnv(); virtual ~ChromiumEnv();
virtual leveldb::Status NewSequentialFile(const std::string& fname,
leveldb::SequentialFile** result);
virtual leveldb::Status NewRandomAccessFile(
const std::string& fname,
leveldb::RandomAccessFile** result);
virtual leveldb::Status NewWritableFile(const std::string& fname,
leveldb::WritableFile** result);
virtual bool FileExists(const std::string& fname); virtual bool FileExists(const std::string& fname);
virtual leveldb::Status GetChildren(const std::string& dir, virtual leveldb::Status GetChildren(const std::string& dir,
std::vector<std::string>* result); std::vector<std::string>* result);
...@@ -125,24 +161,15 @@ class ChromiumEnv : public leveldb::Env, ...@@ -125,24 +161,15 @@ class ChromiumEnv : public leveldb::Env,
virtual void Schedule(void (*function)(void*), void* arg); virtual void Schedule(void (*function)(void*), void* arg);
virtual void StartThread(void (*function)(void* arg), void* arg); virtual void StartThread(void (*function)(void* arg), void* arg);
virtual leveldb::Status GetTestDirectory(std::string* path); virtual leveldb::Status GetTestDirectory(std::string* path);
virtual leveldb::Status NewLogger(const std::string& fname,
leveldb::Logger** result);
virtual uint64_t NowMicros(); virtual uint64_t NowMicros();
virtual void SleepForMicroseconds(int micros); virtual void SleepForMicroseconds(int micros);
protected: protected:
ChromiumEnv();
virtual void DidCreateNewFile(const std::string& fname); virtual void DidCreateNewFile(const std::string& fname);
virtual bool DoesDirNeedSync(const std::string& fname); virtual bool DoesDirNeedSync(const std::string& fname);
virtual void DidSyncDir(const std::string& fname); virtual void DidSyncDir(const std::string& fname);
virtual base::PlatformFileError GetDirectoryEntries(
const base::FilePath& dir_param,
std::vector<base::FilePath>* result) const = 0;
virtual void RecordErrorAt(MethodID method) const;
virtual void RecordOSError(MethodID method, int saved_errno) const;
virtual void RecordOSError(MethodID method,
base::PlatformFileError error) const;
base::HistogramBase* GetMaxFDHistogram(const std::string& type) const;
base::HistogramBase* GetOSErrorHistogram(MethodID method, int limit) const;
std::string name_; std::string name_;
bool make_backup_; bool make_backup_;
...@@ -175,12 +202,19 @@ class ChromiumEnv : public leveldb::Env, ...@@ -175,12 +202,19 @@ class ChromiumEnv : public leveldb::Env,
reinterpret_cast<ChromiumEnv*>(arg)->BGThread(); reinterpret_cast<ChromiumEnv*>(arg)->BGThread();
} }
virtual void RecordErrorAt(MethodID method) const;
virtual void RecordOSError(MethodID method, int saved_errno) const;
virtual void RecordOSError(MethodID method,
base::PlatformFileError error) const;
virtual void RecordBackupResult(bool result) const; virtual void RecordBackupResult(bool result) const;
void RestoreIfNecessary(const std::string& dir, void RestoreIfNecessary(const std::string& dir,
std::vector<std::string>* children); std::vector<std::string>* children);
base::FilePath RestoreFromBackup(const base::FilePath& base_name); base::FilePath RestoreFromBackup(const base::FilePath& base_name);
void RecordOpenFilesLimit(const std::string& type);
void RecordLockFileAncestors(int num_missing_ancestors) const; void RecordLockFileAncestors(int num_missing_ancestors) const;
base::HistogramBase* GetOSErrorHistogram(MethodID method, int limit) const;
base::HistogramBase* GetMethodIOErrorHistogram() const; base::HistogramBase* GetMethodIOErrorHistogram() const;
base::HistogramBase* GetMaxFDHistogram(const std::string& type) const;
base::HistogramBase* GetLockFileAncestorHistogram() const; base::HistogramBase* GetLockFileAncestorHistogram() const;
// RetrierProvider implementation. // RetrierProvider implementation.
......
// Copyright (c) 2011-2013 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include <errno.h>
#include "base/debug/trace_event.h"
#include "base/metrics/histogram.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/utf_string_conversions.h"
#include "chromium_logger.h"
#include "env_chromium_stdio.h"
#if defined(OS_WIN)
#include <io.h>
#include "base/win/win_util.h"
#endif
#if defined(OS_POSIX)
#include <dirent.h>
#include <fcntl.h>
#include <sys/resource.h>
#endif
using namespace leveldb;
namespace leveldb_env {
namespace {
#if (defined(OS_POSIX) && !defined(OS_LINUX)) || defined(OS_WIN)
// The following are glibc-specific
size_t fread_unlocked(void* ptr, size_t size, size_t n, FILE* file) {
return fread(ptr, size, n, file);
}
size_t fwrite_unlocked(const void* ptr, size_t size, size_t n, FILE* file) {
return fwrite(ptr, size, n, file);
}
int fflush_unlocked(FILE* file) { return fflush(file); }
#if !defined(OS_ANDROID)
int fdatasync(int fildes) {
#if defined(OS_WIN)
return _commit(fildes);
#else
return HANDLE_EINTR(fsync(fildes));
#endif
}
#endif
#endif
// Wide-char safe fopen wrapper.
FILE* fopen_internal(const char* fname, const char* mode) {
#if defined(OS_WIN)
return _wfopen(base::UTF8ToUTF16(fname).c_str(),
base::ASCIIToUTF16(mode).c_str());
#else
return fopen(fname, mode);
#endif
}
class ChromiumSequentialFile : public SequentialFile {
private:
std::string filename_;
FILE* file_;
const UMALogger* uma_logger_;
public:
ChromiumSequentialFile(const std::string& fname,
FILE* f,
const UMALogger* uma_logger)
: filename_(fname), file_(f), uma_logger_(uma_logger) {}
virtual ~ChromiumSequentialFile() { fclose(file_); }
virtual Status Read(size_t n, Slice* result, char* scratch) {
Status s;
size_t r = fread_unlocked(scratch, 1, n, file_);
*result = Slice(scratch, r);
if (r < n) {
if (feof(file_)) {
// We leave status as ok if we hit the end of the file
} else {
// A partial read with an error: return a non-ok status
s = MakeIOError(filename_, strerror(errno), kSequentialFileRead, errno);
uma_logger_->RecordErrorAt(kSequentialFileRead);
}
}
return s;
}
virtual Status Skip(uint64_t n) {
if (fseek(file_, n, SEEK_CUR)) {
int saved_errno = errno;
uma_logger_->RecordErrorAt(kSequentialFileSkip);
return MakeIOError(
filename_, strerror(saved_errno), kSequentialFileSkip, saved_errno);
}
return Status::OK();
}
};
class ChromiumRandomAccessFile : public RandomAccessFile {
private:
std::string filename_;
::base::PlatformFile file_;
const UMALogger* uma_logger_;
public:
ChromiumRandomAccessFile(const std::string& fname,
::base::PlatformFile file,
const UMALogger* uma_logger)
: filename_(fname), file_(file), uma_logger_(uma_logger) {}
virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_); }
virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch)
const {
Status s;
int r = ::base::ReadPlatformFile(file_, offset, scratch, n);
*result = Slice(scratch, (r < 0) ? 0 : r);
if (r < 0) {
// An error: return a non-ok status
s = MakeIOError(
filename_, "Could not perform read", kRandomAccessFileRead);
uma_logger_->RecordErrorAt(kRandomAccessFileRead);
}
return s;
}
};
} // unnamed namespace
ChromiumWritableFile::ChromiumWritableFile(const std::string& fname,
FILE* f,
const UMALogger* uma_logger,
WriteTracker* tracker,
bool make_backup)
: filename_(fname),
file_(f),
uma_logger_(uma_logger),
tracker_(tracker),
file_type_(kOther),
make_backup_(make_backup) {
base::FilePath path = base::FilePath::FromUTF8Unsafe(fname);
if (FilePathToString(path.BaseName()).find("MANIFEST") == 0)
file_type_ = kManifest;
else if (ChromiumEnv::HasTableExtension(path))
file_type_ = kTable;
if (file_type_ != kManifest)
tracker_->DidCreateNewFile(filename_);
parent_dir_ = FilePathToString(ChromiumEnv::CreateFilePath(fname).DirName());
}
ChromiumWritableFile::~ChromiumWritableFile() {
if (file_ != NULL) {
// Ignoring any potential errors
fclose(file_);
}
}
Status ChromiumWritableFile::SyncParent() {
Status s;
#if !defined(OS_WIN)
TRACE_EVENT0("leveldb", "SyncParent");
int parent_fd = HANDLE_EINTR(open(parent_dir_.c_str(), O_RDONLY));
if (parent_fd < 0) {
int saved_errno = errno;
return MakeIOError(
parent_dir_, strerror(saved_errno), kSyncParent, saved_errno);
}
if (HANDLE_EINTR(fsync(parent_fd)) != 0) {
int saved_errno = errno;
s = MakeIOError(
parent_dir_, strerror(saved_errno), kSyncParent, saved_errno);
};
close(parent_fd);
#endif
return s;
}
Status ChromiumWritableFile::Append(const Slice& data) {
if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) {
Status s = SyncParent();
if (!s.ok())
return s;
tracker_->DidSyncDir(filename_);
}
size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_);
if (r != data.size()) {
int saved_errno = errno;
uma_logger_->RecordOSError(kWritableFileAppend, saved_errno);
return MakeIOError(
filename_, strerror(saved_errno), kWritableFileAppend, saved_errno);
}
return Status::OK();
}
Status ChromiumWritableFile::Close() {
Status result;
if (fclose(file_) != 0) {
result = MakeIOError(filename_, strerror(errno), kWritableFileClose, errno);
uma_logger_->RecordErrorAt(kWritableFileClose);
}
file_ = NULL;
return result;
}
Status ChromiumWritableFile::Flush() {
Status result;
if (HANDLE_EINTR(fflush_unlocked(file_))) {
int saved_errno = errno;
result = MakeIOError(
filename_, strerror(saved_errno), kWritableFileFlush, saved_errno);
uma_logger_->RecordOSError(kWritableFileFlush, saved_errno);
}
return result;
}
Status ChromiumWritableFile::Sync() {
TRACE_EVENT0("leveldb", "ChromiumEnvStdio::Sync");
Status result;
int error = 0;
if (HANDLE_EINTR(fflush_unlocked(file_)))
error = errno;
// Sync even if fflush gave an error; perhaps the data actually got out,
// even though something went wrong.
if (fdatasync(fileno(file_)) && !error)
error = errno;
// Report the first error we found.
if (error) {
result = MakeIOError(filename_, strerror(error), kWritableFileSync, error);
uma_logger_->RecordErrorAt(kWritableFileSync);
} else if (make_backup_ && file_type_ == kTable) {
bool success = ChromiumEnv::MakeBackup(filename_);
uma_logger_->RecordBackupResult(success);
}
return result;
}
ChromiumEnvStdio::ChromiumEnvStdio() {}
ChromiumEnvStdio::~ChromiumEnvStdio() {}
Status ChromiumEnvStdio::NewSequentialFile(const std::string& fname,
SequentialFile** result) {
FILE* f = fopen_internal(fname.c_str(), "rb");
if (f == NULL) {
*result = NULL;
int saved_errno = errno;
RecordOSError(kNewSequentialFile, saved_errno);
return MakeIOError(
fname, strerror(saved_errno), kNewSequentialFile, saved_errno);
} else {
*result = new ChromiumSequentialFile(fname, f, this);
return Status::OK();
}
}
void ChromiumEnvStdio::RecordOpenFilesLimit(const std::string& type) {
#if defined(OS_POSIX)
struct rlimit nofile;
if (getrlimit(RLIMIT_NOFILE, &nofile))
return;
GetMaxFDHistogram(type)->Add(nofile.rlim_cur);
#endif
}
Status ChromiumEnvStdio::NewRandomAccessFile(const std::string& fname,
RandomAccessFile** result) {
int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN;
bool created;
::base::PlatformFileError error_code;
::base::PlatformFile file = ::base::CreatePlatformFile(
ChromiumEnv::CreateFilePath(fname), flags, &created, &error_code);
if (error_code == ::base::PLATFORM_FILE_OK) {
*result = new ChromiumRandomAccessFile(fname, file, this);
RecordOpenFilesLimit("Success");
return Status::OK();
}
if (error_code == ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED)
RecordOpenFilesLimit("TooManyOpened");
else
RecordOpenFilesLimit("OtherError");
*result = NULL;
RecordOSError(kNewRandomAccessFile, error_code);
return MakeIOError(fname,
PlatformFileErrorString(error_code),
kNewRandomAccessFile,
error_code);
}
Status ChromiumEnvStdio::NewWritableFile(const std::string& fname,
WritableFile** result) {
*result = NULL;
FILE* f = fopen_internal(fname.c_str(), "wb");
if (f == NULL) {
int saved_errno = errno;
RecordErrorAt(kNewWritableFile);
return MakeIOError(
fname, strerror(saved_errno), kNewWritableFile, saved_errno);
} else {
*result = new ChromiumWritableFile(fname, f, this, this, make_backup_);
return Status::OK();
}
}
#if defined(OS_WIN)
base::PlatformFileError ChromiumEnvStdio::GetDirectoryEntries(
const base::FilePath& dir_param,
std::vector<base::FilePath>* result) const {
result->clear();
base::FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
WIN32_FIND_DATA find_data;
HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
if (find_handle == INVALID_HANDLE_VALUE) {
DWORD last_error = GetLastError();
if (last_error == ERROR_FILE_NOT_FOUND)
return base::PLATFORM_FILE_OK;
return base::LastErrorToPlatformFileError(last_error);
}
do {
base::FilePath filepath(find_data.cFileName);
base::FilePath::StringType basename = filepath.BaseName().value();
if (basename == FILE_PATH_LITERAL(".") ||
basename == FILE_PATH_LITERAL(".."))
continue;
result->push_back(filepath.BaseName());
} while (FindNextFile(find_handle, &find_data));
DWORD last_error = GetLastError();
base::PlatformFileError return_value = base::PLATFORM_FILE_OK;
if (last_error != ERROR_NO_MORE_FILES)
return_value = base::LastErrorToPlatformFileError(last_error);
FindClose(find_handle);
return return_value;
}
#else
base::PlatformFileError ChromiumEnvStdio::GetDirectoryEntries(
const base::FilePath& dir_filepath,
std::vector<base::FilePath>* result) const {
const std::string dir_string = FilePathToString(dir_filepath);
result->clear();
DIR* dir = opendir(dir_string.c_str());
if (!dir)
return base::ErrnoToPlatformFileError(errno);
struct dirent dent_buf;
struct dirent* dent;
int readdir_result;
while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent) {
if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
continue;
result->push_back(ChromiumEnv::CreateFilePath(dent->d_name));
}
int saved_errno = errno;
closedir(dir);
if (readdir_result != 0)
return base::ErrnoToPlatformFileError(saved_errno);
return base::PLATFORM_FILE_OK;
}
#endif
Status ChromiumEnvStdio::NewLogger(const std::string& fname, Logger** result) {
FILE* f = fopen_internal(fname.c_str(), "w");
if (f == NULL) {
*result = NULL;
int saved_errno = errno;
RecordOSError(kNewLogger, saved_errno);
return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno);
} else {
*result = new ChromiumLogger(f);
return Status::OK();
}
}
} // namespace leveldb_env
// Copyright (c) 2013 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#ifndef THIRD_PARTY_LEVELDATABASE_ENV_CHROMIUM_STDIO_H_
#define THIRD_PARTY_LEVELDATABASE_ENV_CHROMIUM_STDIO_H_
#include "env_chromium.h"
namespace leveldb_env {
class ChromiumWritableFile : public leveldb::WritableFile {
public:
ChromiumWritableFile(const std::string& fname,
FILE* f,
const UMALogger* uma_logger,
WriteTracker* tracker,
bool make_backup);
virtual ~ChromiumWritableFile();
virtual leveldb::Status Append(const leveldb::Slice& data);
virtual leveldb::Status Close();
virtual leveldb::Status Flush();
virtual leveldb::Status Sync();
private:
enum Type {
kManifest,
kTable,
kOther
};
leveldb::Status SyncParent();
std::string filename_;
FILE* file_;
const UMALogger* uma_logger_;
WriteTracker* tracker_;
Type file_type_;
std::string parent_dir_;
bool make_backup_;
};
class ChromiumEnvStdio : public ChromiumEnv {
public:
ChromiumEnvStdio();
virtual ~ChromiumEnvStdio();
virtual leveldb::Status NewSequentialFile(const std::string& fname,
leveldb::SequentialFile** result);
virtual leveldb::Status NewRandomAccessFile(
const std::string& fname,
leveldb::RandomAccessFile** result);
virtual leveldb::Status NewWritableFile(const std::string& fname,
leveldb::WritableFile** result);
virtual leveldb::Status NewLogger(const std::string& fname,
leveldb::Logger** result);
protected:
virtual base::PlatformFileError GetDirectoryEntries(
const base::FilePath& dir_param,
std::vector<base::FilePath>* result) const;
private:
// BGThread() is the body of the background thread
void BGThread();
static void BGThreadWrapper(void* arg) {
reinterpret_cast<ChromiumEnvStdio*>(arg)->BGThread();
}
void RecordOpenFilesLimit(const std::string& type);
};
} // namespace leveldb_env
#endif
...@@ -7,10 +7,7 @@ ...@@ -7,10 +7,7 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/test/test_suite.h" #include "base/test/test_suite.h"
#include "env_chromium_stdio.h" #include "env_chromium.h"
#if defined(OS_WIN)
#include "env_chromium_win.h"
#endif
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/leveldatabase/env_idb.h" #include "third_party/leveldatabase/env_idb.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h" #include "third_party/leveldatabase/src/include/leveldb/db.h"
...@@ -57,21 +54,6 @@ TEST(ErrorEncoding, Errno) { ...@@ -57,21 +54,6 @@ TEST(ErrorEncoding, Errno) {
EXPECT_EQ(some_errno, error); EXPECT_EQ(some_errno, error);
} }
#if defined(OS_WIN)
TEST(ErrorEncoding, ErrnoWin32) {
const MethodID in_method = kWritableFileFlush;
const DWORD some_errno = ERROR_FILE_NOT_FOUND;
const Status s =
MakeIOErrorWin("Somefile.txt", "message", in_method, some_errno);
MethodID method;
int error;
EXPECT_EQ(METHOD_AND_ERRNO,
ParseMethodAndError(s.ToString().c_str(), &method, &error));
EXPECT_EQ(in_method, method);
EXPECT_EQ(some_errno, error);
}
#endif
TEST(ErrorEncoding, NoEncodedMessage) { TEST(ErrorEncoding, NoEncodedMessage) {
Status s = Status::IOError("Some message", "from leveldb itself"); Status s = Status::IOError("Some message", "from leveldb itself");
MethodID method = kRandomAccessFileRead; MethodID method = kRandomAccessFileRead;
...@@ -81,8 +63,7 @@ TEST(ErrorEncoding, NoEncodedMessage) { ...@@ -81,8 +63,7 @@ TEST(ErrorEncoding, NoEncodedMessage) {
EXPECT_EQ(4, error); EXPECT_EQ(4, error);
} }
template <typename T> class MyEnv : public ChromiumEnv {
class MyEnv : public T {
public: public:
MyEnv() : directory_syncs_(0) {} MyEnv() : directory_syncs_(0) {}
int directory_syncs() { return directory_syncs_; } int directory_syncs() { return directory_syncs_; }
...@@ -97,21 +78,8 @@ class MyEnv : public T { ...@@ -97,21 +78,8 @@ class MyEnv : public T {
int directory_syncs_; int directory_syncs_;
}; };
template <typename T> TEST(ChromiumEnv, DirectorySyncing) {
class ChromiumEnvMultiPlatformTests : public ::testing::Test { MyEnv env;
public:
};
#if defined(OS_WIN)
typedef ::testing::Types<ChromiumEnvStdio, ChromiumEnvWin> ChromiumEnvMultiPlatformTestsTypes;
#else
typedef ::testing::Types<ChromiumEnvStdio> ChromiumEnvMultiPlatformTestsTypes;
#endif
TYPED_TEST_CASE(ChromiumEnvMultiPlatformTests, ChromiumEnvMultiPlatformTestsTypes);
TYPED_TEST(ChromiumEnvMultiPlatformTests, DirectorySyncing) {
MyEnv<TypeParam> env;
base::ScopedTempDir dir; base::ScopedTempDir dir;
dir.CreateUniqueTempDir(); dir.CreateUniqueTempDir();
base::FilePath dir_path = dir.path(); base::FilePath dir_path = dir.path();
......
// Copyright (c) 2013 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include "base/debug/trace_event.h"
#include "base/files/file_path.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/win_util.h"
#include "chromium_logger.h"
#include "env_chromium_stdio.h"
#include "env_chromium_win.h"
using namespace leveldb;
namespace leveldb_env {
namespace {
static std::string GetWindowsErrorMessage(DWORD err) {
LPTSTR errorText(NULL);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) & errorText,
0,
NULL);
if (errorText != NULL) {
std::string message(base::UTF16ToUTF8(errorText));
// FormatMessage adds CR/LF to messages so we remove it.
TrimWhitespace(message, TRIM_TRAILING, &message);
LocalFree(errorText);
return message;
} else {
return std::string();
}
}
class ChromiumSequentialFileWin : public SequentialFile {
private:
std::string filename_;
HANDLE file_;
const UMALogger* uma_logger_;
public:
ChromiumSequentialFileWin(const std::string& fname,
HANDLE f,
const UMALogger* uma_logger)
: filename_(fname), file_(f), uma_logger_(uma_logger) {
DCHECK(file_ != INVALID_HANDLE_VALUE);
}
virtual ~ChromiumSequentialFileWin() {
DCHECK(file_ != INVALID_HANDLE_VALUE);
CloseHandle(file_);
file_ = INVALID_HANDLE_VALUE;
}
virtual Status Read(size_t n, Slice* result, char* scratch) {
Status s;
DWORD bytes_read(0);
DCHECK(file_ != INVALID_HANDLE_VALUE);
if (ReadFile(file_, scratch, n, &bytes_read, NULL)) {
*result = Slice(scratch, bytes_read);
} else {
DWORD err = GetLastError();
s = MakeIOErrorWin(
filename_, GetWindowsErrorMessage(err), kSequentialFileRead, err);
uma_logger_->RecordErrorAt(kSequentialFileRead);
if (bytes_read > 0)
*result = Slice(scratch, bytes_read);
}
return s;
}
virtual Status Skip(uint64_t n) {
LARGE_INTEGER li;
li.QuadPart = n;
if (SetFilePointer(file_, li.LowPart, &li.HighPart, FILE_CURRENT) ==
INVALID_SET_FILE_POINTER) {
DWORD err = GetLastError();
uma_logger_->RecordErrorAt(kSequentialFileSkip);
return MakeIOErrorWin(
filename_, GetWindowsErrorMessage(err), kSequentialFileSkip, err);
}
return Status::OK();
}
};
class ChromiumRandomAccessFileWin : public RandomAccessFile {
private:
std::string filename_;
::base::PlatformFile file_;
const UMALogger* uma_logger_;
public:
ChromiumRandomAccessFileWin(const std::string& fname,
::base::PlatformFile file,
const UMALogger* uma_logger)
: filename_(fname), file_(file), uma_logger_(uma_logger) {}
virtual ~ChromiumRandomAccessFileWin() { ::base::ClosePlatformFile(file_); }
virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch)
const {
Status s;
int r = ::base::ReadPlatformFile(file_, offset, scratch, n);
*result = Slice(scratch, (r < 0) ? 0 : r);
if (r < 0) {
// An error: return a non-ok status
s = MakeIOError(
filename_, "Could not perform read", kRandomAccessFileRead);
uma_logger_->RecordErrorAt(kRandomAccessFileRead);
}
return s;
}
};
} // unnamed namespace
Status MakeIOErrorWin(Slice filename,
const std::string& message,
MethodID method,
DWORD error) {
char buf[512];
if (snprintf(buf,
sizeof(buf),
"%s (ChromeMethodErrno: %d::%s::%u)",
message.c_str(),
method,
MethodIDToString(method),
error) >= 0) {
return Status::IOError(filename, buf);
} else {
return Status::IOError(filename, "<unknown>");
}
}
ChromiumWritableFileWin::ChromiumWritableFileWin(
const std::string& fname,
HANDLE f,
const UMALogger* uma_logger,
WriteTracker* tracker,
bool make_backup)
: filename_(fname),
file_(f),
uma_logger_(uma_logger),
tracker_(tracker),
file_type_(kOther),
make_backup_(make_backup) {
DCHECK(f != INVALID_HANDLE_VALUE);
base::FilePath path = base::FilePath::FromUTF8Unsafe(fname);
if (FilePathToString(path.BaseName()).find("MANIFEST") == 0)
file_type_ = kManifest;
else if (ChromiumEnv::HasTableExtension(path))
file_type_ = kTable;
if (file_type_ != kManifest)
tracker_->DidCreateNewFile(filename_);
parent_dir_ = FilePathToString(ChromiumEnv::CreateFilePath(fname).DirName());
}
ChromiumWritableFileWin::~ChromiumWritableFileWin() {
if (file_ != INVALID_HANDLE_VALUE) {
// Ignoring any potential errors
CloseHandle(file_);
file_ = INVALID_HANDLE_VALUE;
}
}
Status ChromiumWritableFileWin::SyncParent() {
// On Windows no need to sync parent directory. It's metadata will be
// updated via the creation of the new file, without an explicit sync.
return Status();
}
Status ChromiumWritableFileWin::Append(const Slice& data) {
if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) {
Status s = SyncParent();
if (!s.ok())
return s;
tracker_->DidSyncDir(filename_);
}
DWORD written(0);
if (!WriteFile(file_, data.data(), data.size(), &written, NULL)) {
DWORD err = GetLastError();
uma_logger_->RecordOSError(kWritableFileAppend,
base::LastErrorToPlatformFileError(err));
return MakeIOErrorWin(
filename_, GetWindowsErrorMessage(err), kWritableFileAppend, err);
}
return Status::OK();
}
Status ChromiumWritableFileWin::Close() {
Status result;
DCHECK(file_ != INVALID_HANDLE_VALUE);
if (!CloseHandle(file_)) {
DWORD err = GetLastError();
result = MakeIOErrorWin(
filename_, GetWindowsErrorMessage(err), kWritableFileClose, err);
uma_logger_->RecordErrorAt(kWritableFileClose);
}
file_ = INVALID_HANDLE_VALUE;
return result;
}
Status ChromiumWritableFileWin::Flush() {
Status result;
if (!FlushFileBuffers(file_)) {
DWORD err = GetLastError();
result = MakeIOErrorWin(
filename_, GetWindowsErrorMessage(err), kWritableFileFlush, err);
uma_logger_->RecordOSError(kWritableFileFlush,
base::LastErrorToPlatformFileError(err));
}
return result;
}
Status ChromiumWritableFileWin::Sync() {
TRACE_EVENT0("leveldb", "ChromiumEnvWin::Sync");
Status result;
DWORD error = ERROR_SUCCESS;
DCHECK(file_ != INVALID_HANDLE_VALUE);
if (!FlushFileBuffers(file_))
error = GetLastError();
if (error != ERROR_SUCCESS) {
result = MakeIOErrorWin(
filename_, GetWindowsErrorMessage(error), kWritableFileSync, error);
uma_logger_->RecordErrorAt(kWritableFileSync);
} else if (make_backup_ && file_type_ == kTable) {
bool success = ChromiumEnv::MakeBackup(filename_);
uma_logger_->RecordBackupResult(success);
}
return result;
}
ChromiumEnvWin::ChromiumEnvWin() {}
ChromiumEnvWin::~ChromiumEnvWin() {}
Status ChromiumEnvWin::NewSequentialFile(const std::string& fname,
SequentialFile** result) {
HANDLE f = CreateFile(base::UTF8ToUTF16(fname).c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (f == INVALID_HANDLE_VALUE) {
*result = NULL;
DWORD err = GetLastError();
RecordOSError(kNewSequentialFile, err);
return MakeIOErrorWin(
fname, GetWindowsErrorMessage(err), kNewSequentialFile, err);
} else {
*result = new ChromiumSequentialFileWin(fname, f, this);
return Status::OK();
}
}
void ChromiumEnvWin::RecordOpenFilesLimit(const std::string& type) {
// The Windows POSIX implementation (which this class doesn't use)
// has an open file limit, but when using the Win32 API this is limited by
// available memory, so no value to report.
}
Status ChromiumEnvWin::NewRandomAccessFile(const std::string& fname,
RandomAccessFile** result) {
int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN;
bool created;
::base::PlatformFileError error_code;
::base::PlatformFile file = ::base::CreatePlatformFile(
ChromiumEnv::CreateFilePath(fname), flags, &created, &error_code);
if (error_code == ::base::PLATFORM_FILE_OK) {
*result = new ChromiumRandomAccessFileWin(fname, file, this);
RecordOpenFilesLimit("Success");
return Status::OK();
}
if (error_code == ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED)
RecordOpenFilesLimit("TooManyOpened");
else
RecordOpenFilesLimit("OtherError");
*result = NULL;
RecordOSError(kNewRandomAccessFile, error_code);
return MakeIOErrorWin(fname,
PlatformFileErrorString(error_code),
kNewRandomAccessFile,
error_code);
}
Status ChromiumEnvWin::NewWritableFile(const std::string& fname,
WritableFile** result) {
*result = NULL;
HANDLE f = CreateFile(base::UTF8ToUTF16(fname).c_str(),
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (f == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
RecordErrorAt(kNewWritableFile);
return MakeIOErrorWin(
fname, GetWindowsErrorMessage(err), kNewWritableFile, err);
} else {
*result = new ChromiumWritableFileWin(fname, f, this, this, make_backup_);
return Status::OK();
}
}
base::PlatformFileError ChromiumEnvWin::GetDirectoryEntries(
const base::FilePath& dir_param,
std::vector<base::FilePath>* result) const {
result->clear();
base::FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
WIN32_FIND_DATA find_data;
HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
if (find_handle == INVALID_HANDLE_VALUE) {
DWORD last_error = GetLastError();
if (last_error == ERROR_FILE_NOT_FOUND)
return base::PLATFORM_FILE_OK;
return base::LastErrorToPlatformFileError(last_error);
}
do {
base::FilePath filepath(find_data.cFileName);
base::FilePath::StringType basename = filepath.BaseName().value();
if (basename == FILE_PATH_LITERAL(".") ||
basename == FILE_PATH_LITERAL(".."))
continue;
result->push_back(filepath.BaseName());
} while (FindNextFile(find_handle, &find_data));
DWORD last_error = GetLastError();
base::PlatformFileError return_value = base::PLATFORM_FILE_OK;
if (last_error != ERROR_NO_MORE_FILES)
return_value = base::LastErrorToPlatformFileError(last_error);
FindClose(find_handle);
return return_value;
}
Status ChromiumEnvWin::NewLogger(const std::string& fname, Logger** result) {
FILE* f = _wfopen(base::UTF8ToUTF16(fname).c_str(), L"w");
if (f == NULL) {
*result = NULL;
int saved_errno = errno;
RecordOSError(kNewLogger, saved_errno);
return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno);
} else {
*result = new ChromiumLogger(f);
return Status::OK();
}
}
void ChromiumEnvWin::RecordOSError(MethodID method, DWORD error) const {
RecordErrorAt(method);
GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
}
} // namespace leveldb_env
// Copyright (c) 2013 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#ifndef THIRD_PARTY_LEVELDATABASE_ENV_CHROMIUM_WIN_H_
#define THIRD_PARTY_LEVELDATABASE_ENV_CHROMIUM_WIN_H_
#include "env_chromium.h"
namespace leveldb_env {
leveldb::Status MakeIOErrorWin(leveldb::Slice filename,
const std::string& message,
MethodID method,
DWORD err);
class ChromiumWritableFileWin : public leveldb::WritableFile {
public:
ChromiumWritableFileWin(const std::string& fname,
HANDLE f,
const UMALogger* uma_logger,
WriteTracker* tracker,
bool make_backup);
virtual ~ChromiumWritableFileWin();
virtual leveldb::Status Append(const leveldb::Slice& data);
virtual leveldb::Status Close();
virtual leveldb::Status Flush();
virtual leveldb::Status Sync();
private:
enum Type {
kManifest,
kTable,
kOther
};
leveldb::Status SyncParent();
std::string filename_;
HANDLE file_;
const UMALogger* uma_logger_;
WriteTracker* tracker_;
Type file_type_;
std::string parent_dir_;
bool make_backup_;
};
class ChromiumEnvWin : public ChromiumEnv {
public:
ChromiumEnvWin();
virtual ~ChromiumEnvWin();
virtual leveldb::Status NewSequentialFile(const std::string& fname,
leveldb::SequentialFile** result);
virtual leveldb::Status NewRandomAccessFile(
const std::string& fname,
leveldb::RandomAccessFile** result);
virtual leveldb::Status NewWritableFile(const std::string& fname,
leveldb::WritableFile** result);
virtual leveldb::Status NewLogger(const std::string& fname,
leveldb::Logger** result);
protected:
virtual base::PlatformFileError GetDirectoryEntries(
const base::FilePath& dir_param,
std::vector<base::FilePath>* result) const;
private:
// BGThread() is the body of the background thread
void BGThread();
static void BGThreadWrapper(void* arg) {
reinterpret_cast<ChromiumEnvWin*>(arg)->BGThread();
}
void RecordOpenFilesLimit(const std::string& type);
virtual void RecordOSError(MethodID method, DWORD err) const;
};
} // namespace leveldb_env
#endif
...@@ -49,12 +49,6 @@ ...@@ -49,12 +49,6 @@
'../../third_party/snappy/snappy.gyp:snappy', '../../third_party/snappy/snappy.gyp:snappy',
], ],
}], }],
['OS=="win"', {
'sources': [
'env_chromium_win.cc',
'env_chromium_win.h',
],
}],
], ],
'direct_dependent_settings': { 'direct_dependent_settings': {
'include_dirs': [ 'include_dirs': [
...@@ -78,8 +72,6 @@ ...@@ -78,8 +72,6 @@
# they don't build. # they don't build.
'env_chromium.cc', 'env_chromium.cc',
'env_chromium.h', 'env_chromium.h',
'env_chromium_stdio.cc',
'env_chromium_stdio.h',
'env_idb.h', 'env_idb.h',
'port/port_chromium.cc', 'port/port_chromium.cc',
'port/port_chromium.h', 'port/port_chromium.h',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment