# found in the LICENSE file. See the AUTHORS file for names of contributors.
# Inherit some settings from environment variables, if available
-CXX ?= g++
-CC ?= gcc
INSTALL_PATH ?= $(CURDIR)
#-----------------------------------------------
# Should we build shared libraries?
ifneq ($(PLATFORM_SHARED_EXT),)
+
+ifneq ($(PLATFORM_SHARED_VERSIONED),true)
+SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
+SHARED2 = $(SHARED1)
+SHARED3 = $(SHARED1)
+SHARED = $(SHARED1)
+else
# Update db.h if you change these.
SHARED_MAJOR = 1
-SHARED_MINOR = 4
+SHARED_MINOR = 5
SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
SHARED2 = $(SHARED1).$(SHARED_MAJOR)
SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
SHARED = $(SHARED1) $(SHARED2) $(SHARED3)
-$(SHARED3):
- $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(INSTALL_PATH)/$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3)
-$(SHARED2): $(SHARED3)
- ln -fs $(SHARED3) $(SHARED2)
$(SHARED1): $(SHARED3)
ln -fs $(SHARED3) $(SHARED1)
+$(SHARED2): $(SHARED3)
+ ln -fs $(SHARED3) $(SHARED2)
endif
+$(SHARED3):
+ $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3)
+
+endif # PLATFORM_SHARED_EXT
+
all: $(SHARED) $(LIBRARY)
check: all $(PROGRAMS) $(TESTS)
ifeq ($(PLATFORM), IOS)
# For iOS, create universal object files to be used on both the simulator and
# a device.
-SIMULATORROOT=/Developer/Platforms/iPhoneSimulator.platform/Developer
-DEVICEROOT=/Developer/Platforms/iPhoneOS.platform/Developer
-IOSVERSION=$(shell defaults read /Developer/Platforms/iPhoneOS.platform/version CFBundleShortVersionString)
+PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms
+SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer
+DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer
+IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString)
.cc.o:
mkdir -p ios-x86/$(dir $@)
# argument, which in turn gets read while processing Makefile.
#
# The output will set the following variables:
+# CC C Compiler path
+# CXX C++ Compiler path
# PLATFORM_LDFLAGS Linker flags
# PLATFORM_SHARED_EXT Extension for shared libraries
# PLATFORM_SHARED_LDFLAGS Flags for building shared library
# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library
# PLATFORM_CCFLAGS C compiler flags
# PLATFORM_CXXFLAGS C++ compiler flags. Will contain:
-# -DLEVELDB_PLATFORM_POSIX if cstdatomic is present
-# -DLEVELDB_PLATFORM_NOATOMIC if it is not
+# PLATFORM_SHARED_VERSIONED Set to 'true' if platform supports versioned
+# shared libraries, empty otherwise.
+#
+# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following:
+#
+# -DLEVELDB_CSTDATOMIC_PRESENT if <cstdatomic> is present
+# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms
+# -DSNAPPY if the Snappy library is present
+#
OUTPUT=$1
if test -z "$OUTPUT"; then
- echo "usage: $0 <output-filename>"
+ echo "usage: $0 <output-filename>" >&2
exit 1
fi
rm -f $OUTPUT
touch $OUTPUT
+if test -z "$CC"; then
+ CC=cc
+fi
+
if test -z "$CXX"; then
CXX=g++
fi
fi
COMMON_FLAGS=
+CROSS_COMPILE=
PLATFORM_CCFLAGS=
PLATFORM_CXXFLAGS=
PLATFORM_LDFLAGS=
PLATFORM_SHARED_EXT="so"
PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl,"
PLATFORM_SHARED_CFLAGS="-fPIC"
+PLATFORM_SHARED_VERSIONED=true
# On GCC, we pick libc's memcmp over GCC's memcmp via -fno-builtin-memcmp
case "$TARGET_OS" in
PORT_FILE=port/port_posix.cc
;;
OS_ANDROID_CROSSCOMPILE)
- PLATFORM="$TARGET_OS"
- COMMON_FLAGS=""
- PLATFORM_LDFLAGS=""
- PORT_FILE=port/port_android.cc
+ PLATFORM=OS_ANDROID
+ COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX"
+ PLATFORM_LDFLAGS="" # All pthread features are in the Android C library
+ PORT_FILE=port/port_posix.cc
+ CROSS_COMPILE=true
;;
*)
- echo "Unknown platform!"
+ echo "Unknown platform!" >&2
exit 1
esac
echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT
echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT
-if [ "$PLATFORM" = "OS_ANDROID_CROSSCOMPILE" ]; then
+if [ "$CROSS_COMPILE" = "true" ]; then
# Cross-compiling; do not try any compilation tests.
true
else
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
+echo "CC=$CC" >> $OUTPUT
+echo "CXX=$CXX" >> $OUTPUT
echo "PLATFORM=$PLATFORM" >> $OUTPUT
echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT
echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT
echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT
echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT
echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT
+echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT
phase = name;
}
+static const char* GetTempDir(void) {
+ const char* ret = getenv("TEST_TMPDIR");
+ if (ret == NULL || ret[0] == '\0')
+ ret = "/tmp";
+ return ret;
+}
+
#define CheckNoError(err) \
if ((err) != NULL) { \
fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \
char* err = NULL;
int run = -1;
- snprintf(dbname, sizeof(dbname), "/tmp/leveldb_c_test-%d",
+ snprintf(dbname, sizeof(dbname),
+ "%s/leveldb_c_test-%d",
+ GetTempDir(),
((int) geteuid()));
StartPhase("create_objects");
static bool FLAGS_use_existing_db = false;
// Use the db with the following name.
-static const char* FLAGS_db = "/tmp/dbbench";
+static const char* FLAGS_db = NULL;
namespace leveldb {
int main(int argc, char** argv) {
FLAGS_write_buffer_size = leveldb::Options().write_buffer_size;
FLAGS_open_files = leveldb::Options().max_open_files;
+ std::string default_db_path;
for (int i = 1; i < argc; i++) {
double d;
}
}
+ // Choose a location for the test database if none given with --db=<path>
+ if (FLAGS_db == NULL) {
+ leveldb::Env::Default()->GetTestDirectory(&default_db_path);
+ default_db_path += "/dbbench";
+ FLAGS_db = default_db_path.c_str();
+ }
+
leveldb::Benchmark benchmark;
benchmark.Run();
return 0;
MutexLock l(&mutex_);
assert(bg_compaction_scheduled_);
if (!shutting_down_.Acquire_Load()) {
- BackgroundCompaction();
+ Status s = BackgroundCompaction();
+ if (!s.ok()) {
+ // Wait a little bit before retrying background compaction in
+ // case this is an environmental problem and we do not want to
+ // chew up resources for failed compactions for the duration of
+ // the problem.
+ bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
+ Log(options_.info_log, "Waiting after background compaction error: %s",
+ s.ToString().c_str());
+ mutex_.Unlock();
+ env_->SleepForMicroseconds(1000000);
+ mutex_.Lock();
+ }
}
+
bg_compaction_scheduled_ = false;
// Previous compaction may have produced too many files in a level,
bg_cv_.SignalAll();
}
-void DBImpl::BackgroundCompaction() {
+Status DBImpl::BackgroundCompaction() {
mutex_.AssertHeld();
if (imm_ != NULL) {
- CompactMemTable();
- return;
+ return CompactMemTable();
}
Compaction* c;
}
manual_compaction_ = NULL;
}
+ return status;
}
void DBImpl::CleanupCompaction(CompactionState* compact) {
WritableFile* lfile = NULL;
s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile);
if (!s.ok()) {
+ // Avoid chewing through file number space in a tight loop.
+ versions_->ReuseFileNumber(new_log_number);
break;
}
delete log_;
void MaybeScheduleCompaction();
static void BGWork(void* db);
void BackgroundCall();
- void BackgroundCompaction();
+ Status BackgroundCompaction();
void CleanupCompaction(CompactionState* compact);
Status DoCompactionWork(CompactionState* compact);
// Simulate no-space errors while this pointer is non-NULL.
port::AtomicPointer no_space_;
+ // Simulate non-writable file system while this pointer is non-NULL
+ port::AtomicPointer non_writable_;
+
bool count_random_reads_;
AtomicCounter random_read_counter_;
+ AtomicCounter sleep_counter_;
+
explicit SpecialEnv(Env* base) : EnvWrapper(base) {
delay_sstable_sync_.Release_Store(NULL);
no_space_.Release_Store(NULL);
+ non_writable_.Release_Store(NULL);
count_random_reads_ = false;
}
}
};
+ if (non_writable_.Acquire_Load() != NULL) {
+ return Status::IOError("simulated write error");
+ }
+
Status s = target()->NewWritableFile(f, r);
if (s.ok()) {
if (strstr(f.c_str(), ".sst") != NULL) {
}
return s;
}
+
+ virtual void SleepForMicroseconds(int micros) {
+ sleep_counter_.Increment();
+ target()->SleepForMicroseconds(micros);
+ }
};
class DBTest {
enum OptionConfig {
kDefault,
kFilter,
+ kUncompressed,
kEnd
};
int option_config_;
// Switch to a fresh database with the next option configuration to
// test. Return false if there are no more configurations to test.
bool ChangeOptions() {
- if (option_config_ == kEnd) {
+ option_config_++;
+ if (option_config_ >= kEnd) {
return false;
} else {
- option_config_++;
DestroyAndReopen();
return true;
}
case kFilter:
options.filter_policy = filter_policy_;
break;
+ case kUncompressed:
+ options.compression = kNoCompression;
+ break;
default:
break;
}
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 1);
- // Step 3: read until level 0 compaction disappears.
- int read_count = 0;
- while (NumTableFilesAtLevel(0) > 0) {
- ASSERT_LE(read_count, 10000) << "did not trigger level 0 compaction";
- read_count++;
+ // Step 3: read a bunch of times
+ for (int i = 0; i < 1000; i++) {
ASSERT_EQ("NOT_FOUND", Get("missing"));
}
+
+ // Step 4: Wait for compaction to finish
+ env_->SleepForMicroseconds(1000000);
+
+ ASSERT_EQ(NumTableFilesAtLevel(0), 0);
} while (ChangeOptions());
}
Compact("a", "z");
const int num_files = CountFiles();
env_->no_space_.Release_Store(env_); // Force out-of-space errors
- for (int i = 0; i < 10; i++) {
+ env_->sleep_counter_.Reset();
+ for (int i = 0; i < 5; i++) {
for (int level = 0; level < config::kNumLevels-1; level++) {
dbfull()->TEST_CompactRange(level, NULL, NULL);
}
}
env_->no_space_.Release_Store(NULL);
- ASSERT_LT(CountFiles(), num_files + 5);
+ ASSERT_LT(CountFiles(), num_files + 3);
+
+ // Check that compaction attempts slept after errors
+ ASSERT_GE(env_->sleep_counter_.Read(), 5);
+}
+
+TEST(DBTest, NonWritableFileSystem) {
+ Options options = CurrentOptions();
+ options.write_buffer_size = 1000;
+ options.env = env_;
+ Reopen(&options);
+ ASSERT_OK(Put("foo", "v1"));
+ env_->non_writable_.Release_Store(env_); // Force errors for new files
+ std::string big(100000, 'x');
+ int errors = 0;
+ for (int i = 0; i < 20; i++) {
+ fprintf(stderr, "iter %d; errors %d\n", i, errors);
+ if (!Put("foo", big).ok()) {
+ errors++;
+ env_->SleepForMicroseconds(100000);
+ }
+ }
+ ASSERT_GT(errors, 0);
+ env_->non_writable_.Release_Store(NULL);
}
TEST(DBTest, FilesDeletedAfterCompaction) {
const Comparator* ucmp = icmp.user_comparator();
if (!disjoint_sorted_files) {
// Need to check against all files
- for (int i = 0; i < files.size(); i++) {
+ for (size_t i = 0; i < files.size(); i++) {
const FileMetaData* f = files[i];
if (AfterFile(ucmp, smallest_user_key, f) ||
BeforeFile(ucmp, largest_user_key, f)) {
// Avoid compacting too much in one shot in case the range is large.
const uint64_t limit = MaxFileSizeForLevel(level);
uint64_t total = 0;
- for (int i = 0; i < inputs.size(); i++) {
+ for (size_t i = 0; i < inputs.size(); i++) {
uint64_t s = inputs[i]->file_size;
total += s;
if (total >= limit) {
// Allocate and return a new file number
uint64_t NewFileNumber() { return next_file_number_++; }
+ // Arrange to reuse "file_number" unless a newer file number has
+ // already been allocated.
+ // REQUIRES: "file_number" was returned by a call to NewFileNumber().
+ void ReuseFileNumber(uint64_t file_number) {
+ if (next_file_number_ == file_number + 1) {
+ next_file_number_ = file_number;
+ }
+ }
+
// Return the number of Table files at the specified level.
int NumLevelFiles(int level) const;
// If true, we enable Write-Ahead Logging
static bool FLAGS_WAL_enabled = true;
+// Use the db with the following name.
+static const char* FLAGS_db = NULL;
+
inline
static void ExecErrorCheck(int status, char *err_msg) {
if (status != SQLITE_OK) {
bytes_(0),
rand_(301) {
std::vector<std::string> files;
- Env::Default()->GetChildren("/tmp", &files);
+ std::string test_dir;
+ Env::Default()->GetTestDirectory(&test_dir);
+ Env::Default()->GetChildren(test_dir, &files);
if (!FLAGS_use_existing_db) {
for (int i = 0; i < files.size(); i++) {
if (Slice(files[i]).starts_with("dbbench_sqlite3")) {
- Env::Default()->DeleteFile("/tmp/" + files[i]);
+ std::string file_name(test_dir);
+ file_name += "/";
+ file_name += files[i];
+ Env::Default()->DeleteFile(file_name.c_str());
}
}
}
db_num_++;
// Open database
- snprintf(file_name, sizeof(file_name), "/tmp/dbbench_sqlite3-%d.db",
+ std::string tmp_dir;
+ Env::Default()->GetTestDirectory(&tmp_dir);
+ snprintf(file_name, sizeof(file_name),
+ "%s/dbbench_sqlite3-%d.db",
+ tmp_dir.c_str(),
db_num_);
status = sqlite3_open(file_name, &db_);
if (status) {
} // namespace leveldb
int main(int argc, char** argv) {
+ std::string default_db_path;
for (int i = 1; i < argc; i++) {
double d;
int n;
} else if (sscanf(argv[i], "--WAL_enabled=%d%c", &n, &junk) == 1 &&
(n == 0 || n == 1)) {
FLAGS_WAL_enabled = n;
+ } else if (strncmp(argv[i], "--db=", 5) == 0) {
+ FLAGS_db = argv[i] + 5;
} else {
fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
exit(1);
}
}
+ // Choose a location for the test database if none given with --db=<path>
+ if (FLAGS_db == NULL) {
+ leveldb::Env::Default()->GetTestDirectory(&default_db_path);
+ default_db_path += "/dbbench";
+ FLAGS_db = default_db_path.c_str();
+ }
+
leveldb::Benchmark benchmark;
benchmark.Run();
return 0;
// is off.
static bool FLAGS_compression = true;
+// Use the db with the following name.
+static const char* FLAGS_db = NULL;
+
inline
static void DBSynchronize(kyotocabinet::TreeDB* db_)
{
bytes_(0),
rand_(301) {
std::vector<std::string> files;
- Env::Default()->GetChildren("/tmp", &files);
+ std::string test_dir;
+ Env::Default()->GetTestDirectory(&test_dir);
+ Env::Default()->GetChildren(test_dir.c_str(), &files);
if (!FLAGS_use_existing_db) {
for (int i = 0; i < files.size(); i++) {
if (Slice(files[i]).starts_with("dbbench_polyDB")) {
- Env::Default()->DeleteFile("/tmp/" + files[i]);
+ std::string file_name(test_dir);
+ file_name += "/";
+ file_name += files[i];
+ Env::Default()->DeleteFile(file_name.c_str());
}
}
}
db_ = new kyotocabinet::TreeDB();
char file_name[100];
db_num_++;
- snprintf(file_name, sizeof(file_name), "/tmp/dbbench_polyDB-%d.kct",
- db_num_);
+ std::string test_dir;
+ Env::Default()->GetTestDirectory(&test_dir);
+ snprintf(file_name, sizeof(file_name),
+ "%s/dbbench_polyDB-%d.kct",
+ test_dir.c_str(),
+ db_num_);
// Create tuning options and open the database
int open_options = kyotocabinet::PolyDB::OWRITER |
} // namespace leveldb
int main(int argc, char** argv) {
+ std::string default_db_path;
for (int i = 1; i < argc; i++) {
double d;
int n;
} else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 &&
(n == 0 || n == 1)) {
FLAGS_compression = (n == 1) ? true : false;
+ } else if (strncmp(argv[i], "--db=", 5) == 0) {
+ FLAGS_db = argv[i] + 5;
} else {
fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
exit(1);
}
}
+ // Choose a location for the test database if none given with --db=<path>
+ if (FLAGS_db == NULL) {
+ leveldb::Env::Default()->GetTestDirectory(&default_db_path);
+ default_db_path += "/dbbench";
+ FLAGS_db = default_db_path.c_str();
+ }
+
leveldb::Benchmark benchmark;
benchmark.Run();
return 0;
// Update Makefile if you change these
static const int kMajorVersion = 1;
-static const int kMinorVersion = 4;
+static const int kMinorVersion = 5;
struct Options;
struct ReadOptions;
}
#define LEVELDB_HAVE_MEMORY_BARRIER
-// ARM
-#elif defined(ARCH_CPU_ARM_FAMILY)
+// ARM Linux
+#elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__)
typedef void (*LinuxKernelMemoryBarrierFunc)(void);
-LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
- (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
+// The Linux ARM kernel provides a highly optimized device-specific memory
+// barrier function at a fixed memory address that is mapped in every
+// user-level process.
+//
+// This beats using CPU-specific instructions which are, on single-core
+// devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more
+// than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking
+// shows that the extra function call cost is completely negligible on
+// multi-core devices.
+//
inline void MemoryBarrier() {
- pLinuxKernelMemoryBarrier();
+ (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)();
}
#define LEVELDB_HAVE_MEMORY_BARRIER
# include "port/port_posix.h"
#elif defined(LEVELDB_PLATFORM_CHROMIUM)
# include "port/port_chromium.h"
-#elif defined(LEVELDB_PLATFORM_ANDROID)
-# include "port/port_android.h"
#endif
#endif // STORAGE_LEVELDB_PORT_PORT_H_
+++ /dev/null
-// 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 "port/port_android.h"
-
-#include <cstdlib>
-
-extern "C" {
-size_t fread_unlocked(void *a, size_t b, size_t c, FILE *d) {
- return fread(a, b, c, d);
-}
-
-size_t fwrite_unlocked(const void *a, size_t b, size_t c, FILE *d) {
- return fwrite(a, b, c, d);
-}
-
-int fflush_unlocked(FILE *f) {
- return fflush(f);
-}
-
-int fdatasync(int fd) {
- return fsync(fd);
-}
-}
-
-namespace leveldb {
-namespace port {
-
-static void PthreadCall(const char* label, int result) {
- if (result != 0) {
- fprintf(stderr, "pthread %s: %s\n", label, strerror(result));
- abort();
- }
-}
-
-Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); }
-Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); }
-void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); }
-void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); }
-
-CondVar::CondVar(Mutex* mu)
- : mu_(mu) {
- PthreadCall("init cv", pthread_cond_init(&cv_, NULL));
-}
-
-CondVar::~CondVar() {
- PthreadCall("destroy cv", pthread_cond_destroy(&cv_));
-}
-
-void CondVar::Wait() {
- PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_));
-}
-
-void CondVar::Signal(){
- PthreadCall("signal", pthread_cond_signal(&cv_));
-}
-
-void CondVar::SignalAll() {
- PthreadCall("broadcast", pthread_cond_broadcast(&cv_));
-}
-
-} // namespace port
-} // namespace leveldb
+++ /dev/null
-// 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.
-//
-// See port_example.h for documentation for the following types/functions.
-
-#ifndef STORAGE_LEVELDB_PORT_PORT_ANDROID_H_
-#define STORAGE_LEVELDB_PORT_PORT_ANDROID_H_
-
-#include <endian.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <string>
-#include <cctype>
-
-// Collapse the plethora of ARM flavors available to an easier to manage set
-// Defs reference is at https://wiki.edubuntu.org/ARM/Thumb2PortingHowto
-#if defined(__ARM_ARCH_6__) || \
- defined(__ARM_ARCH_6J__) || \
- defined(__ARM_ARCH_6K__) || \
- defined(__ARM_ARCH_6Z__) || \
- defined(__ARM_ARCH_6T2__) || \
- defined(__ARM_ARCH_6ZK__) || \
- defined(__ARM_ARCH_7__) || \
- defined(__ARM_ARCH_7R__) || \
- defined(__ARM_ARCH_7A__)
-#define ARMV6_OR_7 1
-#endif
-
-extern "C" {
- size_t fread_unlocked(void *a, size_t b, size_t c, FILE *d);
- size_t fwrite_unlocked(const void *a, size_t b, size_t c, FILE *d);
- int fflush_unlocked(FILE *f);
- int fdatasync (int fd);
-}
-
-namespace leveldb {
-namespace port {
-
-static const bool kLittleEndian = __BYTE_ORDER == __LITTLE_ENDIAN;
-
-class CondVar;
-
-class Mutex {
- public:
- Mutex();
- ~Mutex();
-
- void Lock();
- void Unlock();
- void AssertHeld() {
- //TODO(gabor): How can I implement this?
- }
-
- private:
- friend class CondVar;
- pthread_mutex_t mu_;
-
- // No copying
- Mutex(const Mutex&);
- void operator=(const Mutex&);
-};
-
-class CondVar {
- public:
- explicit CondVar(Mutex* mu);
- ~CondVar();
- void Wait();
- void Signal();
- void SignalAll();
- private:
- Mutex* mu_;
- pthread_cond_t cv_;
-};
-
-#ifndef ARMV6_OR_7
-// On ARM chipsets <V6, 0xffff0fa0 is the hard coded address of a
-// memory barrier function provided by the kernel.
-typedef void (*LinuxKernelMemoryBarrierFunc)(void);
-// TODO(user): ATTRIBUTE_WEAK is undefined, so this fails to build on
-// non-ARMV6_OR_7. We may be able to replace it with __attribute__((weak)) for
-// older ARM builds, but x86 builds will require a different memory barrier.
-LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier ATTRIBUTE_WEAK =
- (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
-#endif
-
-// Storage for a lock-free pointer
-class AtomicPointer {
- private:
- void* rep_;
-
- inline void MemoryBarrier() const {
- // TODO(gabor): This only works on Android instruction sets >= V6
-#ifdef ARMV6_OR_7
- __asm__ __volatile__("dmb" : : : "memory");
-#else
- pLinuxKernelMemoryBarrier();
-#endif
- }
-
- public:
- AtomicPointer() { }
- explicit AtomicPointer(void* v) : rep_(v) { }
- inline void* Acquire_Load() const {
- void* r = rep_;
- MemoryBarrier();
- return r;
- }
- inline void Release_Store(void* v) {
- MemoryBarrier();
- rep_ = v;
- }
- inline void* NoBarrier_Load() const {
- void* r = rep_;
- return r;
- }
- inline void NoBarrier_Store(void* v) {
- rep_ = v;
- }
-};
-
-// TODO(gabor): Implement compress
-inline bool Snappy_Compress(
- const char* input,
- size_t input_length,
- std::string* output) {
- return false;
-}
-
-// TODO(gabor): Implement uncompress
-inline bool Snappy_GetUncompressedLength(const char* input, size_t length,
- size_t* result) {
- return false;
-}
-
-// TODO(gabor): Implement uncompress
-inline bool Snappy_Uncompress(
- const char* input_data,
- size_t input_length,
- char* output) {
- return false;
-}
-
-inline uint64_t ThreadIdentifier() {
- pthread_t tid = pthread_self();
- uint64_t r = 0;
- memcpy(&r, &tid, sizeof(r) < sizeof(tid) ? sizeof(r) : sizeof(tid));
- return r;
-}
-
-inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) {
- return false;
-}
-
-} // namespace port
-} // namespace leveldb
-
-#endif // STORAGE_LEVELDB_PORT_PORT_ANDROID_H_
void SignallAll();
};
+// Thread-safe initialization.
+// Used as follows:
+// static port::OnceType init_control = LEVELDB_ONCE_INIT;
+// static void Initializer() { ... do something ...; }
+// ...
+// port::InitOnce(&init_control, &Initializer);
+typedef intptr_t OnceType;
+#define LEVELDB_ONCE_INIT 0
+extern void InitOnce(port::OnceType*, void (*initializer)());
+
// A type that holds a pointer that can be read or written atomically
// (i.e., without word-tearing.)
class AtomicPointer {
PthreadCall("broadcast", pthread_cond_broadcast(&cv_));
}
+void InitOnce(OnceType* once, void (*initializer)()) {
+ PthreadCall("once", pthread_once(once, initializer));
+}
+
} // namespace port
} // namespace leveldb
#ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_
#define STORAGE_LEVELDB_PORT_PORT_POSIX_H_
+#undef PLATFORM_IS_LITTLE_ENDIAN
#if defined(OS_MACOSX)
#include <machine/endian.h>
+ #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER)
+ #define PLATFORM_IS_LITTLE_ENDIAN \
+ (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN)
+ #endif
#elif defined(OS_SOLARIS)
#include <sys/isa_defs.h>
#ifdef _LITTLE_ENDIAN
- #define LITTLE_ENDIAN
+ #define PLATFORM_IS_LITTLE_ENDIAN true
#else
- #define BIG_ENDIAN
+ #define PLATFORM_IS_LITTLE_ENDIAN false
#endif
#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\
- defined(OS_DRAGONFLYBSD)
+ defined(OS_DRAGONFLYBSD) || defined(OS_ANDROID)
#include <sys/types.h>
#include <sys/endian.h>
#else
#include <string>
#include "port/atomic_pointer.h"
-#ifdef LITTLE_ENDIAN
-#define IS_LITTLE_ENDIAN true
-#else
-#define IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN)
+#ifndef PLATFORM_IS_LITTLE_ENDIAN
+#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN)
#endif
#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\
- defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD)
+ defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\
+ defined(OS_ANDROID)
// Use fread/fwrite/fflush on platforms without _unlocked variants
#define fread_unlocked fread
#define fwrite_unlocked fwrite
#define fdatasync fsync
#endif
+#if defined(OS_ANDROID) && __ANDROID_API__ < 9
+// fdatasync() was only introduced in API level 9 on Android. Use fsync()
+// when targetting older platforms.
+#define fdatasync fsync
+#endif
+
namespace leveldb {
namespace port {
-static const bool kLittleEndian = IS_LITTLE_ENDIAN;
+static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN;
+#undef PLATFORM_IS_LITTLE_ENDIAN
class CondVar;
Mutex* mu_;
};
+typedef pthread_once_t OnceType;
+#define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT
+extern void InitOnce(OnceType* once, void (*initializer)());
+
inline bool Snappy_Compress(const char* input, size_t length,
::std::string* output) {
#ifdef SNAPPY
}
}
+// Test that encoding routines generate little-endian encodings
+TEST(Coding, EncodingOutput) {
+ std::string dst;
+ PutFixed32(&dst, 0x04030201);
+ ASSERT_EQ(4, dst.size());
+ ASSERT_EQ(0x01, static_cast<int>(dst[0]));
+ ASSERT_EQ(0x02, static_cast<int>(dst[1]));
+ ASSERT_EQ(0x03, static_cast<int>(dst[2]));
+ ASSERT_EQ(0x04, static_cast<int>(dst[3]));
+
+ dst.clear();
+ PutFixed64(&dst, 0x0807060504030201ull);
+ ASSERT_EQ(8, dst.size());
+ ASSERT_EQ(0x01, static_cast<int>(dst[0]));
+ ASSERT_EQ(0x02, static_cast<int>(dst[1]));
+ ASSERT_EQ(0x03, static_cast<int>(dst[2]));
+ ASSERT_EQ(0x04, static_cast<int>(dst[3]));
+ ASSERT_EQ(0x05, static_cast<int>(dst[4]));
+ ASSERT_EQ(0x06, static_cast<int>(dst[5]));
+ ASSERT_EQ(0x07, static_cast<int>(dst[6]));
+ ASSERT_EQ(0x08, static_cast<int>(dst[7]));
+}
+
TEST(Coding, Varint32) {
std::string s;
for (uint32_t i = 0; i < (32 * 32); i++) {
#include <stdint.h>
#include "leveldb/comparator.h"
#include "leveldb/slice.h"
+#include "port/port.h"
#include "util/logging.h"
namespace leveldb {
};
} // namespace
-// Intentionally not destroyed to prevent destructor racing
-// with background threads.
-static const Comparator* bytewise = new BytewiseComparatorImpl;
+static port::OnceType once = LEVELDB_ONCE_INIT;
+static const Comparator* bytewise;
+
+static void InitModule() {
+ bytewise = new BytewiseComparatorImpl;
+}
const Comparator* BytewiseComparator() {
+ port::InitOnce(&once, InitModule);
return bytewise;
}