| Index: third_party/leveldatabase/env_chromium.cc
|
| diff --git a/third_party/leveldatabase/env_chromium.cc b/third_party/leveldatabase/env_chromium.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..905c0df1588dbd5d27eb75c76488c26eca4096e8
|
| --- /dev/null
|
| +++ b/third_party/leveldatabase/env_chromium.cc
|
| @@ -0,0 +1,543 @@
|
| +// Copyright (c) 2011 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 <deque>
|
| +#include <errno.h>
|
| +#include <stdio.h>
|
| +#include "base/at_exit.h"
|
| +#include "base/file_path.h"
|
| +#include "base/file_util.h"
|
| +#include "base/lazy_instance.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/message_loop.h"
|
| +#include "base/platform_file.h"
|
| +#include "base/process_util.h"
|
| +#include "base/stringprintf.h"
|
| +#include "base/synchronization/lock.h"
|
| +#include "base/sys_info.h"
|
| +#include "base/task.h"
|
| +#include "base/threading/platform_thread.h"
|
| +#include "base/threading/thread.h"
|
| +#include "base/utf_string_conversions.h"
|
| +#include "leveldb/env.h"
|
| +#include "leveldb/slice.h"
|
| +#include "port/port.h"
|
| +#include "util/logging.h"
|
| +
|
| +#if defined(OS_WIN)
|
| +#include <io.h>
|
| +#include "base/win/win_util.h"
|
| +#endif
|
| +
|
| +#if defined(OS_MACOSX) || defined(OS_WIN)
|
| +// The following are glibc-specific
|
| +namespace {
|
| +
|
| +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);
|
| +}
|
| +
|
| +int fdatasync(int fildes) {
|
| +#if defined(OS_WIN)
|
| + return _commit(fildes);
|
| +#else
|
| + return fsync(fildes);
|
| +#endif
|
| +}
|
| +
|
| +}
|
| +#endif
|
| +
|
| +namespace leveldb {
|
| +
|
| +namespace {
|
| +
|
| +class Thread;
|
| +
|
| +static const ::FilePath::CharType kLevelDBTestDirectoryPrefix[]
|
| + = FILE_PATH_LITERAL("leveldb-test-");
|
| +
|
| +::FilePath CreateFilePath(const std::string& file_path) {
|
| +#if defined(OS_WIN)
|
| + return FilePath(UTF8ToUTF16(file_path));
|
| +#else
|
| + return FilePath(file_path);
|
| +#endif
|
| +}
|
| +
|
| +std::string FilePathToString(const ::FilePath& file_path) {
|
| +#if defined(OS_WIN)
|
| + return UTF16ToUTF8(file_path.value());
|
| +#else
|
| + return file_path.value();
|
| +#endif
|
| +}
|
| +
|
| +// TODO(jorlow): This should be moved into Chromium's base.
|
| +const char* PlatformFileErrorString(const ::base::PlatformFileError& error) {
|
| + switch (error) {
|
| + case ::base::PLATFORM_FILE_ERROR_FAILED:
|
| + return "Opening file failed.";
|
| + 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_OK:
|
| + return "OK.";
|
| + }
|
| + NOTIMPLEMENTED();
|
| + return "Unknown error.";
|
| +}
|
| +
|
| +class ChromiumSequentialFile: public SequentialFile {
|
| + private:
|
| + std::string filename_;
|
| + FILE* file_;
|
| +
|
| + public:
|
| + ChromiumSequentialFile(const std::string& fname, FILE* f)
|
| + : filename_(fname), file_(f) { }
|
| + 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 = Status::IOError(filename_, strerror(errno));
|
| + }
|
| + }
|
| + return s;
|
| + }
|
| +
|
| + virtual Status Skip(uint64_t n) {
|
| + if (fseek(file_, n, SEEK_CUR)) {
|
| + return Status::IOError(filename_, strerror(errno));
|
| + }
|
| + return Status::OK();
|
| + }
|
| +};
|
| +
|
| +class ChromiumRandomAccessFile: public RandomAccessFile {
|
| + private:
|
| + std::string filename_;
|
| + ::base::PlatformFile file_;
|
| +
|
| + public:
|
| + ChromiumRandomAccessFile(const std::string& fname, ::base::PlatformFile file)
|
| + : filename_(fname), file_(file) { }
|
| + 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 = Status::IOError(filename_, "Could not preform read");
|
| + }
|
| + return s;
|
| + }
|
| +};
|
| +
|
| +class ChromiumWritableFile : public WritableFile {
|
| + private:
|
| + std::string filename_;
|
| + FILE* file_;
|
| +
|
| + public:
|
| + ChromiumWritableFile(const std::string& fname, FILE* f)
|
| + : filename_(fname), file_(f) { }
|
| +
|
| + ~ChromiumWritableFile() {
|
| + if (file_ != NULL) {
|
| + // Ignoring any potential errors
|
| + fclose(file_);
|
| + }
|
| + }
|
| +
|
| + virtual Status Append(const Slice& data) {
|
| + size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_);
|
| + Status result;
|
| + if (r != data.size()) {
|
| + result = Status::IOError(filename_, strerror(errno));
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + virtual Status Close() {
|
| + Status result;
|
| + if (fclose(file_) != 0) {
|
| + result = Status::IOError(filename_, strerror(errno));
|
| + }
|
| + file_ = NULL;
|
| + return result;
|
| + }
|
| +
|
| + virtual Status Flush() {
|
| + Status result;
|
| + if (fflush_unlocked(file_) != 0) {
|
| + result = Status::IOError(filename_, strerror(errno));
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + virtual Status Sync() {
|
| + Status result;
|
| + if ((fflush_unlocked(file_) != 0) ||
|
| + (fdatasync(fileno(file_)) != 0)) {
|
| + result = Status::IOError(filename_, strerror(errno));
|
| + }
|
| + return result;
|
| + }
|
| +};
|
| +
|
| +class ChromiumFileLock : public FileLock {
|
| + public:
|
| + ::base::PlatformFile file_;
|
| +};
|
| +
|
| +class ChromiumEnv : public Env {
|
| + public:
|
| + ChromiumEnv();
|
| + virtual ~ChromiumEnv() {
|
| + fprintf(stderr, "Destroying Env::Default()\n");
|
| + exit(1);
|
| + }
|
| +
|
| + virtual Status NewSequentialFile(const std::string& fname,
|
| + SequentialFile** result) {
|
| + FILE* f = fopen(fname.c_str(), "rb");
|
| + if (f == NULL) {
|
| + *result = NULL;
|
| + return Status::IOError(fname, strerror(errno));
|
| + } else {
|
| + *result = new ChromiumSequentialFile(fname, f);
|
| + return Status::OK();
|
| + }
|
| + }
|
| +
|
| + virtual Status 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(
|
| + CreateFilePath(fname), flags, &created, &error_code);
|
| + if (error_code != ::base::PLATFORM_FILE_OK) {
|
| + *result = NULL;
|
| + return Status::IOError(fname, PlatformFileErrorString(error_code));
|
| + }
|
| + *result = new ChromiumRandomAccessFile(fname, file);
|
| + return Status::OK();
|
| + }
|
| +
|
| + virtual Status NewWritableFile(const std::string& fname,
|
| + WritableFile** result) {
|
| + *result = NULL;
|
| + FILE* f = fopen(fname.c_str(), "wb");
|
| + if (f == NULL) {
|
| + return Status::IOError(fname, strerror(errno));
|
| + } else {
|
| + *result = new ChromiumWritableFile(fname, f);
|
| + return Status::OK();
|
| + }
|
| + }
|
| +
|
| + virtual bool FileExists(const std::string& fname) {
|
| + return ::file_util::PathExists(CreateFilePath(fname));
|
| + }
|
| +
|
| + virtual Status GetChildren(const std::string& dir,
|
| + std::vector<std::string>* result) {
|
| + result->clear();
|
| + ::file_util::FileEnumerator iter(
|
| + CreateFilePath(dir), false, ::file_util::FileEnumerator::FILES);
|
| + ::FilePath current = iter.Next();
|
| + while (!current.empty()) {
|
| + result->push_back(FilePathToString(current.BaseName()));
|
| + current = iter.Next();
|
| + }
|
| + // TODO(jorlow): Unfortunately, the FileEnumerator swallows errors, so
|
| + // we'll always return OK. Maybe manually check for error
|
| + // conditions like the file not existing?
|
| + return Status::OK();
|
| + }
|
| +
|
| + virtual Status DeleteFile(const std::string& fname) {
|
| + Status result;
|
| + // TODO(jorlow): Should we assert this is a file?
|
| + if (!::file_util::Delete(CreateFilePath(fname), false)) {
|
| + result = Status::IOError(fname, "Could not delete file.");
|
| + }
|
| + return result;
|
| + };
|
| +
|
| + virtual Status CreateDir(const std::string& name) {
|
| + Status result;
|
| + if (!::file_util::CreateDirectory(CreateFilePath(name))) {
|
| + result = Status::IOError(name, "Could not create directory.");
|
| + }
|
| + return result;
|
| + };
|
| +
|
| + virtual Status DeleteDir(const std::string& name) {
|
| + Status result;
|
| + // TODO(jorlow): Should we assert this is a directory?
|
| + if (!::file_util::Delete(CreateFilePath(name), false)) {
|
| + result = Status::IOError(name, "Could not delete directory.");
|
| + }
|
| + return result;
|
| + };
|
| +
|
| + virtual Status GetFileSize(const std::string& fname, uint64_t* size) {
|
| + Status s;
|
| + int64_t signed_size;
|
| + if (!::file_util::GetFileSize(CreateFilePath(fname), &signed_size)) {
|
| + *size = 0;
|
| + s = Status::IOError(fname, "Could not determine file size.");
|
| + } else {
|
| + *size = static_cast<uint64_t>(signed_size);
|
| + }
|
| + return s;
|
| + }
|
| +
|
| + virtual Status RenameFile(const std::string& src, const std::string& dst) {
|
| + Status result;
|
| + if (!::file_util::ReplaceFile(CreateFilePath(src), CreateFilePath(dst))) {
|
| + result = Status::IOError(src, "Could not rename file.");
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + virtual Status LockFile(const std::string& fname, FileLock** lock) {
|
| + *lock = NULL;
|
| + Status result;
|
| + int flags = ::base::PLATFORM_FILE_OPEN_ALWAYS |
|
| + ::base::PLATFORM_FILE_READ |
|
| + ::base::PLATFORM_FILE_WRITE |
|
| + ::base::PLATFORM_FILE_EXCLUSIVE_READ |
|
| + ::base::PLATFORM_FILE_EXCLUSIVE_WRITE;
|
| + bool created;
|
| + ::base::PlatformFileError error_code;
|
| + ::base::PlatformFile file = ::base::CreatePlatformFile(
|
| + CreateFilePath(fname), flags, &created, &error_code);
|
| + if (error_code != ::base::PLATFORM_FILE_OK) {
|
| + result = Status::IOError(fname, PlatformFileErrorString(error_code));
|
| + } else {
|
| + ChromiumFileLock* my_lock = new ChromiumFileLock;
|
| + my_lock->file_ = file;
|
| + *lock = my_lock;
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + virtual Status UnlockFile(FileLock* lock) {
|
| + ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
|
| + Status result;
|
| + if (!::base::ClosePlatformFile(my_lock->file_)) {
|
| + result = Status::IOError("Could not close lock file.");
|
| + }
|
| + delete my_lock;
|
| + return result;
|
| + }
|
| +
|
| + virtual void Schedule(void (*function)(void*), void* arg);
|
| +
|
| + virtual void StartThread(void (*function)(void* arg), void* arg);
|
| +
|
| + virtual std::string UserIdentifier() {
|
| +#if defined(OS_WIN)
|
| + std::wstring user_sid;
|
| + bool ret = ::base::win::GetUserSidString(&user_sid);
|
| + DCHECK(ret);
|
| + return UTF16ToUTF8(user_sid);
|
| +#else
|
| + char buf[100];
|
| + snprintf(buf, sizeof(buf), "%d", int(geteuid()));
|
| + return buf;
|
| +#endif
|
| + }
|
| +
|
| + virtual Status GetTestDirectory(std::string* path) {
|
| + mu_.Acquire();
|
| + if (test_directory_.empty()) {
|
| + if (!::file_util::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
|
| + &test_directory_)) {
|
| + mu_.Release();
|
| + return Status::IOError("Could not create temp directory.");
|
| + }
|
| + }
|
| + *path = FilePathToString(test_directory_);
|
| + mu_.Release();
|
| + return Status::OK();
|
| + }
|
| +
|
| + class ChromiumLogger : public Logger {
|
| + public:
|
| + ChromiumLogger(const std::string& filename) : filename_(filename) {
|
| + }
|
| +
|
| + virtual void Logv(const char* format, va_list ap) {
|
| + VLOG(5) << "LevelDB: " << filename_ << " " << StringPrintf(format, ap);
|
| + }
|
| +
|
| + private:
|
| + std::string filename_;
|
| + };
|
| +
|
| + virtual Status NewLogger(const std::string& fname, Logger** result) {
|
| + *result = new ChromiumLogger(fname);
|
| + return Status::OK();
|
| + }
|
| +
|
| + virtual uint64_t NowMicros() {
|
| + return ::base::TimeTicks::HighResNow().ToInternalValue();
|
| + }
|
| +
|
| + virtual void SleepForMicroseconds(int micros) {
|
| + // Round up to the next millisecond.
|
| + ::base::PlatformThread::Sleep((micros + 999) / 1000);
|
| + }
|
| +
|
| + private:
|
| + // BGThread() is the body of the background thread
|
| + void BGThread();
|
| + static void BGThreadWrapper(void* arg) {
|
| + reinterpret_cast<ChromiumEnv*>(arg)->BGThread();
|
| + }
|
| +
|
| + FilePath test_directory_;
|
| +
|
| + size_t page_size_;
|
| + ::base::Lock mu_;
|
| + ::base::ConditionVariable bgsignal_;
|
| + bool started_bgthread_;
|
| +
|
| + // Entry per Schedule() call
|
| + struct BGItem { void* arg; void (*function)(void*); };
|
| + typedef std::deque<BGItem> BGQueue;
|
| + BGQueue queue_;
|
| +};
|
| +
|
| +ChromiumEnv::ChromiumEnv()
|
| + : page_size_(::base::SysInfo::VMAllocationGranularity()),
|
| + bgsignal_(&mu_),
|
| + started_bgthread_(false) {
|
| +#if defined(OS_MACOSX)
|
| + ::base::EnableTerminationOnHeapCorruption();
|
| + ::base::EnableTerminationOnOutOfMemory();
|
| +#endif // OS_MACOSX
|
| +}
|
| +
|
| +class Thread : public ::base::PlatformThread::Delegate {
|
| + public:
|
| + Thread(void (*function)(void* arg), void* arg)
|
| + : function_(function), arg_(arg) {
|
| + ::base::PlatformThreadHandle handle;
|
| + bool success = ::base::PlatformThread::Create(0, this, &handle);
|
| + DCHECK(success);
|
| + }
|
| + virtual ~Thread() {}
|
| + virtual void ThreadMain() {
|
| + (*function_)(arg_);
|
| + delete this;
|
| + }
|
| +
|
| + private:
|
| + void (*function_)(void* arg);
|
| + void* arg_;
|
| +};
|
| +
|
| +void ChromiumEnv::Schedule(void (*function)(void*), void* arg) {
|
| + mu_.Acquire();
|
| +
|
| + // Start background thread if necessary
|
| + if (!started_bgthread_) {
|
| + started_bgthread_ = true;
|
| + StartThread(&ChromiumEnv::BGThreadWrapper, this);
|
| + }
|
| +
|
| + // If the queue is currently empty, the background thread may currently be
|
| + // waiting.
|
| + if (queue_.empty()) {
|
| + bgsignal_.Signal();
|
| + }
|
| +
|
| + // Add to priority queue
|
| + queue_.push_back(BGItem());
|
| + queue_.back().function = function;
|
| + queue_.back().arg = arg;
|
| +
|
| + mu_.Release();
|
| +}
|
| +
|
| +void ChromiumEnv::BGThread() {
|
| + while (true) {
|
| + // Wait until there is an item that is ready to run
|
| + mu_.Acquire();
|
| + while (queue_.empty()) {
|
| + bgsignal_.Wait();
|
| + }
|
| +
|
| + void (*function)(void*) = queue_.front().function;
|
| + void* arg = queue_.front().arg;
|
| + queue_.pop_front();
|
| +
|
| + mu_.Release();
|
| + (*function)(arg);
|
| + }
|
| +}
|
| +
|
| +void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
|
| + new Thread(function, arg); // Will self-delete.
|
| +}
|
| +
|
| +::base::LazyInstance<ChromiumEnv, ::base::LeakyLazyInstanceTraits<ChromiumEnv> >
|
| + default_env(::base::LINKER_INITIALIZED);
|
| +
|
| +}
|
| +
|
| +Env* Env::Default() {
|
| + return default_env.Pointer();
|
| +}
|
| +
|
| +}
|
|
|