|
|
- // Copyright (c) 2018 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.
-
- // Prevent Windows headers from defining min/max macros and instead
- // use STL.
- #ifndef NOMINMAX
- #define NOMINMAX
- #endif // ifndef NOMINMAX
- #include <windows.h>
-
- #include <algorithm>
- #include <atomic>
- #include <chrono>
- #include <condition_variable>
- #include <cstddef>
- #include <cstdint>
- #include <cstdlib>
- #include <cstring>
- #include <memory>
- #include <mutex>
- #include <queue>
- #include <sstream>
- #include <string>
- #include <vector>
-
- #include "leveldb/env.h"
- #include "leveldb/slice.h"
- #include "port/port.h"
- #include "port/thread_annotations.h"
- #include "util/env_windows_test_helper.h"
- #include "util/logging.h"
- #include "util/mutexlock.h"
- #include "util/windows_logger.h"
-
- namespace leveldb {
-
- namespace {
-
- constexpr const size_t kWritableFileBufferSize = 65536;
-
- // Up to 1000 mmaps for 64-bit binaries; none for 32-bit.
- constexpr int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0;
-
- // Can be set by by EnvWindowsTestHelper::SetReadOnlyMMapLimit().
- int g_mmap_limit = kDefaultMmapLimit;
-
- std::string GetWindowsErrorMessage(DWORD error_code) {
- std::string message;
- char* error_text = nullptr;
- // Use MBCS version of FormatMessage to match return value.
- size_t error_text_size = ::FormatMessageA(
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- reinterpret_cast<char*>(&error_text), 0, nullptr);
- if (!error_text) {
- return message;
- }
- message.assign(error_text, error_text_size);
- ::LocalFree(error_text);
- return message;
- }
-
- Status WindowsError(const std::string& context, DWORD error_code) {
- if (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND)
- return Status::NotFound(context, GetWindowsErrorMessage(error_code));
- return Status::IOError(context, GetWindowsErrorMessage(error_code));
- }
-
- class ScopedHandle {
- public:
- ScopedHandle(HANDLE handle) : handle_(handle) {}
- ScopedHandle(const ScopedHandle&) = delete;
- ScopedHandle(ScopedHandle&& other) noexcept : handle_(other.Release()) {}
- ~ScopedHandle() { Close(); }
-
- ScopedHandle& operator=(const ScopedHandle&) = delete;
-
- ScopedHandle& operator=(ScopedHandle&& rhs) noexcept {
- if (this != &rhs) handle_ = rhs.Release();
- return *this;
- }
-
- bool Close() {
- if (!is_valid()) {
- return true;
- }
- HANDLE h = handle_;
- handle_ = INVALID_HANDLE_VALUE;
- return ::CloseHandle(h);
- }
-
- bool is_valid() const {
- return handle_ != INVALID_HANDLE_VALUE && handle_ != nullptr;
- }
-
- HANDLE get() const { return handle_; }
-
- HANDLE Release() {
- HANDLE h = handle_;
- handle_ = INVALID_HANDLE_VALUE;
- return h;
- }
-
- private:
- HANDLE handle_;
- };
-
- // Helper class to limit resource usage to avoid exhaustion.
- // Currently used to limit read-only file descriptors and mmap file usage
- // so that we do not run out of file descriptors or virtual memory, or run into
- // kernel performance problems for very large databases.
- class Limiter {
- public:
- // Limit maximum number of resources to |max_acquires|.
- Limiter(int max_acquires)
- :
- #if !defined(NDEBUG)
- max_acquires_(max_acquires),
- #endif // !defined(NDEBUG)
- acquires_allowed_(max_acquires) {
- assert(max_acquires >= 0);
- }
-
- Limiter(const Limiter&) = delete;
- Limiter operator=(const Limiter&) = delete;
-
- // If another resource is available, acquire it and return true.
- // Else return false.
- bool Acquire() {
- int old_acquires_allowed =
- acquires_allowed_.fetch_sub(1, std::memory_order_relaxed);
-
- if (old_acquires_allowed > 0) return true;
-
- acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
- return false;
- }
-
- // Release a resource acquired by a previous call to Acquire() that returned
- // true.
- void Release() {
- int old_acquires_allowed =
- acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
-
- // Silence compiler warnings about unused arguments when NDEBUG is defined.
- (void)old_acquires_allowed;
- // If the check below fails, Release() was called more times than acquire.
- assert(old_acquires_allowed < max_acquires_);
- }
-
- private:
- #if !defined(NDEBUG)
- // Catches an excessive number of Release() calls.
- const int max_acquires_;
- #endif // !defined(NDEBUG)
-
- // The number of available resources.
- //
- // This is a counter and is not tied to the invariants of any other class, so
- // it can be operated on safely using std::memory_order_relaxed.
- std::atomic<int> acquires_allowed_;
- };
-
- class WindowsSequentialFile : public SequentialFile {
- public:
- WindowsSequentialFile(std::string filename, ScopedHandle handle)
- : handle_(std::move(handle)), filename_(std::move(filename)) {}
- ~WindowsSequentialFile() override {}
-
- Status Read(size_t n, Slice* result, char* scratch) override {
- DWORD bytes_read;
- // DWORD is 32-bit, but size_t could technically be larger. However leveldb
- // files are limited to leveldb::Options::max_file_size which is clamped to
- // 1<<30 or 1 GiB.
- assert(n <= std::numeric_limits<DWORD>::max());
- if (!::ReadFile(handle_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
- nullptr)) {
- return WindowsError(filename_, ::GetLastError());
- }
-
- *result = Slice(scratch, bytes_read);
- return Status::OK();
- }
-
- Status Skip(uint64_t n) override {
- LARGE_INTEGER distance;
- distance.QuadPart = n;
- if (!::SetFilePointerEx(handle_.get(), distance, nullptr, FILE_CURRENT)) {
- return WindowsError(filename_, ::GetLastError());
- }
- return Status::OK();
- }
-
- private:
- const ScopedHandle handle_;
- const std::string filename_;
- };
-
- class WindowsRandomAccessFile : public RandomAccessFile {
- public:
- WindowsRandomAccessFile(std::string filename, ScopedHandle handle)
- : handle_(std::move(handle)), filename_(std::move(filename)) {}
-
- ~WindowsRandomAccessFile() override = default;
-
- Status Read(uint64_t offset, size_t n, Slice* result,
- char* scratch) const override {
- DWORD bytes_read = 0;
- OVERLAPPED overlapped = {0};
-
- overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
- overlapped.Offset = static_cast<DWORD>(offset);
- if (!::ReadFile(handle_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
- &overlapped)) {
- DWORD error_code = ::GetLastError();
- if (error_code != ERROR_HANDLE_EOF) {
- *result = Slice(scratch, 0);
- return Status::IOError(filename_, GetWindowsErrorMessage(error_code));
- }
- }
-
- *result = Slice(scratch, bytes_read);
- return Status::OK();
- }
-
- private:
- const ScopedHandle handle_;
- const std::string filename_;
- };
-
- class WindowsMmapReadableFile : public RandomAccessFile {
- public:
- // base[0,length-1] contains the mmapped contents of the file.
- WindowsMmapReadableFile(std::string filename, char* mmap_base, size_t length,
- Limiter* mmap_limiter)
- : mmap_base_(mmap_base),
- length_(length),
- mmap_limiter_(mmap_limiter),
- filename_(std::move(filename)) {}
-
- ~WindowsMmapReadableFile() override {
- ::UnmapViewOfFile(mmap_base_);
- mmap_limiter_->Release();
- }
-
- Status Read(uint64_t offset, size_t n, Slice* result,
- char* scratch) const override {
- if (offset + n > length_) {
- *result = Slice();
- return WindowsError(filename_, ERROR_INVALID_PARAMETER);
- }
-
- *result = Slice(mmap_base_ + offset, n);
- return Status::OK();
- }
-
- private:
- char* const mmap_base_;
- const size_t length_;
- Limiter* const mmap_limiter_;
- const std::string filename_;
- };
-
- class WindowsWritableFile : public WritableFile {
- public:
- WindowsWritableFile(std::string filename, ScopedHandle handle)
- : pos_(0), handle_(std::move(handle)), filename_(std::move(filename)) {}
-
- ~WindowsWritableFile() override = default;
-
- Status Append(const Slice& data) override {
- size_t write_size = data.size();
- const char* write_data = data.data();
-
- // Fit as much as possible into buffer.
- size_t copy_size = std::min(write_size, kWritableFileBufferSize - pos_);
- std::memcpy(buf_ + pos_, write_data, copy_size);
- write_data += copy_size;
- write_size -= copy_size;
- pos_ += copy_size;
- if (write_size == 0) {
- return Status::OK();
- }
-
- // Can't fit in buffer, so need to do at least one write.
- Status status = FlushBuffer();
- if (!status.ok()) {
- return status;
- }
-
- // Small writes go to buffer, large writes are written directly.
- if (write_size < kWritableFileBufferSize) {
- std::memcpy(buf_, write_data, write_size);
- pos_ = write_size;
- return Status::OK();
- }
- return WriteUnbuffered(write_data, write_size);
- }
-
- Status Close() override {
- Status status = FlushBuffer();
- if (!handle_.Close() && status.ok()) {
- status = WindowsError(filename_, ::GetLastError());
- }
- return status;
- }
-
- Status Flush() override { return FlushBuffer(); }
-
- Status Sync() override {
- // On Windows no need to sync parent directory. Its metadata will be updated
- // via the creation of the new file, without an explicit sync.
-
- Status status = FlushBuffer();
- if (!status.ok()) {
- return status;
- }
-
- if (!::FlushFileBuffers(handle_.get())) {
- return Status::IOError(filename_,
- GetWindowsErrorMessage(::GetLastError()));
- }
- return Status::OK();
- }
-
- private:
- Status FlushBuffer() {
- Status status = WriteUnbuffered(buf_, pos_);
- pos_ = 0;
- return status;
- }
-
- Status WriteUnbuffered(const char* data, size_t size) {
- DWORD bytes_written;
- if (!::WriteFile(handle_.get(), data, static_cast<DWORD>(size),
- &bytes_written, nullptr)) {
- return Status::IOError(filename_,
- GetWindowsErrorMessage(::GetLastError()));
- }
- return Status::OK();
- }
-
- // buf_[0, pos_-1] contains data to be written to handle_.
- char buf_[kWritableFileBufferSize];
- size_t pos_;
-
- ScopedHandle handle_;
- const std::string filename_;
- };
-
- // Lock or unlock the entire file as specified by |lock|. Returns true
- // when successful, false upon failure. Caller should call ::GetLastError()
- // to determine cause of failure
- bool LockOrUnlock(HANDLE handle, bool lock) {
- if (lock) {
- return ::LockFile(handle,
- /*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0,
- /*nNumberOfBytesToLockLow=*/MAXDWORD,
- /*nNumberOfBytesToLockHigh=*/MAXDWORD);
- } else {
- return ::UnlockFile(handle,
- /*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0,
- /*nNumberOfBytesToLockLow=*/MAXDWORD,
- /*nNumberOfBytesToLockHigh=*/MAXDWORD);
- }
- }
-
- class WindowsFileLock : public FileLock {
- public:
- WindowsFileLock(ScopedHandle handle, std::string filename)
- : handle_(std::move(handle)), filename_(std::move(filename)) {}
-
- const ScopedHandle& handle() const { return handle_; }
- const std::string& filename() const { return filename_; }
-
- private:
- const ScopedHandle handle_;
- const std::string filename_;
- };
-
- class WindowsEnv : public Env {
- public:
- WindowsEnv();
- ~WindowsEnv() override {
- static const char msg[] =
- "WindowsEnv singleton destroyed. Unsupported behavior!\n";
- std::fwrite(msg, 1, sizeof(msg), stderr);
- std::abort();
- }
-
- Status NewSequentialFile(const std::string& filename,
- SequentialFile** result) override {
- *result = nullptr;
- DWORD desired_access = GENERIC_READ;
- DWORD share_mode = FILE_SHARE_READ;
- ScopedHandle handle = ::CreateFileA(
- filename.c_str(), desired_access, share_mode,
- /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
- /*hTemplateFile=*/nullptr);
- if (!handle.is_valid()) {
- return WindowsError(filename, ::GetLastError());
- }
-
- *result = new WindowsSequentialFile(filename, std::move(handle));
- return Status::OK();
- }
-
- Status NewRandomAccessFile(const std::string& filename,
- RandomAccessFile** result) override {
- *result = nullptr;
- DWORD desired_access = GENERIC_READ;
- DWORD share_mode = FILE_SHARE_READ;
- ScopedHandle handle =
- ::CreateFileA(filename.c_str(), desired_access, share_mode,
- /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING,
- FILE_ATTRIBUTE_READONLY,
- /*hTemplateFile=*/nullptr);
- if (!handle.is_valid()) {
- return WindowsError(filename, ::GetLastError());
- }
- if (!mmap_limiter_.Acquire()) {
- *result = new WindowsRandomAccessFile(filename, std::move(handle));
- return Status::OK();
- }
-
- LARGE_INTEGER file_size;
- Status status;
- if (!::GetFileSizeEx(handle.get(), &file_size)) {
- mmap_limiter_.Release();
- return WindowsError(filename, ::GetLastError());
- }
-
- ScopedHandle mapping =
- ::CreateFileMappingA(handle.get(),
- /*security attributes=*/nullptr, PAGE_READONLY,
- /*dwMaximumSizeHigh=*/0,
- /*dwMaximumSizeLow=*/0,
- /*lpName=*/nullptr);
- if (mapping.is_valid()) {
- void* mmap_base = ::MapViewOfFile(mapping.get(), FILE_MAP_READ,
- /*dwFileOffsetHigh=*/0,
- /*dwFileOffsetLow=*/0,
- /*dwNumberOfBytesToMap=*/0);
- if (mmap_base) {
- *result = new WindowsMmapReadableFile(
- filename, reinterpret_cast<char*>(mmap_base),
- static_cast<size_t>(file_size.QuadPart), &mmap_limiter_);
- return Status::OK();
- }
- }
- mmap_limiter_.Release();
- return WindowsError(filename, ::GetLastError());
- }
-
- Status NewWritableFile(const std::string& filename,
- WritableFile** result) override {
- DWORD desired_access = GENERIC_WRITE;
- DWORD share_mode = 0; // Exclusive access.
- ScopedHandle handle = ::CreateFileA(
- filename.c_str(), desired_access, share_mode,
- /*lpSecurityAttributes=*/nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
- /*hTemplateFile=*/nullptr);
- if (!handle.is_valid()) {
- *result = nullptr;
- return WindowsError(filename, ::GetLastError());
- }
-
- *result = new WindowsWritableFile(filename, std::move(handle));
- return Status::OK();
- }
-
- Status NewAppendableFile(const std::string& filename,
- WritableFile** result) override {
- DWORD desired_access = FILE_APPEND_DATA;
- DWORD share_mode = 0; // Exclusive access.
- ScopedHandle handle = ::CreateFileA(
- filename.c_str(), desired_access, share_mode,
- /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
- /*hTemplateFile=*/nullptr);
- if (!handle.is_valid()) {
- *result = nullptr;
- return WindowsError(filename, ::GetLastError());
- }
-
- *result = new WindowsWritableFile(filename, std::move(handle));
- return Status::OK();
- }
-
- bool FileExists(const std::string& filename) override {
- return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES;
- }
-
- Status GetChildren(const std::string& directory_path,
- std::vector<std::string>* result) override {
- const std::string find_pattern = directory_path + "\\*";
- WIN32_FIND_DATAA find_data;
- HANDLE dir_handle = ::FindFirstFileA(find_pattern.c_str(), &find_data);
- if (dir_handle == INVALID_HANDLE_VALUE) {
- DWORD last_error = ::GetLastError();
- if (last_error == ERROR_FILE_NOT_FOUND) {
- return Status::OK();
- }
- return WindowsError(directory_path, last_error);
- }
- do {
- char base_name[_MAX_FNAME];
- char ext[_MAX_EXT];
-
- if (!_splitpath_s(find_data.cFileName, nullptr, 0, nullptr, 0, base_name,
- ARRAYSIZE(base_name), ext, ARRAYSIZE(ext))) {
- result->emplace_back(std::string(base_name) + ext);
- }
- } while (::FindNextFileA(dir_handle, &find_data));
- DWORD last_error = ::GetLastError();
- ::FindClose(dir_handle);
- if (last_error != ERROR_NO_MORE_FILES) {
- return WindowsError(directory_path, last_error);
- }
- return Status::OK();
- }
-
- Status RemoveFile(const std::string& filename) override {
- if (!::DeleteFileA(filename.c_str())) {
- return WindowsError(filename, ::GetLastError());
- }
- return Status::OK();
- }
-
- Status CreateDir(const std::string& dirname) override {
- if (!::CreateDirectoryA(dirname.c_str(), nullptr)) {
- return WindowsError(dirname, ::GetLastError());
- }
- return Status::OK();
- }
-
- Status RemoveDir(const std::string& dirname) override {
- if (!::RemoveDirectoryA(dirname.c_str())) {
- return WindowsError(dirname, ::GetLastError());
- }
- return Status::OK();
- }
-
- Status GetFileSize(const std::string& filename, uint64_t* size) override {
- WIN32_FILE_ATTRIBUTE_DATA file_attributes;
- if (!::GetFileAttributesExA(filename.c_str(), GetFileExInfoStandard,
- &file_attributes)) {
- return WindowsError(filename, ::GetLastError());
- }
- ULARGE_INTEGER file_size;
- file_size.HighPart = file_attributes.nFileSizeHigh;
- file_size.LowPart = file_attributes.nFileSizeLow;
- *size = file_size.QuadPart;
- return Status::OK();
- }
-
- Status RenameFile(const std::string& from, const std::string& to) override {
- // Try a simple move first. It will only succeed when |to| doesn't already
- // exist.
- if (::MoveFileA(from.c_str(), to.c_str())) {
- return Status::OK();
- }
- DWORD move_error = ::GetLastError();
-
- // Try the full-blown replace if the move fails, as ReplaceFile will only
- // succeed when |to| does exist. When writing to a network share, we may not
- // be able to change the ACLs. Ignore ACL errors then
- // (REPLACEFILE_IGNORE_MERGE_ERRORS).
- if (::ReplaceFileA(to.c_str(), from.c_str(), /*lpBackupFileName=*/nullptr,
- REPLACEFILE_IGNORE_MERGE_ERRORS,
- /*lpExclude=*/nullptr, /*lpReserved=*/nullptr)) {
- return Status::OK();
- }
- DWORD replace_error = ::GetLastError();
- // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that
- // |to| does not exist. In this case, the more relevant error comes from the
- // call to MoveFile.
- if (replace_error == ERROR_FILE_NOT_FOUND ||
- replace_error == ERROR_PATH_NOT_FOUND) {
- return WindowsError(from, move_error);
- } else {
- return WindowsError(from, replace_error);
- }
- }
-
- Status LockFile(const std::string& filename, FileLock** lock) override {
- *lock = nullptr;
- Status result;
- ScopedHandle handle = ::CreateFileA(
- filename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
- /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
- nullptr);
- if (!handle.is_valid()) {
- result = WindowsError(filename, ::GetLastError());
- } else if (!LockOrUnlock(handle.get(), true)) {
- result = WindowsError("lock " + filename, ::GetLastError());
- } else {
- *lock = new WindowsFileLock(std::move(handle), filename);
- }
- return result;
- }
-
- Status UnlockFile(FileLock* lock) override {
- WindowsFileLock* windows_file_lock =
- reinterpret_cast<WindowsFileLock*>(lock);
- if (!LockOrUnlock(windows_file_lock->handle().get(), false)) {
- return WindowsError("unlock " + windows_file_lock->filename(),
- ::GetLastError());
- }
- delete windows_file_lock;
- return Status::OK();
- }
-
- void Schedule(void (*background_work_function)(void* background_work_arg),
- void* background_work_arg) override;
-
- void StartThread(void (*thread_main)(void* thread_main_arg),
- void* thread_main_arg) override {
- std::thread new_thread(thread_main, thread_main_arg);
- new_thread.detach();
- }
-
- Status GetTestDirectory(std::string* result) override {
- const char* env = getenv("TEST_TMPDIR");
- if (env && env[0] != '\0') {
- *result = env;
- return Status::OK();
- }
-
- char tmp_path[MAX_PATH];
- if (!GetTempPathA(ARRAYSIZE(tmp_path), tmp_path)) {
- return WindowsError("GetTempPath", ::GetLastError());
- }
- std::stringstream ss;
- ss << tmp_path << "leveldbtest-" << std::this_thread::get_id();
- *result = ss.str();
-
- // Directory may already exist
- CreateDir(*result);
- return Status::OK();
- }
-
- Status NewLogger(const std::string& filename, Logger** result) override {
- std::FILE* fp = std::fopen(filename.c_str(), "wN");
- if (fp == nullptr) {
- *result = nullptr;
- return WindowsError(filename, ::GetLastError());
- } else {
- *result = new WindowsLogger(fp);
- return Status::OK();
- }
- }
-
- uint64_t NowMicros() override {
- // GetSystemTimeAsFileTime typically has a resolution of 10-20 msec.
- // TODO(cmumford): Switch to GetSystemTimePreciseAsFileTime which is
- // available in Windows 8 and later.
- FILETIME ft;
- ::GetSystemTimeAsFileTime(&ft);
- // Each tick represents a 100-nanosecond intervals since January 1, 1601
- // (UTC).
- uint64_t num_ticks =
- (static_cast<uint64_t>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
- return num_ticks / 10;
- }
-
- void SleepForMicroseconds(int micros) override {
- std::this_thread::sleep_for(std::chrono::microseconds(micros));
- }
-
- private:
- void BackgroundThreadMain();
-
- static void BackgroundThreadEntryPoint(WindowsEnv* env) {
- env->BackgroundThreadMain();
- }
-
- // Stores the work item data in a Schedule() call.
- //
- // Instances are constructed on the thread calling Schedule() and used on the
- // background thread.
- //
- // This structure is thread-safe because it is immutable.
- struct BackgroundWorkItem {
- explicit BackgroundWorkItem(void (*function)(void* arg), void* arg)
- : function(function), arg(arg) {}
-
- void (*const function)(void*);
- void* const arg;
- };
-
- port::Mutex background_work_mutex_;
- port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_);
- bool started_background_thread_ GUARDED_BY(background_work_mutex_);
-
- std::queue<BackgroundWorkItem> background_work_queue_
- GUARDED_BY(background_work_mutex_);
-
- Limiter mmap_limiter_; // Thread-safe.
- };
-
- // Return the maximum number of concurrent mmaps.
- int MaxMmaps() { return g_mmap_limit; }
-
- WindowsEnv::WindowsEnv()
- : background_work_cv_(&background_work_mutex_),
- started_background_thread_(false),
- mmap_limiter_(MaxMmaps()) {}
-
- void WindowsEnv::Schedule(
- void (*background_work_function)(void* background_work_arg),
- void* background_work_arg) {
- background_work_mutex_.Lock();
-
- // Start the background thread, if we haven't done so already.
- if (!started_background_thread_) {
- started_background_thread_ = true;
- std::thread background_thread(WindowsEnv::BackgroundThreadEntryPoint, this);
- background_thread.detach();
- }
-
- // If the queue is empty, the background thread may be waiting for work.
- if (background_work_queue_.empty()) {
- background_work_cv_.Signal();
- }
-
- background_work_queue_.emplace(background_work_function, background_work_arg);
- background_work_mutex_.Unlock();
- }
-
- void WindowsEnv::BackgroundThreadMain() {
- while (true) {
- background_work_mutex_.Lock();
-
- // Wait until there is work to be done.
- while (background_work_queue_.empty()) {
- background_work_cv_.Wait();
- }
-
- assert(!background_work_queue_.empty());
- auto background_work_function = background_work_queue_.front().function;
- void* background_work_arg = background_work_queue_.front().arg;
- background_work_queue_.pop();
-
- background_work_mutex_.Unlock();
- background_work_function(background_work_arg);
- }
- }
-
- // Wraps an Env instance whose destructor is never created.
- //
- // Intended usage:
- // using PlatformSingletonEnv = SingletonEnv<PlatformEnv>;
- // void ConfigurePosixEnv(int param) {
- // PlatformSingletonEnv::AssertEnvNotInitialized();
- // // set global configuration flags.
- // }
- // Env* Env::Default() {
- // static PlatformSingletonEnv default_env;
- // return default_env.env();
- // }
- template <typename EnvType>
- class SingletonEnv {
- public:
- SingletonEnv() {
- #if !defined(NDEBUG)
- env_initialized_.store(true, std::memory_order_relaxed);
- #endif // !defined(NDEBUG)
- static_assert(sizeof(env_storage_) >= sizeof(EnvType),
- "env_storage_ will not fit the Env");
- static_assert(alignof(decltype(env_storage_)) >= alignof(EnvType),
- "env_storage_ does not meet the Env's alignment needs");
- new (&env_storage_) EnvType();
- }
- ~SingletonEnv() = default;
-
- SingletonEnv(const SingletonEnv&) = delete;
- SingletonEnv& operator=(const SingletonEnv&) = delete;
-
- Env* env() { return reinterpret_cast<Env*>(&env_storage_); }
-
- static void AssertEnvNotInitialized() {
- #if !defined(NDEBUG)
- assert(!env_initialized_.load(std::memory_order_relaxed));
- #endif // !defined(NDEBUG)
- }
-
- private:
- typename std::aligned_storage<sizeof(EnvType), alignof(EnvType)>::type
- env_storage_;
- #if !defined(NDEBUG)
- static std::atomic<bool> env_initialized_;
- #endif // !defined(NDEBUG)
- };
-
- #if !defined(NDEBUG)
- template <typename EnvType>
- std::atomic<bool> SingletonEnv<EnvType>::env_initialized_;
- #endif // !defined(NDEBUG)
-
- using WindowsDefaultEnv = SingletonEnv<WindowsEnv>;
-
- } // namespace
-
- void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) {
- WindowsDefaultEnv::AssertEnvNotInitialized();
- g_mmap_limit = limit;
- }
-
- Env* Env::Default() {
- static WindowsDefaultEnv env_container;
- return env_container.env();
- }
-
- } // namespace leveldb
|