| // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
| // This source code is licensed under both the GPLv2 (found in the |
| // COPYING file in the root directory) and Apache 2.0 License |
| // (found in the LICENSE.Apache file in the root directory). |
| // |
| // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. See the AUTHORS file for names of contributors. |
| |
| #include "port/win/env_win.h" |
| #include "port/win/win_thread.h" |
| #include <algorithm> |
| #include <ctime> |
| #include <thread> |
| |
| #include <errno.h> |
| #include <process.h> // _getpid |
| #include <io.h> // _access |
| #include <direct.h> // _rmdir, _mkdir, _getcwd |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #include "rocksdb/env.h" |
| #include "rocksdb/slice.h" |
| |
| #include "port/port.h" |
| #include "port/dirent.h" |
| #include "port/win/win_logger.h" |
| #include "port/win/io_win.h" |
| |
| #include "monitoring/iostats_context_imp.h" |
| |
| #include "monitoring/thread_status_updater.h" |
| #include "monitoring/thread_status_util.h" |
| |
| #include <rpc.h> // for uuid generation |
| #include <windows.h> |
| |
| namespace rocksdb { |
| |
| ThreadStatusUpdater* CreateThreadStatusUpdater() { |
| return new ThreadStatusUpdater(); |
| } |
| |
| namespace { |
| |
| // RAII helpers for HANDLEs |
| const auto CloseHandleFunc = [](HANDLE h) { ::CloseHandle(h); }; |
| typedef std::unique_ptr<void, decltype(CloseHandleFunc)> UniqueCloseHandlePtr; |
| |
| void WinthreadCall(const char* label, std::error_code result) { |
| if (0 != result.value()) { |
| fprintf(stderr, "pthread %s: %s\n", label, strerror(result.value())); |
| abort(); |
| } |
| } |
| |
| } |
| |
| namespace port { |
| |
| WinEnvIO::WinEnvIO(Env* hosted_env) |
| : hosted_env_(hosted_env), |
| page_size_(4 * 1012), |
| allocation_granularity_(page_size_), |
| perf_counter_frequency_(0), |
| GetSystemTimePreciseAsFileTime_(NULL) { |
| |
| SYSTEM_INFO sinfo; |
| GetSystemInfo(&sinfo); |
| |
| page_size_ = sinfo.dwPageSize; |
| allocation_granularity_ = sinfo.dwAllocationGranularity; |
| |
| { |
| LARGE_INTEGER qpf; |
| BOOL ret = QueryPerformanceFrequency(&qpf); |
| assert(ret == TRUE); |
| perf_counter_frequency_ = qpf.QuadPart; |
| } |
| |
| HMODULE module = GetModuleHandle("kernel32.dll"); |
| if (module != NULL) { |
| GetSystemTimePreciseAsFileTime_ = (FnGetSystemTimePreciseAsFileTime)GetProcAddress( |
| module, "GetSystemTimePreciseAsFileTime"); |
| } |
| } |
| |
| WinEnvIO::~WinEnvIO() { |
| } |
| |
| Status WinEnvIO::DeleteFile(const std::string& fname) { |
| Status result; |
| |
| if (_unlink(fname.c_str())) { |
| result = IOError("Failed to delete: " + fname, errno); |
| } |
| |
| return result; |
| } |
| |
| Status WinEnvIO::GetCurrentTime(int64_t* unix_time) { |
| time_t time = std::time(nullptr); |
| if (time == (time_t)(-1)) { |
| return Status::NotSupported("Failed to get time"); |
| } |
| |
| *unix_time = time; |
| return Status::OK(); |
| } |
| |
| Status WinEnvIO::NewSequentialFile(const std::string& fname, |
| std::unique_ptr<SequentialFile>* result, |
| const EnvOptions& options) { |
| Status s; |
| |
| result->reset(); |
| |
| // Corruption test needs to rename and delete files of these kind |
| // while they are still open with another handle. For that reason we |
| // allow share_write and delete(allows rename). |
| HANDLE hFile = INVALID_HANDLE_VALUE; |
| |
| DWORD fileFlags = FILE_ATTRIBUTE_READONLY; |
| |
| if (options.use_direct_reads && !options.use_mmap_reads) { |
| fileFlags |= FILE_FLAG_NO_BUFFERING; |
| } |
| |
| { |
| IOSTATS_TIMER_GUARD(open_nanos); |
| hFile = CreateFileA( |
| fname.c_str(), GENERIC_READ, |
| FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, |
| OPEN_EXISTING, // Original fopen mode is "rb" |
| fileFlags, NULL); |
| } |
| |
| if (INVALID_HANDLE_VALUE == hFile) { |
| auto lastError = GetLastError(); |
| s = IOErrorFromWindowsError("Failed to open NewSequentialFile" + fname, |
| lastError); |
| } else { |
| result->reset(new WinSequentialFile(fname, hFile, options)); |
| } |
| return s; |
| } |
| |
| Status WinEnvIO::NewRandomAccessFile(const std::string& fname, |
| std::unique_ptr<RandomAccessFile>* result, |
| const EnvOptions& options) { |
| result->reset(); |
| Status s; |
| |
| // Open the file for read-only random access |
| // Random access is to disable read-ahead as the system reads too much data |
| DWORD fileFlags = FILE_ATTRIBUTE_READONLY; |
| |
| if (options.use_direct_reads && !options.use_mmap_reads) { |
| fileFlags |= FILE_FLAG_NO_BUFFERING; |
| } else { |
| fileFlags |= FILE_FLAG_RANDOM_ACCESS; |
| } |
| |
| /// Shared access is necessary for corruption test to pass |
| // almost all tests would work with a possible exception of fault_injection |
| HANDLE hFile = 0; |
| { |
| IOSTATS_TIMER_GUARD(open_nanos); |
| hFile = |
| CreateFileA(fname.c_str(), GENERIC_READ, |
| FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
| NULL, OPEN_EXISTING, fileFlags, NULL); |
| } |
| |
| if (INVALID_HANDLE_VALUE == hFile) { |
| auto lastError = GetLastError(); |
| return IOErrorFromWindowsError( |
| "NewRandomAccessFile failed to Create/Open: " + fname, lastError); |
| } |
| |
| UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc); |
| |
| // CAUTION! This will map the entire file into the process address space |
| if (options.use_mmap_reads && sizeof(void*) >= 8) { |
| // Use mmap when virtual address-space is plentiful. |
| uint64_t fileSize; |
| |
| s = GetFileSize(fname, &fileSize); |
| |
| if (s.ok()) { |
| // Will not map empty files |
| if (fileSize == 0) { |
| return IOError( |
| "NewRandomAccessFile failed to map empty file: " + fname, EINVAL); |
| } |
| |
| HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, |
| 0, // Whole file at its present length |
| 0, |
| NULL); // Mapping name |
| |
| if (!hMap) { |
| auto lastError = GetLastError(); |
| return IOErrorFromWindowsError( |
| "Failed to create file mapping for NewRandomAccessFile: " + fname, |
| lastError); |
| } |
| |
| UniqueCloseHandlePtr mapGuard(hMap, CloseHandleFunc); |
| |
| const void* mapped_region = |
| MapViewOfFileEx(hMap, FILE_MAP_READ, |
| 0, // High DWORD of access start |
| 0, // Low DWORD |
| fileSize, |
| NULL); // Let the OS choose the mapping |
| |
| if (!mapped_region) { |
| auto lastError = GetLastError(); |
| return IOErrorFromWindowsError( |
| "Failed to MapViewOfFile for NewRandomAccessFile: " + fname, |
| lastError); |
| } |
| |
| result->reset(new WinMmapReadableFile(fname, hFile, hMap, mapped_region, |
| fileSize)); |
| |
| mapGuard.release(); |
| fileGuard.release(); |
| } |
| } else { |
| result->reset(new WinRandomAccessFile(fname, hFile, page_size_, options)); |
| fileGuard.release(); |
| } |
| return s; |
| } |
| |
| Status WinEnvIO::OpenWritableFile(const std::string& fname, |
| std::unique_ptr<WritableFile>* result, |
| const EnvOptions& options, |
| bool reopen) { |
| |
| const size_t c_BufferCapacity = 64 * 1024; |
| |
| EnvOptions local_options(options); |
| |
| result->reset(); |
| Status s; |
| |
| DWORD fileFlags = FILE_ATTRIBUTE_NORMAL; |
| |
| if (local_options.use_direct_writes && !local_options.use_mmap_writes) { |
| fileFlags = FILE_FLAG_NO_BUFFERING; |
| } |
| |
| // Desired access. We are want to write only here but if we want to memory |
| // map |
| // the file then there is no write only mode so we have to create it |
| // Read/Write |
| // However, MapViewOfFile specifies only Write only |
| DWORD desired_access = GENERIC_WRITE; |
| DWORD shared_mode = FILE_SHARE_READ; |
| |
| if (local_options.use_mmap_writes) { |
| desired_access |= GENERIC_READ; |
| } |
| else { |
| // Adding this solely for tests to pass (fault_injection_test, |
| // wal_manager_test). |
| shared_mode |= (FILE_SHARE_WRITE | FILE_SHARE_DELETE); |
| } |
| |
| // This will always truncate the file |
| DWORD creation_disposition = CREATE_ALWAYS; |
| if (reopen) { |
| creation_disposition = OPEN_ALWAYS; |
| } |
| |
| HANDLE hFile = 0; |
| { |
| IOSTATS_TIMER_GUARD(open_nanos); |
| hFile = CreateFileA( |
| fname.c_str(), |
| desired_access, // Access desired |
| shared_mode, |
| NULL, // Security attributes |
| creation_disposition, // Posix env says (reopen) ? (O_CREATE | O_APPEND) : O_CREAT | O_TRUNC |
| fileFlags, // Flags |
| NULL); // Template File |
| } |
| |
| if (INVALID_HANDLE_VALUE == hFile) { |
| auto lastError = GetLastError(); |
| return IOErrorFromWindowsError( |
| "Failed to create a NewWriteableFile: " + fname, lastError); |
| } |
| |
| // We will start writing at the end, appending |
| if (reopen) { |
| LARGE_INTEGER zero_move; |
| zero_move.QuadPart = 0; |
| BOOL ret = SetFilePointerEx(hFile, zero_move, NULL, FILE_END); |
| if (!ret) { |
| auto lastError = GetLastError(); |
| return IOErrorFromWindowsError( |
| "Failed to create a ReopenWritableFile move to the end: " + fname, lastError); |
| } |
| } |
| |
| if (options.use_mmap_writes) { |
| // We usually do not use mmmapping on SSD and thus we pass memory |
| // page_size |
| result->reset(new WinMmapFile(fname, hFile, page_size_, |
| allocation_granularity_, local_options)); |
| } else { |
| // Here we want the buffer allocation to be aligned by the SSD page size |
| // and to be a multiple of it |
| result->reset(new WinWritableFile(fname, hFile, page_size_, |
| c_BufferCapacity, local_options)); |
| } |
| return s; |
| } |
| |
| Status WinEnvIO::NewRandomRWFile(const std::string & fname, |
| std::unique_ptr<RandomRWFile>* result, const EnvOptions & options) { |
| |
| Status s; |
| |
| // Open the file for read-only random access |
| // Random access is to disable read-ahead as the system reads too much data |
| DWORD desired_access = GENERIC_READ | GENERIC_WRITE; |
| DWORD shared_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; |
| DWORD creation_disposition = OPEN_ALWAYS; // Create if necessary or open existing |
| DWORD file_flags = FILE_FLAG_RANDOM_ACCESS; |
| |
| if (options.use_direct_reads && options.use_direct_writes) { |
| file_flags |= FILE_FLAG_NO_BUFFERING; |
| } |
| |
| /// Shared access is necessary for corruption test to pass |
| // almost all tests would work with a possible exception of fault_injection |
| HANDLE hFile = 0; |
| { |
| IOSTATS_TIMER_GUARD(open_nanos); |
| hFile = |
| CreateFileA(fname.c_str(), |
| desired_access, |
| shared_mode, |
| NULL, // Security attributes |
| creation_disposition, |
| file_flags, |
| NULL); |
| } |
| |
| if (INVALID_HANDLE_VALUE == hFile) { |
| auto lastError = GetLastError(); |
| return IOErrorFromWindowsError( |
| "NewRandomRWFile failed to Create/Open: " + fname, lastError); |
| } |
| |
| UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc); |
| result->reset(new WinRandomRWFile(fname, hFile, page_size_, options)); |
| fileGuard.release(); |
| |
| return s; |
| } |
| |
| Status WinEnvIO::NewDirectory(const std::string& name, |
| std::unique_ptr<Directory>* result) { |
| Status s; |
| // Must be nullptr on failure |
| result->reset(); |
| // Must fail if directory does not exist |
| if (!DirExists(name)) { |
| s = IOError("Directory does not exist: " + name, EEXIST); |
| } else { |
| IOSTATS_TIMER_GUARD(open_nanos); |
| result->reset(new WinDirectory); |
| } |
| return s; |
| } |
| |
| Status WinEnvIO::FileExists(const std::string& fname) { |
| // F_OK == 0 |
| const int F_OK_ = 0; |
| return _access(fname.c_str(), F_OK_) == 0 ? Status::OK() |
| : Status::NotFound(); |
| } |
| |
| Status WinEnvIO::GetChildren(const std::string& dir, |
| std::vector<std::string>* result) { |
| |
| result->clear(); |
| std::vector<std::string> output; |
| |
| Status status; |
| |
| auto CloseDir = [](DIR* p) { closedir(p); }; |
| std::unique_ptr<DIR, decltype(CloseDir)> dirp(opendir(dir.c_str()), |
| CloseDir); |
| |
| if (!dirp) { |
| switch (errno) { |
| case EACCES: |
| case ENOENT: |
| case ENOTDIR: |
| return Status::NotFound(); |
| default: |
| return IOError(dir, errno); |
| } |
| } else { |
| if (result->capacity() > 0) { |
| output.reserve(result->capacity()); |
| } |
| |
| struct dirent* ent = readdir(dirp.get()); |
| while (ent) { |
| output.push_back(ent->d_name); |
| ent = readdir(dirp.get()); |
| } |
| } |
| |
| output.swap(*result); |
| |
| return status; |
| } |
| |
| Status WinEnvIO::CreateDir(const std::string& name) { |
| Status result; |
| |
| if (_mkdir(name.c_str()) != 0) { |
| auto code = errno; |
| result = IOError("Failed to create dir: " + name, code); |
| } |
| |
| return result; |
| } |
| |
| Status WinEnvIO::CreateDirIfMissing(const std::string& name) { |
| Status result; |
| |
| if (DirExists(name)) { |
| return result; |
| } |
| |
| if (_mkdir(name.c_str()) != 0) { |
| if (errno == EEXIST) { |
| result = |
| Status::IOError("`" + name + "' exists but is not a directory"); |
| } else { |
| auto code = errno; |
| result = IOError("Failed to create dir: " + name, code); |
| } |
| } |
| |
| return result; |
| } |
| |
| Status WinEnvIO::DeleteDir(const std::string& name) { |
| Status result; |
| if (_rmdir(name.c_str()) != 0) { |
| auto code = errno; |
| result = IOError("Failed to remove dir: " + name, code); |
| } |
| return result; |
| } |
| |
| Status WinEnvIO::GetFileSize(const std::string& fname, |
| uint64_t* size) { |
| Status s; |
| |
| WIN32_FILE_ATTRIBUTE_DATA attrs; |
| if (GetFileAttributesExA(fname.c_str(), GetFileExInfoStandard, &attrs)) { |
| ULARGE_INTEGER file_size; |
| file_size.HighPart = attrs.nFileSizeHigh; |
| file_size.LowPart = attrs.nFileSizeLow; |
| *size = file_size.QuadPart; |
| } else { |
| auto lastError = GetLastError(); |
| s = IOErrorFromWindowsError("Can not get size for: " + fname, lastError); |
| } |
| return s; |
| } |
| |
| uint64_t WinEnvIO::FileTimeToUnixTime(const FILETIME& ftTime) { |
| const uint64_t c_FileTimePerSecond = 10000000U; |
| // UNIX epoch starts on 1970-01-01T00:00:00Z |
| // Windows FILETIME starts on 1601-01-01T00:00:00Z |
| // Therefore, we need to subtract the below number of seconds from |
| // the seconds that we obtain from FILETIME with an obvious loss of |
| // precision |
| const uint64_t c_SecondBeforeUnixEpoch = 11644473600U; |
| |
| ULARGE_INTEGER li; |
| li.HighPart = ftTime.dwHighDateTime; |
| li.LowPart = ftTime.dwLowDateTime; |
| |
| uint64_t result = |
| (li.QuadPart / c_FileTimePerSecond) - c_SecondBeforeUnixEpoch; |
| return result; |
| } |
| |
| Status WinEnvIO::GetFileModificationTime(const std::string& fname, |
| uint64_t* file_mtime) { |
| Status s; |
| |
| WIN32_FILE_ATTRIBUTE_DATA attrs; |
| if (GetFileAttributesExA(fname.c_str(), GetFileExInfoStandard, &attrs)) { |
| *file_mtime = FileTimeToUnixTime(attrs.ftLastWriteTime); |
| } else { |
| auto lastError = GetLastError(); |
| s = IOErrorFromWindowsError( |
| "Can not get file modification time for: " + fname, lastError); |
| *file_mtime = 0; |
| } |
| |
| return s; |
| } |
| |
| Status WinEnvIO::RenameFile(const std::string& src, |
| const std::string& target) { |
| Status result; |
| |
| // rename() is not capable of replacing the existing file as on Linux |
| // so use OS API directly |
| if (!MoveFileExA(src.c_str(), target.c_str(), MOVEFILE_REPLACE_EXISTING)) { |
| DWORD lastError = GetLastError(); |
| |
| std::string text("Failed to rename: "); |
| text.append(src).append(" to: ").append(target); |
| |
| result = IOErrorFromWindowsError(text, lastError); |
| } |
| |
| return result; |
| } |
| |
| Status WinEnvIO::LinkFile(const std::string& src, |
| const std::string& target) { |
| Status result; |
| |
| if (!CreateHardLinkA(target.c_str(), src.c_str(), NULL)) { |
| DWORD lastError = GetLastError(); |
| |
| std::string text("Failed to link: "); |
| text.append(src).append(" to: ").append(target); |
| |
| result = IOErrorFromWindowsError(text, lastError); |
| } |
| |
| return result; |
| } |
| |
| Status WinEnvIO::LockFile(const std::string& lockFname, |
| FileLock** lock) { |
| assert(lock != nullptr); |
| |
| *lock = NULL; |
| Status result; |
| |
| // No-sharing, this is a LOCK file |
| const DWORD ExclusiveAccessON = 0; |
| |
| // Obtain exclusive access to the LOCK file |
| // Previously, instead of NORMAL attr we set DELETE on close and that worked |
| // well except with fault_injection test that insists on deleting it. |
| HANDLE hFile = 0; |
| { |
| IOSTATS_TIMER_GUARD(open_nanos); |
| hFile = CreateFileA(lockFname.c_str(), (GENERIC_READ | GENERIC_WRITE), |
| ExclusiveAccessON, NULL, CREATE_ALWAYS, |
| FILE_ATTRIBUTE_NORMAL, NULL); |
| } |
| |
| if (INVALID_HANDLE_VALUE == hFile) { |
| auto lastError = GetLastError(); |
| result = IOErrorFromWindowsError( |
| "Failed to create lock file: " + lockFname, lastError); |
| } else { |
| *lock = new WinFileLock(hFile); |
| } |
| |
| return result; |
| } |
| |
| Status WinEnvIO::UnlockFile(FileLock* lock) { |
| Status result; |
| |
| assert(lock != nullptr); |
| |
| delete lock; |
| |
| return result; |
| } |
| |
| Status WinEnvIO::GetTestDirectory(std::string* result) { |
| std::string output; |
| |
| const char* env = getenv("TEST_TMPDIR"); |
| if (env && env[0] != '\0') { |
| output = env; |
| CreateDir(output); |
| } else { |
| env = getenv("TMP"); |
| |
| if (env && env[0] != '\0') { |
| output = env; |
| } else { |
| output = "c:\\tmp"; |
| } |
| |
| CreateDir(output); |
| } |
| |
| output.append("\\testrocksdb-"); |
| output.append(std::to_string(_getpid())); |
| |
| CreateDir(output); |
| |
| output.swap(*result); |
| |
| return Status::OK(); |
| } |
| |
| Status WinEnvIO::NewLogger(const std::string& fname, |
| std::shared_ptr<Logger>* result) { |
| Status s; |
| |
| result->reset(); |
| |
| HANDLE hFile = 0; |
| { |
| IOSTATS_TIMER_GUARD(open_nanos); |
| hFile = CreateFileA( |
| fname.c_str(), GENERIC_WRITE, |
| FILE_SHARE_READ | FILE_SHARE_DELETE, // In RocksDb log files are |
| // renamed and deleted before |
| // they are closed. This enables |
| // doing so. |
| NULL, |
| CREATE_ALWAYS, // Original fopen mode is "w" |
| FILE_ATTRIBUTE_NORMAL, NULL); |
| } |
| |
| if (INVALID_HANDLE_VALUE == hFile) { |
| auto lastError = GetLastError(); |
| s = IOErrorFromWindowsError("Failed to open LogFile" + fname, lastError); |
| } else { |
| { |
| // With log files we want to set the true creation time as of now |
| // because the system |
| // for some reason caches the attributes of the previous file that just |
| // been renamed from |
| // this name so auto_roll_logger_test fails |
| FILETIME ft; |
| GetSystemTimeAsFileTime(&ft); |
| // Set creation, last access and last write time to the same value |
| SetFileTime(hFile, &ft, &ft, &ft); |
| } |
| result->reset(new WinLogger(&WinEnvThreads::gettid, hosted_env_, hFile)); |
| } |
| return s; |
| } |
| |
| uint64_t WinEnvIO::NowMicros() { |
| |
| if (GetSystemTimePreciseAsFileTime_ != NULL) { |
| // all std::chrono clocks on windows proved to return |
| // values that may repeat that is not good enough for some uses. |
| const int64_t c_UnixEpochStartTicks = 116444736000000000LL; |
| const int64_t c_FtToMicroSec = 10; |
| |
| // This interface needs to return system time and not |
| // just any microseconds because it is often used as an argument |
| // to TimedWait() on condition variable |
| FILETIME ftSystemTime; |
| GetSystemTimePreciseAsFileTime_(&ftSystemTime); |
| |
| LARGE_INTEGER li; |
| li.LowPart = ftSystemTime.dwLowDateTime; |
| li.HighPart = ftSystemTime.dwHighDateTime; |
| // Subtract unix epoch start |
| li.QuadPart -= c_UnixEpochStartTicks; |
| // Convert to microsecs |
| li.QuadPart /= c_FtToMicroSec; |
| return li.QuadPart; |
| } |
| using namespace std::chrono; |
| return duration_cast<microseconds>(system_clock::now().time_since_epoch()).count(); |
| } |
| |
| uint64_t WinEnvIO::NowNanos() { |
| // all std::chrono clocks on windows have the same resolution that is only |
| // good enough for microseconds but not nanoseconds |
| // On Windows 8 and Windows 2012 Server |
| // GetSystemTimePreciseAsFileTime(¤t_time) can be used |
| LARGE_INTEGER li; |
| QueryPerformanceCounter(&li); |
| // Convert to nanoseconds first to avoid loss of precision |
| // and divide by frequency |
| li.QuadPart *= std::nano::den; |
| li.QuadPart /= perf_counter_frequency_; |
| return li.QuadPart; |
| } |
| |
| Status WinEnvIO::GetHostName(char* name, uint64_t len) { |
| Status s; |
| DWORD nSize = static_cast<DWORD>( |
| std::min<uint64_t>(len, std::numeric_limits<DWORD>::max())); |
| |
| if (!::GetComputerNameA(name, &nSize)) { |
| auto lastError = GetLastError(); |
| s = IOErrorFromWindowsError("GetHostName", lastError); |
| } else { |
| name[nSize] = 0; |
| } |
| |
| return s; |
| } |
| |
| Status WinEnvIO::GetAbsolutePath(const std::string& db_path, |
| std::string* output_path) { |
| // Check if we already have an absolute path |
| // that starts with non dot and has a semicolon in it |
| if ((!db_path.empty() && (db_path[0] == '/' || db_path[0] == '\\')) || |
| (db_path.size() > 2 && db_path[0] != '.' && |
| ((db_path[1] == ':' && db_path[2] == '\\') || |
| (db_path[1] == ':' && db_path[2] == '/')))) { |
| *output_path = db_path; |
| return Status::OK(); |
| } |
| |
| std::string result; |
| result.resize(_MAX_PATH); |
| |
| char* ret = _getcwd(&result[0], _MAX_PATH); |
| if (ret == nullptr) { |
| return Status::IOError("Failed to get current working directory", |
| strerror(errno)); |
| } |
| |
| result.resize(strlen(result.data())); |
| |
| result.swap(*output_path); |
| return Status::OK(); |
| } |
| |
| std::string WinEnvIO::TimeToString(uint64_t secondsSince1970) { |
| std::string result; |
| |
| const time_t seconds = secondsSince1970; |
| const int maxsize = 64; |
| |
| struct tm t; |
| errno_t ret = localtime_s(&t, &seconds); |
| |
| if (ret) { |
| result = std::to_string(seconds); |
| } else { |
| result.resize(maxsize); |
| char* p = &result[0]; |
| |
| int len = snprintf(p, maxsize, "%04d/%02d/%02d-%02d:%02d:%02d ", |
| t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, |
| t.tm_min, t.tm_sec); |
| assert(len > 0); |
| |
| result.resize(len); |
| } |
| |
| return result; |
| } |
| |
| EnvOptions WinEnvIO::OptimizeForLogWrite(const EnvOptions& env_options, |
| const DBOptions& db_options) const { |
| EnvOptions optimized = env_options; |
| optimized.bytes_per_sync = db_options.wal_bytes_per_sync; |
| optimized.use_mmap_writes = false; |
| // This is because we flush only whole pages on unbuffered io and |
| // the last records are not guaranteed to be flushed. |
| optimized.use_direct_writes = false; |
| // TODO(icanadi) it's faster if fallocate_with_keep_size is false, but it |
| // breaks TransactionLogIteratorStallAtLastRecord unit test. Fix the unit |
| // test and make this false |
| optimized.fallocate_with_keep_size = true; |
| return optimized; |
| } |
| |
| EnvOptions WinEnvIO::OptimizeForManifestWrite( |
| const EnvOptions& env_options) const { |
| EnvOptions optimized = env_options; |
| optimized.use_mmap_writes = false; |
| optimized.use_direct_writes = false; |
| optimized.fallocate_with_keep_size = true; |
| return optimized; |
| } |
| |
| // Returns true iff the named directory exists and is a directory. |
| bool WinEnvIO::DirExists(const std::string& dname) { |
| WIN32_FILE_ATTRIBUTE_DATA attrs; |
| if (GetFileAttributesExA(dname.c_str(), GetFileExInfoStandard, &attrs)) { |
| return 0 != (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); |
| } |
| return false; |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // WinEnvThreads |
| |
| WinEnvThreads::WinEnvThreads(Env* hosted_env) : hosted_env_(hosted_env), thread_pools_(Env::Priority::TOTAL) { |
| |
| for (int pool_id = 0; pool_id < Env::Priority::TOTAL; ++pool_id) { |
| thread_pools_[pool_id].SetThreadPriority( |
| static_cast<Env::Priority>(pool_id)); |
| // This allows later initializing the thread-local-env of each thread. |
| thread_pools_[pool_id].SetHostEnv(hosted_env); |
| } |
| } |
| |
| WinEnvThreads::~WinEnvThreads() { |
| |
| WaitForJoin(); |
| |
| for (auto& thpool : thread_pools_) { |
| thpool.JoinAllThreads(); |
| } |
| } |
| |
| void WinEnvThreads::Schedule(void(*function)(void*), void* arg, Env::Priority pri, |
| void* tag, void(*unschedFunction)(void* arg)) { |
| assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); |
| thread_pools_[pri].Schedule(function, arg, tag, unschedFunction); |
| } |
| |
| int WinEnvThreads::UnSchedule(void* arg, Env::Priority pri) { |
| return thread_pools_[pri].UnSchedule(arg); |
| } |
| |
| namespace { |
| |
| struct StartThreadState { |
| void(*user_function)(void*); |
| void* arg; |
| }; |
| |
| void* StartThreadWrapper(void* arg) { |
| std::unique_ptr<StartThreadState> state( |
| reinterpret_cast<StartThreadState*>(arg)); |
| state->user_function(state->arg); |
| return nullptr; |
| } |
| |
| } |
| |
| void WinEnvThreads::StartThread(void(*function)(void* arg), void* arg) { |
| std::unique_ptr<StartThreadState> state(new StartThreadState); |
| state->user_function = function; |
| state->arg = arg; |
| try { |
| |
| rocksdb::port::WindowsThread th(&StartThreadWrapper, state.get()); |
| state.release(); |
| |
| std::lock_guard<std::mutex> lg(mu_); |
| threads_to_join_.push_back(std::move(th)); |
| |
| } catch (const std::system_error& ex) { |
| WinthreadCall("start thread", ex.code()); |
| } |
| } |
| |
| void WinEnvThreads::WaitForJoin() { |
| for (auto& th : threads_to_join_) { |
| th.join(); |
| } |
| threads_to_join_.clear(); |
| } |
| |
| unsigned int WinEnvThreads::GetThreadPoolQueueLen(Env::Priority pri) const { |
| assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); |
| return thread_pools_[pri].GetQueueLen(); |
| } |
| |
| uint64_t WinEnvThreads::gettid() { |
| uint64_t thread_id = GetCurrentThreadId(); |
| return thread_id; |
| } |
| |
| uint64_t WinEnvThreads::GetThreadID() const { return gettid(); } |
| |
| void WinEnvThreads::SleepForMicroseconds(int micros) { |
| std::this_thread::sleep_for(std::chrono::microseconds(micros)); |
| } |
| |
| void WinEnvThreads::SetBackgroundThreads(int num, Env::Priority pri) { |
| assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); |
| thread_pools_[pri].SetBackgroundThreads(num); |
| } |
| |
| int WinEnvThreads::GetBackgroundThreads(Env::Priority pri) { |
| assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); |
| return thread_pools_[pri].GetBackgroundThreads(); |
| } |
| |
| void WinEnvThreads::IncBackgroundThreadsIfNeeded(int num, Env::Priority pri) { |
| assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); |
| thread_pools_[pri].IncBackgroundThreadsIfNeeded(num); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////// |
| // WinEnv |
| |
| WinEnv::WinEnv() : winenv_io_(this), winenv_threads_(this) { |
| // Protected member of the base class |
| thread_status_updater_ = CreateThreadStatusUpdater(); |
| } |
| |
| |
| WinEnv::~WinEnv() { |
| // All threads must be joined before the deletion of |
| // thread_status_updater_. |
| delete thread_status_updater_; |
| } |
| |
| Status WinEnv::GetThreadList( |
| std::vector<ThreadStatus>* thread_list) { |
| assert(thread_status_updater_); |
| return thread_status_updater_->GetThreadList(thread_list); |
| } |
| |
| Status WinEnv::DeleteFile(const std::string& fname) { |
| return winenv_io_.DeleteFile(fname); |
| } |
| |
| Status WinEnv::GetCurrentTime(int64_t* unix_time) { |
| return winenv_io_.GetCurrentTime(unix_time); |
| } |
| |
| Status WinEnv::NewSequentialFile(const std::string& fname, |
| std::unique_ptr<SequentialFile>* result, |
| const EnvOptions& options) { |
| return winenv_io_.NewSequentialFile(fname, result, options); |
| } |
| |
| Status WinEnv::NewRandomAccessFile(const std::string& fname, |
| std::unique_ptr<RandomAccessFile>* result, |
| const EnvOptions& options) { |
| return winenv_io_.NewRandomAccessFile(fname, result, options); |
| } |
| |
| Status WinEnv::NewWritableFile(const std::string& fname, |
| std::unique_ptr<WritableFile>* result, |
| const EnvOptions& options) { |
| return winenv_io_.OpenWritableFile(fname, result, options, false); |
| } |
| |
| Status WinEnv::ReopenWritableFile(const std::string& fname, |
| std::unique_ptr<WritableFile>* result, const EnvOptions& options) { |
| return winenv_io_.OpenWritableFile(fname, result, options, true); |
| } |
| |
| Status WinEnv::NewRandomRWFile(const std::string & fname, |
| unique_ptr<RandomRWFile>* result, const EnvOptions & options) { |
| return winenv_io_.NewRandomRWFile(fname, result, options); |
| } |
| |
| Status WinEnv::NewDirectory(const std::string& name, |
| std::unique_ptr<Directory>* result) { |
| return winenv_io_.NewDirectory(name, result); |
| } |
| |
| Status WinEnv::FileExists(const std::string& fname) { |
| return winenv_io_.FileExists(fname); |
| } |
| |
| Status WinEnv::GetChildren(const std::string& dir, |
| std::vector<std::string>* result) { |
| return winenv_io_.GetChildren(dir, result); |
| } |
| |
| Status WinEnv::CreateDir(const std::string& name) { |
| return winenv_io_.CreateDir(name); |
| } |
| |
| Status WinEnv::CreateDirIfMissing(const std::string& name) { |
| return winenv_io_.CreateDirIfMissing(name); |
| } |
| |
| Status WinEnv::DeleteDir(const std::string& name) { |
| return winenv_io_.DeleteDir(name); |
| } |
| |
| Status WinEnv::GetFileSize(const std::string& fname, |
| uint64_t* size) { |
| return winenv_io_.GetFileSize(fname, size); |
| } |
| |
| Status WinEnv::GetFileModificationTime(const std::string& fname, |
| uint64_t* file_mtime) { |
| return winenv_io_.GetFileModificationTime(fname, file_mtime); |
| } |
| |
| Status WinEnv::RenameFile(const std::string& src, |
| const std::string& target) { |
| return winenv_io_.RenameFile(src, target); |
| } |
| |
| Status WinEnv::LinkFile(const std::string& src, |
| const std::string& target) { |
| return winenv_io_.LinkFile(src, target); |
| } |
| |
| Status WinEnv::LockFile(const std::string& lockFname, |
| FileLock** lock) { |
| return winenv_io_.LockFile(lockFname, lock); |
| } |
| |
| Status WinEnv::UnlockFile(FileLock* lock) { |
| return winenv_io_.UnlockFile(lock); |
| } |
| |
| Status WinEnv::GetTestDirectory(std::string* result) { |
| return winenv_io_.GetTestDirectory(result); |
| } |
| |
| Status WinEnv::NewLogger(const std::string& fname, |
| std::shared_ptr<Logger>* result) { |
| return winenv_io_.NewLogger(fname, result); |
| } |
| |
| uint64_t WinEnv::NowMicros() { |
| return winenv_io_.NowMicros(); |
| } |
| |
| uint64_t WinEnv::NowNanos() { |
| return winenv_io_.NowNanos(); |
| } |
| |
| Status WinEnv::GetHostName(char* name, uint64_t len) { |
| return winenv_io_.GetHostName(name, len); |
| } |
| |
| Status WinEnv::GetAbsolutePath(const std::string& db_path, |
| std::string* output_path) { |
| return winenv_io_.GetAbsolutePath(db_path, output_path); |
| } |
| |
| std::string WinEnv::TimeToString(uint64_t secondsSince1970) { |
| return winenv_io_.TimeToString(secondsSince1970); |
| } |
| |
| void WinEnv::Schedule(void(*function)(void*), void* arg, Env::Priority pri, |
| void* tag, |
| void(*unschedFunction)(void* arg)) { |
| return winenv_threads_.Schedule(function, arg, pri, tag, unschedFunction); |
| } |
| |
| int WinEnv::UnSchedule(void* arg, Env::Priority pri) { |
| return winenv_threads_.UnSchedule(arg, pri); |
| } |
| |
| void WinEnv::StartThread(void(*function)(void* arg), void* arg) { |
| return winenv_threads_.StartThread(function, arg); |
| } |
| |
| void WinEnv::WaitForJoin() { |
| return winenv_threads_.WaitForJoin(); |
| } |
| |
| unsigned int WinEnv::GetThreadPoolQueueLen(Env::Priority pri) const { |
| return winenv_threads_.GetThreadPoolQueueLen(pri); |
| } |
| |
| uint64_t WinEnv::GetThreadID() const { |
| return winenv_threads_.GetThreadID(); |
| } |
| |
| void WinEnv::SleepForMicroseconds(int micros) { |
| return winenv_threads_.SleepForMicroseconds(micros); |
| } |
| |
| // Allow increasing the number of worker threads. |
| void WinEnv::SetBackgroundThreads(int num, Env::Priority pri) { |
| return winenv_threads_.SetBackgroundThreads(num, pri); |
| } |
| |
| int WinEnv::GetBackgroundThreads(Env::Priority pri) { |
| return winenv_threads_.GetBackgroundThreads(pri); |
| } |
| |
| void WinEnv::IncBackgroundThreadsIfNeeded(int num, Env::Priority pri) { |
| return winenv_threads_.IncBackgroundThreadsIfNeeded(num, pri); |
| } |
| |
| EnvOptions WinEnv::OptimizeForLogWrite(const EnvOptions& env_options, |
| const DBOptions& db_options) const { |
| return winenv_io_.OptimizeForLogWrite(env_options, db_options); |
| } |
| |
| EnvOptions WinEnv::OptimizeForManifestWrite( |
| const EnvOptions& env_options) const { |
| return winenv_io_.OptimizeForManifestWrite(env_options); |
| } |
| |
| } // namespace port |
| |
| std::string Env::GenerateUniqueId() { |
| std::string result; |
| |
| UUID uuid; |
| UuidCreateSequential(&uuid); |
| |
| RPC_CSTR rpc_str; |
| auto status = UuidToStringA(&uuid, &rpc_str); |
| (void)status; |
| assert(status == RPC_S_OK); |
| |
| result = reinterpret_cast<char*>(rpc_str); |
| |
| status = RpcStringFreeA(&rpc_str); |
| assert(status == RPC_S_OK); |
| |
| return result; |
| } |
| |
| } // namespace rocksdb |