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 */
|