Align EnvPosix and EnvWindows.
Fixes #695. PiperOrigin-RevId: 252895299
This commit is contained in:
parent
69061b464a
commit
e0d5f83a4f
@ -45,7 +45,7 @@ int g_open_read_only_file_limit = -1;
|
|||||||
// Up to 1000 mmap regions for 64-bit binaries; none for 32-bit.
|
// Up to 1000 mmap regions for 64-bit binaries; none for 32-bit.
|
||||||
constexpr const int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0;
|
constexpr const int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0;
|
||||||
|
|
||||||
// Can be set using EnvPosixTestHelper::SetReadOnlyMMapLimit.
|
// Can be set using EnvPosixTestHelper::SetReadOnlyMMapLimit().
|
||||||
int g_mmap_limit = kDefaultMmapLimit;
|
int g_mmap_limit = kDefaultMmapLimit;
|
||||||
|
|
||||||
// Common flags defined for all posix open operations
|
// Common flags defined for all posix open operations
|
||||||
@ -491,7 +491,8 @@ class PosixEnv : public Env {
|
|||||||
public:
|
public:
|
||||||
PosixEnv();
|
PosixEnv();
|
||||||
~PosixEnv() override {
|
~PosixEnv() override {
|
||||||
static char msg[] = "PosixEnv singleton destroyed. Unsupported behavior!\n";
|
static const char msg[] =
|
||||||
|
"PosixEnv singleton destroyed. Unsupported behavior!\n";
|
||||||
std::fwrite(msg, 1, sizeof(msg), stderr);
|
std::fwrite(msg, 1, sizeof(msg), stderr);
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
@ -663,7 +664,10 @@ class PosixEnv : public Env {
|
|||||||
void* background_work_arg) override;
|
void* background_work_arg) override;
|
||||||
|
|
||||||
void StartThread(void (*thread_main)(void* thread_main_arg),
|
void StartThread(void (*thread_main)(void* thread_main_arg),
|
||||||
void* thread_main_arg) override;
|
void* thread_main_arg) override {
|
||||||
|
std::thread new_thread(thread_main, thread_main_arg);
|
||||||
|
new_thread.detach();
|
||||||
|
}
|
||||||
|
|
||||||
Status GetTestDirectory(std::string* result) override {
|
Status GetTestDirectory(std::string* result) override {
|
||||||
const char* env = std::getenv("TEST_TMPDIR");
|
const char* env = std::getenv("TEST_TMPDIR");
|
||||||
@ -708,7 +712,9 @@ class PosixEnv : public Env {
|
|||||||
return static_cast<uint64_t>(tv.tv_sec) * kUsecondsPerSecond + tv.tv_usec;
|
return static_cast<uint64_t>(tv.tv_sec) * kUsecondsPerSecond + tv.tv_usec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SleepForMicroseconds(int micros) override { ::usleep(micros); }
|
void SleepForMicroseconds(int micros) override {
|
||||||
|
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void BackgroundThreadMain();
|
void BackgroundThreadMain();
|
||||||
@ -869,12 +875,6 @@ using PosixDefaultEnv = SingletonEnv<PosixEnv>;
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void PosixEnv::StartThread(void (*thread_main)(void* thread_main_arg),
|
|
||||||
void* thread_main_arg) {
|
|
||||||
std::thread new_thread(thread_main, thread_main_arg);
|
|
||||||
new_thread.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) {
|
void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) {
|
||||||
PosixDefaultEnv::AssertEnvNotInitialized();
|
PosixDefaultEnv::AssertEnvNotInitialized();
|
||||||
g_open_read_only_file_limit = limit;
|
g_open_read_only_file_limit = limit;
|
||||||
|
@ -13,9 +13,13 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <deque>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -40,9 +44,9 @@ namespace {
|
|||||||
constexpr const size_t kWritableFileBufferSize = 65536;
|
constexpr const size_t kWritableFileBufferSize = 65536;
|
||||||
|
|
||||||
// Up to 1000 mmaps for 64-bit binaries; none for 32-bit.
|
// Up to 1000 mmaps for 64-bit binaries; none for 32-bit.
|
||||||
constexpr int kDefaultMmapLimit = sizeof(void*) >= 8 ? 1000 : 0;
|
constexpr int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0;
|
||||||
|
|
||||||
// Modified by EnvWindowsTestHelper::SetReadOnlyMMapLimit().
|
// Can be set by by EnvWindowsTestHelper::SetReadOnlyMMapLimit().
|
||||||
int g_mmap_limit = kDefaultMmapLimit;
|
int g_mmap_limit = kDefaultMmapLimit;
|
||||||
|
|
||||||
std::string GetWindowsErrorMessage(DWORD error_code) {
|
std::string GetWindowsErrorMessage(DWORD error_code) {
|
||||||
@ -71,9 +75,12 @@ Status WindowsError(const std::string& context, DWORD error_code) {
|
|||||||
class ScopedHandle {
|
class ScopedHandle {
|
||||||
public:
|
public:
|
||||||
ScopedHandle(HANDLE handle) : handle_(handle) {}
|
ScopedHandle(HANDLE handle) : handle_(handle) {}
|
||||||
|
ScopedHandle(const ScopedHandle&) = delete;
|
||||||
ScopedHandle(ScopedHandle&& other) noexcept : handle_(other.Release()) {}
|
ScopedHandle(ScopedHandle&& other) noexcept : handle_(other.Release()) {}
|
||||||
~ScopedHandle() { Close(); }
|
~ScopedHandle() { Close(); }
|
||||||
|
|
||||||
|
ScopedHandle& operator=(const ScopedHandle&) = delete;
|
||||||
|
|
||||||
ScopedHandle& operator=(ScopedHandle&& rhs) noexcept {
|
ScopedHandle& operator=(ScopedHandle&& rhs) noexcept {
|
||||||
if (this != &rhs) handle_ = rhs.Release();
|
if (this != &rhs) handle_ = rhs.Release();
|
||||||
return *this;
|
return *this;
|
||||||
@ -142,44 +149,43 @@ class Limiter {
|
|||||||
|
|
||||||
class WindowsSequentialFile : public SequentialFile {
|
class WindowsSequentialFile : public SequentialFile {
|
||||||
public:
|
public:
|
||||||
WindowsSequentialFile(std::string fname, ScopedHandle file)
|
WindowsSequentialFile(std::string filename, ScopedHandle handle)
|
||||||
: filename_(fname), file_(std::move(file)) {}
|
: handle_(std::move(handle)), filename_(std::move(filename)) {}
|
||||||
~WindowsSequentialFile() override {}
|
~WindowsSequentialFile() override {}
|
||||||
|
|
||||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||||
Status s;
|
|
||||||
DWORD bytes_read;
|
DWORD bytes_read;
|
||||||
// DWORD is 32-bit, but size_t could technically be larger. However leveldb
|
// 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
|
// files are limited to leveldb::Options::max_file_size which is clamped to
|
||||||
// 1<<30 or 1 GiB.
|
// 1<<30 or 1 GiB.
|
||||||
assert(n <= std::numeric_limits<DWORD>::max());
|
assert(n <= std::numeric_limits<DWORD>::max());
|
||||||
if (!::ReadFile(file_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
|
if (!::ReadFile(handle_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
|
||||||
nullptr)) {
|
nullptr)) {
|
||||||
s = WindowsError(filename_, ::GetLastError());
|
return WindowsError(filename_, ::GetLastError());
|
||||||
} else {
|
|
||||||
*result = Slice(scratch, bytes_read);
|
|
||||||
}
|
}
|
||||||
return s;
|
|
||||||
|
*result = Slice(scratch, bytes_read);
|
||||||
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Skip(uint64_t n) override {
|
Status Skip(uint64_t n) override {
|
||||||
LARGE_INTEGER distance;
|
LARGE_INTEGER distance;
|
||||||
distance.QuadPart = n;
|
distance.QuadPart = n;
|
||||||
if (!::SetFilePointerEx(file_.get(), distance, nullptr, FILE_CURRENT)) {
|
if (!::SetFilePointerEx(handle_.get(), distance, nullptr, FILE_CURRENT)) {
|
||||||
return WindowsError(filename_, ::GetLastError());
|
return WindowsError(filename_, ::GetLastError());
|
||||||
}
|
}
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string filename_;
|
const ScopedHandle handle_;
|
||||||
ScopedHandle file_;
|
const std::string filename_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindowsRandomAccessFile : public RandomAccessFile {
|
class WindowsRandomAccessFile : public RandomAccessFile {
|
||||||
public:
|
public:
|
||||||
WindowsRandomAccessFile(std::string fname, ScopedHandle handle)
|
WindowsRandomAccessFile(std::string filename, ScopedHandle handle)
|
||||||
: filename_(fname), handle_(std::move(handle)) {}
|
: handle_(std::move(handle)), filename_(std::move(filename)) {}
|
||||||
|
|
||||||
~WindowsRandomAccessFile() override = default;
|
~WindowsRandomAccessFile() override = default;
|
||||||
|
|
||||||
@ -204,107 +210,116 @@ class WindowsRandomAccessFile : public RandomAccessFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string filename_;
|
const ScopedHandle handle_;
|
||||||
ScopedHandle handle_;
|
const std::string filename_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindowsMmapReadableFile : public RandomAccessFile {
|
class WindowsMmapReadableFile : public RandomAccessFile {
|
||||||
public:
|
public:
|
||||||
// base[0,length-1] contains the mmapped contents of the file.
|
// base[0,length-1] contains the mmapped contents of the file.
|
||||||
WindowsMmapReadableFile(std::string fname, void* base, size_t length,
|
WindowsMmapReadableFile(std::string filename, char* mmap_base, size_t length,
|
||||||
Limiter* limiter)
|
Limiter* mmap_limiter)
|
||||||
: filename_(std::move(fname)),
|
: mmap_base_(mmap_base),
|
||||||
mmapped_region_(base),
|
|
||||||
length_(length),
|
length_(length),
|
||||||
limiter_(limiter) {}
|
mmap_limiter_(mmap_limiter),
|
||||||
|
filename_(std::move(filename)) {}
|
||||||
|
|
||||||
~WindowsMmapReadableFile() override {
|
~WindowsMmapReadableFile() override {
|
||||||
::UnmapViewOfFile(mmapped_region_);
|
::UnmapViewOfFile(mmap_base_);
|
||||||
limiter_->Release();
|
mmap_limiter_->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||||
char* scratch) const override {
|
char* scratch) const override {
|
||||||
Status s;
|
|
||||||
if (offset + n > length_) {
|
if (offset + n > length_) {
|
||||||
*result = Slice();
|
*result = Slice();
|
||||||
s = WindowsError(filename_, ERROR_INVALID_PARAMETER);
|
return WindowsError(filename_, ERROR_INVALID_PARAMETER);
|
||||||
} else {
|
|
||||||
*result = Slice(reinterpret_cast<char*>(mmapped_region_) + offset, n);
|
|
||||||
}
|
}
|
||||||
return s;
|
|
||||||
|
*result = Slice(mmap_base_ + offset, n);
|
||||||
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string filename_;
|
char* const mmap_base_;
|
||||||
void* mmapped_region_;
|
const size_t length_;
|
||||||
size_t length_;
|
Limiter* const mmap_limiter_;
|
||||||
Limiter* limiter_;
|
const std::string filename_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindowsWritableFile : public WritableFile {
|
class WindowsWritableFile : public WritableFile {
|
||||||
public:
|
public:
|
||||||
WindowsWritableFile(std::string fname, ScopedHandle handle)
|
WindowsWritableFile(std::string filename, ScopedHandle handle)
|
||||||
: filename_(std::move(fname)), handle_(std::move(handle)), pos_(0) {}
|
: pos_(0), handle_(std::move(handle)), filename_(std::move(filename)) {}
|
||||||
|
|
||||||
~WindowsWritableFile() override = default;
|
~WindowsWritableFile() override = default;
|
||||||
|
|
||||||
Status Append(const Slice& data) override {
|
Status Append(const Slice& data) override {
|
||||||
size_t n = data.size();
|
size_t write_size = data.size();
|
||||||
const char* p = data.data();
|
const char* write_data = data.data();
|
||||||
|
|
||||||
// Fit as much as possible into buffer.
|
// Fit as much as possible into buffer.
|
||||||
size_t copy = std::min(n, kWritableFileBufferSize - pos_);
|
size_t copy_size = std::min(write_size, kWritableFileBufferSize - pos_);
|
||||||
memcpy(buf_ + pos_, p, copy);
|
std::memcpy(buf_ + pos_, write_data, copy_size);
|
||||||
p += copy;
|
write_data += copy_size;
|
||||||
n -= copy;
|
write_size -= copy_size;
|
||||||
pos_ += copy;
|
pos_ += copy_size;
|
||||||
if (n == 0) {
|
if (write_size == 0) {
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't fit in buffer, so need to do at least one write.
|
// Can't fit in buffer, so need to do at least one write.
|
||||||
Status s = FlushBuffered();
|
Status status = FlushBuffer();
|
||||||
if (!s.ok()) {
|
if (!status.ok()) {
|
||||||
return s;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Small writes go to buffer, large writes are written directly.
|
// Small writes go to buffer, large writes are written directly.
|
||||||
if (n < kWritableFileBufferSize) {
|
if (write_size < kWritableFileBufferSize) {
|
||||||
memcpy(buf_, p, n);
|
std::memcpy(buf_, write_data, write_size);
|
||||||
pos_ = n;
|
pos_ = write_size;
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
return WriteRaw(p, n);
|
return WriteUnbuffered(write_data, write_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Close() override {
|
Status Close() override {
|
||||||
Status result = FlushBuffered();
|
Status status = FlushBuffer();
|
||||||
if (!handle_.Close() && result.ok()) {
|
if (!handle_.Close() && status.ok()) {
|
||||||
result = WindowsError(filename_, ::GetLastError());
|
status = WindowsError(filename_, ::GetLastError());
|
||||||
}
|
}
|
||||||
return result;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Flush() override { return FlushBuffered(); }
|
Status Flush() override { return FlushBuffer(); }
|
||||||
|
|
||||||
Status Sync() override {
|
Status Sync() override {
|
||||||
// On Windows no need to sync parent directory. It's metadata will be
|
// On Windows no need to sync parent directory. Its metadata will be updated
|
||||||
// updated via the creation of the new file, without an explicit sync.
|
// via the creation of the new file, without an explicit sync.
|
||||||
return FlushBuffered();
|
|
||||||
|
Status status = FlushBuffer();
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!::FlushFileBuffers(handle_.get())) {
|
||||||
|
return Status::IOError(filename_,
|
||||||
|
GetWindowsErrorMessage(::GetLastError()));
|
||||||
|
}
|
||||||
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Status FlushBuffered() {
|
Status FlushBuffer() {
|
||||||
Status s = WriteRaw(buf_, pos_);
|
Status status = WriteUnbuffered(buf_, pos_);
|
||||||
pos_ = 0;
|
pos_ = 0;
|
||||||
return s;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status WriteRaw(const char* p, size_t n) {
|
Status WriteUnbuffered(const char* data, size_t size) {
|
||||||
DWORD bytes_written;
|
DWORD bytes_written;
|
||||||
if (!::WriteFile(handle_.get(), p, static_cast<DWORD>(n), &bytes_written,
|
if (!::WriteFile(handle_.get(), data, static_cast<DWORD>(size),
|
||||||
nullptr)) {
|
&bytes_written, nullptr)) {
|
||||||
return Status::IOError(filename_,
|
return Status::IOError(filename_,
|
||||||
GetWindowsErrorMessage(::GetLastError()));
|
GetWindowsErrorMessage(::GetLastError()));
|
||||||
}
|
}
|
||||||
@ -312,10 +327,11 @@ class WindowsWritableFile : public WritableFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buf_[0, pos_-1] contains data to be written to handle_.
|
// buf_[0, pos_-1] contains data to be written to handle_.
|
||||||
const std::string filename_;
|
|
||||||
ScopedHandle handle_;
|
|
||||||
char buf_[kWritableFileBufferSize];
|
char buf_[kWritableFileBufferSize];
|
||||||
size_t pos_;
|
size_t pos_;
|
||||||
|
|
||||||
|
ScopedHandle handle_;
|
||||||
|
const std::string filename_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Lock or unlock the entire file as specified by |lock|. Returns true
|
// Lock or unlock the entire file as specified by |lock|. Returns true
|
||||||
@ -337,124 +353,132 @@ bool LockOrUnlock(HANDLE handle, bool lock) {
|
|||||||
|
|
||||||
class WindowsFileLock : public FileLock {
|
class WindowsFileLock : public FileLock {
|
||||||
public:
|
public:
|
||||||
WindowsFileLock(ScopedHandle handle, std::string name)
|
WindowsFileLock(ScopedHandle handle, std::string filename)
|
||||||
: handle_(std::move(handle)), name_(std::move(name)) {}
|
: handle_(std::move(handle)), filename_(std::move(filename)) {}
|
||||||
|
|
||||||
ScopedHandle& handle() { return handle_; }
|
const ScopedHandle& handle() const { return handle_; }
|
||||||
const std::string& name() const { return name_; }
|
const std::string& filename() const { return filename_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ScopedHandle handle_;
|
const ScopedHandle handle_;
|
||||||
std::string name_;
|
const std::string filename_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindowsEnv : public Env {
|
class WindowsEnv : public Env {
|
||||||
public:
|
public:
|
||||||
WindowsEnv();
|
WindowsEnv();
|
||||||
~WindowsEnv() override {
|
~WindowsEnv() override {
|
||||||
static char msg[] = "Destroying Env::Default()\n";
|
static const char msg[] =
|
||||||
fwrite(msg, 1, sizeof(msg), stderr);
|
"WindowsEnv singleton destroyed. Unsupported behavior!\n";
|
||||||
abort();
|
std::fwrite(msg, 1, sizeof(msg), stderr);
|
||||||
|
std::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status NewSequentialFile(const std::string& fname,
|
Status NewSequentialFile(const std::string& filename,
|
||||||
SequentialFile** result) override {
|
SequentialFile** result) override {
|
||||||
*result = nullptr;
|
*result = nullptr;
|
||||||
DWORD desired_access = GENERIC_READ;
|
DWORD desired_access = GENERIC_READ;
|
||||||
DWORD share_mode = FILE_SHARE_READ;
|
DWORD share_mode = FILE_SHARE_READ;
|
||||||
ScopedHandle handle =
|
ScopedHandle handle = ::CreateFileA(
|
||||||
::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr,
|
filename.c_str(), desired_access, share_mode,
|
||||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
/*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
||||||
|
/*hTemplateFile=*/nullptr);
|
||||||
if (!handle.is_valid()) {
|
if (!handle.is_valid()) {
|
||||||
return WindowsError(fname, ::GetLastError());
|
return WindowsError(filename, ::GetLastError());
|
||||||
}
|
}
|
||||||
*result = new WindowsSequentialFile(fname, std::move(handle));
|
|
||||||
|
*result = new WindowsSequentialFile(filename, std::move(handle));
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status NewRandomAccessFile(const std::string& fname,
|
Status NewRandomAccessFile(const std::string& filename,
|
||||||
RandomAccessFile** result) override {
|
RandomAccessFile** result) override {
|
||||||
*result = nullptr;
|
*result = nullptr;
|
||||||
DWORD desired_access = GENERIC_READ;
|
DWORD desired_access = GENERIC_READ;
|
||||||
DWORD share_mode = FILE_SHARE_READ;
|
DWORD share_mode = FILE_SHARE_READ;
|
||||||
DWORD file_flags = FILE_ATTRIBUTE_READONLY;
|
|
||||||
|
|
||||||
ScopedHandle handle =
|
ScopedHandle handle =
|
||||||
::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr,
|
::CreateFileA(filename.c_str(), desired_access, share_mode,
|
||||||
OPEN_EXISTING, file_flags, nullptr);
|
/*lpSecurityAttributes=*/nullptr, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_READONLY,
|
||||||
|
/*hTemplateFile=*/nullptr);
|
||||||
if (!handle.is_valid()) {
|
if (!handle.is_valid()) {
|
||||||
return WindowsError(fname, ::GetLastError());
|
return WindowsError(filename, ::GetLastError());
|
||||||
}
|
}
|
||||||
if (!mmap_limiter_.Acquire()) {
|
if (!mmap_limiter_.Acquire()) {
|
||||||
*result = new WindowsRandomAccessFile(fname, std::move(handle));
|
*result = new WindowsRandomAccessFile(filename, std::move(handle));
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
LARGE_INTEGER file_size;
|
LARGE_INTEGER file_size;
|
||||||
|
Status status;
|
||||||
if (!::GetFileSizeEx(handle.get(), &file_size)) {
|
if (!::GetFileSizeEx(handle.get(), &file_size)) {
|
||||||
return WindowsError(fname, ::GetLastError());
|
mmap_limiter_.Release();
|
||||||
|
return WindowsError(filename, ::GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedHandle mapping =
|
ScopedHandle mapping =
|
||||||
::CreateFileMappingA(handle.get(),
|
::CreateFileMappingA(handle.get(),
|
||||||
/*security attributes=*/nullptr, PAGE_READONLY,
|
/*security attributes=*/nullptr, PAGE_READONLY,
|
||||||
/*dwMaximumSizeHigh=*/0,
|
/*dwMaximumSizeHigh=*/0,
|
||||||
/*dwMaximumSizeLow=*/0, nullptr);
|
/*dwMaximumSizeLow=*/0,
|
||||||
|
/*lpName=*/nullptr);
|
||||||
if (mapping.is_valid()) {
|
if (mapping.is_valid()) {
|
||||||
void* base = MapViewOfFile(mapping.get(), FILE_MAP_READ, 0, 0, 0);
|
void* mmap_base = ::MapViewOfFile(mapping.get(), FILE_MAP_READ,
|
||||||
if (base) {
|
/*dwFileOffsetHigh=*/0,
|
||||||
|
/*dwFileOffsetLow=*/0,
|
||||||
|
/*dwNumberOfBytesToMap=*/0);
|
||||||
|
if (mmap_base) {
|
||||||
*result = new WindowsMmapReadableFile(
|
*result = new WindowsMmapReadableFile(
|
||||||
fname, base, static_cast<size_t>(file_size.QuadPart),
|
filename, reinterpret_cast<char*>(mmap_base),
|
||||||
&mmap_limiter_);
|
static_cast<size_t>(file_size.QuadPart), &mmap_limiter_);
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Status s = WindowsError(fname, ::GetLastError());
|
mmap_limiter_.Release();
|
||||||
|
return WindowsError(filename, ::GetLastError());
|
||||||
if (!s.ok()) {
|
|
||||||
mmap_limiter_.Release();
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Status NewWritableFile(const std::string& fname,
|
Status NewWritableFile(const std::string& filename,
|
||||||
WritableFile** result) override {
|
WritableFile** result) override {
|
||||||
DWORD desired_access = GENERIC_WRITE;
|
DWORD desired_access = GENERIC_WRITE;
|
||||||
DWORD share_mode = 0;
|
DWORD share_mode = 0; // Exclusive access.
|
||||||
|
ScopedHandle handle = ::CreateFileA(
|
||||||
ScopedHandle handle =
|
filename.c_str(), desired_access, share_mode,
|
||||||
::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr,
|
/*lpSecurityAttributes=*/nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
||||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
/*hTemplateFile=*/nullptr);
|
||||||
if (!handle.is_valid()) {
|
if (!handle.is_valid()) {
|
||||||
*result = nullptr;
|
*result = nullptr;
|
||||||
return WindowsError(fname, ::GetLastError());
|
return WindowsError(filename, ::GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
*result = new WindowsWritableFile(fname, std::move(handle));
|
*result = new WindowsWritableFile(filename, std::move(handle));
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status NewAppendableFile(const std::string& fname,
|
Status NewAppendableFile(const std::string& filename,
|
||||||
WritableFile** result) override {
|
WritableFile** result) override {
|
||||||
ScopedHandle handle =
|
DWORD desired_access = FILE_APPEND_DATA;
|
||||||
::CreateFileA(fname.c_str(), FILE_APPEND_DATA, 0, nullptr, OPEN_ALWAYS,
|
DWORD share_mode = 0; // Exclusive access.
|
||||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
ScopedHandle handle = ::CreateFileA(
|
||||||
|
filename.c_str(), desired_access, share_mode,
|
||||||
|
/*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
||||||
|
/*hTemplateFile=*/nullptr);
|
||||||
if (!handle.is_valid()) {
|
if (!handle.is_valid()) {
|
||||||
*result = nullptr;
|
*result = nullptr;
|
||||||
return WindowsError(fname, ::GetLastError());
|
return WindowsError(filename, ::GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
*result = new WindowsWritableFile(fname, std::move(handle));
|
*result = new WindowsWritableFile(filename, std::move(handle));
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileExists(const std::string& fname) override {
|
bool FileExists(const std::string& filename) override {
|
||||||
return GetFileAttributesA(fname.c_str()) != INVALID_FILE_ATTRIBUTES;
|
return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status GetChildren(const std::string& dir,
|
Status GetChildren(const std::string& directory_path,
|
||||||
std::vector<std::string>* result) override {
|
std::vector<std::string>* result) override {
|
||||||
const std::string find_pattern = dir + "\\*";
|
const std::string find_pattern = directory_path + "\\*";
|
||||||
WIN32_FIND_DATAA find_data;
|
WIN32_FIND_DATAA find_data;
|
||||||
HANDLE dir_handle = ::FindFirstFileA(find_pattern.c_str(), &find_data);
|
HANDLE dir_handle = ::FindFirstFileA(find_pattern.c_str(), &find_data);
|
||||||
if (dir_handle == INVALID_HANDLE_VALUE) {
|
if (dir_handle == INVALID_HANDLE_VALUE) {
|
||||||
@ -462,7 +486,7 @@ class WindowsEnv : public Env {
|
|||||||
if (last_error == ERROR_FILE_NOT_FOUND) {
|
if (last_error == ERROR_FILE_NOT_FOUND) {
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
return WindowsError(dir, last_error);
|
return WindowsError(directory_path, last_error);
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
char base_name[_MAX_FNAME];
|
char base_name[_MAX_FNAME];
|
||||||
@ -476,105 +500,109 @@ class WindowsEnv : public Env {
|
|||||||
DWORD last_error = ::GetLastError();
|
DWORD last_error = ::GetLastError();
|
||||||
::FindClose(dir_handle);
|
::FindClose(dir_handle);
|
||||||
if (last_error != ERROR_NO_MORE_FILES) {
|
if (last_error != ERROR_NO_MORE_FILES) {
|
||||||
return WindowsError(dir, last_error);
|
return WindowsError(directory_path, last_error);
|
||||||
}
|
}
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status DeleteFile(const std::string& fname) override {
|
Status DeleteFile(const std::string& filename) override {
|
||||||
if (!::DeleteFileA(fname.c_str())) {
|
if (!::DeleteFileA(filename.c_str())) {
|
||||||
return WindowsError(fname, ::GetLastError());
|
return WindowsError(filename, ::GetLastError());
|
||||||
}
|
}
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status CreateDir(const std::string& name) override {
|
Status CreateDir(const std::string& dirname) override {
|
||||||
if (!::CreateDirectoryA(name.c_str(), nullptr)) {
|
if (!::CreateDirectoryA(dirname.c_str(), nullptr)) {
|
||||||
return WindowsError(name, ::GetLastError());
|
return WindowsError(dirname, ::GetLastError());
|
||||||
}
|
}
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status DeleteDir(const std::string& name) override {
|
Status DeleteDir(const std::string& dirname) override {
|
||||||
if (!::RemoveDirectoryA(name.c_str())) {
|
if (!::RemoveDirectoryA(dirname.c_str())) {
|
||||||
return WindowsError(name, ::GetLastError());
|
return WindowsError(dirname, ::GetLastError());
|
||||||
}
|
}
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status GetFileSize(const std::string& fname, uint64_t* size) override {
|
Status GetFileSize(const std::string& filename, uint64_t* size) override {
|
||||||
WIN32_FILE_ATTRIBUTE_DATA attrs;
|
WIN32_FILE_ATTRIBUTE_DATA file_attributes;
|
||||||
if (!::GetFileAttributesExA(fname.c_str(), GetFileExInfoStandard, &attrs)) {
|
if (!::GetFileAttributesExA(filename.c_str(), GetFileExInfoStandard,
|
||||||
return WindowsError(fname, ::GetLastError());
|
&file_attributes)) {
|
||||||
|
return WindowsError(filename, ::GetLastError());
|
||||||
}
|
}
|
||||||
ULARGE_INTEGER file_size;
|
ULARGE_INTEGER file_size;
|
||||||
file_size.HighPart = attrs.nFileSizeHigh;
|
file_size.HighPart = file_attributes.nFileSizeHigh;
|
||||||
file_size.LowPart = attrs.nFileSizeLow;
|
file_size.LowPart = file_attributes.nFileSizeLow;
|
||||||
*size = file_size.QuadPart;
|
*size = file_size.QuadPart;
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status RenameFile(const std::string& src,
|
Status RenameFile(const std::string& from, const std::string& to) override {
|
||||||
const std::string& target) override {
|
// Try a simple move first. It will only succeed when |to| doesn't already
|
||||||
// Try a simple move first. It will only succeed when |to_path| doesn't
|
// exist.
|
||||||
// already exist.
|
if (::MoveFileA(from.c_str(), to.c_str())) {
|
||||||
if (::MoveFileA(src.c_str(), target.c_str())) {
|
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
DWORD move_error = ::GetLastError();
|
DWORD move_error = ::GetLastError();
|
||||||
|
|
||||||
// Try the full-blown replace if the move fails, as ReplaceFile will only
|
// Try the full-blown replace if the move fails, as ReplaceFile will only
|
||||||
// succeed when |to_path| does exist. When writing to a network share, we
|
// succeed when |to| does exist. When writing to a network share, we may not
|
||||||
// may not be able to change the ACLs. Ignore ACL errors then
|
// be able to change the ACLs. Ignore ACL errors then
|
||||||
// (REPLACEFILE_IGNORE_MERGE_ERRORS).
|
// (REPLACEFILE_IGNORE_MERGE_ERRORS).
|
||||||
if (::ReplaceFileA(target.c_str(), src.c_str(), nullptr,
|
if (::ReplaceFileA(to.c_str(), from.c_str(), /*lpBackupFileName=*/nullptr,
|
||||||
REPLACEFILE_IGNORE_MERGE_ERRORS, nullptr, nullptr)) {
|
REPLACEFILE_IGNORE_MERGE_ERRORS,
|
||||||
|
/*lpExclude=*/nullptr, /*lpReserved=*/nullptr)) {
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
DWORD replace_error = ::GetLastError();
|
DWORD replace_error = ::GetLastError();
|
||||||
// In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely
|
// In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that
|
||||||
// that |to_path| does not exist. In this case, the more relevant error
|
// |to| does not exist. In this case, the more relevant error comes from the
|
||||||
// comes from the call to MoveFile.
|
// call to MoveFile.
|
||||||
if (replace_error == ERROR_FILE_NOT_FOUND ||
|
if (replace_error == ERROR_FILE_NOT_FOUND ||
|
||||||
replace_error == ERROR_PATH_NOT_FOUND) {
|
replace_error == ERROR_PATH_NOT_FOUND) {
|
||||||
return WindowsError(src, move_error);
|
return WindowsError(from, move_error);
|
||||||
} else {
|
} else {
|
||||||
return WindowsError(src, replace_error);
|
return WindowsError(from, replace_error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Status LockFile(const std::string& fname, FileLock** lock) override {
|
Status LockFile(const std::string& filename, FileLock** lock) override {
|
||||||
*lock = nullptr;
|
*lock = nullptr;
|
||||||
Status result;
|
Status result;
|
||||||
ScopedHandle handle = ::CreateFileA(
|
ScopedHandle handle = ::CreateFileA(
|
||||||
fname.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
|
filename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
|
||||||
/*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
/*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
||||||
nullptr);
|
nullptr);
|
||||||
if (!handle.is_valid()) {
|
if (!handle.is_valid()) {
|
||||||
result = WindowsError(fname, ::GetLastError());
|
result = WindowsError(filename, ::GetLastError());
|
||||||
} else if (!LockOrUnlock(handle.get(), true)) {
|
} else if (!LockOrUnlock(handle.get(), true)) {
|
||||||
result = WindowsError("lock " + fname, ::GetLastError());
|
result = WindowsError("lock " + filename, ::GetLastError());
|
||||||
} else {
|
} else {
|
||||||
*lock = new WindowsFileLock(std::move(handle), std::move(fname));
|
*lock = new WindowsFileLock(std::move(handle), filename);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status UnlockFile(FileLock* lock) override {
|
Status UnlockFile(FileLock* lock) override {
|
||||||
std::unique_ptr<WindowsFileLock> my_lock(
|
WindowsFileLock* windows_file_lock =
|
||||||
reinterpret_cast<WindowsFileLock*>(lock));
|
reinterpret_cast<WindowsFileLock*>(lock);
|
||||||
Status result;
|
if (!LockOrUnlock(windows_file_lock->handle().get(), false)) {
|
||||||
if (!LockOrUnlock(my_lock->handle().get(), false)) {
|
return WindowsError("unlock " + windows_file_lock->filename(),
|
||||||
result = WindowsError("unlock", ::GetLastError());
|
::GetLastError());
|
||||||
}
|
}
|
||||||
return result;
|
delete windows_file_lock;
|
||||||
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Schedule(void (*function)(void*), void* arg) override;
|
void Schedule(void (*background_work_function)(void* background_work_arg),
|
||||||
|
void* background_work_arg) override;
|
||||||
|
|
||||||
void StartThread(void (*function)(void* arg), void* arg) override {
|
void StartThread(void (*thread_main)(void* thread_main_arg),
|
||||||
std::thread t(function, arg);
|
void* thread_main_arg) override {
|
||||||
t.detach();
|
std::thread new_thread(thread_main, thread_main_arg);
|
||||||
|
new_thread.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status GetTestDirectory(std::string* result) override {
|
Status GetTestDirectory(std::string* result) override {
|
||||||
@ -601,7 +629,7 @@ class WindowsEnv : public Env {
|
|||||||
std::FILE* fp = std::fopen(filename.c_str(), "w");
|
std::FILE* fp = std::fopen(filename.c_str(), "w");
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr) {
|
||||||
*result = nullptr;
|
*result = nullptr;
|
||||||
return WindowsError("NewLogger", ::GetLastError());
|
return WindowsError(filename, ::GetLastError());
|
||||||
} else {
|
} else {
|
||||||
*result = new WindowsLogger(fp);
|
*result = new WindowsLogger(fp);
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
@ -626,86 +654,147 @@ class WindowsEnv : public Env {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Entry per Schedule() call
|
void BackgroundThreadMain();
|
||||||
struct BGItem {
|
|
||||||
void* arg;
|
static void BackgroundThreadEntryPoint(WindowsEnv* env) {
|
||||||
void (*function)(void*);
|
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 beacuse it is immutable.
|
||||||
|
struct BackgroundWorkItem {
|
||||||
|
explicit BackgroundWorkItem(void (*function)(void* arg), void* arg)
|
||||||
|
: function(function), arg(arg) {}
|
||||||
|
|
||||||
|
void (*const function)(void*);
|
||||||
|
void* const arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
// BGThread() is the body of the background thread
|
port::Mutex background_work_mutex_;
|
||||||
void BGThread();
|
port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_);
|
||||||
|
bool started_background_thread_ GUARDED_BY(background_work_mutex_);
|
||||||
|
|
||||||
std::mutex mu_;
|
std::queue<BackgroundWorkItem> background_work_queue_
|
||||||
std::condition_variable bgsignal_;
|
GUARDED_BY(background_work_mutex_);
|
||||||
bool started_bgthread_;
|
|
||||||
std::deque<BGItem> queue_;
|
Limiter mmap_limiter_; // Thread-safe.
|
||||||
Limiter mmap_limiter_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return the maximum number of concurrent mmaps.
|
// Return the maximum number of concurrent mmaps.
|
||||||
int MaxMmaps() {
|
int MaxMmaps() { return g_mmap_limit; }
|
||||||
if (g_mmap_limit >= 0) {
|
|
||||||
return g_mmap_limit;
|
|
||||||
}
|
|
||||||
// Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes.
|
|
||||||
g_mmap_limit = sizeof(void*) >= 8 ? 1000 : 0;
|
|
||||||
return g_mmap_limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowsEnv::WindowsEnv()
|
WindowsEnv::WindowsEnv()
|
||||||
: started_bgthread_(false), mmap_limiter_(MaxMmaps()) {}
|
: background_work_cv_(&background_work_mutex_),
|
||||||
|
started_background_thread_(false),
|
||||||
|
mmap_limiter_(MaxMmaps()) {}
|
||||||
|
|
||||||
void WindowsEnv::Schedule(void (*function)(void*), void* arg) {
|
void WindowsEnv::Schedule(
|
||||||
std::lock_guard<std::mutex> guard(mu_);
|
void (*background_work_function)(void* background_work_arg),
|
||||||
|
void* background_work_arg) {
|
||||||
|
background_work_mutex_.Lock();
|
||||||
|
|
||||||
// Start background thread if necessary
|
// Start the background thread, if we haven't done so already.
|
||||||
if (!started_bgthread_) {
|
if (!started_background_thread_) {
|
||||||
started_bgthread_ = true;
|
started_background_thread_ = true;
|
||||||
std::thread t(&WindowsEnv::BGThread, this);
|
std::thread background_thread(WindowsEnv::BackgroundThreadEntryPoint, this);
|
||||||
t.detach();
|
background_thread.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the queue is currently empty, the background thread may currently be
|
// If the queue is empty, the background thread may be waiting for work.
|
||||||
// waiting.
|
if (background_work_queue_.empty()) {
|
||||||
if (queue_.empty()) {
|
background_work_cv_.Signal();
|
||||||
bgsignal_.notify_one();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to priority queue
|
background_work_queue_.emplace(background_work_function, background_work_arg);
|
||||||
queue_.push_back(BGItem());
|
background_work_mutex_.Unlock();
|
||||||
queue_.back().function = function;
|
|
||||||
queue_.back().arg = arg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsEnv::BGThread() {
|
void WindowsEnv::BackgroundThreadMain() {
|
||||||
while (true) {
|
while (true) {
|
||||||
// Wait until there is an item that is ready to run
|
background_work_mutex_.Lock();
|
||||||
std::unique_lock<std::mutex> lk(mu_);
|
|
||||||
bgsignal_.wait(lk, [this] { return !queue_.empty(); });
|
|
||||||
|
|
||||||
void (*function)(void*) = queue_.front().function;
|
// Wait until there is work to be done.
|
||||||
void* arg = queue_.front().arg;
|
while (background_work_queue_.empty()) {
|
||||||
queue_.pop_front();
|
background_work_cv_.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
lk.unlock();
|
assert(!background_work_queue_.empty());
|
||||||
(*function)(arg);
|
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::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::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
|
} // namespace
|
||||||
|
|
||||||
static std::once_flag once;
|
|
||||||
static Env* default_env;
|
|
||||||
static void InitDefaultEnv() { default_env = new WindowsEnv(); }
|
|
||||||
|
|
||||||
void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) {
|
void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) {
|
||||||
assert(default_env == nullptr);
|
WindowsDefaultEnv::AssertEnvNotInitialized();
|
||||||
g_mmap_limit = limit;
|
g_mmap_limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
Env* Env::Default() {
|
Env* Env::Default() {
|
||||||
std::call_once(once, InitDefaultEnv);
|
static WindowsDefaultEnv env_container;
|
||||||
return default_env;
|
return env_container.env();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace leveldb
|
} // namespace leveldb
|
||||||
|
Loading…
x
Reference in New Issue
Block a user