1 // Copyright (c) 2018 The LevelDB Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. See the AUTHORS file for names of contributors.
4
5 // Prevent Windows headers from defining min/max macros and instead
6 // use STL.
7 #ifndef NOMINMAX
8 #define NOMINMAX
9 #endif // ifndef NOMINMAX
10
11 #include <algorithm>
12 #include <atomic>
13 #include <chrono>
14 #include <condition_variable>
15 #include <cstddef>
16 #include <cstdint>
17 #include <cstdlib>
18 #include <cstring>
19 #include <memory>
20 #include <mutex>
21 #include <queue>
22 #include <sstream>
23 #include <string>
24 #include <vector>
25
26 #include "leveldb/env.h"
27 #include "leveldb/slice.h"
28 #include "port/port.h"
29 #include "port/thread_annotations.h"
30 #include "util/env_windows_test_helper.h"
31 #include "util/logging.h"
32 #include "util/mutexlock.h"
33 #include "util/windows_logger.h"
34
35 namespace leveldb {
36
37 namespace {
38
39 constexpr const size_t kWritableFileBufferSize = 65536;
40
41 // Up to 1000 mmaps for 64-bit binaries; none for 32-bit.
42 constexpr int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0;
43
44 // Can be set by by EnvWindowsTestHelper::SetReadOnlyMMapLimit().
45 int g_mmap_limit = kDefaultMmapLimit;
46
GetWindowsErrorMessage(DWORD error_code)47 std::string GetWindowsErrorMessage(DWORD error_code) {
48 std::string message;
49 char* error_text = nullptr;
50 // Use MBCS version of FormatMessage to match return value.
51 size_t error_text_size = ::FormatMessageA(
52 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
53 FORMAT_MESSAGE_IGNORE_INSERTS,
54 nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
55 reinterpret_cast<char*>(&error_text), 0, nullptr);
56 if (!error_text) {
57 return message;
58 }
59 message.assign(error_text, error_text_size);
60 ::LocalFree(error_text);
61 return message;
62 }
63
WindowsError(const std::string & context,DWORD error_code)64 Status WindowsError(const std::string& context, DWORD error_code) {
65 if (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND)
66 return Status::NotFound(context, GetWindowsErrorMessage(error_code));
67 return Status::IOError(context, GetWindowsErrorMessage(error_code));
68 }
69
70 class ScopedHandle {
71 public:
ScopedHandle(HANDLE handle)72 ScopedHandle(HANDLE handle) : handle_(handle) {}
73 ScopedHandle(const ScopedHandle&) = delete;
ScopedHandle(ScopedHandle && other)74 ScopedHandle(ScopedHandle&& other) noexcept : handle_(other.Release()) {}
~ScopedHandle()75 ~ScopedHandle() { Close(); }
76
77 ScopedHandle& operator=(const ScopedHandle&) = delete;
78
operator =(ScopedHandle && rhs)79 ScopedHandle& operator=(ScopedHandle&& rhs) noexcept {
80 if (this != &rhs) handle_ = rhs.Release();
81 return *this;
82 }
83
Close()84 bool Close() {
85 if (!is_valid()) {
86 return true;
87 }
88 HANDLE h = handle_;
89 handle_ = INVALID_HANDLE_VALUE;
90 return ::CloseHandle(h);
91 }
92
is_valid() const93 bool is_valid() const {
94 return handle_ != INVALID_HANDLE_VALUE && handle_ != nullptr;
95 }
96
get() const97 HANDLE get() const { return handle_; }
98
Release()99 HANDLE Release() {
100 HANDLE h = handle_;
101 handle_ = INVALID_HANDLE_VALUE;
102 return h;
103 }
104
105 private:
106 HANDLE handle_;
107 };
108
109 // Helper class to limit resource usage to avoid exhaustion.
110 // Currently used to limit read-only file descriptors and mmap file usage
111 // so that we do not run out of file descriptors or virtual memory, or run into
112 // kernel performance problems for very large databases.
113 class Limiter {
114 public:
115 // Limit maximum number of resources to |max_acquires|.
Limiter(int max_acquires)116 Limiter(int max_acquires) : acquires_allowed_(max_acquires) {}
117
118 Limiter(const Limiter&) = delete;
119 Limiter operator=(const Limiter&) = delete;
120
121 // If another resource is available, acquire it and return true.
122 // Else return false.
Acquire()123 bool Acquire() {
124 int old_acquires_allowed =
125 acquires_allowed_.fetch_sub(1, std::memory_order_relaxed);
126
127 if (old_acquires_allowed > 0) return true;
128
129 acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
130 return false;
131 }
132
133 // Release a resource acquired by a previous call to Acquire() that returned
134 // true.
Release()135 void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); }
136
137 private:
138 // The number of available resources.
139 //
140 // This is a counter and is not tied to the invariants of any other class, so
141 // it can be operated on safely using std::memory_order_relaxed.
142 std::atomic<int> acquires_allowed_;
143 };
144
145 class WindowsSequentialFile : public SequentialFile {
146 public:
WindowsSequentialFile(std::string filename,ScopedHandle handle)147 WindowsSequentialFile(std::string filename, ScopedHandle handle)
148 : handle_(std::move(handle)), filename_(std::move(filename)) {}
~WindowsSequentialFile()149 ~WindowsSequentialFile() override {}
150
Read(size_t n,Slice * result,char * scratch)151 Status Read(size_t n, Slice* result, char* scratch) override {
152 DWORD bytes_read;
153 // DWORD is 32-bit, but size_t could technically be larger. However leveldb
154 // files are limited to leveldb::Options::max_file_size which is clamped to
155 // 1<<30 or 1 GiB.
156 assert(n <= std::numeric_limits<DWORD>::max());
157 if (!::ReadFile(handle_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
158 nullptr)) {
159 return WindowsError(filename_, ::GetLastError());
160 }
161
162 *result = Slice(scratch, bytes_read);
163 return Status::OK();
164 }
165
Skip(uint64_t n)166 Status Skip(uint64_t n) override {
167 LARGE_INTEGER distance;
168 distance.QuadPart = n;
169 if (!::SetFilePointerEx(handle_.get(), distance, nullptr, FILE_CURRENT)) {
170 return WindowsError(filename_, ::GetLastError());
171 }
172 return Status::OK();
173 }
174
175 private:
176 const ScopedHandle handle_;
177 const std::string filename_;
178 };
179
180 class WindowsRandomAccessFile : public RandomAccessFile {
181 public:
WindowsRandomAccessFile(std::string filename,ScopedHandle handle)182 WindowsRandomAccessFile(std::string filename, ScopedHandle handle)
183 : handle_(std::move(handle)), filename_(std::move(filename)) {}
184
185 ~WindowsRandomAccessFile() override = default;
186
Read(uint64_t offset,size_t n,Slice * result,char * scratch) const187 Status Read(uint64_t offset, size_t n, Slice* result,
188 char* scratch) const override {
189 DWORD bytes_read = 0;
190 OVERLAPPED overlapped = {0};
191
192 overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
193 overlapped.Offset = static_cast<DWORD>(offset);
194 if (!::ReadFile(handle_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
195 &overlapped)) {
196 DWORD error_code = ::GetLastError();
197 if (error_code != ERROR_HANDLE_EOF) {
198 *result = Slice(scratch, 0);
199 return Status::IOError(filename_, GetWindowsErrorMessage(error_code));
200 }
201 }
202
203 *result = Slice(scratch, bytes_read);
204 return Status::OK();
205 }
206
207 private:
208 const ScopedHandle handle_;
209 const std::string filename_;
210 };
211
212 class WindowsMmapReadableFile : public RandomAccessFile {
213 public:
214 // base[0,length-1] contains the mmapped contents of the file.
WindowsMmapReadableFile(std::string filename,char * mmap_base,size_t length,Limiter * mmap_limiter)215 WindowsMmapReadableFile(std::string filename, char* mmap_base, size_t length,
216 Limiter* mmap_limiter)
217 : mmap_base_(mmap_base),
218 length_(length),
219 mmap_limiter_(mmap_limiter),
220 filename_(std::move(filename)) {}
221
~WindowsMmapReadableFile()222 ~WindowsMmapReadableFile() override {
223 ::UnmapViewOfFile(mmap_base_);
224 mmap_limiter_->Release();
225 }
226
Read(uint64_t offset,size_t n,Slice * result,char * scratch) const227 Status Read(uint64_t offset, size_t n, Slice* result,
228 char* scratch) const override {
229 if (offset + n > length_) {
230 *result = Slice();
231 return WindowsError(filename_, ERROR_INVALID_PARAMETER);
232 }
233
234 *result = Slice(mmap_base_ + offset, n);
235 return Status::OK();
236 }
237
238 private:
239 char* const mmap_base_;
240 const size_t length_;
241 Limiter* const mmap_limiter_;
242 const std::string filename_;
243 };
244
245 class WindowsWritableFile : public WritableFile {
246 public:
WindowsWritableFile(std::string filename,ScopedHandle handle)247 WindowsWritableFile(std::string filename, ScopedHandle handle)
248 : pos_(0), handle_(std::move(handle)), filename_(std::move(filename)) {}
249
250 ~WindowsWritableFile() override = default;
251
Append(const Slice & data)252 Status Append(const Slice& data) override {
253 size_t write_size = data.size();
254 const char* write_data = data.data();
255
256 // Fit as much as possible into buffer.
257 size_t copy_size = std::min(write_size, kWritableFileBufferSize - pos_);
258 std::memcpy(buf_ + pos_, write_data, copy_size);
259 write_data += copy_size;
260 write_size -= copy_size;
261 pos_ += copy_size;
262 if (write_size == 0) {
263 return Status::OK();
264 }
265
266 // Can't fit in buffer, so need to do at least one write.
267 Status status = FlushBuffer();
268 if (!status.ok()) {
269 return status;
270 }
271
272 // Small writes go to buffer, large writes are written directly.
273 if (write_size < kWritableFileBufferSize) {
274 std::memcpy(buf_, write_data, write_size);
275 pos_ = write_size;
276 return Status::OK();
277 }
278 return WriteUnbuffered(write_data, write_size);
279 }
280
Close()281 Status Close() override {
282 Status status = FlushBuffer();
283 if (!handle_.Close() && status.ok()) {
284 status = WindowsError(filename_, ::GetLastError());
285 }
286 return status;
287 }
288
Flush()289 Status Flush() override { return FlushBuffer(); }
290
Sync()291 Status Sync() override {
292 // On Windows no need to sync parent directory. Its metadata will be updated
293 // via the creation of the new file, without an explicit sync.
294
295 Status status = FlushBuffer();
296 if (!status.ok()) {
297 return status;
298 }
299
300 if (!::FlushFileBuffers(handle_.get())) {
301 return Status::IOError(filename_,
302 GetWindowsErrorMessage(::GetLastError()));
303 }
304 return Status::OK();
305 }
306
307 private:
FlushBuffer()308 Status FlushBuffer() {
309 Status status = WriteUnbuffered(buf_, pos_);
310 pos_ = 0;
311 return status;
312 }
313
WriteUnbuffered(const char * data,size_t size)314 Status WriteUnbuffered(const char* data, size_t size) {
315 DWORD bytes_written;
316 if (!::WriteFile(handle_.get(), data, static_cast<DWORD>(size),
317 &bytes_written, nullptr)) {
318 return Status::IOError(filename_,
319 GetWindowsErrorMessage(::GetLastError()));
320 }
321 return Status::OK();
322 }
323
324 // buf_[0, pos_-1] contains data to be written to handle_.
325 char buf_[kWritableFileBufferSize];
326 size_t pos_;
327
328 ScopedHandle handle_;
329 const std::string filename_;
330 };
331
332 // Lock or unlock the entire file as specified by |lock|. Returns true
333 // when successful, false upon failure. Caller should call ::GetLastError()
334 // to determine cause of failure
LockOrUnlock(HANDLE handle,bool lock)335 bool LockOrUnlock(HANDLE handle, bool lock) {
336 if (lock) {
337 return ::LockFile(handle,
338 /*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0,
339 /*nNumberOfBytesToLockLow=*/MAXDWORD,
340 /*nNumberOfBytesToLockHigh=*/MAXDWORD);
341 } else {
342 return ::UnlockFile(handle,
343 /*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0,
344 /*nNumberOfBytesToLockLow=*/MAXDWORD,
345 /*nNumberOfBytesToLockHigh=*/MAXDWORD);
346 }
347 }
348
349 class WindowsFileLock : public FileLock {
350 public:
WindowsFileLock(ScopedHandle handle,std::string filename)351 WindowsFileLock(ScopedHandle handle, std::string filename)
352 : handle_(std::move(handle)), filename_(std::move(filename)) {}
353
handle() const354 const ScopedHandle& handle() const { return handle_; }
filename() const355 const std::string& filename() const { return filename_; }
356
357 private:
358 const ScopedHandle handle_;
359 const std::string filename_;
360 };
361
362 class WindowsEnv : public Env {
363 public:
364 WindowsEnv();
~WindowsEnv()365 ~WindowsEnv() override {
366 static const char msg[] =
367 "WindowsEnv singleton destroyed. Unsupported behavior!\n";
368 std::fwrite(msg, 1, sizeof(msg), stderr);
369 std::abort();
370 }
371
NewSequentialFile(const std::string & filename,SequentialFile ** result)372 Status NewSequentialFile(const std::string& filename,
373 SequentialFile** result) override {
374 *result = nullptr;
375 DWORD desired_access = GENERIC_READ;
376 DWORD share_mode = FILE_SHARE_READ;
377 ScopedHandle handle = ::CreateFileA(
378 filename.c_str(), desired_access, share_mode,
379 /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
380 /*hTemplateFile=*/nullptr);
381 if (!handle.is_valid()) {
382 return WindowsError(filename, ::GetLastError());
383 }
384
385 *result = new WindowsSequentialFile(filename, std::move(handle));
386 return Status::OK();
387 }
388
NewRandomAccessFile(const std::string & filename,RandomAccessFile ** result)389 Status NewRandomAccessFile(const std::string& filename,
390 RandomAccessFile** result) override {
391 *result = nullptr;
392 DWORD desired_access = GENERIC_READ;
393 DWORD share_mode = FILE_SHARE_READ;
394 ScopedHandle handle =
395 ::CreateFileA(filename.c_str(), desired_access, share_mode,
396 /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING,
397 FILE_ATTRIBUTE_READONLY,
398 /*hTemplateFile=*/nullptr);
399 if (!handle.is_valid()) {
400 return WindowsError(filename, ::GetLastError());
401 }
402 if (!mmap_limiter_.Acquire()) {
403 *result = new WindowsRandomAccessFile(filename, std::move(handle));
404 return Status::OK();
405 }
406
407 LARGE_INTEGER file_size;
408 Status status;
409 if (!::GetFileSizeEx(handle.get(), &file_size)) {
410 mmap_limiter_.Release();
411 return WindowsError(filename, ::GetLastError());
412 }
413
414 ScopedHandle mapping =
415 ::CreateFileMappingA(handle.get(),
416 /*security attributes=*/nullptr, PAGE_READONLY,
417 /*dwMaximumSizeHigh=*/0,
418 /*dwMaximumSizeLow=*/0,
419 /*lpName=*/nullptr);
420 if (mapping.is_valid()) {
421 void* mmap_base = ::MapViewOfFile(mapping.get(), FILE_MAP_READ,
422 /*dwFileOffsetHigh=*/0,
423 /*dwFileOffsetLow=*/0,
424 /*dwNumberOfBytesToMap=*/0);
425 if (mmap_base) {
426 *result = new WindowsMmapReadableFile(
427 filename, reinterpret_cast<char*>(mmap_base),
428 static_cast<size_t>(file_size.QuadPart), &mmap_limiter_);
429 return Status::OK();
430 }
431 }
432 mmap_limiter_.Release();
433 return WindowsError(filename, ::GetLastError());
434 }
435
NewWritableFile(const std::string & filename,WritableFile ** result)436 Status NewWritableFile(const std::string& filename,
437 WritableFile** result) override {
438 DWORD desired_access = GENERIC_WRITE;
439 DWORD share_mode = 0; // Exclusive access.
440 ScopedHandle handle = ::CreateFileA(
441 filename.c_str(), desired_access, share_mode,
442 /*lpSecurityAttributes=*/nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
443 /*hTemplateFile=*/nullptr);
444 if (!handle.is_valid()) {
445 *result = nullptr;
446 return WindowsError(filename, ::GetLastError());
447 }
448
449 *result = new WindowsWritableFile(filename, std::move(handle));
450 return Status::OK();
451 }
452
NewAppendableFile(const std::string & filename,WritableFile ** result)453 Status NewAppendableFile(const std::string& filename,
454 WritableFile** result) override {
455 DWORD desired_access = FILE_APPEND_DATA;
456 DWORD share_mode = 0; // Exclusive access.
457 ScopedHandle handle = ::CreateFileA(
458 filename.c_str(), desired_access, share_mode,
459 /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
460 /*hTemplateFile=*/nullptr);
461 if (!handle.is_valid()) {
462 *result = nullptr;
463 return WindowsError(filename, ::GetLastError());
464 }
465
466 *result = new WindowsWritableFile(filename, std::move(handle));
467 return Status::OK();
468 }
469
FileExists(const std::string & filename)470 bool FileExists(const std::string& filename) override {
471 return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES;
472 }
473
GetChildren(const std::string & directory_path,std::vector<std::string> * result)474 Status GetChildren(const std::string& directory_path,
475 std::vector<std::string>* result) override {
476 const std::string find_pattern = directory_path + "\\*";
477 WIN32_FIND_DATAA find_data;
478 HANDLE dir_handle = ::FindFirstFileA(find_pattern.c_str(), &find_data);
479 if (dir_handle == INVALID_HANDLE_VALUE) {
480 DWORD last_error = ::GetLastError();
481 if (last_error == ERROR_FILE_NOT_FOUND) {
482 return Status::OK();
483 }
484 return WindowsError(directory_path, last_error);
485 }
486 do {
487 char base_name[_MAX_FNAME];
488 char ext[_MAX_EXT];
489
490 if (!_splitpath_s(find_data.cFileName, nullptr, 0, nullptr, 0, base_name,
491 ARRAYSIZE(base_name), ext, ARRAYSIZE(ext))) {
492 result->emplace_back(std::string(base_name) + ext);
493 }
494 } while (::FindNextFileA(dir_handle, &find_data));
495 DWORD last_error = ::GetLastError();
496 ::FindClose(dir_handle);
497 if (last_error != ERROR_NO_MORE_FILES) {
498 return WindowsError(directory_path, last_error);
499 }
500 return Status::OK();
501 }
502
RemoveFile(const std::string & filename)503 Status RemoveFile(const std::string& filename) override {
504 if (!::DeleteFileA(filename.c_str())) {
505 return WindowsError(filename, ::GetLastError());
506 }
507 return Status::OK();
508 }
509
CreateDir(const std::string & dirname)510 Status CreateDir(const std::string& dirname) override {
511 if (!::CreateDirectoryA(dirname.c_str(), nullptr)) {
512 return WindowsError(dirname, ::GetLastError());
513 }
514 return Status::OK();
515 }
516
RemoveDir(const std::string & dirname)517 Status RemoveDir(const std::string& dirname) override {
518 if (!::RemoveDirectoryA(dirname.c_str())) {
519 return WindowsError(dirname, ::GetLastError());
520 }
521 return Status::OK();
522 }
523
GetFileSize(const std::string & filename,uint64_t * size)524 Status GetFileSize(const std::string& filename, uint64_t* size) override {
525 WIN32_FILE_ATTRIBUTE_DATA file_attributes;
526 if (!::GetFileAttributesExA(filename.c_str(), GetFileExInfoStandard,
527 &file_attributes)) {
528 return WindowsError(filename, ::GetLastError());
529 }
530 ULARGE_INTEGER file_size;
531 file_size.HighPart = file_attributes.nFileSizeHigh;
532 file_size.LowPart = file_attributes.nFileSizeLow;
533 *size = file_size.QuadPart;
534 return Status::OK();
535 }
536
RenameFile(const std::string & from,const std::string & to)537 Status RenameFile(const std::string& from, const std::string& to) override {
538 // Try a simple move first. It will only succeed when |to| doesn't already
539 // exist.
540 if (::MoveFileA(from.c_str(), to.c_str())) {
541 return Status::OK();
542 }
543 DWORD move_error = ::GetLastError();
544
545 // Try the full-blown replace if the move fails, as ReplaceFile will only
546 // succeed when |to| does exist. When writing to a network share, we may not
547 // be able to change the ACLs. Ignore ACL errors then
548 // (REPLACEFILE_IGNORE_MERGE_ERRORS).
549 if (::ReplaceFileA(to.c_str(), from.c_str(), /*lpBackupFileName=*/nullptr,
550 REPLACEFILE_IGNORE_MERGE_ERRORS,
551 /*lpExclude=*/nullptr, /*lpReserved=*/nullptr)) {
552 return Status::OK();
553 }
554 DWORD replace_error = ::GetLastError();
555 // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that
556 // |to| does not exist. In this case, the more relevant error comes from the
557 // call to MoveFile.
558 if (replace_error == ERROR_FILE_NOT_FOUND ||
559 replace_error == ERROR_PATH_NOT_FOUND) {
560 return WindowsError(from, move_error);
561 } else {
562 return WindowsError(from, replace_error);
563 }
564 }
565
LockFile(const std::string & filename,FileLock ** lock)566 Status LockFile(const std::string& filename, FileLock** lock) override {
567 *lock = nullptr;
568 Status result;
569 ScopedHandle handle = ::CreateFileA(
570 filename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
571 /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
572 nullptr);
573 if (!handle.is_valid()) {
574 result = WindowsError(filename, ::GetLastError());
575 } else if (!LockOrUnlock(handle.get(), true)) {
576 result = WindowsError("lock " + filename, ::GetLastError());
577 } else {
578 *lock = new WindowsFileLock(std::move(handle), filename);
579 }
580 return result;
581 }
582
UnlockFile(FileLock * lock)583 Status UnlockFile(FileLock* lock) override {
584 WindowsFileLock* windows_file_lock =
585 reinterpret_cast<WindowsFileLock*>(lock);
586 if (!LockOrUnlock(windows_file_lock->handle().get(), false)) {
587 return WindowsError("unlock " + windows_file_lock->filename(),
588 ::GetLastError());
589 }
590 delete windows_file_lock;
591 return Status::OK();
592 }
593
594 void Schedule(void (*background_work_function)(void* background_work_arg),
595 void* background_work_arg) override;
596
StartThread(void (* thread_main)(void * thread_main_arg),void * thread_main_arg)597 void StartThread(void (*thread_main)(void* thread_main_arg),
598 void* thread_main_arg) override {
599 std::thread new_thread(thread_main, thread_main_arg);
600 new_thread.detach();
601 }
602
GetTestDirectory(std::string * result)603 Status GetTestDirectory(std::string* result) override {
604 const char* env = getenv("TEST_TMPDIR");
605 if (env && env[0] != '\0') {
606 *result = env;
607 return Status::OK();
608 }
609
610 char tmp_path[MAX_PATH];
611 if (!GetTempPathA(ARRAYSIZE(tmp_path), tmp_path)) {
612 return WindowsError("GetTempPath", ::GetLastError());
613 }
614 std::stringstream ss;
615 ss << tmp_path << "leveldbtest-" << std::this_thread::get_id();
616 *result = ss.str();
617
618 // Directory may already exist
619 CreateDir(*result);
620 return Status::OK();
621 }
622
NewLogger(const std::string & filename,Logger ** result)623 Status NewLogger(const std::string& filename, Logger** result) override {
624 std::FILE* fp = std::fopen(filename.c_str(), "w");
625 if (fp == nullptr) {
626 *result = nullptr;
627 return WindowsError(filename, ::GetLastError());
628 } else {
629 *result = new WindowsLogger(fp);
630 return Status::OK();
631 }
632 }
633
NowMicros()634 uint64_t NowMicros() override {
635 // GetSystemTimeAsFileTime typically has a resolution of 10-20 msec.
636 // TODO(cmumford): Switch to GetSystemTimePreciseAsFileTime which is
637 // available in Windows 8 and later.
638 FILETIME ft;
639 ::GetSystemTimeAsFileTime(&ft);
640 // Each tick represents a 100-nanosecond intervals since January 1, 1601
641 // (UTC).
642 uint64_t num_ticks =
643 (static_cast<uint64_t>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
644 return num_ticks / 10;
645 }
646
SleepForMicroseconds(int micros)647 void SleepForMicroseconds(int micros) override {
648 std::this_thread::sleep_for(std::chrono::microseconds(micros));
649 }
650
651 private:
652 void BackgroundThreadMain();
653
BackgroundThreadEntryPoint(WindowsEnv * env)654 static void BackgroundThreadEntryPoint(WindowsEnv* env) {
655 env->BackgroundThreadMain();
656 }
657
658 // Stores the work item data in a Schedule() call.
659 //
660 // Instances are constructed on the thread calling Schedule() and used on the
661 // background thread.
662 //
663 // This structure is thread-safe beacuse it is immutable.
664 struct BackgroundWorkItem {
BackgroundWorkItemleveldb::__anon0d865ec80111::WindowsEnv::BackgroundWorkItem665 explicit BackgroundWorkItem(void (*function)(void* arg), void* arg)
666 : function(function), arg(arg) {}
667
668 void (*const function)(void*);
669 void* const arg;
670 };
671
672 port::Mutex background_work_mutex_;
673 port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_);
674 bool started_background_thread_ GUARDED_BY(background_work_mutex_);
675
676 std::queue<BackgroundWorkItem> background_work_queue_
677 GUARDED_BY(background_work_mutex_);
678
679 Limiter mmap_limiter_; // Thread-safe.
680 };
681
682 // Return the maximum number of concurrent mmaps.
MaxMmaps()683 int MaxMmaps() { return g_mmap_limit; }
684
WindowsEnv()685 WindowsEnv::WindowsEnv()
686 : background_work_cv_(&background_work_mutex_),
687 started_background_thread_(false),
688 mmap_limiter_(MaxMmaps()) {}
689
Schedule(void (* background_work_function)(void * background_work_arg),void * background_work_arg)690 void WindowsEnv::Schedule(
691 void (*background_work_function)(void* background_work_arg),
692 void* background_work_arg) {
693 background_work_mutex_.Lock();
694
695 // Start the background thread, if we haven't done so already.
696 if (!started_background_thread_) {
697 started_background_thread_ = true;
698 std::thread background_thread(WindowsEnv::BackgroundThreadEntryPoint, this);
699 background_thread.detach();
700 }
701
702 // If the queue is empty, the background thread may be waiting for work.
703 if (background_work_queue_.empty()) {
704 background_work_cv_.Signal();
705 }
706
707 background_work_queue_.emplace(background_work_function, background_work_arg);
708 background_work_mutex_.Unlock();
709 }
710
BackgroundThreadMain()711 void WindowsEnv::BackgroundThreadMain() {
712 while (true) {
713 background_work_mutex_.Lock();
714
715 // Wait until there is work to be done.
716 while (background_work_queue_.empty()) {
717 background_work_cv_.Wait();
718 }
719
720 assert(!background_work_queue_.empty());
721 auto background_work_function = background_work_queue_.front().function;
722 void* background_work_arg = background_work_queue_.front().arg;
723 background_work_queue_.pop();
724
725 background_work_mutex_.Unlock();
726 background_work_function(background_work_arg);
727 }
728 }
729
730 // Wraps an Env instance whose destructor is never created.
731 //
732 // Intended usage:
733 // using PlatformSingletonEnv = SingletonEnv<PlatformEnv>;
734 // void ConfigurePosixEnv(int param) {
735 // PlatformSingletonEnv::AssertEnvNotInitialized();
736 // // set global configuration flags.
737 // }
738 // Env* Env::Default() {
739 // static PlatformSingletonEnv default_env;
740 // return default_env.env();
741 // }
742 template <typename EnvType>
743 class SingletonEnv {
744 public:
SingletonEnv()745 SingletonEnv() {
746 #if !defined(NDEBUG)
747 env_initialized_.store(true, std::memory_order::memory_order_relaxed);
748 #endif // !defined(NDEBUG)
749 static_assert(sizeof(env_storage_) >= sizeof(EnvType),
750 "env_storage_ will not fit the Env");
751 static_assert(alignof(decltype(env_storage_)) >= alignof(EnvType),
752 "env_storage_ does not meet the Env's alignment needs");
753 new (&env_storage_) EnvType();
754 }
755 ~SingletonEnv() = default;
756
757 SingletonEnv(const SingletonEnv&) = delete;
758 SingletonEnv& operator=(const SingletonEnv&) = delete;
759
env()760 Env* env() { return reinterpret_cast<Env*>(&env_storage_); }
761
AssertEnvNotInitialized()762 static void AssertEnvNotInitialized() {
763 #if !defined(NDEBUG)
764 assert(!env_initialized_.load(std::memory_order::memory_order_relaxed));
765 #endif // !defined(NDEBUG)
766 }
767
768 private:
769 typename std::aligned_storage<sizeof(EnvType), alignof(EnvType)>::type
770 env_storage_;
771 #if !defined(NDEBUG)
772 static std::atomic<bool> env_initialized_;
773 #endif // !defined(NDEBUG)
774 };
775
776 #if !defined(NDEBUG)
777 template <typename EnvType>
778 std::atomic<bool> SingletonEnv<EnvType>::env_initialized_;
779 #endif // !defined(NDEBUG)
780
781 using WindowsDefaultEnv = SingletonEnv<WindowsEnv>;
782
783 } // namespace
784
SetReadOnlyMMapLimit(int limit)785 void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) {
786 WindowsDefaultEnv::AssertEnvNotInitialized();
787 g_mmap_limit = limit;
788 }
789
Default()790 Env* Env::Default() {
791 static WindowsDefaultEnv env_container;
792 return env_container.env();
793 }
794
795 } // namespace leveldb
796