From 94a50fb339d72f64721b75b66fdfe28b1633fbdc Mon Sep 17 00:00:00 2001 From: justmoon Date: Sat, 21 Jul 2012 14:54:55 +0200 Subject: [PATCH] Leveldb Windows port by Edouard Alligand, adapted for MingW by me. --- src/leveldb/Makefile | 46 +-- src/leveldb/build_detect_platform | 33 +- src/leveldb/port/port.h | 2 + src/leveldb/port/port_win.cc | 182 +++++++++ src/leveldb/port/port_win.h | 161 ++++++++ src/leveldb/util/env_boost.cc | 591 ++++++++++++++++++++++++++++++ src/leveldb/util/win_logger.cc | 96 +++++ src/leveldb/util/win_logger.h | 28 ++ 8 files changed, 1106 insertions(+), 33 deletions(-) mode change 100644 => 100755 src/leveldb/Makefile create mode 100644 src/leveldb/port/port_win.cc create mode 100644 src/leveldb/port/port_win.h create mode 100644 src/leveldb/util/env_boost.cc create mode 100644 src/leveldb/util/win_logger.cc create mode 100644 src/leveldb/util/win_logger.h diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile old mode 100644 new mode 100755 index c648a285e..7f658cfdf --- a/src/leveldb/Makefile +++ b/src/leveldb/Makefile @@ -82,7 +82,7 @@ $(SHARED2): $(SHARED3) endif $(SHARED3): - $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) + $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) $(PLATFORM_EXTRALIBS) -o $(SHARED3) endif # PLATFORM_SHARED_EXT @@ -100,74 +100,74 @@ $(LIBRARY): $(LIBOBJECTS) $(AR) -rs $@ $(LIBOBJECTS) db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) + $(CXX) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) -lsqlite3 + $(CXX) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) -lsqlite3 db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) -lkyotocabinet + $(CXX) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) -lkyotocabinet arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) $(MEMENVLIBRARY) : $(MEMENVOBJECTS) rm -f $@ $(AR) -rs $@ $(MEMENVOBJECTS) memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) - $(CXX) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LDFLAGS) + $(CXX) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) ifeq ($(PLATFORM), IOS) # For iOS, create universal object files to be used on both the simulator and diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform index 959a7d6f5..5a8d72373 100755 --- a/src/leveldb/build_detect_platform +++ b/src/leveldb/build_detect_platform @@ -50,6 +50,8 @@ CROSS_COMPILE= PLATFORM_CCFLAGS= PLATFORM_CXXFLAGS= PLATFORM_LDFLAGS= +PLATFORM_EXTRALIBS= +PLATFORM_SOURCES= PLATFORM_SHARED_EXT="so" PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," PLATFORM_SHARED_CFLAGS="-fPIC" @@ -62,49 +64,59 @@ case "$TARGET_OS" in COMMON_FLAGS="-fno-builtin-memcmp -DOS_MACOSX" PLATFORM_SHARED_EXT=dylib PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name " - PORT_FILE=port/port_posix.cc + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" ;; Linux) PLATFORM=OS_LINUX COMMON_FLAGS="-fno-builtin-memcmp -pthread -DOS_LINUX" PLATFORM_LDFLAGS="-pthread" - PORT_FILE=port/port_posix.cc + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" ;; SunOS) PLATFORM=OS_SOLARIS COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_SOLARIS" PLATFORM_LDFLAGS="-lpthread -lrt" - PORT_FILE=port/port_posix.cc + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" ;; FreeBSD) PLATFORM=OS_FREEBSD COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_FREEBSD" PLATFORM_LDFLAGS="-lpthread" - PORT_FILE=port/port_posix.cc + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" ;; NetBSD) PLATFORM=OS_NETBSD COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_NETBSD" PLATFORM_LDFLAGS="-lpthread -lgcc_s" - PORT_FILE=port/port_posix.cc + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" ;; OpenBSD) PLATFORM=OS_OPENBSD COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_OPENBSD" PLATFORM_LDFLAGS="-pthread" - PORT_FILE=port/port_posix.cc + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" ;; DragonFly) PLATFORM=OS_DRAGONFLYBSD COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_DRAGONFLYBSD" PLATFORM_LDFLAGS="-lpthread" - PORT_FILE=port/port_posix.cc + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" ;; OS_ANDROID_CROSSCOMPILE) 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 + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" + CROSS_COMPILE=true + ;; + OS_WINDOWS_CROSSCOMPILE) + PLATFORM=OS_WINDOWS + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DBOOST_THREAD_USE_LIB" + PLATFORM_CXXFLAGS="-std=c++0x" + PLATFORM_LDFLAGS="" + PLATFORM_SHARED_CFLAGS="" + PLATFORM_SOURCES="port/port_win.cc util/env_boost.cc util/win_logger.cc" + PLATFORM_EXTRALIBS="-lboost_system-mt-s -lboost_filesystem-mt-s -lboost_thread_win32-mt-s" CROSS_COMPILE=true ;; *) @@ -120,12 +132,12 @@ DIRS="util db table" set -f # temporarily disable globbing so that our patterns aren't expanded PRUNE_TEST="-name *test*.cc -prune" PRUNE_BENCH="-name *_bench.cc -prune" -PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o -name '*.cc' -print | sort | tr "\n" " "` +PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o -name '*.cc' -not -name 'env_*.cc' -not -name '*_logger.cc' -print | sort | tr "\n" " "` set +f # re-enable globbing # The sources consist of the portable files, plus the platform-specific port # file. -echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT +echo "SOURCES=$PORTABLE_FILES $PLATFORM_SOURCES" >> $OUTPUT echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT if [ "$CROSS_COMPILE" = "true" ]; then @@ -173,6 +185,7 @@ echo "PLATFORM=$PLATFORM" >> $OUTPUT echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT +echo "PLATFORM_EXTRALIBS=$PLATFORM_EXTRALIBS" >> $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 diff --git a/src/leveldb/port/port.h b/src/leveldb/port/port.h index e667db40d..4baafa8e2 100644 --- a/src/leveldb/port/port.h +++ b/src/leveldb/port/port.h @@ -14,6 +14,8 @@ # include "port/port_posix.h" #elif defined(LEVELDB_PLATFORM_CHROMIUM) # include "port/port_chromium.h" +#elif defined(LEVELDB_PLATFORM_WINDOWS) +# include "port/port_win.h" #endif #endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/src/leveldb/port/port_win.cc b/src/leveldb/port/port_win.cc new file mode 100644 index 000000000..4ca64acbe --- /dev/null +++ b/src/leveldb/port/port_win.cc @@ -0,0 +1,182 @@ +// LevelDB 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. + +// 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 the University of California, Berkeley 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 REGENTS 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 REGENTS AND 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 "port/port_win.h" + +#include +#include + +namespace leveldb { +namespace port { + +Mutex::Mutex() : + mutex_(::CreateMutex(NULL, FALSE, NULL)) { + assert(mutex_); +} + +Mutex::~Mutex() { + assert(mutex_); + ::CloseHandle(mutex_); +} + +void Mutex::Lock() { + assert(mutex_); + ::WaitForSingleObject(mutex_, INFINITE); +} + +void Mutex::Unlock() { + assert(mutex_); + ::ReleaseMutex(mutex_); +} + +void Mutex::AssertHeld() { + assert(mutex_); + assert(1); +} + +CondVar::CondVar(Mutex* mu) : + waiting_(0), + mu_(mu), + sema_(::CreateSemaphore(NULL, 0, 0x7fffffff, NULL)), + event_(::CreateEvent(NULL, FALSE, FALSE, NULL)), + broadcasted_(false){ + assert(mu_); +} + +CondVar::~CondVar() { + ::CloseHandle(sema_); + ::CloseHandle(event_); +} + +void CondVar::Wait() { + wait_mtx_.Lock(); + ++waiting_; + assert(waiting_ > 0); + wait_mtx_.Unlock(); + + ::SignalObjectAndWait(mu_->mutex_, sema_, INFINITE, FALSE); + + wait_mtx_.Lock(); + bool last = broadcasted_ && (--waiting_ == 0); + assert(waiting_ >= 0); + wait_mtx_.Unlock(); + + // we leave this function with the mutex held + if (last) + { + ::SignalObjectAndWait(event_, mu_->mutex_, INFINITE, FALSE); + } + else + { + ::WaitForSingleObject(mu_->mutex_, INFINITE); + } +} + +void CondVar::Signal() { + wait_mtx_.Lock(); + bool waiters = waiting_ > 0; + wait_mtx_.Unlock(); + + if (waiters) + { + ::ReleaseSemaphore(sema_, 1, 0); + } +} + +void CondVar::SignalAll() { + wait_mtx_.Lock(); + + broadcasted_ = (waiting_ > 0); + + if (broadcasted_) + { + // release all + ::ReleaseSemaphore(sema_, waiting_, 0); + wait_mtx_.Unlock(); + ::WaitForSingleObject(event_, INFINITE); + broadcasted_ = false; + } + else + { + wait_mtx_.Unlock(); + } +} + +AtomicPointer::AtomicPointer(void* v) { + Release_Store(v); +} + +void* AtomicPointer::Acquire_Load() const { + void * p = nullptr; + InterlockedExchangePointer(&p, rep_); + return p; +} + +void AtomicPointer::Release_Store(void* v) { + InterlockedExchangePointer(&rep_, v); +} + +void* AtomicPointer::NoBarrier_Load() const { + return rep_; +} + +void AtomicPointer::NoBarrier_Store(void* v) { + rep_ = v; +} + +enum InitializationState +{ + Uninitialized = 0, + Running = 1, + Initialized = 2 +}; + +void InitOnce(OnceType* once, void (*initializer)()) { + + static_assert(Uninitialized == LEVELDB_ONCE_INIT, "Invalid uninitialized state value"); + + InitializationState state = static_cast(InterlockedCompareExchange(once, Running, Uninitialized)); + + if (state == Uninitialized) { + initializer(); + *once = Initialized; + } + + if (state == Running) { + while(*once != Initialized) { + Sleep(0); // yield + } + } + + assert(*once == Initialized); +} + +} +} diff --git a/src/leveldb/port/port_win.h b/src/leveldb/port/port_win.h new file mode 100644 index 000000000..b53d6ef70 --- /dev/null +++ b/src/leveldb/port/port_win.h @@ -0,0 +1,161 @@ +// LevelDB 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. + +// 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 the University of California, Berkeley 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 REGENTS 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 REGENTS AND 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. +// + +#ifndef STORAGE_LEVELDB_PORT_PORT_WIN_H_ +#define STORAGE_LEVELDB_PORT_PORT_WIN_H_ + +#ifdef _MSC_VER +#define snprintf _snprintf +#define close _close +#define fread_unlocked _fread_nolock +#endif + + +#ifdef SNAPPY +#include +#endif + +#include + +#include + +namespace leveldb { +namespace port { + +// Windows is little endian (for now :p) +static const bool kLittleEndian = true; + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld(); + + private: + friend class CondVar; + // critical sections are more efficient than mutexes + // but they are not recursive and can only be used to synchronize threads within the same process + // additionnaly they cannot be used with SignalObjectAndWait that we use for CondVar + // we use opaque void * to avoid including windows.h in port_win.h + void * mutex_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +// the Win32 API offers a dependable condition variable mechanism, but only starting with +// Windows 2008 and Vista +// no matter what we will implement our own condition variable with a semaphore +// implementation as described in a paper written by Douglas C. Schmidt and Irfan Pyarali +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + Mutex* mu_; + + Mutex wait_mtx_; + long waiting_; + + void * sema_; + void * event_; + + bool broadcasted_; +}; + +// Storage for a lock-free pointer +class AtomicPointer { + private: + void * rep_; + public: + AtomicPointer() : rep_(nullptr) { } + explicit AtomicPointer(void* v); + void* Acquire_Load() const; + + void Release_Store(void* v); + + void* NoBarrier_Load() const; + + void NoBarrier_Store(void* v); +}; + +typedef volatile long OnceType; +#define LEVELDB_ONCE_INIT (0) + +extern void InitOnce(OnceType* once, void (*initializer)()); + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} +} + +#endif // STORAGE_LEVELDB_PORT_PORT_WIN_H_ diff --git a/src/leveldb/util/env_boost.cc b/src/leveldb/util/env_boost.cc new file mode 100644 index 000000000..0190f9fb7 --- /dev/null +++ b/src/leveldb/util/env_boost.cc @@ -0,0 +1,591 @@ +// 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 + +#ifdef LEVELDB_PLATFORM_WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#if defined(LEVELDB_PLATFORM_ANDROID) +#include +#endif +#include "leveldb/env.h" +#include "leveldb/slice.h" + +#ifdef LEVELDB_PLATFORM_WINDOWS +#include "util/win_logger.h" +#else +#include "util/posix_logger.h" +#endif +#include "port/port.h" +#include "util/logging.h" + +#ifdef __linux +#include +#include +#endif + +#include + +// Boost includes - see WINDOWS file to see which modules to install +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace leveldb { +namespace { + +// returns the ID of the current process +static boost::uint32_t current_process_id(void) { +#ifdef _WIN32 + return static_cast(::GetCurrentProcessId()); +#else + return static_cast(::getpid()); +#endif +} + +// returns the ID of the current thread +static boost::uint32_t current_thread_id(void) { +#ifdef _WIN32 + return static_cast(::GetCurrentThreadId()); +#else +#ifdef __linux + return static_cast(::syscall(__NR_gettid)); +#else + // just return the pid + return current_process_id(); +#endif +#endif +} + +static char global_read_only_buf[0x8000]; + +class PosixSequentialFile: public SequentialFile { + private: + std::string filename_; + FILE* file_; + + public: + PosixSequentialFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + virtual ~PosixSequentialFile() { fclose(file_); } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s; +#if defined(BSD) || defined(__MINGW32__) + // fread_unlocked doesn't exist on FreeBSD or MingW + size_t r = fread(scratch, 1, n, file_); +#else + size_t r = fread_unlocked(scratch, 1, n, file_); +#endif + *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 PosixRandomAccessFile: public RandomAccessFile { + private: + std::string filename_; + int fd_; + mutable boost::mutex mu_; + + public: + PosixRandomAccessFile(const std::string& fname, int fd) + : filename_(fname), fd_(fd) { } + virtual ~PosixRandomAccessFile() { close(fd_); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; +#ifdef LEVELDB_PLATFORM_WINDOWS + // no pread on Windows so we emulate it with a mutex + boost::unique_lock lock(mu_); + + if (::_lseeki64(fd_, offset, SEEK_SET) == -1L) { + return Status::IOError(filename_, strerror(errno)); + } + + int r = ::_read(fd_, scratch, n); + *result = Slice(scratch, (r < 0) ? 0 : r); + lock.unlock(); +#else + ssize_t r = pread(fd_, scratch, n, static_cast(offset)); + *result = Slice(scratch, (r < 0) ? 0 : r); +#endif + if (r < 0) { + // An error: return a non-ok status + s = Status::IOError(filename_, strerror(errno)); + } + return s; + } +}; + +// We preallocate up to an extra megabyte and use memcpy to append new +// data to the file. This is safe since we either properly close the +// file before reading from it, or for log files, the reading code +// knows enough to skip zero suffixes. + +class BoostFile : public WritableFile { + +public: + explicit BoostFile(std::string path) : path_(path), written_(0) { + Open(); + } + + virtual ~BoostFile() { + Close(); + } + +private: + void Open() { + // we truncate the file as implemented in env_posix + file_.open(path_.generic_string().c_str(), + std::ios_base::trunc | std::ios_base::out | std::ios_base::binary); + written_ = 0; + } + +public: + virtual Status Append(const Slice& data) { + Status result; + file_.write(data.data(), data.size()); + if (!file_.good()) { + result = Status::IOError( + path_.generic_string() + " Append", "cannot write"); + } + return result; + } + + virtual Status Close() { + Status result; + + try { + if (file_.is_open()) { + Sync(); + file_.close(); + } + } catch (const std::exception & e) { + result = Status::IOError(path_.generic_string() + " close", e.what()); + } + + return result; + } + + virtual Status Flush() { + file_.flush(); + return Status::OK(); + } + + virtual Status Sync() { + Status result; + try { + Flush(); + } catch (const std::exception & e) { + result = Status::IOError(path_.string() + " sync", e.what()); + } + + return result; + } + +private: + boost::filesystem::path path_; + boost::uint64_t written_; + std::ofstream file_; +}; + + + +class BoostFileLock : public FileLock { + public: + boost::interprocess::file_lock fl_; +}; + +class PosixEnv : public Env { + public: + PosixEnv(); + virtual ~PosixEnv() { + 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 PosixSequentialFile(fname, f); + return Status::OK(); + } + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { +#ifdef LEVELDB_PLATFORM_WINDOWS + int fd = _open(fname.c_str(), _O_RDONLY | _O_RANDOM | _O_BINARY); +#else + int fd = open(fname.c_str(), O_RDONLY); +#endif + if (fd < 0) { + *result = NULL; + return Status::IOError(fname, strerror(errno)); + } + *result = new PosixRandomAccessFile(fname, fd); + return Status::OK(); + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + Status s; + try { + // will create a new empty file to write to + *result = new BoostFile(fname); + } + catch (const std::exception & e) { + s = Status::IOError(fname, e.what()); + } + + return s; + } + + virtual bool FileExists(const std::string& fname) { + return boost::filesystem::exists(fname); + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + result->clear(); + + boost::system::error_code ec; + boost::filesystem::directory_iterator current(dir, ec); + if (ec != 0) { + return Status::IOError(dir, ec.message()); + } + + boost::filesystem::directory_iterator end; + + for(; current != end; ++current) { + result->push_back(current->path().filename().generic_string()); + } + + return Status::OK(); + } + + virtual Status DeleteFile(const std::string& fname) { + boost::system::error_code ec; + + boost::filesystem::remove(fname, ec); + + Status result; + + if (ec != 0) { + result = Status::IOError(fname, ec.message()); + } + + return result; + } + + virtual Status CreateDir(const std::string& name) { + Status result; + + if (boost::filesystem::exists(name) && + boost::filesystem::is_directory(name)) { + return result; + } + + boost::system::error_code ec; + + if (!boost::filesystem::create_directories(name, ec)) { + result = Status::IOError(name, ec.message()); + } + + return result; + }; + + virtual Status DeleteDir(const std::string& name) { + Status result; + + boost::system::error_code ec; + if (!boost::filesystem::remove_all(name, ec)) { + result = Status::IOError(name, ec.message()); + } + + return result; + }; + + virtual Status GetFileSize(const std::string& fname, uint64_t* size) { + boost::system::error_code ec; + + Status result; + + *size = static_cast(boost::filesystem::file_size(fname, ec)); + if (ec != 0) { + *size = 0; + result = Status::IOError(fname, ec.message()); + } + + return result; + } + + virtual Status RenameFile(const std::string& src, const std::string& target) { + boost::system::error_code ec; + + boost::filesystem::rename(src, target, ec); + + Status result; + + if (ec != 0) { + result = Status::IOError(src, ec.message()); + } + + return result; + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = NULL; + + Status result; + + try { + if (!boost::filesystem::exists(fname)) { + std::ofstream of(fname.c_str(), std::ios_base::trunc | std::ios_base::out); + } + + assert(boost::filesystem::exists(fname)); + + boost::interprocess::file_lock fl(fname.c_str()); + BoostFileLock * my_lock = new BoostFileLock(); + my_lock->fl_ = std::move(fl); + if (!my_lock->fl_.try_lock()) { + return Status::IOError("database already in use: could not acquire exclusive lock"); + } + *lock = my_lock; + } catch (const std::exception & e) { + result = Status::IOError("lock " + fname, e.what()); + } + + return result; + } + + virtual Status UnlockFile(FileLock* lock) { + + Status result; + + try { + BoostFileLock * my_lock = static_cast(lock); + my_lock->fl_.unlock(); + delete my_lock; + } catch (const std::exception & e) { + result = Status::IOError("unlock", e.what()); + } + + 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) { + boost::system::error_code ec; + boost::filesystem::path temp_dir = + boost::filesystem::temp_directory_path(ec); + if (ec != 0) { + temp_dir = "tmp"; + } + + temp_dir /= "leveldb_tests"; + temp_dir /= boost::lexical_cast(current_process_id()); + + // Directory may already exist + CreateDir(temp_dir.generic_string()); + + *result = temp_dir.generic_string(); + + return Status::OK(); + } + +#ifndef LEVELDB_PLATFORM_WINDOWS + 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; + } +#endif + + virtual Status NewLogger(const std::string& fname, Logger** result) { + FILE* f = fopen(fname.c_str(), "wt"); + if (f == NULL) { + *result = NULL; + return Status::IOError(fname, strerror(errno)); + } else { +#ifdef LEVELDB_PLATFORM_WINDOWS + *result = new WinLogger(f); +#else + *result = new PosixLogger(f, &PosixEnv::gettid); +#endif + return Status::OK(); + } + } + + virtual uint64_t NowMicros() { + return static_cast( + boost::posix_time::microsec_clock::universal_time() + .time_of_day().total_microseconds()); + } + + virtual void SleepForMicroseconds(int micros) { + boost::this_thread::sleep(boost::posix_time::microseconds(micros)); + } + + private: + void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + exit(1); + } + } + + // BGThread() is the body of the background thread + void BGThread(); + + static void* BGThreadWrapper(void* arg) { + reinterpret_cast(arg)->BGThread(); + return NULL; + } + + boost::mutex mu_; + boost::condition_variable bgsignal_; + boost::scoped_ptr bgthread_; + + // Entry per Schedule() call + struct BGItem { void* arg; void (*function)(void*); }; + typedef std::deque BGQueue; + BGQueue queue_; +}; + +PosixEnv::PosixEnv() { } + +void PosixEnv::Schedule(void (*function)(void*), void* arg) { + boost::unique_lock lock(mu_); + + // Start background thread if necessary + if (!bgthread_) { + bgthread_.reset( + new boost::thread(boost::bind(&PosixEnv::BGThreadWrapper, this))); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; + + lock.unlock(); + + bgsignal_.notify_one(); + +} + +void PosixEnv::BGThread() { + while (true) { + // Wait until there is an item that is ready to run + boost::unique_lock lock(mu_); + + while (queue_.empty()) { + bgsignal_.wait(lock); + } + + void (*function)(void*) = queue_.front().function; + void* arg = queue_.front().arg; + queue_.pop_front(); + + lock.unlock(); + (*function)(arg); + } +} + +namespace { +struct StartThreadState { + void (*user_function)(void*); + void* arg; +}; +} + +static void* StartThreadWrapper(void* arg) { + StartThreadState* state = reinterpret_cast(arg); + state->user_function(state->arg); + delete state; + return NULL; +} + +void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { + StartThreadState* state = new StartThreadState; + state->user_function = function; + state->arg = arg; + + boost::thread t(boost::bind(&StartThreadWrapper, state)); +} + +} + +static boost::once_flag once = BOOST_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { + ::memset(global_read_only_buf, 0, sizeof(global_read_only_buf)); + default_env = new PosixEnv; +} + +Env* Env::Default() { + boost::call_once(once, InitDefaultEnv); + + return default_env; +} + +} diff --git a/src/leveldb/util/win_logger.cc b/src/leveldb/util/win_logger.cc new file mode 100644 index 000000000..834c98cc7 --- /dev/null +++ b/src/leveldb/util/win_logger.cc @@ -0,0 +1,96 @@ +// 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 "util/win_logger.h" + +#include + +namespace leveldb { + +void WinLogger::Logv(const char* format, va_list ap) { + const uint64_t thread_id = static_cast(::GetCurrentThreadId()); + + // 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; + + SYSTEMTIME st; + + // GetSystemTime returns UTC time, we want local time! + ::GetLocalTime(&st); + +#ifdef _MSC_VER + p += _snprintf_s(p, limit - p, _TRUNCATE, + "%04d/%02d/%02d-%02d:%02d:%02d.%03d %llx ", + st.wYear, + st.wMonth, + st.wDay, + st.wHour, + st.wMinute, + st.wSecond, + st.wMilliseconds, + static_cast(thread_id)); +#else +#ifdef __MINGW32__ + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%03d %llx ", + st.wYear, + st.wMonth, + st.wDay, + st.wHour, + st.wMinute, + st.wSecond, + st.wMilliseconds, + static_cast(thread_id)); +#else +#error Unable to detect Windows compiler (neither _MSC_VER nor __MINGW32__ are set) +#endif +#endif + + // Print the message + if (p < limit) { + va_list 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; + } +} + +} \ No newline at end of file diff --git a/src/leveldb/util/win_logger.h b/src/leveldb/util/win_logger.h new file mode 100644 index 000000000..b155d5c31 --- /dev/null +++ b/src/leveldb/util/win_logger.h @@ -0,0 +1,28 @@ +// 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. + +// Logger implementation for Windows + +#ifndef STORAGE_LEVELDB_UTIL_WIN_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_WIN_LOGGER_H_ + +#include +#include "leveldb/env.h" + +namespace leveldb { + +class WinLogger : public Logger { + private: + FILE* file_; + public: + explicit WinLogger(FILE* f) : file_(f) { assert(file_); } + virtual ~WinLogger() { + fclose(file_); + } + virtual void Logv(const char* format, va_list ap); + +}; + +} +#endif // STORAGE_LEVELDB_UTIL_WIN_LOGGER_H_