Merge upstream changes into Ledger's version of leveldb.
Updates our version of leveldb and makes necessary changes to BUILD.gn
to make it build.
LE-523 That starts the transition, but doesn't include changes to our
implementation of env.h
Change-Id: I27b2163aa96e7eef16ca90a64ca3f89942e1428f
diff --git a/BUILD.gn b/BUILD.gn
new file mode 100644
index 0000000..ca06958
--- /dev/null
+++ b/BUILD.gn
@@ -0,0 +1,282 @@
+# Copyright 2016 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/package.gni")
+
+package("leveldb_tests") {
+ testonly = true
+ deprecated_system_image = true
+
+ deps = [
+ ":leveldb_tests_bin",
+ ]
+
+ binaries = [ {
+ name = "leveldb_tests"
+ } ]
+}
+
+defines = [
+ "LEVELDB_PLATFORM_POSIX=1",
+ "LEVELDB_IS_BIG_ENDIAN=0",
+ "HAVE_SNAPPY=1",
+ "HAVE_CRC32C=0",
+]
+
+config("leveldb_config") {
+ include_dirs = [
+ ".",
+ "include",
+ ]
+}
+
+config("leveldb_warnings_config") {
+ cflags = [
+ "-Wall",
+ "-Werror",
+ "-Wno-sign-compare",
+ "-Wno-unused-variable",
+ ]
+}
+
+static_library("leveldb") {
+ sources = [
+ "db/builder.cc",
+ "db/builder.h",
+ "db/db_impl.cc",
+ "db/db_impl.h",
+ "db/db_iter.cc",
+ "db/db_iter.h",
+ "db/dbformat.cc",
+ "db/dbformat.h",
+ "db/dumpfile.cc",
+ "db/filename.cc",
+ "db/filename.h",
+ "db/log_format.h",
+ "db/log_reader.cc",
+ "db/log_reader.h",
+ "db/log_writer.cc",
+ "db/log_writer.h",
+ "db/memtable.cc",
+ "db/memtable.h",
+ "db/repair.cc",
+ "db/skiplist.h",
+ "db/snapshot.h",
+ "db/table_cache.cc",
+ "db/table_cache.h",
+ "db/version_edit.cc",
+ "db/version_edit.h",
+ "db/version_set.cc",
+ "db/version_set.h",
+ "db/write_batch.cc",
+ "db/write_batch_internal.h",
+ "helpers/memenv/memenv.cc",
+ "helpers/memenv/memenv.h",
+ "include/leveldb/cache.h",
+ "include/leveldb/comparator.h",
+ "include/leveldb/db.h",
+ "include/leveldb/dumpfile.h",
+ "include/leveldb/env.h",
+ "include/leveldb/export.h",
+ "include/leveldb/filter_policy.h",
+ "include/leveldb/iterator.h",
+ "include/leveldb/options.h",
+ "include/leveldb/slice.h",
+ "include/leveldb/status.h",
+ "include/leveldb/table.h",
+ "include/leveldb/table_builder.h",
+ "include/leveldb/write_batch.h",
+ "port/port.h",
+ "port/port_stdxx.h",
+ "table/block.cc",
+ "table/block.h",
+ "table/block_builder.cc",
+ "table/block_builder.h",
+ "table/filter_block.cc",
+ "table/filter_block.h",
+ "table/format.cc",
+ "table/format.h",
+ "table/iterator.cc",
+ "table/iterator_wrapper.h",
+ "table/merger.cc",
+ "table/merger.h",
+ "table/table.cc",
+ "table/table_builder.cc",
+ "table/two_level_iterator.cc",
+ "table/two_level_iterator.h",
+ "util/arena.cc",
+ "util/arena.h",
+ "util/bloom.cc",
+ "util/cache.cc",
+ "util/coding.cc",
+ "util/coding.h",
+ "util/comparator.cc",
+ "util/crc32c.cc",
+ "util/crc32c.h",
+ "util/env.cc",
+ "util/env_fuchsia.cc",
+ "util/env_fuchsia.h",
+ "util/filter_policy.cc",
+ "util/hash.cc",
+ "util/hash.h",
+ "util/logging.cc",
+ "util/logging.h",
+ "util/mutexlock.h",
+ "util/options.cc",
+ "util/random.h",
+ "util/status.cc",
+ ]
+
+ public_deps = [
+ "//third_party/re2",
+ "//third_party/snappy",
+ ]
+
+ public_configs = [ ":leveldb_config" ]
+
+ configs -= [ "//build/config:default_warnings" ]
+ configs += [ ":leveldb_warnings_config" ]
+}
+
+source_set("leveldb_testutil") {
+ sources = [
+ "util/histogram.cc",
+ "util/histogram.h",
+ "util/testharness.cc",
+ "util/testharness.h",
+ "util/testutil.cc",
+ "util/testutil.h",
+ ]
+
+ public_deps = [
+ ":leveldb",
+ ]
+
+ configs -= [ "//build/config:default_warnings" ]
+ configs += [ ":leveldb_warnings_config" ]
+}
+
+# These leveldb *_test.cc files all define their own identical main function which
+# just calls the same RunAllTests helper. Having this many different binaries is
+# really difficult to deal with in our setup, so we glob them into one binary
+# called leveldb_tests. To do this we need to give each file a different name
+# for its main symbol which we'll ignore. We do this with the magic and horror
+# of a GN template to generate distinct defines for each file.
+
+template("leveldb_test_source_wrapper") {
+ assert(defined(invoker.test), "Must define test")
+
+ source_set(target_name) {
+ testonly = true
+
+ sources = [
+ invoker.test,
+ ]
+
+ deps = [
+ ":leveldb_testutil",
+ "//third_party/googletest:gtest",
+ ]
+
+ cflags = [ "-Dmain=fake_main_${target_name}" ]
+
+ configs -= [ "//build/config:default_warnings" ]
+ configs += [ ":leveldb_warnings_config" ]
+ }
+}
+
+leveldb_test_source_wrapper("corruption_test") {
+ test = "db/corruption_test.cc"
+}
+leveldb_test_source_wrapper("dbformat_test") {
+ test = "db/dbformat_test.cc"
+}
+leveldb_test_source_wrapper("filename_test") {
+ test = "db/filename_test.cc"
+}
+leveldb_test_source_wrapper("log_test") {
+ test = "db/log_test.cc"
+}
+leveldb_test_source_wrapper("skiplist_test") {
+ test = "db/skiplist_test.cc"
+}
+leveldb_test_source_wrapper("version_edit_test") {
+ test = "db/version_edit_test.cc"
+}
+leveldb_test_source_wrapper("write_batch_test") {
+ test = "db/write_batch_test.cc"
+}
+leveldb_test_source_wrapper("filter_block_test") {
+ test = "table/filter_block_test.cc"
+}
+leveldb_test_source_wrapper("table_test") {
+ test = "table/table_test.cc"
+}
+leveldb_test_source_wrapper("arena_test") {
+ test = "util/arena_test.cc"
+}
+leveldb_test_source_wrapper("bloom_test") {
+ test = "util/bloom_test.cc"
+}
+leveldb_test_source_wrapper("cache_test") {
+ test = "util/cache_test.cc"
+}
+leveldb_test_source_wrapper("crc32c_test") {
+ test = "util/crc32c_test.cc"
+}
+
+
+group("leveldb_test_sources") {
+ testonly = true
+
+ deps = [
+ ":arena_test",
+ ":bloom_test",
+ ":cache_test",
+ ":corruption_test",
+ ":crc32c_test",
+ ":dbformat_test",
+ ":filename_test",
+ ":filter_block_test",
+ ":log_test",
+ ":skiplist_test",
+ ":table_test",
+ ":version_edit_test",
+ ":write_batch_test",
+ ]
+}
+
+executable("leveldb_tests_bin") {
+ output_name = "leveldb_tests"
+ testonly = true
+
+ sources = [
+ # Use the actual main from env_test.cc (arbitrarily chosen).
+ # The rest of the test sources come from :leveldb_test_sources
+ "util/env_test.cc",
+ ]
+
+ deps = [
+ ":leveldb_test_sources",
+ ":leveldb_testutil",
+ "//third_party/googletest:gtest",
+ ]
+
+ configs -= [ "//build/config:default_warnings" ]
+ configs += [ ":leveldb_warnings_config" ]
+}
+
+executable("leveldb_db_bench") {
+ testonly = true
+
+ sources = [
+ "db/db_bench.cc",
+ ]
+
+ deps = [
+ ":leveldb_testutil",
+ ]
+}
+
+# TODO: Figure out "db/db_test.cc",
diff --git a/README.fuchsia b/README.fuchsia
new file mode 100644
index 0000000..a46c5bb
--- /dev/null
+++ b/README.fuchsia
@@ -0,0 +1,9 @@
+Name: LevelDB
+License: BSD
+License File: LICENSE
+Upstream Git: https://github.com/google/leveldb
+Description:
+
+LevelDB is a fast key-value storage library written at Google that
+provides an ordered mapping from string keys to string values.
+
diff --git a/db/write_batch_test.cc b/db/write_batch_test.cc
index 8d38023..a9cefff 100644
--- a/db/write_batch_test.cc
+++ b/db/write_batch_test.cc
@@ -54,7 +54,7 @@
class WriteBatchTest { };
-TEST(WriteBatchTest, Empty) {
+TEST(WriteBatchTest, WriteBatchEmpty) {
WriteBatch batch;
ASSERT_EQ("", PrintContents(&batch));
ASSERT_EQ(0, WriteBatchInternal::Count(&batch));
diff --git a/table/table_test.cc b/table/table_test.cc
index e47db3d..e8be2cc 100644
--- a/table/table_test.cc
+++ b/table/table_test.cc
@@ -645,7 +645,7 @@
};
// Test empty table/block.
-TEST(Harness, Empty) {
+TEST(Harness, HarnessEmpty) {
for (int i = 0; i < kNumTestArgs; i++) {
Init(kTestArgList[i]);
Random rnd(test::RandomSeed() + 1);
diff --git a/util/arena_test.cc b/util/arena_test.cc
index 58e870e..7842fd9 100644
--- a/util/arena_test.cc
+++ b/util/arena_test.cc
@@ -11,11 +11,11 @@
class ArenaTest { };
-TEST(ArenaTest, Empty) {
+TEST(ArenaTest, ArenaTestEmpty) {
Arena arena;
}
-TEST(ArenaTest, Simple) {
+TEST(ArenaTest, ArenaTestSimple) {
std::vector<std::pair<size_t, char*> > allocated;
Arena arena;
const int N = 100000;
diff --git a/util/env_fuchsia.cc b/util/env_fuchsia.cc
new file mode 100644
index 0000000..138e720
--- /dev/null
+++ b/util/env_fuchsia.cc
@@ -0,0 +1,642 @@
+// 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.
+
+// Copyright 2016 The Fuchsia Authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <deque>
+#include <set>
+#include "leveldb/env.h"
+#include "leveldb/slice.h"
+#include "port/port.h"
+#include "util/logging.h"
+#include "util/mutexlock.h"
+
+namespace leveldb {
+
+namespace {
+
+// Equivalent modes for open to fopen "r", "w" and "a" modes respectively.
+constexpr int kReadMode = O_RDONLY;
+constexpr int kWriteMode = O_WRONLY | O_CREAT | O_TRUNC;
+constexpr int kAppendMode = O_WRONLY | O_CREAT | O_APPEND;
+
+DIR* OpenDirAt(int root_fd, const char* path) {
+ int fd = openat(root_fd, path, kReadMode);
+ if (fd < 0) {
+ return nullptr;
+ }
+ return fdopendir(fd);
+}
+
+FILE* FOpenAt(int root_fd, const char* path, int mode) {
+ const char* fopen_mode = nullptr;
+ switch (mode) {
+ case kReadMode:
+ fopen_mode = "r";
+ break;
+ case kWriteMode:
+ fopen_mode = "w";
+ break;
+ case kAppendMode:
+ fopen_mode = "a";
+ break;
+ default:
+ abort();
+ }
+ int fd = openat(root_fd, path, mode);
+ if (fd < 0) {
+ return nullptr;
+ }
+ return fdopen(fd, fopen_mode);
+}
+
+class FuchsiaLogger : public Logger {
+ private:
+ FILE* file_;
+ uint64_t (*gettid_)(); // Return the thread id for the current thread
+ public:
+ FuchsiaLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { }
+ virtual ~FuchsiaLogger() {
+ fclose(file_);
+ }
+ virtual void Logv(const char* format, va_list ap) {
+ const uint64_t thread_id = (*gettid_)();
+
+ // We try twice: the first time with a fixed-size stack allocated buffer,
+ // and the second time with a much larger dynamically allocated buffer.
+ char buffer[500];
+ for (int iter = 0; iter < 2; iter++) {
+ char* base;
+ int bufsize;
+ if (iter == 0) {
+ bufsize = sizeof(buffer);
+ base = buffer;
+ } else {
+ bufsize = 30000;
+ base = new char[bufsize];
+ }
+ char* p = base;
+ char* limit = base + bufsize;
+
+ struct timeval now_tv;
+ gettimeofday(&now_tv, NULL);
+ p += snprintf(p, limit - p,
+ "%06d %llx ",
+ static_cast<int>(now_tv.tv_usec),
+ static_cast<long long unsigned int>(thread_id));
+
+ // Print the message
+ if (p < limit) {
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ p += vsnprintf(p, limit - p, format, backup_ap);
+ va_end(backup_ap);
+ }
+
+ // Truncate to available space if necessary
+ if (p >= limit) {
+ if (iter == 0) {
+ continue; // Try again with larger buffer
+ } else {
+ p = limit - 1;
+ }
+ }
+
+ // Add newline if necessary
+ if (p == base || p[-1] != '\n') {
+ *p++ = '\n';
+ }
+
+ assert(p <= limit);
+ fwrite(base, 1, p - base, file_);
+ fflush(file_);
+ if (base != buffer) {
+ delete[] base;
+ }
+ break;
+ }
+ }
+};
+
+static Status IOError(const std::string& context, int err_number) {
+ return Status::IOError(context, strerror(err_number));
+}
+
+class FuchsiaSequentialFile: public SequentialFile {
+ private:
+ std::string filename_;
+ FILE* file_;
+
+ public:
+ FuchsiaSequentialFile(const std::string& fname, FILE* f)
+ : filename_(fname), file_(f) { }
+ virtual ~FuchsiaSequentialFile() { 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 = IOError(filename_, errno);
+ }
+ }
+ return s;
+ }
+
+ virtual Status Skip(uint64_t n) {
+ if (fseek(file_, n, SEEK_CUR)) {
+ return IOError(filename_, errno);
+ }
+ return Status::OK();
+ }
+};
+
+// This is typically less efficient that mmap-ing the file, but file-backed mmap
+// is not currently available on Fuchsia.
+// TODO(ppi): when mmaping files is possible of Fuchsia, bring in mmap-based
+// impl.
+class FuchsiaRandomAccessFile: public RandomAccessFile {
+ private:
+ std::string filename_;
+ int fd_;
+
+ public:
+ FuchsiaRandomAccessFile(const std::string& fname, int fd)
+ : filename_(fname), fd_(fd) { }
+ virtual ~FuchsiaRandomAccessFile() { close(fd_); }
+
+ virtual Status Read(uint64_t offset, size_t n, Slice* result,
+ char* scratch) const {
+ Status s;
+ ssize_t r = pread(fd_, scratch, n, static_cast<off_t>(offset));
+ *result = Slice(scratch, (r < 0) ? 0 : r);
+ if (r < 0) {
+ // An error: return a non-ok status
+ s = IOError(filename_, errno);
+ }
+ return s;
+ }
+};
+
+class FuchsiaWritableFile : public WritableFile {
+ private:
+ int root_fd_;
+ std::string filename_;
+ FILE* file_;
+
+ public:
+ FuchsiaWritableFile(int root_fd, const std::string& fname, FILE* f)
+ : root_fd_(root_fd), filename_(fname), file_(f) { }
+
+ ~FuchsiaWritableFile() {
+ 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_);
+ if (r != data.size()) {
+ return IOError(filename_, errno);
+ }
+ return Status::OK();
+ }
+
+ virtual Status Close() {
+ Status result;
+ if (fclose(file_) != 0) {
+ result = IOError(filename_, errno);
+ }
+ file_ = NULL;
+ return result;
+ }
+
+ virtual Status Flush() {
+ if (fflush_unlocked(file_) != 0) {
+ return IOError(filename_, errno);
+ }
+ return Status::OK();
+ }
+
+ Status SyncDirIfManifest() {
+ const char* f = filename_.c_str();
+ const char* sep = strrchr(f, '/');
+ Slice basename;
+ std::string dir;
+ if (sep == NULL) {
+ dir = ".";
+ basename = f;
+ } else {
+ dir = std::string(f, sep - f);
+ basename = sep + 1;
+ }
+ Status s;
+ if (basename.starts_with("MANIFEST")) {
+ int fd = openat(root_fd_, dir.c_str(), kReadMode);
+ if (fd < 0) {
+ s = IOError(dir, errno);
+ } else {
+ if (fsync(fd) < 0) {
+ s = IOError(dir, errno);
+ }
+ close(fd);
+ }
+ }
+ return s;
+ }
+
+ virtual Status Sync() {
+ // Ensure new files referred to by the manifest are in the filesystem.
+ Status s = SyncDirIfManifest();
+ if (!s.ok()) {
+ return s;
+ }
+ if (fflush_unlocked(file_) != 0 ||
+ fdatasync(fileno(file_)) != 0) {
+ s = Status::IOError(filename_, strerror(errno));
+ }
+ return s;
+ }
+};
+
+class FuchsiaFileLock : public FileLock {
+ public:
+ std::string name_;
+};
+
+// Set of locked files. This is used to guard against multiple uses from one
+// process.
+class LockTable {
+ private:
+ port::Mutex mu_;
+ std::set<std::string> locked_files_;
+ public:
+ bool Insert(const std::string& fname) {
+ MutexLock l(&mu_);
+ return locked_files_.insert(fname).second;
+ }
+ void Remove(const std::string& fname) {
+ MutexLock l(&mu_);
+ locked_files_.erase(fname);
+ }
+};
+
+class FuchsiaEnv : public Env {
+ public:
+ FuchsiaEnv(int root_fd);
+
+ virtual ~FuchsiaEnv() {
+ if (this == Env::Default()) {
+ char msg[] = "Destroying Env::Default()\n";
+ fwrite(msg, 1, sizeof(msg), stderr);
+ abort();
+ }
+ }
+
+ virtual Status NewSequentialFile(const std::string& fname,
+ SequentialFile** result) {
+ FILE* f = FOpenAt(root_fd_, fname.c_str(), kReadMode);
+ if (f == NULL) {
+ *result = NULL;
+ return IOError(fname, errno);
+ } else {
+ *result = new FuchsiaSequentialFile(fname, f);
+ return Status::OK();
+ }
+ }
+
+ virtual Status NewRandomAccessFile(const std::string& fname,
+ RandomAccessFile** result) {
+ *result = NULL;
+ Status s;
+ int fd = openat(root_fd_, fname.c_str(), kReadMode);
+ if (fd < 0) {
+ s = IOError(fname, errno);
+ } else {
+ *result = new FuchsiaRandomAccessFile(fname, fd);
+ }
+ return s;
+ }
+
+ virtual Status NewWritableFile(const std::string& fname,
+ WritableFile** result) {
+ Status s;
+ FILE* f = FOpenAt(root_fd_, fname.c_str(), kWriteMode);
+ if (f == NULL) {
+ *result = NULL;
+ s = IOError(fname, errno);
+ } else {
+ *result = new FuchsiaWritableFile(root_fd_, fname, f);
+ }
+ return s;
+ }
+
+ virtual Status NewAppendableFile(const std::string& fname,
+ WritableFile** result) {
+ Status s;
+ FILE* f = FOpenAt(root_fd_, fname.c_str(), kAppendMode);
+ if (f == NULL) {
+ *result = NULL;
+ s = IOError(fname, errno);
+ } else {
+ *result = new FuchsiaWritableFile(root_fd_, fname, f);
+ }
+ return s;
+ }
+
+ virtual bool FileExists(const std::string& fname) {
+ return faccessat(root_fd_, fname.c_str(), F_OK, 0) == 0;
+ }
+
+ virtual Status GetChildren(const std::string& dir,
+ std::vector<std::string>* result) {
+ result->clear();
+ DIR* d = OpenDirAt(root_fd_, dir.c_str());
+ if (d == NULL) {
+ return IOError(dir, errno);
+ }
+ struct dirent* entry;
+ while ((entry = readdir(d)) != NULL) {
+ result->push_back(entry->d_name);
+ }
+ closedir(d);
+ return Status::OK();
+ }
+
+ virtual Status DeleteFile(const std::string& fname) {
+ Status result;
+ if (unlinkat(root_fd_, fname.c_str(), 0) != 0) {
+ result = IOError(fname, errno);
+ }
+ return result;
+ }
+
+ virtual Status CreateDir(const std::string& name) {
+ Status result;
+ if (mkdirat(root_fd_, name.c_str(), 0755) != 0) {
+ result = IOError(name, errno);
+ }
+ return result;
+ }
+
+ virtual Status DeleteDir(const std::string& name) {
+ Status result;
+ if (unlinkat(root_fd_, name.c_str(), AT_REMOVEDIR) != 0) {
+ result = IOError(name, errno);
+ }
+ return result;
+ }
+
+ virtual Status GetFileSize(const std::string& fname, uint64_t* size) {
+ Status s;
+ struct stat sbuf;
+ if (fstatat(root_fd_, fname.c_str(), &sbuf, 0) != 0) {
+ *size = 0;
+ s = IOError(fname, errno);
+ } else {
+ *size = sbuf.st_size;
+ }
+ return s;
+ }
+
+ virtual Status RenameFile(const std::string& src, const std::string& target) {
+ Status result;
+ if (renameat(root_fd_, src.c_str(), root_fd_, target.c_str()) != 0) {
+ result = IOError(src, errno);
+ }
+ return result;
+ }
+
+ // Our implementation uses the |locks_| table to guard against creating
+ // multiple databases backed by the same file within one process. This does
+ // not guard against multiple processes using the same file concurrently.
+ virtual Status LockFile(const std::string& fname, FileLock** lock) {
+ *lock = NULL;
+ Status result;
+ if (!locks_.Insert(fname)) {
+ result = Status::IOError("lock " + fname, "already held by process");
+ } else {
+ FuchsiaFileLock* my_lock = new FuchsiaFileLock;
+ my_lock->name_ = fname;
+ *lock = my_lock;
+ }
+ return result;
+ }
+
+ virtual Status UnlockFile(FileLock* lock) {
+ FuchsiaFileLock* my_lock = reinterpret_cast<FuchsiaFileLock*>(lock);
+ Status result;
+ locks_.Remove(my_lock->name_);
+ delete my_lock;
+ return result;
+ }
+
+ virtual void Schedule(void (*function)(void*), void* arg);
+
+ virtual void StartThread(void (*function)(void* arg), void* arg);
+
+ virtual Status GetTestDirectory(std::string* result) {
+ const char* env = getenv("TEST_TMPDIR");
+ if (env && env[0] != '\0') {
+ *result = env;
+ } else {
+ char buf[100];
+ snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid()));
+ *result = buf;
+ }
+ // Directory may already exist
+ CreateDir(*result);
+ return Status::OK();
+ }
+
+ static uint64_t gettid() {
+ pthread_t tid = pthread_self();
+ uint64_t thread_id = 0;
+ memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid)));
+ return thread_id;
+ }
+
+ virtual Status NewLogger(const std::string& fname, Logger** result) {
+ FILE* f = FOpenAt(root_fd_, fname.c_str(), kWriteMode);
+ if (f == NULL) {
+ *result = NULL;
+ return IOError(fname, errno);
+ } else {
+ *result = new FuchsiaLogger(f, &FuchsiaEnv::gettid);
+ return Status::OK();
+ }
+ }
+
+ virtual uint64_t NowMicros() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
+ }
+
+ virtual void SleepForMicroseconds(int micros) {
+ usleep(micros);
+ }
+
+ private:
+ void PthreadCall(const char* label, int result) {
+ if (result != 0) {
+ fprintf(stderr, "pthread %s: %s\n", label, strerror(result));
+ abort();
+ }
+ }
+
+ // BGThread() is the body of the background thread
+ void BGThread();
+ static void* BGThreadWrapper(void* arg) {
+ reinterpret_cast<FuchsiaEnv*>(arg)->BGThread();
+ return NULL;
+ }
+
+ int root_fd_;
+
+ pthread_mutex_t mu_;
+ pthread_cond_t bgsignal_;
+ pthread_t bgthread_;
+ bool started_bgthread_;
+
+ // Entry per Schedule() call
+ struct BGItem { void* arg; void (*function)(void*); };
+ typedef std::deque<BGItem> BGQueue;
+ BGQueue queue_;
+
+ LockTable locks_;
+};
+
+FuchsiaEnv::FuchsiaEnv(int root_fd) : root_fd_(root_fd), started_bgthread_(false) {
+ if (root_fd_ != AT_FDCWD && root_fd_ < 0) {
+ abort();
+ }
+ PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL));
+ PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL));
+}
+
+void FuchsiaEnv::Schedule(void (*function)(void*), void* arg) {
+ PthreadCall("lock", pthread_mutex_lock(&mu_));
+
+ // Start background thread if necessary
+ if (!started_bgthread_) {
+ started_bgthread_ = true;
+ PthreadCall(
+ "create thread",
+ pthread_create(&bgthread_, NULL, &FuchsiaEnv::BGThreadWrapper, this));
+ }
+
+ // If the queue is currently empty, the background thread may currently be
+ // waiting.
+ if (queue_.empty()) {
+ PthreadCall("signal", pthread_cond_signal(&bgsignal_));
+ }
+
+ // Add to priority queue
+ queue_.push_back(BGItem());
+ queue_.back().function = function;
+ queue_.back().arg = arg;
+
+ PthreadCall("unlock", pthread_mutex_unlock(&mu_));
+}
+
+void FuchsiaEnv::BGThread() {
+ while (true) {
+ // Wait until there is an item that is ready to run
+ PthreadCall("lock", pthread_mutex_lock(&mu_));
+ while (queue_.empty()) {
+ PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_));
+ }
+
+ void (*function)(void*) = queue_.front().function;
+ void* arg = queue_.front().arg;
+ queue_.pop_front();
+
+ PthreadCall("unlock", pthread_mutex_unlock(&mu_));
+ (*function)(arg);
+ }
+}
+
+namespace {
+struct StartThreadState {
+ void (*user_function)(void*);
+ void* arg;
+};
+}
+static void* StartThreadWrapper(void* arg) {
+ StartThreadState* state = reinterpret_cast<StartThreadState*>(arg);
+ state->user_function(state->arg);
+ delete state;
+ return NULL;
+}
+
+void FuchsiaEnv::StartThread(void (*function)(void* arg), void* arg) {
+ pthread_t t;
+ StartThreadState* state = new StartThreadState;
+ state->user_function = function;
+ state->arg = arg;
+ PthreadCall("start thread",
+ pthread_create(&t, NULL, &StartThreadWrapper, state));
+}
+
+} // namespace
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static Env* default_env;
+
+static void InitDefaultEnv() { default_env = new FuchsiaEnv(AT_FDCWD); }
+
+Env* Env::Default() {
+ pthread_once(&once, InitDefaultEnv);
+ return default_env;
+}
+
+std::unique_ptr<Env> MakeFuchsiaEnv(int root_fd) {
+ return std::make_unique<FuchsiaEnv>(root_fd);
+}
+
+} // namespace leveldb
diff --git a/util/env_fuchsia.h b/util/env_fuchsia.h
new file mode 100644
index 0000000..906dc19
--- /dev/null
+++ b/util/env_fuchsia.h
@@ -0,0 +1,42 @@
+// 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.
+
+// Copyright 2016 The Fuchsia Authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <memory>
+#include "leveldb/env.h"
+
+namespace leveldb {
+
+// Creates a new environment for fuchsia. The database paths will consider
+// |root_fd| as root. This method will not take ownsership of |root_fd| that
+// must stay open until the environment is destructed.
+std::unique_ptr<Env> MakeFuchsiaEnv(int root_fd);
+
+} // namespace leveldb