3e745053b7
Summary: This PR addresses some build and functional issues on MSVC targets, as a step towards an eventual goal of having RocksDB build successfully for Windows on ARM64. Addressed issues include: - BitsSetToOne and CountTrailingZeroBits do not compile on non-x64 MSVC targets. A fallback implementation of BitsSetToOne when Intel intrinsics are not available is added, based on the C++20 `<bit>` popcount implementation in Microsoft's STL. - The implementation of FloorLog2 for MSVC targets (including x64) gives incorrect results. The unit test easily detects this, but CircleCI is currently configured to only run a specific set of tests for Windows CMake builds, so this seems to have been unnoticed. - AsmVolatilePause does not use YieldProcessor on Windows ARM64 targets, even though it is available. - When CondVar::TimedWait calls Microsoft STL's condition_variable::wait_for, it can potentially trigger a bug (just recently fixed in the upcoming VS 16.8's STL) that deadlocks various tests that wait for a timer to execute, since `Timer::Run` doesn't get a chance to execute before being blocked by the test function acquiring the mutex. - In c_test, `GetTempDir` assumes a POSIX-style temp path. - `NormalizePath` did not eliminate consecutive POSIX-style path separators on Windows, resulting in test failures in e.g., wal_manager_test. - Various other test failures. In a followup PR I hope to modify CircleCI's config.yml to invoke all RocksDB unit tests in Windows CMake builds with CTest, instead of the current use of `run_ci_db_test.ps1` which requires individual tests to be specified and is missing many of the existing tests. Notes from peterd: FloorLog2 is not yet used in production code (it's for something in progress). I also added a few more inexpensive platform-dependent tests to Windows CircleCI runs. And included facebook/folly#1461 as requested Pull Request resolved: https://github.com/facebook/rocksdb/pull/7439 Reviewed By: jay-zhuang Differential Revision: D24021563 Pulled By: pdillinger fbshipit-source-id: 0ec2027c0d6a494d8a0fe38d9667fc2f7e29f7e7
412 lines
9.6 KiB
C++
412 lines
9.6 KiB
C++
// 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.
|
|
//
|
|
// See port_example.h for documentation for the following types/functions.
|
|
|
|
#pragma once
|
|
|
|
// Always want minimum headers
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <string.h>
|
|
#include <mutex>
|
|
#include <limits>
|
|
#include <condition_variable>
|
|
#include <malloc.h>
|
|
#include <intrin.h>
|
|
#include <process.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "port/win/win_thread.h"
|
|
|
|
#include "rocksdb/options.h"
|
|
|
|
#undef min
|
|
#undef max
|
|
#undef DeleteFile
|
|
#undef GetCurrentTime
|
|
|
|
|
|
#ifndef strcasecmp
|
|
#define strcasecmp _stricmp
|
|
#endif
|
|
|
|
#undef GetCurrentTime
|
|
#undef DeleteFile
|
|
|
|
#ifndef _SSIZE_T_DEFINED
|
|
typedef SSIZE_T ssize_t;
|
|
#endif
|
|
|
|
// size_t printf formatting named in the manner of C99 standard formatting
|
|
// strings such as PRIu64
|
|
// in fact, we could use that one
|
|
#ifndef ROCKSDB_PRIszt
|
|
#define ROCKSDB_PRIszt "Iu"
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#define __attribute__(A)
|
|
|
|
// Thread local storage on Linux
|
|
// There is thread_local in C++11
|
|
#ifndef __thread
|
|
#define __thread __declspec(thread)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
#define PREFETCH(addr, rw, locality)
|
|
|
|
extern const bool kDefaultToAdaptiveMutex;
|
|
|
|
namespace port {
|
|
|
|
// VS < 2015
|
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
|
|
|
// VS 15 has snprintf
|
|
#define snprintf _snprintf
|
|
|
|
#define ROCKSDB_NOEXCEPT
|
|
// std::numeric_limits<size_t>::max() is not constexpr just yet
|
|
// therefore, use the same limits
|
|
|
|
// For use at db/file_indexer.h kLevelMaxIndex
|
|
const uint32_t kMaxUint32 = UINT32_MAX;
|
|
const int kMaxInt32 = INT32_MAX;
|
|
const int kMinInt32 = INT32_MIN;
|
|
const int64_t kMaxInt64 = INT64_MAX;
|
|
const int64_t kMinInt64 = INT64_MIN;
|
|
const uint64_t kMaxUint64 = UINT64_MAX;
|
|
|
|
#ifdef _WIN64
|
|
const size_t kMaxSizet = UINT64_MAX;
|
|
#else
|
|
const size_t kMaxSizet = UINT_MAX;
|
|
#endif
|
|
|
|
#else // VS >= 2015 or MinGW
|
|
|
|
#define ROCKSDB_NOEXCEPT noexcept
|
|
|
|
// For use at db/file_indexer.h kLevelMaxIndex
|
|
const uint32_t kMaxUint32 = std::numeric_limits<uint32_t>::max();
|
|
const int kMaxInt32 = std::numeric_limits<int>::max();
|
|
const int kMinInt32 = std::numeric_limits<int>::min();
|
|
const uint64_t kMaxUint64 = std::numeric_limits<uint64_t>::max();
|
|
const int64_t kMaxInt64 = std::numeric_limits<int64_t>::max();
|
|
const int64_t kMinInt64 = std::numeric_limits<int64_t>::min();
|
|
|
|
const size_t kMaxSizet = std::numeric_limits<size_t>::max();
|
|
|
|
#endif //_MSC_VER
|
|
|
|
// "Windows is designed to run on little-endian computer architectures."
|
|
// https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types
|
|
constexpr bool kLittleEndian = true;
|
|
#undef PLATFORM_IS_LITTLE_ENDIAN
|
|
|
|
class CondVar;
|
|
|
|
class Mutex {
|
|
public:
|
|
|
|
/* implicit */ Mutex(bool adaptive = kDefaultToAdaptiveMutex)
|
|
#ifndef NDEBUG
|
|
: locked_(false)
|
|
#endif
|
|
{ }
|
|
|
|
~Mutex();
|
|
|
|
void Lock() {
|
|
mutex_.lock();
|
|
#ifndef NDEBUG
|
|
locked_ = true;
|
|
#endif
|
|
}
|
|
|
|
void Unlock() {
|
|
#ifndef NDEBUG
|
|
locked_ = false;
|
|
#endif
|
|
mutex_.unlock();
|
|
}
|
|
|
|
// this will assert if the mutex is not locked
|
|
// it does NOT verify that mutex is held by a calling thread
|
|
void AssertHeld() {
|
|
#ifndef NDEBUG
|
|
assert(locked_);
|
|
#endif
|
|
}
|
|
|
|
// Mutex is move only with lock ownership transfer
|
|
Mutex(const Mutex&) = delete;
|
|
void operator=(const Mutex&) = delete;
|
|
|
|
private:
|
|
|
|
friend class CondVar;
|
|
|
|
std::mutex& getLock() {
|
|
return mutex_;
|
|
}
|
|
|
|
std::mutex mutex_;
|
|
#ifndef NDEBUG
|
|
bool locked_;
|
|
#endif
|
|
};
|
|
|
|
class RWMutex {
|
|
public:
|
|
RWMutex() { InitializeSRWLock(&srwLock_); }
|
|
// No copying allowed
|
|
RWMutex(const RWMutex&) = delete;
|
|
void operator=(const RWMutex&) = delete;
|
|
|
|
void ReadLock() { AcquireSRWLockShared(&srwLock_); }
|
|
|
|
void WriteLock() { AcquireSRWLockExclusive(&srwLock_); }
|
|
|
|
void ReadUnlock() { ReleaseSRWLockShared(&srwLock_); }
|
|
|
|
void WriteUnlock() { ReleaseSRWLockExclusive(&srwLock_); }
|
|
|
|
// Empty as in POSIX
|
|
void AssertHeld() {}
|
|
|
|
private:
|
|
SRWLOCK srwLock_;
|
|
};
|
|
|
|
class CondVar {
|
|
public:
|
|
explicit CondVar(Mutex* mu) : mu_(mu) {
|
|
}
|
|
|
|
~CondVar();
|
|
void Wait();
|
|
bool TimedWait(uint64_t expiration_time);
|
|
void Signal();
|
|
void SignalAll();
|
|
|
|
// Condition var is not copy/move constructible
|
|
CondVar(const CondVar&) = delete;
|
|
CondVar& operator=(const CondVar&) = delete;
|
|
|
|
CondVar(CondVar&&) = delete;
|
|
CondVar& operator=(CondVar&&) = delete;
|
|
|
|
private:
|
|
std::condition_variable cv_;
|
|
Mutex* mu_;
|
|
};
|
|
|
|
|
|
#ifdef _POSIX_THREADS
|
|
using Thread = std::thread;
|
|
#else
|
|
// Wrapper around the platform efficient
|
|
// or otherwise preferrable implementation
|
|
using Thread = WindowsThread;
|
|
#endif
|
|
|
|
// OnceInit type helps emulate
|
|
// Posix semantics with initialization
|
|
// adopted in the project
|
|
struct OnceType {
|
|
|
|
struct Init {};
|
|
|
|
OnceType() {}
|
|
OnceType(const Init&) {}
|
|
OnceType(const OnceType&) = delete;
|
|
OnceType& operator=(const OnceType&) = delete;
|
|
|
|
std::once_flag flag_;
|
|
};
|
|
|
|
#define LEVELDB_ONCE_INIT port::OnceType::Init()
|
|
extern void InitOnce(OnceType* once, void (*initializer)());
|
|
|
|
#ifndef CACHE_LINE_SIZE
|
|
#define CACHE_LINE_SIZE 64U
|
|
#endif
|
|
|
|
#ifdef ROCKSDB_JEMALLOC
|
|
// Separate inlines so they can be replaced if needed
|
|
void* jemalloc_aligned_alloc(size_t size, size_t alignment) ROCKSDB_NOEXCEPT;
|
|
void jemalloc_aligned_free(void* p) ROCKSDB_NOEXCEPT;
|
|
#endif
|
|
|
|
inline void *cacheline_aligned_alloc(size_t size) {
|
|
#ifdef ROCKSDB_JEMALLOC
|
|
return jemalloc_aligned_alloc(size, CACHE_LINE_SIZE);
|
|
#else
|
|
return _aligned_malloc(size, CACHE_LINE_SIZE);
|
|
#endif
|
|
}
|
|
|
|
inline void cacheline_aligned_free(void *memblock) {
|
|
#ifdef ROCKSDB_JEMALLOC
|
|
jemalloc_aligned_free(memblock);
|
|
#else
|
|
_aligned_free(memblock);
|
|
#endif
|
|
}
|
|
|
|
extern const size_t kPageSize;
|
|
|
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 for MINGW32
|
|
// could not be worked around with by -mno-ms-bitfields
|
|
#ifndef __MINGW32__
|
|
#define ALIGN_AS(n) __declspec(align(n))
|
|
#else
|
|
#define ALIGN_AS(n)
|
|
#endif
|
|
|
|
static inline void AsmVolatilePause() {
|
|
#if defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) || defined(_M_ARM)
|
|
YieldProcessor();
|
|
#endif
|
|
// it would be nice to get "wfe" on ARM here
|
|
}
|
|
|
|
extern int PhysicalCoreID();
|
|
|
|
// For Thread Local Storage abstraction
|
|
typedef DWORD pthread_key_t;
|
|
|
|
inline int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)) {
|
|
// Not used
|
|
(void)destructor;
|
|
|
|
pthread_key_t k = TlsAlloc();
|
|
if (TLS_OUT_OF_INDEXES == k) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
*key = k;
|
|
return 0;
|
|
}
|
|
|
|
inline int pthread_key_delete(pthread_key_t key) {
|
|
if (!TlsFree(key)) {
|
|
return EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
inline int pthread_setspecific(pthread_key_t key, const void* value) {
|
|
if (!TlsSetValue(key, const_cast<void*>(value))) {
|
|
return ENOMEM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
inline void* pthread_getspecific(pthread_key_t key) {
|
|
void* result = TlsGetValue(key);
|
|
if (!result) {
|
|
if (GetLastError() != ERROR_SUCCESS) {
|
|
errno = EINVAL;
|
|
} else {
|
|
errno = NOERROR;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// UNIX equiv although errno numbers will be off
|
|
// using C-runtime to implement. Note, this does not
|
|
// feel space with zeros in case the file is extended.
|
|
int truncate(const char* path, int64_t length);
|
|
int Truncate(std::string path, int64_t length);
|
|
void Crash(const std::string& srcfile, int srcline);
|
|
extern int GetMaxOpenFiles();
|
|
std::string utf16_to_utf8(const std::wstring& utf16);
|
|
std::wstring utf8_to_utf16(const std::string& utf8);
|
|
|
|
using ThreadId = int;
|
|
|
|
extern void SetCpuPriority(ThreadId id, CpuPriority priority);
|
|
|
|
} // namespace port
|
|
|
|
|
|
#ifdef ROCKSDB_WINDOWS_UTF8_FILENAMES
|
|
|
|
#define RX_FILESTRING std::wstring
|
|
#define RX_FN(a) ROCKSDB_NAMESPACE::port::utf8_to_utf16(a)
|
|
#define FN_TO_RX(a) ROCKSDB_NAMESPACE::port::utf16_to_utf8(a)
|
|
#define RX_FNLEN(a) ::wcslen(a)
|
|
|
|
#define RX_DeleteFile DeleteFileW
|
|
#define RX_CreateFile CreateFileW
|
|
#define RX_CreateFileMapping CreateFileMappingW
|
|
#define RX_GetFileAttributesEx GetFileAttributesExW
|
|
#define RX_FindFirstFileEx FindFirstFileExW
|
|
#define RX_FindNextFile FindNextFileW
|
|
#define RX_WIN32_FIND_DATA WIN32_FIND_DATAW
|
|
#define RX_CreateDirectory CreateDirectoryW
|
|
#define RX_RemoveDirectory RemoveDirectoryW
|
|
#define RX_GetFileAttributesEx GetFileAttributesExW
|
|
#define RX_MoveFileEx MoveFileExW
|
|
#define RX_CreateHardLink CreateHardLinkW
|
|
#define RX_PathIsRelative PathIsRelativeW
|
|
#define RX_GetCurrentDirectory GetCurrentDirectoryW
|
|
#define RX_GetDiskFreeSpaceEx GetDiskFreeSpaceExW
|
|
#define RX_PathIsDirectory PathIsDirectoryW
|
|
|
|
#else
|
|
|
|
#define RX_FILESTRING std::string
|
|
#define RX_FN(a) a
|
|
#define FN_TO_RX(a) a
|
|
#define RX_FNLEN(a) strlen(a)
|
|
|
|
#define RX_DeleteFile DeleteFileA
|
|
#define RX_CreateFile CreateFileA
|
|
#define RX_CreateFileMapping CreateFileMappingA
|
|
#define RX_GetFileAttributesEx GetFileAttributesExA
|
|
#define RX_FindFirstFileEx FindFirstFileExA
|
|
#define RX_CreateDirectory CreateDirectoryA
|
|
#define RX_FindNextFile FindNextFileA
|
|
#define RX_WIN32_FIND_DATA WIN32_FIND_DATA
|
|
#define RX_CreateDirectory CreateDirectoryA
|
|
#define RX_RemoveDirectory RemoveDirectoryA
|
|
#define RX_GetFileAttributesEx GetFileAttributesExA
|
|
#define RX_MoveFileEx MoveFileExA
|
|
#define RX_CreateHardLink CreateHardLinkA
|
|
#define RX_PathIsRelative PathIsRelativeA
|
|
#define RX_GetCurrentDirectory GetCurrentDirectoryA
|
|
#define RX_GetDiskFreeSpaceEx GetDiskFreeSpaceExA
|
|
#define RX_PathIsDirectory PathIsDirectoryA
|
|
|
|
#endif
|
|
|
|
using port::pthread_key_t;
|
|
using port::pthread_key_create;
|
|
using port::pthread_key_delete;
|
|
using port::pthread_setspecific;
|
|
using port::pthread_getspecific;
|
|
using port::truncate;
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|