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(); |
+} |
+ |
+} |