mirror of
https://github.com/apple/foundationdb.git
synced 2025-06-02 19:25:52 +08:00
* REST KmsConnector implementation Description diff-1: Address review comments. Add utility interface to Platform namespace to create and operate on tmpfile diff-2: Address review comments Link Boost::filesystem to CMake build process Major changes includes: 1. Implement REST based KmsConnector implementation. 2. Salient features of the connector: 2.1. Two required configuration are: a. Discovery KMS URLs - enable KMS discovery on bootstrap b. Endpoint path configuration to construct URI to fetch/refresh encryption keys c. Configuration to provide "validationTokens" to connect with external KMS. Patch implements file-based token validation scheme. 2.2. On startup, RESTKmsConnector discovers KMS Urls and caches them in-memory. Extracts "validationTokens" based on input config. 2.3. Expose endpoints to allow fetch/refresh of encryption keys. 2.4. Defines JSON format to interact with external KMS - request & response payload format. 3. Extend Platform namespace with an interface to create and operate on tmp files. 4. Update Platform 'readFileBytes' and 'writeFileBytes' to leverage fstream supported implementation. NOTE: KMS URLs fetched after initial discovery will be persisted using DynamicKnobs. It is TODO at the moment and shall be completed once DynamicKnobs is feature complete Testing Unit test to validation following: 1. Parsing on "validation tokens" logic. 2. Construction and parsing of REST JSON request and response strings.
876 lines
30 KiB
C++
876 lines
30 KiB
C++
/*
|
|
* Platform.h
|
|
*
|
|
* This source file is part of the FoundationDB open source project
|
|
*
|
|
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef FLOW_PLATFORM_H
|
|
#define FLOW_PLATFORM_H
|
|
#pragma once
|
|
|
|
#include "flow/config.h"
|
|
|
|
#if (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__))
|
|
#define __unixish__ 1
|
|
#endif
|
|
|
|
#define FLOW_THREAD_SAFE 0
|
|
|
|
#include <stdlib.h>
|
|
|
|
#define FDB_EXIT_SUCCESS 0
|
|
#define FDB_EXIT_ERROR 1
|
|
#define FDB_EXIT_ABORT 3
|
|
#define FDB_EXIT_MAIN_ERROR 10
|
|
#define FDB_EXIT_MAIN_EXCEPTION 11
|
|
#define FDB_EXIT_NO_MEM 20
|
|
#define FDB_EXIT_INIT_SEMAPHORE 21
|
|
|
|
#ifdef __cplusplus
|
|
#define EXTERNC extern "C"
|
|
|
|
#include <cstdlib>
|
|
#include <cstdint>
|
|
#include <stdio.h>
|
|
|
|
#ifdef __unixish__
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if !(defined(_WIN32) || defined(__unixish__))
|
|
#error Compiling on unknown platform
|
|
#endif
|
|
|
|
#if defined(__linux__)
|
|
#if defined(__clang__)
|
|
#if ((__clang_major__ * 100 + __clang_minor__) < 303)
|
|
#error Clang 3.3 or later is required on this platform
|
|
#endif
|
|
#elif ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40500)
|
|
#error GCC 4.5.0 or later required on this platform
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(_WIN32) && (_MSC_VER < 1600)
|
|
#error Visual Studio 2010 required on this platform
|
|
#endif
|
|
|
|
#if defined(__APPLE__) && \
|
|
(!((__clang__ == 1) || ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40800)))
|
|
#error Either Clang or GCC 4.8.0 or later required on this platform
|
|
#endif
|
|
|
|
#if (__clang__ == 1)
|
|
#define DISABLE_ZERO_DIVISION_FLAG _Pragma("GCC diagnostic ignored \"-Wdivision-by-zero\"")
|
|
#elif defined(_MSC_VER)
|
|
#define DISABLE_ZERO_DIVISION_FLAG __pragma("GCC diagnostic ignored \"-Wdiv-by-zero\"")
|
|
#else
|
|
#define DISABLE_ZERO_DIVISION_FLAG _Pragma("GCC diagnostic ignored \"-Wdiv-by-zero\"")
|
|
#endif
|
|
|
|
#if defined(__GNUG__)
|
|
#define force_inline inline __attribute__((__always_inline__))
|
|
#elif defined(_MSC_VER)
|
|
#define force_inline __forceinline
|
|
#else
|
|
#error Missing force inline
|
|
#endif
|
|
|
|
/*
|
|
* Visual Studio (.NET 2003 and beyond) has an __assume compiler
|
|
* intrinsic to hint to the compiler that a given condition is true
|
|
* and will remain true until the expression is altered. This can be
|
|
* emulated on GCC and ignored elsewhere.
|
|
*
|
|
* http://en.chys.info/2010/07/counterpart-of-assume-in-gcc/
|
|
*/
|
|
#ifndef _MSC_VER
|
|
#if defined(__GNUG__)
|
|
#define __assume(cond) \
|
|
do { \
|
|
if (!(cond)) \
|
|
__builtin_unreachable(); \
|
|
} while (0)
|
|
#else
|
|
#define __assume(cond)
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef __unixish__
|
|
#include <pthread.h>
|
|
#define CRITICAL_SECTION pthread_mutex_t
|
|
#define InitializeCriticalSection(m) \
|
|
do { \
|
|
pthread_mutexattr_t mta; \
|
|
pthread_mutexattr_init(&mta); \
|
|
pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); \
|
|
pthread_mutex_init(m, &mta); \
|
|
pthread_mutexattr_destroy(&mta); \
|
|
} while (0)
|
|
#define DeleteCriticalSection(m) pthread_mutex_destroy(m)
|
|
#define EnterCriticalSection(m) pthread_mutex_lock(m)
|
|
#define LeaveCriticalSection(m) pthread_mutex_unlock(m)
|
|
#endif
|
|
|
|
#if (defined(__GNUG__))
|
|
#include <memory>
|
|
#include <functional>
|
|
#endif
|
|
|
|
// g++ requires that non-dependent names have to be looked up at
|
|
// template definition, which makes circular dependencies a royal
|
|
// pain. (For whatever it's worth, g++ appears to be adhering to spec
|
|
// here.) Fixing this properly requires quite a bit of reordering
|
|
// and/or splitting things into multiple files, but Scherer pointed
|
|
// out that it's simple to force a name to be dependent, which is what
|
|
// we'll do for now.
|
|
template <class Ignore, class T>
|
|
inline static T& makeDependent(T& value) {
|
|
return value;
|
|
}
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#if defined(_WIN32)
|
|
#include <process.h>
|
|
#define THREAD_FUNC static void __cdecl
|
|
#define THREAD_FUNC_RETURN void
|
|
#define THREAD_HANDLE void*
|
|
THREAD_HANDLE startThread(void(func)(void*), void* arg, int stackSize = 0, const char* name = nullptr);
|
|
#define THREAD_RETURN return
|
|
#elif defined(__unixish__)
|
|
#define THREAD_FUNC static void*
|
|
#define THREAD_FUNC_RETURN void*
|
|
#define THREAD_HANDLE pthread_t
|
|
// The last parameter is an optional name for the thread. It is only supported on Linux and has a
|
|
// limit of 16 characters.
|
|
THREAD_HANDLE startThread(void*(func)(void*), void* arg, int stackSize = 0, const char* name = nullptr);
|
|
#define THREAD_RETURN return NULL
|
|
#else
|
|
#error How do I start a new thread on this platform?
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
#define DYNAMIC_LIB_EXT ".dll"
|
|
#elif defined(__linux)
|
|
#define DYNAMIC_LIB_EXT ".so"
|
|
#elif defined(__FreeBSD__)
|
|
#define DYNAMIC_LIB_EXT ".so"
|
|
#elif defined(__APPLE__)
|
|
#define DYNAMIC_LIB_EXT ".dylib"
|
|
#else
|
|
#error Port me
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
#define ENV_VAR_PATH_SEPARATOR ';'
|
|
#elif defined(__unixish__)
|
|
#define ENV_VAR_PATH_SEPARATOR ':'
|
|
#else
|
|
#error Port me
|
|
#endif
|
|
|
|
void waitThread(THREAD_HANDLE thread);
|
|
|
|
// Linux-only for now. Set thread priority.
|
|
void setThreadPriority(int pri);
|
|
|
|
#define DEBUG_DETERMINISM 0
|
|
|
|
std::string removeWhitespace(const std::string& t);
|
|
|
|
struct SystemStatistics {
|
|
bool initialized;
|
|
double elapsed;
|
|
double processCPUSeconds, mainThreadCPUSeconds;
|
|
uint64_t processMemory;
|
|
uint64_t processResidentMemory;
|
|
uint64_t processDiskTotalBytes;
|
|
uint64_t processDiskFreeBytes;
|
|
double processDiskQueueDepth;
|
|
double processDiskIdleSeconds;
|
|
double processDiskReadSeconds;
|
|
double processDiskWriteSeconds;
|
|
double processDiskRead;
|
|
double processDiskWrite;
|
|
uint64_t processDiskReadCount;
|
|
uint64_t processDiskWriteCount;
|
|
double processDiskWriteSectors;
|
|
double processDiskReadSectors;
|
|
double machineMegabitsSent;
|
|
double machineMegabitsReceived;
|
|
uint64_t machineOutSegs;
|
|
uint64_t machineRetransSegs;
|
|
double machineCPUSeconds;
|
|
int64_t machineTotalRAM;
|
|
int64_t machineCommittedRAM;
|
|
int64_t machineAvailableRAM;
|
|
|
|
SystemStatistics()
|
|
: initialized(false), elapsed(0), processCPUSeconds(0), mainThreadCPUSeconds(0), processMemory(0),
|
|
processResidentMemory(0), processDiskTotalBytes(0), processDiskFreeBytes(0), processDiskQueueDepth(0),
|
|
processDiskIdleSeconds(0), processDiskReadSeconds(0), processDiskWriteSeconds(0), processDiskRead(0),
|
|
processDiskWrite(0), processDiskReadCount(0), processDiskWriteCount(0), processDiskWriteSectors(0),
|
|
processDiskReadSectors(0), machineMegabitsSent(0), machineMegabitsReceived(0), machineOutSegs(0),
|
|
machineRetransSegs(0), machineCPUSeconds(0), machineTotalRAM(0), machineCommittedRAM(0),
|
|
machineAvailableRAM(0) {}
|
|
};
|
|
|
|
struct SystemStatisticsState;
|
|
|
|
struct IPAddress;
|
|
|
|
SystemStatistics getSystemStatistics(std::string const& dataFolder,
|
|
const IPAddress* ip,
|
|
SystemStatisticsState** statState,
|
|
bool logDetails);
|
|
|
|
double getProcessorTimeThread();
|
|
|
|
double getProcessorTimeProcess();
|
|
|
|
uint64_t getMemoryUsage();
|
|
|
|
uint64_t getResidentMemoryUsage();
|
|
|
|
struct MachineRAMInfo {
|
|
int64_t total;
|
|
int64_t committed;
|
|
int64_t available;
|
|
};
|
|
|
|
void getMachineRAMInfo(MachineRAMInfo& memInfo);
|
|
|
|
void getDiskBytes(std::string const& directory, int64_t& free, int64_t& total);
|
|
|
|
void getNetworkTraffic(uint64_t& bytesSent, uint64_t& bytesReceived, uint64_t& outSegs, uint64_t& retransSegs);
|
|
|
|
void getDiskStatistics(std::string const& directory,
|
|
uint64_t& currentIOs,
|
|
uint64_t& readMilliSecs,
|
|
uint64_t& writeMilliSecs,
|
|
uint64_t& IOMilliSecs,
|
|
uint64_t& reads,
|
|
uint64_t& writes,
|
|
uint64_t& writeSectors);
|
|
|
|
void getMachineLoad(uint64_t& idleTime, uint64_t& totalTime, bool logDetails);
|
|
|
|
double
|
|
timer(); // Returns the system real time clock with high precision. May jump around when system time is adjusted!
|
|
double timer_monotonic(); // Returns a high precision monotonic clock which is adjusted to be kind of similar to timer()
|
|
// at startup, but might not be a globally accurate time.
|
|
uint64_t timer_int(); // Return timer as uint64_t representing epoch nanoseconds
|
|
|
|
void getLocalTime(const time_t* timep, struct tm* result);
|
|
|
|
// get GMT time string from an epoch seconds double
|
|
std::string epochsToGMTString(double epochs);
|
|
|
|
void setMemoryQuota(size_t limit);
|
|
|
|
void* allocate(size_t length, bool allowLargePages, bool includeGuardPages);
|
|
|
|
void setAffinity(int proc);
|
|
|
|
void threadSleep(double seconds);
|
|
|
|
void threadYield(); // Attempt to yield to other processes or threads
|
|
|
|
// Returns true iff the file exists
|
|
bool fileExists(std::string const& filename);
|
|
|
|
// Returns true iff the directory exists
|
|
bool directoryExists(std::string const& path);
|
|
|
|
// Returns size of file in bytes
|
|
int64_t fileSize(std::string const& filename);
|
|
|
|
// Returns true if file is deleted, false if it was not found, throws platform_error() otherwise
|
|
// Consider using IAsyncFileSystem::filesystem()->deleteFile() instead, especially if you need durability!
|
|
bool deleteFile(std::string const& filename);
|
|
|
|
// Renames the given file. Does not fsync the directory.
|
|
void renameFile(std::string const& fromPath, std::string const& toPath);
|
|
|
|
// Atomically replaces the contents of the specified file.
|
|
void atomicReplace(std::string const& path, std::string const& content, bool textmode = true);
|
|
|
|
// Read a file into memory
|
|
std::string readFileBytes(std::string const& filename, int maxSize);
|
|
|
|
// Read a file into memory supplied by the caller
|
|
// If 'len' is greater than file size, then read the filesize bytes.
|
|
void readFileBytes(std::string const& filename, uint8_t* buff, int64_t len);
|
|
|
|
// Write data buffer into file
|
|
void writeFileBytes(std::string const& filename, const char* data, size_t count);
|
|
|
|
// Write text into file
|
|
void writeFile(std::string const& filename, std::string const& content);
|
|
|
|
std::string joinPath(std::string const& directory, std::string const& filename);
|
|
|
|
// cleanPath() does a 'logical' resolution of the given path string to a canonical form *without*
|
|
// following symbolic links or verifying the existence of any path components. It removes redundant
|
|
// "." references and duplicate separators, and resolves any ".." references that can be resolved
|
|
// using the preceding path components.
|
|
// Relative paths remain relative and are NOT rebased on the current working directory.
|
|
std::string cleanPath(std::string const& path);
|
|
|
|
// Removes the last component from a path string (if possible) and returns the result with one trailing separator.
|
|
// If there is only one path component, the result will be "" for relative paths and "/" for absolute paths.
|
|
// Note that this is NOT the same as getting the parent of path, as the final component could be ".."
|
|
// or "." and it would still be simply removed.
|
|
// ALL of the following inputs will yield the result "/a/"
|
|
// /a/b
|
|
// /a/b/
|
|
// /a/..
|
|
// /a/../
|
|
// /a/.
|
|
// /a/./
|
|
// /a//..//
|
|
std::string popPath(const std::string& path);
|
|
|
|
// abspath() resolves the given path to a canonical form.
|
|
// If path is relative, the result will be based on the current working directory.
|
|
// If resolveLinks is true then symbolic links will be expanded BEFORE resolving '..' references.
|
|
// An empty path or a non-existent path when mustExist is true will result in a platform_error() exception.
|
|
// Upon success, all '..' references will be resolved with the assumption that non-existent components
|
|
// are NOT symbolic links.
|
|
// User directory references such as '~' or '~user' are effectively treated as symbolic links which
|
|
// are impossible to resolve, so resolveLinks=true results in failure and resolveLinks=false results
|
|
// in the reference being left in-tact prior to resolving '..' references.
|
|
std::string abspath(std::string const& path, bool resolveLinks = true, bool mustExist = false);
|
|
|
|
// parentDirectory() returns the parent directory of the given file or directory in a canonical form,
|
|
// with a single trailing path separator.
|
|
// It uses absPath() with the same bool options to initially obtain a canonical form, and upon success
|
|
// removes the final path component, if present.
|
|
std::string parentDirectory(std::string const& path, bool resolveLinks = true, bool mustExist = false);
|
|
|
|
// Returns the portion of the path following the last path separator (e.g. the filename or directory name)
|
|
std::string basename(std::string const& filename);
|
|
|
|
// Returns the home directory of the current user
|
|
std::string getUserHomeDirectory();
|
|
|
|
namespace platform {
|
|
|
|
// Returns true if directory was created, false if it existed, throws platform_error() otherwise
|
|
bool createDirectory(std::string const& directory);
|
|
|
|
// e.g. extension==".fdb", returns filenames relative to directory
|
|
std::vector<std::string> listFiles(std::string const& directory, std::string const& extension = "");
|
|
|
|
// returns directory names relative to directory
|
|
std::vector<std::string> listDirectories(std::string const& directory);
|
|
|
|
void findFilesRecursively(std::string const& path, std::vector<std::string>& out);
|
|
|
|
// Tag the given file as "temporary", i.e. not really needing commits to disk
|
|
void makeTemporary(const char* filename);
|
|
|
|
void setCloseOnExec(int fd);
|
|
|
|
// Logs an out of memory error and exits the program
|
|
void outOfMemory();
|
|
|
|
int getRandomSeed();
|
|
|
|
bool getEnvironmentVar(const char* name, std::string& value);
|
|
int setEnvironmentVar(const char* name, const char* value, int overwrite);
|
|
|
|
std::string getWorkingDirectory();
|
|
|
|
// Returns the absolute platform-dependant path for server-based files
|
|
std::string getDefaultConfigPath();
|
|
|
|
// Returns the absolute platform-dependant path for the default fdb.cluster file
|
|
std::string getDefaultClusterFilePath();
|
|
|
|
struct ImageInfo {
|
|
void* offset = nullptr;
|
|
std::string fileName = "unknown";
|
|
std::string symbolFileName = "unknown";
|
|
};
|
|
|
|
ImageInfo getImageInfo();
|
|
|
|
// Places the frame pointers in a string formatted as parameters for addr2line.
|
|
size_t raw_backtrace(void** addresses, int maxStackDepth);
|
|
std::string get_backtrace();
|
|
std::string format_backtrace(void** addresses, int numAddresses);
|
|
|
|
// Avoid in production code: not atomic, not fast, not reliable in all environments
|
|
int eraseDirectoryRecursive(std::string const& directory);
|
|
|
|
bool isHwCrcSupported();
|
|
|
|
// Creates a temporary file; file gets destroyed/deleted along with object destruction.
|
|
// If 'tmpDir' is empty, code defaults to 'boost::filesystem::temp_directory_path()'
|
|
// If 'pattern' is empty, code defaults to 'fdbtmp'
|
|
struct TmpFile {
|
|
public:
|
|
TmpFile();
|
|
TmpFile(const std::string& tempDir);
|
|
TmpFile(const std::string& tempDir, std::string const& prefix);
|
|
~TmpFile();
|
|
size_t read(uint8_t* buff, size_t len);
|
|
void write(const uint8_t* buff, size_t len);
|
|
bool destroyFile();
|
|
std::string getFileName() const { return filename; }
|
|
|
|
private:
|
|
std::string filename;
|
|
constexpr static std::string_view defaultPrefix = "fdbtmp";
|
|
|
|
void createTmpFile(const std::string_view dir, const std::string_view prefix);
|
|
};
|
|
|
|
} // namespace platform
|
|
|
|
#ifdef __linux__
|
|
typedef struct {
|
|
double timestamp;
|
|
size_t length;
|
|
void* frames[];
|
|
} ProfilingSample;
|
|
|
|
dev_t getDeviceId(std::string path);
|
|
#endif
|
|
|
|
#if defined(__aarch64__)
|
|
#include "sse2neon.h"
|
|
// aarch64 does not have rdtsc counter
|
|
// Use cntvct_el0 virtual counter instead
|
|
inline static uint64_t timestampCounter() {
|
|
uint64_t timer;
|
|
asm volatile("mrs %0, cntvct_el0" : "=r"(timer));
|
|
return timer;
|
|
}
|
|
#elif defined(_powerpc64_)
|
|
#include <emmintrin.h>
|
|
#elif defined(__linux__)
|
|
#include <x86intrin.h>
|
|
#define timestampCounter() __rdtsc()
|
|
#elif defined(__APPLE__) // macOS on Intel
|
|
// Version of CLang bundled with XCode doesn't yet include ia32intrin.h.
|
|
#if !(__has_builtin(__rdtsc))
|
|
inline static uint64_t timestampCounter() {
|
|
uint64_t lo, hi;
|
|
asm("rdtsc" : "=a"(lo), "=d"(hi));
|
|
return (lo | (hi << 32));
|
|
}
|
|
#else
|
|
#define timestampCounter() __rdtsc()
|
|
#endif
|
|
#else
|
|
#define timestampCounter() __rdtsc()
|
|
#endif
|
|
|
|
#ifdef __FreeBSD__
|
|
#if !(__has_builtin(__rdtsc))
|
|
inline static uint64_t __rdtsc() {
|
|
uint64_t lo, hi;
|
|
asm("rdtsc" : "=a"(lo), "=d"(hi));
|
|
return (lo | (hi << 32));
|
|
}
|
|
#endif
|
|
#elif defined(__powerpc64__) || defined(__ppc64__)
|
|
inline static uint64_t __rdtsc() {
|
|
uint64_t tb;
|
|
__asm__ volatile("mfspr %0, 268" : "=r"(tb));
|
|
return tb;
|
|
}
|
|
#endif
|
|
|
|
#if defined(__linux__)
|
|
#include <features.h>
|
|
#endif
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <intrin.h>
|
|
inline static int32_t interlockedIncrement(volatile int32_t* a) {
|
|
return _InterlockedIncrement((long*)a);
|
|
}
|
|
inline static int64_t interlockedIncrement64(volatile int64_t* a) {
|
|
return _InterlockedIncrement64(a);
|
|
}
|
|
inline static int32_t interlockedDecrement(volatile int32_t* a) {
|
|
return _InterlockedDecrement((long*)a);
|
|
}
|
|
inline static int64_t interlockedDecrement64(volatile int64_t* a) {
|
|
return _InterlockedDecrement64(a);
|
|
}
|
|
inline static int32_t interlockedCompareExchange(volatile int32_t* a, int32_t b, int32_t c) {
|
|
return _InterlockedCompareExchange((long*)a, (long)b, (long)c);
|
|
}
|
|
inline static int64_t interlockedExchangeAdd64(volatile int64_t* a, int64_t b) {
|
|
return _InterlockedExchangeAdd64(a, b);
|
|
}
|
|
inline static int64_t interlockedExchange64(volatile int64_t* a, int64_t b) {
|
|
return _InterlockedExchange64(a, b);
|
|
}
|
|
inline static int64_t interlockedOr64(volatile int64_t* a, int64_t b) {
|
|
return _InterlockedOr64(a, b);
|
|
}
|
|
#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)
|
|
#ifndef __aarch64__
|
|
#include <xmmintrin.h>
|
|
#endif
|
|
inline static int32_t interlockedIncrement(volatile int32_t* a) {
|
|
return __sync_add_and_fetch(a, 1);
|
|
}
|
|
inline static int64_t interlockedIncrement64(volatile int64_t* a) {
|
|
return __sync_add_and_fetch(a, 1);
|
|
}
|
|
inline static int32_t interlockedDecrement(volatile int32_t* a) {
|
|
return __sync_add_and_fetch(a, -1);
|
|
}
|
|
inline static int64_t interlockedDecrement64(volatile int64_t* a) {
|
|
return __sync_add_and_fetch(a, -1);
|
|
}
|
|
inline static int32_t interlockedCompareExchange(volatile int32_t* a, int32_t b, int32_t c) {
|
|
return __sync_val_compare_and_swap(a, c, b);
|
|
}
|
|
inline static int64_t interlockedExchangeAdd64(volatile int64_t* a, int64_t b) {
|
|
return __sync_fetch_and_add(a, b);
|
|
}
|
|
inline static int64_t interlockedExchange64(volatile int64_t* a, int64_t b) {
|
|
__sync_synchronize();
|
|
return __sync_lock_test_and_set(a, b);
|
|
}
|
|
inline static int64_t interlockedOr64(volatile int64_t* a, int64_t b) {
|
|
return __sync_fetch_and_or(a, b);
|
|
}
|
|
#else
|
|
#error No implementation of atomic instructions
|
|
#endif
|
|
|
|
template <class T>
|
|
inline static T* interlockedExchangePtr(T* volatile* a, T* b) {
|
|
static_assert(sizeof(T*) == sizeof(int64_t), "Port me!");
|
|
return (T*)interlockedExchange64((volatile int64_t*)a, (int64_t)b);
|
|
}
|
|
|
|
#if FLOW_THREAD_SAFE
|
|
#define thread_volatile volatile
|
|
inline static int64_t flowInterlockedExchangeAdd64(volatile int64_t* p, int64_t a) {
|
|
return interlockedExchangeAdd64(p, a);
|
|
}
|
|
inline static int64_t flowInterlockedIncrement64(volatile int64_t* p) {
|
|
return interlockedIncrement64(p);
|
|
}
|
|
inline static int64_t flowInterlockedDecrement64(volatile int64_t* p) {
|
|
return interlockedDecrement64(p);
|
|
}
|
|
inline static int64_t flowInterlockedExchange64(volatile int64_t* p, int64_t a) {
|
|
return interlockedExchange64(p, a);
|
|
}
|
|
inline static int64_t flowInterlockedOr64(volatile int64_t* p, int64_t a) {
|
|
return interlockedOr64(p, a);
|
|
}
|
|
inline static int64_t flowInterlockedAnd64(volatile int64_t* p, int64_t a) {
|
|
return interlockedAnd64(p, a);
|
|
}
|
|
#else
|
|
#define thread_volatile
|
|
inline static int64_t flowInterlockedExchangeAdd64(int64_t* p, int64_t a) {
|
|
auto old = *p;
|
|
*p += a;
|
|
return old;
|
|
}
|
|
inline static int64_t flowInterlockedIncrement64(int64_t* p) {
|
|
return ++*p;
|
|
}
|
|
inline static int64_t flowInterlockedDecrement64(int64_t* p) {
|
|
return --*p;
|
|
}
|
|
inline static int64_t flowInterlockedExchange64(int64_t* p, int64_t a) {
|
|
auto old = *p;
|
|
*p = a;
|
|
return old;
|
|
}
|
|
inline static int64_t flowInterlockedOr64(int64_t* p, int64_t a) {
|
|
auto old = *p;
|
|
*p |= a;
|
|
return old;
|
|
}
|
|
inline static int64_t flowInterlockedAnd64(int64_t* p, int64_t a) {
|
|
auto old = *p;
|
|
*p &= a;
|
|
return old;
|
|
}
|
|
#endif
|
|
|
|
// We only run on little-endian system, so conversion to/from bigEndian64 is always a byte swap
|
|
#ifdef _MSC_VER
|
|
#define bigEndian16(value) uint16_t(_byteswap_ushort(value))
|
|
#define bigEndian32(value) uint32_t(_byteswap_ulong(value))
|
|
#define bigEndian64(value) uint64_t(_byteswap_uint64(value))
|
|
#define fromBigEndian16(value) uint16_t(_byteswap_ushort(value))
|
|
#define fromBigEndian32(value) uint32_t(_byteswap_ulong(value))
|
|
#define fromBigEndian64(value) uint64_t(_byteswap_uint64(value))
|
|
#elif __GNUG__
|
|
#define bigEndian16(value) uint16_t((value >> 8) | (value << 8))
|
|
#define bigEndian32(value) uint32_t(__builtin_bswap32(value))
|
|
#define bigEndian64(value) uint64_t(__builtin_bswap64(value))
|
|
#define fromBigEndian16(value) uint16_t((value >> 8) | (value << 8))
|
|
#define fromBigEndian32(value) uint32_t(__builtin_bswap32(value))
|
|
#define fromBigEndian64(value) uint64_t(__builtin_bswap64(value))
|
|
#else
|
|
#error Missing byte swap methods
|
|
#endif
|
|
|
|
#define littleEndian16(value) uint16_t(value)
|
|
#define littleEndian32(value) uint32_t(value)
|
|
#define littleEndian64(value) uint64_t(value)
|
|
|
|
#if defined(_WIN32)
|
|
inline static void flushOutputStreams() {
|
|
_flushall();
|
|
}
|
|
#elif defined(__unixish__)
|
|
inline static void flushOutputStreams() {
|
|
fflush(nullptr);
|
|
}
|
|
#else
|
|
#error Missing flush output stream
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
#define DLLEXPORT __declspec(dllexport)
|
|
#elif defined(__GNUG__)
|
|
#undef DLLEXPORT
|
|
#define DLLEXPORT __attribute__((visibility("default")))
|
|
#else
|
|
#error Missing symbol export
|
|
#endif
|
|
|
|
#define crashAndDie() (*(volatile int*)0 = 0)
|
|
|
|
#ifdef _WIN32
|
|
#define strcasecmp stricmp
|
|
#endif
|
|
|
|
#if defined(__GNUG__)
|
|
#define DEFAULT_CONSTRUCTORS(X) \
|
|
X(X const& rhs) = default; \
|
|
X& operator=(X const& rhs) = default;
|
|
#else
|
|
#define DEFAULT_CONSTRUCTORS(X)
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
#define strtoull(nptr, endptr, base) _strtoui64(nptr, endptr, base)
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
inline static void* aligned_alloc(size_t alignment, size_t size) {
|
|
return _aligned_malloc(size, alignment);
|
|
}
|
|
inline static void aligned_free(void* ptr) {
|
|
_aligned_free(ptr);
|
|
}
|
|
#elif defined(__linux__)
|
|
#include <malloc.h>
|
|
inline static void aligned_free(void* ptr) {
|
|
free(ptr);
|
|
}
|
|
#if (!defined(_ISOC11_SOURCE)) // old libc versions
|
|
inline static void* aligned_alloc(size_t alignment, size_t size) {
|
|
return memalign(alignment, size);
|
|
}
|
|
#endif
|
|
#elif defined(__FreeBSD__)
|
|
inline static void aligned_free(void* ptr) {
|
|
free(ptr);
|
|
}
|
|
#elif defined(__APPLE__)
|
|
#if !defined(HAS_ALIGNED_ALLOC)
|
|
#include <cstdlib>
|
|
inline static void* aligned_alloc(size_t alignment, size_t size) {
|
|
void* ptr = nullptr;
|
|
posix_memalign(&ptr, alignment, size);
|
|
return ptr;
|
|
}
|
|
#endif
|
|
inline static void aligned_free(void* ptr) {
|
|
free(ptr);
|
|
}
|
|
#endif
|
|
|
|
// lib_path may be a relative or absolute path or a name to be
|
|
// resolved by whatever linker is hanging around on this system
|
|
bool isLibraryLoaded(const char* lib_path);
|
|
void* loadLibrary(const char* lib_path);
|
|
void closeLibrary(void* handle);
|
|
void* loadFunction(void* lib, const char* func_name);
|
|
|
|
std::string exePath();
|
|
|
|
// get the absolute path
|
|
std::string getExecPath();
|
|
|
|
#ifdef _WIN32
|
|
inline static int ctzll(uint64_t value) {
|
|
unsigned long count = 0;
|
|
if (_BitScanForward64(&count, value)) {
|
|
return count;
|
|
}
|
|
return 64;
|
|
}
|
|
inline static int clzll(uint64_t value) {
|
|
unsigned long count = 0;
|
|
if (_BitScanReverse64(&count, value)) {
|
|
return 63 - count;
|
|
}
|
|
return 64;
|
|
}
|
|
inline static int ctz(uint32_t value) {
|
|
unsigned long count = 0;
|
|
if (_BitScanForward(&count, value)) {
|
|
return count;
|
|
}
|
|
return 64;
|
|
}
|
|
inline static int clz(uint32_t value) {
|
|
unsigned long count = 0;
|
|
if (_BitScanReverse(&count, value)) {
|
|
return 63 - count;
|
|
}
|
|
return 64;
|
|
}
|
|
#else
|
|
#define ctzll __builtin_ctzll
|
|
#define clzll __builtin_clzll
|
|
#define ctz __builtin_ctz
|
|
#define clz __builtin_clz
|
|
#endif
|
|
|
|
// These return thread local counts
|
|
int64_t getNumProfilesDeferred();
|
|
int64_t getNumProfilesOverflowed();
|
|
int64_t getNumProfilesCaptured();
|
|
|
|
#else
|
|
#define EXTERNC
|
|
#endif // __cplusplus
|
|
|
|
/*
|
|
* Multiply Defined Symbol (support for weak function declaration).
|
|
*/
|
|
#ifndef MULTIPLY_DEFINED_SYMBOL
|
|
#if defined(_MSC_VER)
|
|
#define MULTIPLY_DEFINED_SYMBOL
|
|
#else
|
|
#define MULTIPLY_DEFINED_SYMBOL __attribute__((weak))
|
|
#endif
|
|
#endif
|
|
|
|
// Logs a critical error message and exits the program
|
|
EXTERNC void criticalError(int exitCode, const char* type, const char* message);
|
|
EXTERNC void flushAndExit(int exitCode);
|
|
|
|
// Initilization code that's run at the beginning of every entry point (except fdbmonitor)
|
|
void platformInit();
|
|
|
|
void registerCrashHandler();
|
|
void setupRunLoopProfiler();
|
|
EXTERNC void setProfilingEnabled(int enabled);
|
|
|
|
// Use _exit() or criticalError(), not exit()
|
|
#define exit static_assert(false, "Calls to exit() are forbidden by policy");
|
|
|
|
#if defined(FDB_CLEAN_BUILD) && !(defined(NDEBUG) && !defined(_DEBUG) && !defined(SQLITE_DEBUG))
|
|
#error Clean builds must define NDEBUG, and not define various debug macros
|
|
#endif
|
|
|
|
// DTrace probing
|
|
#if defined(DTRACE_PROBES)
|
|
#include <sys/sdt.h>
|
|
#define FDB_TRACE_PROBE_STRING_EXPAND(x) x
|
|
#define FDB_TRACE_PROBE_STRING_CONCAT2(h, t) h##t
|
|
#define FDB_TRACE_PROBE_STRING_CONCAT(h, t) FDB_TRACE_PROBE_STRING_CONCAT2(h, t)
|
|
#define FDB_TRACE_PROBE_EXPAND_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, NAME, ...) NAME
|
|
#define FDB_TRACE_PROBE(...) \
|
|
FDB_TRACE_PROBE_EXPAND_MACRO(__VA_ARGS__, \
|
|
DTRACE_PROBE12, \
|
|
DTRACE_PROBE11, \
|
|
DTRACE_PROBE10, \
|
|
DTRACE_PROBE9, \
|
|
DTRACE_PROBE8, \
|
|
DTRACE_PROBE7, \
|
|
DTRACE_PROBE6, \
|
|
DTRACE_PROBE5, \
|
|
DTRACE_PROBE4, \
|
|
DTRACE_PROBE3, \
|
|
DTRACE_PROBE2, \
|
|
DTRACE_PROBE1, \
|
|
DTRACE_PROBE) \
|
|
(foundationdb, __VA_ARGS__)
|
|
|
|
extern void fdb_probe_actor_create(const char* name, unsigned long id);
|
|
extern void fdb_probe_actor_destroy(const char* name, unsigned long id);
|
|
extern void fdb_probe_actor_enter(const char* name, unsigned long, int index);
|
|
extern void fdb_probe_actor_exit(const char* name, unsigned long, int index);
|
|
#else
|
|
#define FDB_TRACE_PROBE_STRING_CONCAT(h, t) h##t
|
|
#define FDB_TRACE_PROBE(...)
|
|
inline void fdb_probe_actor_create(const char* name, unsigned long id) {}
|
|
inline void fdb_probe_actor_destroy(const char* name, unsigned long id) {}
|
|
inline void fdb_probe_actor_enter(const char* name, unsigned long id, int index) {}
|
|
inline void fdb_probe_actor_exit(const char* name, unsigned long id, int index) {}
|
|
#endif
|
|
|
|
// CRC32C
|
|
#ifdef __aarch64__
|
|
// aarch64
|
|
#include <inttypes.h>
|
|
static inline uint32_t hwCrc32cU8(unsigned int crc, unsigned char v) {
|
|
uint32_t ret;
|
|
asm volatile("crc32cb %w[r], %w[c], %w[v]" : [r] "=r"(ret) : [c] "r"(crc), [v] "r"(v));
|
|
return ret;
|
|
}
|
|
static inline uint32_t hwCrc32cU32(unsigned int crc, unsigned int v) {
|
|
uint32_t ret;
|
|
asm volatile("crc32cw %w[r], %w[c], %w[v]" : [r] "=r"(ret) : [c] "r"(crc), [v] "r"(v));
|
|
return ret;
|
|
}
|
|
static inline uint64_t hwCrc32cU64(uint64_t crc, uint64_t v) {
|
|
uint64_t ret;
|
|
asm volatile("crc32cx %w[r], %w[c], %x[v]" : [r] "=r"(ret) : [c] "r"(crc), [v] "r"(v));
|
|
return ret;
|
|
}
|
|
#else
|
|
#ifndef __powerpc64__
|
|
// Intel
|
|
#define hwCrc32cU8(c, v) _mm_crc32_u8(c, v)
|
|
#define hwCrc32cU32(c, v) _mm_crc32_u32(c, v)
|
|
#define hwCrc32cU64(c, v) _mm_crc32_u64(c, v)
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__aarch64__)
|
|
#define _MM_HINT_T0 0 /* dummy -- not used */
|
|
#endif
|
|
|
|
#endif /* FLOW_PLATFORM_H */
|