diff options
author | Gabor Cselle <gabor@google.com> | 2011-09-01 13:41:15 -0700 |
---|---|---|
committer | Gabor Cselle <gabor@google.com> | 2011-09-01 13:41:15 -0700 |
commit | db88e3e3594f58d3ae99562f7a22da318dc71842 (patch) | |
tree | b336cc1054e1e2c04e95150065330bb6004e1a80 | |
parent | 72630236513e7384cb0a2e8fffcae232135a5adc (diff) | |
download | leveldb-db88e3e3594f58d3ae99562f7a22da318dc71842.tar.gz |
Windows port for LevelDB based on Boost libraries. See files README and WINDOWS for details.
-rw-r--r-- | README | 11 | ||||
-rw-r--r-- | WINDOWS | 42 | ||||
-rw-r--r-- | port/port.h | 2 | ||||
-rwxr-xr-x | port/port_win.cc | 145 | ||||
-rwxr-xr-x | port/port_win.h | 148 | ||||
-rwxr-xr-x | util/env_boost.cc | 589 | ||||
-rw-r--r-- | util/posix_logger.h | 9 | ||||
-rwxr-xr-x | util/win_logger.cc | 79 | ||||
-rwxr-xr-x | util/win_logger.h | 28 |
9 files changed, 1049 insertions, 4 deletions
@@ -1,3 +1,14 @@ +Windows port for LevelDB based on Boost libraries. + +See file "WINDOWS" for instructions on how to build this in Windows. +- You'll need to install some Boost libraries (www.boost.org) to build against +- You'll need to create a Microsoft Visual C++ project to build this +- The WINDOWS file explains both of these processes. + +We're looking for volunteers to build a true Win32 port of LevelDB for Windows. + +==== + leveldb: A key-value store Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) @@ -0,0 +1,42 @@ +INSTRUCTIONS FOR BUILDING LEVELDB ON WINDOWS / MSVC++ +(Tested with Microsoft Visual C++ 2010 Express) + +1. Install Boost + You'll need the Boost libraries to compile LevelDB on Windows: + http://www.boost.org/users/download/ + You'll need at least the following Boost packages: + - date_time + - filesystem + - thread + - interprocess + +2. To create your LevelDB project, choose: + New -> "Project From Existing Code" + and point Visual Studio to the leveldb root directory. + +3. To just build the benchmarking tools, choose: + Project Type: "Console application project" + +4. In the configuration settings, make sure you include + Preprocessor definitions: LEVELDB_PLATFORM_WINDOWS;OS_WIN + You can also add these later in: + Project -> Properties -> + Configuration Properties -> C/C++ -> Preprocessor Definitions + + Include the root directory of your LevelDB sources in header search paths. + You can also add this later in: + Project -> Properties -> + Configuration Properties -> C/C++ -> Additional Include Directories + +5. Add boost/lib directory to Linker paths: + Project -> Properties -> Linker -> General -> Additional Library Dependencies + +6. Manually exclude the following files from the build + (Solution Explorer -> right-click on file -> Exclude from Project) + - port/port_android.cc + - port/port_posix.cc + - util/env_chromium.cc + - util/env_posix.cc + +7. Manually exclude all the *_test.cc and *_bench.cc files you don't want + to build. There should only be one .cc file with a main() in your project.
\ No newline at end of file diff --git a/port/port.h b/port/port.h index 816826b..8bf2027 100644 --- a/port/port.h +++ b/port/port.h @@ -16,6 +16,8 @@ # include "port/port_chromium.h" #elif defined(LEVELDB_PLATFORM_ANDROID) # include "port/port_android.h" +#elif defined(LEVELDB_PLATFORM_WINDOWS) +# include "port/port_posix.h" #endif #endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/port/port_win.cc b/port/port_win.cc new file mode 100755 index 0000000..9d3628f --- /dev/null +++ b/port/port_win.cc @@ -0,0 +1,145 @@ +// 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 <windows.h> +#include <cassert> + +namespace leveldb { +namespace port { + +Mutex::Mutex() : + cs_(nullptr) { + assert(!cs_); + cs_ = static_cast<void *>(new CRITICAL_SECTION()); + ::InitializeCriticalSection(static_cast<CRITICAL_SECTION *>(cs_)); + assert(cs_); +} + +Mutex::~Mutex() { + assert(cs_); + ::DeleteCriticalSection(static_cast<CRITICAL_SECTION *>(cs_)); + delete static_cast<CRITICAL_SECTION *>(cs_); + cs_ = nullptr; + assert(!cs_); +} + +void Mutex::Lock() { + assert(cs_); + ::EnterCriticalSection(static_cast<CRITICAL_SECTION *>(cs_)); +} + +void Mutex::Unlock() { + assert(cs_); + ::LeaveCriticalSection(static_cast<CRITICAL_SECTION *>(cs_)); +} + +void Mutex::AssertHeld() { + assert(cs_); + assert(1); +} + +CondVar::CondVar(Mutex* mu) : + waiting_(0), + mu_(mu), + sem1_(::CreateSemaphore(NULL, 0, 10000, NULL)), + sem2_(::CreateSemaphore(NULL, 0, 10000, NULL)) { + assert(mu_); +} + +CondVar::~CondVar() { + ::CloseHandle(sem1_); + ::CloseHandle(sem2_); +} + +void CondVar::Wait() { + mu_->AssertHeld(); + + wait_mtx_.Lock(); + ++waiting_; + wait_mtx_.Unlock(); + + mu_->Unlock(); + + // initiate handshake + ::WaitForSingleObject(sem1_, INFINITE); + ::ReleaseSemaphore(sem2_, 1, NULL); + mu_->Lock(); +} + +void CondVar::Signal() { + wait_mtx_.Lock(); + if (waiting_ > 0) { + --waiting_; + + // finalize handshake + ::ReleaseSemaphore(sem1_, 1, NULL); + ::WaitForSingleObject(sem2_, INFINITE); + } + wait_mtx_.Unlock(); +} + +void CondVar::SignalAll() { + wait_mtx_.Lock(); + for(long i = 0; i < waiting_; ++i) { + ::ReleaseSemaphore(sem1_, 1, NULL); + while(waiting_ > 0) { + --waiting_; + ::WaitForSingleObject(sem2_, INFINITE); + } + } + 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; +} + +} +} diff --git a/port/port_win.h b/port/port_win.h new file mode 100755 index 0000000..2ffa62c --- /dev/null +++ b/port/port_win.h @@ -0,0 +1,148 @@ +// 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_ + +#define snprintf _snprintf +#define close _close +#define fread_unlocked _fread_nolock + +#include <string> + +#include <stdint.h> + +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 + // we use opaque void * to avoid including windows.h in port_win.h + void * cs_; + + // 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 Andrew D. Birrell in 2003 +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + Mutex* mu_; + + Mutex wait_mtx_; + long waiting_; + + void * sem1_; + void * sem2_; + + +}; + +// 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); +}; + +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/util/env_boost.cc b/util/env_boost.cc new file mode 100755 index 0000000..1c419e3 --- /dev/null +++ b/util/env_boost.cc @@ -0,0 +1,589 @@ +// 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> + +#ifdef WIN32 +#include <windows.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <io.h> +#else +#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 <sys/param.h> +#include <time.h> +#include <unistd.h> +#endif +#if defined(LEVELDB_PLATFORM_ANDROID) +#include <sys/stat.h> +#endif +#include "leveldb/env.h" +#include "leveldb/slice.h" + +#ifdef WIN32 +#include "util/win_logger.h" +#else +#include "util/posix_logger.h" +#endif +#include "port/port.h" +#include "util/logging.h" + +#ifdef __linux +#include <sys/sysinfo.h> +#include <linux/unistd.h> +#endif + +#include <fstream> + +// Boost includes - see WINDOWS file to see which modules to install +#include <boost/date_time/gregorian/gregorian.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/filesystem/convenience.hpp> +#include <boost/thread/once.hpp> +#include <boost/thread/thread.hpp> +#include <boost/bind.hpp> +#include <boost/scoped_ptr.hpp> +#include <boost/interprocess/sync/file_lock.hpp> +#include <boost/thread/condition_variable.hpp> + +namespace leveldb { +namespace { + +// returns the ID of the current process +static boost::uint32_t current_process_id(void) { +#ifdef _WIN32 + return static_cast<boost::uint32_t>(::GetCurrentProcessId()); +#else + return static_cast<boost::uint32_t>(::getpid()); +#endif +} + +// returns the ID of the current thread +static boost::uint32_t current_thread_id(void) { +#ifdef _WIN32 + return static_cast<boost::uint32_t>(::GetCurrentThreadId()); +#else +#ifdef __linux + return static_cast<boost::uint32_t>(::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; +#ifdef BSD + // fread_unlocked doesn't exist on FreeBSD + 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 WIN32 + // no pread on Windows so we emulate it with a mutex + boost::unique_lock<boost::mutex> 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<off_t>(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 WIN32 + 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<std::string>* result) { + result->clear(); + + boost::system::error_code ec; + boost::filesystem::directory_iterator current(dir, ec); + if (ec) { + 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) { + 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<uint64_t>(boost::filesystem::file_size(fname, ec)); + if (ec) { + *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) { + 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, 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); + my_lock->fl_.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<BoostFileLock *>(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) { + temp_dir = "tmp"; + } + + temp_dir /= "leveldb_tests"; + temp_dir /= boost::lexical_cast<std::string>(current_process_id()); + + // Directory may already exist + CreateDir(temp_dir.generic_string()); + + *result = temp_dir.generic_string(); + + return Status::OK(); + } + +#ifndef WIN32 + 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 WIN32 + *result = new WinLogger(f); +#else + *result = new PosixLogger(f, &PosixEnv::gettid); +#endif + return Status::OK(); + } + } + + virtual uint64_t NowMicros() { + return static_cast<uint64_t>( + 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<PosixEnv*>(arg)->BGThread(); + return NULL; + } + + boost::mutex mu_; + boost::condition_variable bgsignal_; + boost::scoped_ptr<boost::thread> bgthread_; + + // Entry per Schedule() call + struct BGItem { void* arg; void (*function)(void*); }; + typedef std::deque<BGItem> BGQueue; + BGQueue queue_; +}; + +PosixEnv::PosixEnv() { } + +void PosixEnv::Schedule(void (*function)(void*), void* arg) { + boost::unique_lock<boost::mutex> 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<boost::mutex> 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<StartThreadState*>(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/util/posix_logger.h b/util/posix_logger.h index 0dbdeaa..4effc7f 100644 --- a/util/posix_logger.h +++ b/util/posix_logger.h @@ -1,8 +1,9 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// Author: sanjay@google.com (Sanjay Ghemawat) -// +// 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 that can be shared by all environments -// where enough posix functionality is available. +// where enough Posix functionality is available. #ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ #define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ diff --git a/util/win_logger.cc b/util/win_logger.cc new file mode 100755 index 0000000..47ffd8e --- /dev/null +++ b/util/win_logger.cc @@ -0,0 +1,79 @@ +// 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 <windows.h> + +namespace leveldb { + +void WinLogger::Logv(const char* format, va_list ap) { + const uint64_t thread_id = static_cast<uint64_t>(::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); + + 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<long long unsigned int>(thread_id)); + + // 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/util/win_logger.h b/util/win_logger.h new file mode 100755 index 0000000..b155d5c --- /dev/null +++ b/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 <stdio.h> +#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_ |