xref: /aosp_15_r20/external/leveldb/util/env_windows.cc (revision 9507f98c5f32dee4b5f9e4a38cd499f3ff5c4490)
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