mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
[scripts] Remove perf-counters (#35308)
This commit is contained in:
committed by
GitHub
parent
2cb08e65b3
commit
c0b7c0d31f
@@ -1,3 +0,0 @@
|
||||
build/jsc-perf: src/*
|
||||
mkdir -p build
|
||||
g++ -std=c++11 -I/usr/include/webkitgtk-1.0/ -ljavascriptcoregtk-1.0 src/jsc-perf.cpp src/hardware-counter.cpp src/thread-local.cpp -o build/jsc-perf
|
||||
@@ -1,16 +0,0 @@
|
||||
# perf-counters
|
||||
|
||||
Lightweight bindings to Linux perf event counters.
|
||||
|
||||
```
|
||||
$ node
|
||||
> var PerfCounters = require('perf-counters');
|
||||
> PerfCounters.init();
|
||||
> var start = PerfCounters.getCounters(); console.log('test'); var end = PerfCounters.getCounters();
|
||||
test
|
||||
> start
|
||||
{ instructions: 1382, loads: 421, stores: 309 }
|
||||
> end
|
||||
{ instructions: 647633, loads: 195771, stores: 133246 }
|
||||
>
|
||||
```
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "perfcounters",
|
||||
"sources": [
|
||||
"src/hardware-counter.cpp",
|
||||
"src/perf-counters.cpp",
|
||||
"src/thread-local.cpp",
|
||||
],
|
||||
"cflags": [
|
||||
"-Wno-sign-compare",
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('bindings')('perfcounters');
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "perf-counters",
|
||||
"version": "0.1.2",
|
||||
"description": "Lightweight bindings to Linux perf event counters.",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bindings": "^1.2.1"
|
||||
}
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "hardware-counter.h"
|
||||
|
||||
#ifndef NO_HARDWARE_COUNTERS
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IMPLEMENT_THREAD_LOCAL_NO_CHECK(HardwareCounter,
|
||||
HardwareCounter::s_counter);
|
||||
|
||||
static bool s_recordSubprocessTimes = false;
|
||||
static bool s_profileHWEnable;
|
||||
static std::string s_profileHWEvents;
|
||||
|
||||
static inline bool useCounters() {
|
||||
#ifdef VALGRIND
|
||||
return false;
|
||||
#else
|
||||
return s_profileHWEnable;
|
||||
#endif
|
||||
}
|
||||
|
||||
class HardwareCounterImpl {
|
||||
public:
|
||||
HardwareCounterImpl(int type, unsigned long config,
|
||||
const char* desc = nullptr)
|
||||
: m_desc(desc ? desc : ""), m_err(0), m_fd(-1), inited(false) {
|
||||
memset (&pe, 0, sizeof (struct perf_event_attr));
|
||||
pe.type = type;
|
||||
pe.size = sizeof (struct perf_event_attr);
|
||||
pe.config = config;
|
||||
pe.inherit = s_recordSubprocessTimes;
|
||||
pe.disabled = 1;
|
||||
pe.pinned = 0;
|
||||
pe.exclude_kernel = 0;
|
||||
pe.exclude_hv = 1;
|
||||
pe.read_format =
|
||||
PERF_FORMAT_TOTAL_TIME_ENABLED|PERF_FORMAT_TOTAL_TIME_RUNNING;
|
||||
}
|
||||
|
||||
~HardwareCounterImpl() {
|
||||
close();
|
||||
}
|
||||
|
||||
void init_if_not() {
|
||||
/*
|
||||
* perf_event_open(struct perf_event_attr *hw_event_uptr, pid_t pid,
|
||||
* int cpu, int group_fd, unsigned long flags)
|
||||
*/
|
||||
if (inited) return;
|
||||
inited = true;
|
||||
m_fd = syscall(__NR_perf_event_open, &pe, 0, -1, -1, 0);
|
||||
if (m_fd < 0) {
|
||||
// Logger::Verbose("perf_event_open failed with: %s",
|
||||
// folly::errnoStr(errno).c_str());
|
||||
m_err = -1;
|
||||
return;
|
||||
}
|
||||
if (ioctl(m_fd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
|
||||
// Logger::Warning("perf_event failed to enable: %s",
|
||||
// folly::errnoStr(errno).c_str());
|
||||
close();
|
||||
m_err = -1;
|
||||
return;
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
int64_t read() {
|
||||
uint64_t values[3];
|
||||
if (readRaw(values)) {
|
||||
if (!values[2]) return 0;
|
||||
int64_t value = (double)values[0] * values[1] / values[2];
|
||||
return value + extra;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void incCount(int64_t amount) {
|
||||
extra += amount;
|
||||
}
|
||||
|
||||
bool readRaw(uint64_t* values) {
|
||||
if (m_err || !useCounters()) return false;
|
||||
init_if_not();
|
||||
|
||||
if (m_fd > 0) {
|
||||
/*
|
||||
* read the count + scaling values
|
||||
*
|
||||
* It is not necessary to stop an event to read its value
|
||||
*/
|
||||
auto ret = ::read(m_fd, values, sizeof(*values) * 3);
|
||||
if (ret == sizeof(*values) * 3) {
|
||||
values[0] -= reset_values[0];
|
||||
values[1] -= reset_values[1];
|
||||
values[2] -= reset_values[2];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (m_err || !useCounters()) return;
|
||||
init_if_not();
|
||||
extra = 0;
|
||||
if (m_fd > 0) {
|
||||
if (ioctl (m_fd, PERF_EVENT_IOC_RESET, 0) < 0) {
|
||||
// Logger::Warning("perf_event failed to reset with: %s",
|
||||
// folly::errnoStr(errno).c_str());
|
||||
m_err = -1;
|
||||
return;
|
||||
}
|
||||
auto ret = ::read(m_fd, reset_values, sizeof(reset_values));
|
||||
if (ret != sizeof(reset_values)) {
|
||||
// Logger::Warning("perf_event failed to reset with: %s",
|
||||
// folly::errnoStr(errno).c_str());
|
||||
m_err = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
std::string m_desc;
|
||||
int m_err;
|
||||
private:
|
||||
int m_fd;
|
||||
struct perf_event_attr pe;
|
||||
bool inited;
|
||||
uint64_t reset_values[3];
|
||||
uint64_t extra{0};
|
||||
|
||||
void close() {
|
||||
if (m_fd > 0) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class InstructionCounter : public HardwareCounterImpl {
|
||||
public:
|
||||
InstructionCounter() :
|
||||
HardwareCounterImpl(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS) {}
|
||||
};
|
||||
|
||||
class LoadCounter : public HardwareCounterImpl {
|
||||
public:
|
||||
LoadCounter() :
|
||||
HardwareCounterImpl(PERF_TYPE_HW_CACHE,
|
||||
(PERF_COUNT_HW_CACHE_L1D | ((PERF_COUNT_HW_CACHE_OP_READ) << 8))) {}
|
||||
};
|
||||
|
||||
class StoreCounter : public HardwareCounterImpl {
|
||||
public:
|
||||
StoreCounter() :
|
||||
HardwareCounterImpl(PERF_TYPE_HW_CACHE,
|
||||
PERF_COUNT_HW_CACHE_L1D | ((PERF_COUNT_HW_CACHE_OP_WRITE) << 8)) {}
|
||||
};
|
||||
|
||||
HardwareCounter::HardwareCounter()
|
||||
: m_countersSet(false) {
|
||||
m_instructionCounter.reset(new InstructionCounter());
|
||||
if (s_profileHWEvents.empty()) {
|
||||
m_loadCounter.reset(new LoadCounter());
|
||||
m_storeCounter.reset(new StoreCounter());
|
||||
} else {
|
||||
m_countersSet = true;
|
||||
setPerfEvents(s_profileHWEvents);
|
||||
}
|
||||
}
|
||||
|
||||
HardwareCounter::~HardwareCounter() {
|
||||
}
|
||||
|
||||
void HardwareCounter::Init(bool enable, const std::string& events,
|
||||
bool subProc) {
|
||||
s_profileHWEnable = enable;
|
||||
s_profileHWEvents = events;
|
||||
s_recordSubprocessTimes = subProc;
|
||||
}
|
||||
|
||||
void HardwareCounter::Reset() {
|
||||
s_counter->reset();
|
||||
}
|
||||
|
||||
void HardwareCounter::reset() {
|
||||
m_instructionCounter->reset();
|
||||
if (!m_countersSet) {
|
||||
m_storeCounter->reset();
|
||||
m_loadCounter->reset();
|
||||
}
|
||||
for (unsigned i = 0; i < m_counters.size(); i++) {
|
||||
m_counters[i]->reset();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t HardwareCounter::GetInstructionCount() {
|
||||
return s_counter->getInstructionCount();
|
||||
}
|
||||
|
||||
int64_t HardwareCounter::getInstructionCount() {
|
||||
return m_instructionCounter->read();
|
||||
}
|
||||
|
||||
int64_t HardwareCounter::GetLoadCount() {
|
||||
return s_counter->getLoadCount();
|
||||
}
|
||||
|
||||
int64_t HardwareCounter::getLoadCount() {
|
||||
return m_loadCounter->read();
|
||||
}
|
||||
|
||||
int64_t HardwareCounter::GetStoreCount() {
|
||||
return s_counter->getStoreCount();
|
||||
}
|
||||
|
||||
int64_t HardwareCounter::getStoreCount() {
|
||||
return m_storeCounter->read();
|
||||
}
|
||||
|
||||
void HardwareCounter::IncInstructionCount(int64_t amount) {
|
||||
s_counter->m_instructionCounter->incCount(amount);
|
||||
}
|
||||
|
||||
void HardwareCounter::IncLoadCount(int64_t amount) {
|
||||
if (!s_counter->m_countersSet) {
|
||||
s_counter->m_loadCounter->incCount(amount);
|
||||
}
|
||||
}
|
||||
|
||||
void HardwareCounter::IncStoreCount(int64_t amount) {
|
||||
if (!s_counter->m_countersSet) {
|
||||
s_counter->m_storeCounter->incCount(amount);
|
||||
}
|
||||
}
|
||||
|
||||
struct PerfTable perfTable[] = {
|
||||
/* PERF_TYPE_HARDWARE events */
|
||||
#define PC(n) PERF_TYPE_HARDWARE, PERF_COUNT_HW_ ## n
|
||||
{ "cpu-cycles", PC(CPU_CYCLES) },
|
||||
{ "cycles", PC(CPU_CYCLES) },
|
||||
{ "instructions", PC(INSTRUCTIONS) },
|
||||
{ "cache-references", PC(CACHE_REFERENCES) },
|
||||
{ "cache-misses", PC(CACHE_MISSES) },
|
||||
{ "branch-instructions", PC(BRANCH_INSTRUCTIONS) },
|
||||
{ "branches", PC(BRANCH_INSTRUCTIONS) },
|
||||
{ "branch-misses", PC(BRANCH_MISSES) },
|
||||
{ "bus-cycles", PC(BUS_CYCLES) },
|
||||
{ "stalled-cycles-frontend", PC(STALLED_CYCLES_FRONTEND) },
|
||||
{ "stalled-cycles-backend", PC(STALLED_CYCLES_BACKEND) },
|
||||
|
||||
/* PERF_TYPE_HW_CACHE hw_cache_id */
|
||||
#define PCC(n) PERF_TYPE_HW_CACHE, PERF_COUNT_HW_CACHE_ ## n
|
||||
{ "L1-dcache-", PCC(L1D) },
|
||||
{ "L1-icache-", PCC(L1I) },
|
||||
{ "LLC-", PCC(LL) },
|
||||
{ "dTLB-", PCC(DTLB) },
|
||||
{ "iTLB-", PCC(ITLB) },
|
||||
{ "branch-", PCC(BPU) },
|
||||
|
||||
/* PERF_TYPE_HW_CACHE hw_cache_op, hw_cache_result */
|
||||
#define PCCO(n, m) PERF_TYPE_HW_CACHE, \
|
||||
((PERF_COUNT_HW_CACHE_OP_ ## n) << 8 | \
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ ## m) << 16)
|
||||
{ "loads", PCCO(READ, ACCESS) },
|
||||
{ "load-misses", PCCO(READ, MISS) },
|
||||
{ "stores", PCCO(WRITE, ACCESS) },
|
||||
{ "store-misses", PCCO(WRITE, MISS) },
|
||||
{ "prefetches", PCCO(PREFETCH, ACCESS) },
|
||||
{ "prefetch-misses", PCCO(PREFETCH, MISS) }
|
||||
};
|
||||
|
||||
static int findEvent(const char *event, struct PerfTable *t,
|
||||
int len, int *match_len) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!strncmp(event, t[i].name, strlen(t[i].name))) {
|
||||
*match_len = strlen(t[i].name);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define CPUID_STEPPING(x) ((x) & 0xf)
|
||||
#define CPUID_MODEL(x) (((x) & 0xf0) >> 4)
|
||||
#define CPUID_FAMILY(x) (((x) & 0xf00) >> 8)
|
||||
#define CPUID_TYPE(x) (((x) & 0x3000) >> 12)
|
||||
|
||||
// hack to get LLC counters on perflab frc machines
|
||||
static bool isIntelE5_2670() {
|
||||
#ifdef __x86_64__
|
||||
unsigned long x;
|
||||
asm volatile ("cpuid" : "=a"(x): "a"(1) : "ebx", "ecx", "edx");
|
||||
return CPUID_STEPPING(x) == 6 && CPUID_MODEL(x) == 0xd
|
||||
&& CPUID_FAMILY(x) == 6 && CPUID_TYPE(x) == 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void checkLLCHack(const char* event, uint32_t& type, uint64_t& config) {
|
||||
if (!strncmp(event, "LLC-load", 8) && isIntelE5_2670()) {
|
||||
type = PERF_TYPE_RAW;
|
||||
if (!strncmp(&event[4], "loads", 5)) {
|
||||
config = 0x534f2e;
|
||||
} else if (!strncmp(&event[4], "load-misses", 11)) {
|
||||
config = 0x53412e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HardwareCounter::addPerfEvent(const char* event) {
|
||||
uint32_t type = 0;
|
||||
uint64_t config = 0;
|
||||
int i, match_len;
|
||||
bool found = false;
|
||||
const char* ev = event;
|
||||
|
||||
while ((i = findEvent(ev, perfTable,
|
||||
sizeof(perfTable)/sizeof(struct PerfTable),
|
||||
&match_len))
|
||||
!= -1) {
|
||||
if (!found) {
|
||||
found = true;
|
||||
type = perfTable[i].type;
|
||||
} else if (type != perfTable[i].type) {
|
||||
// Logger::Warning("failed to find perf event: %s", event);
|
||||
return false;
|
||||
}
|
||||
config |= perfTable[i].config;
|
||||
ev = &ev[match_len];
|
||||
}
|
||||
|
||||
checkLLCHack(event, type, config);
|
||||
|
||||
// Check if we have a raw spec.
|
||||
if (!found && event[0] == 'r' && event[1] != 0) {
|
||||
config = strtoull(event + 1, const_cast<char**>(&ev), 16);
|
||||
if (*ev == 0) {
|
||||
found = true;
|
||||
type = PERF_TYPE_RAW;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found || *ev) {
|
||||
// Logger::Warning("failed to find perf event: %s", event);
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<HardwareCounterImpl> hwc(
|
||||
new HardwareCounterImpl(type, config, event));
|
||||
if (hwc->m_err) {
|
||||
// Logger::Warning("failed to set perf event: %s", event);
|
||||
return false;
|
||||
}
|
||||
m_counters.emplace_back(std::move(hwc));
|
||||
if (!m_countersSet) {
|
||||
// reset load and store counters. This is because
|
||||
// perf does not seem to handle more than three counters
|
||||
// very well.
|
||||
m_loadCounter.reset();
|
||||
m_storeCounter.reset();
|
||||
m_countersSet = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HardwareCounter::eventExists(const char *event) {
|
||||
// hopefully m_counters set is small, so a linear scan does not hurt
|
||||
for(unsigned i = 0; i < m_counters.size(); i++) {
|
||||
if (!strcmp(event, m_counters[i]->m_desc.c_str())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HardwareCounter::setPerfEvents(std::string sevents) {
|
||||
// Make a copy of the string for use with strtok.
|
||||
auto const sevents_buf = static_cast<char*>(malloc(sevents.size() + 1));
|
||||
memcpy(sevents_buf, sevents.data(), sevents.size());
|
||||
sevents_buf[sevents.size()] = '\0';
|
||||
|
||||
char* strtok_buf = nullptr;
|
||||
char* s = strtok_r(sevents_buf, ",", &strtok_buf);
|
||||
bool success = true;
|
||||
while (s) {
|
||||
if (!eventExists(s) && !addPerfEvent(s)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
s = strtok_r(nullptr, ",", &strtok_buf);
|
||||
}
|
||||
free(sevents_buf);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool HardwareCounter::SetPerfEvents(std::string events) {
|
||||
return s_counter->setPerfEvents(events);
|
||||
}
|
||||
|
||||
void HardwareCounter::clearPerfEvents() {
|
||||
m_counters.clear();
|
||||
}
|
||||
|
||||
void HardwareCounter::ClearPerfEvents() {
|
||||
s_counter->clearPerfEvents();
|
||||
}
|
||||
|
||||
const std::string
|
||||
s_instructions("instructions"),
|
||||
s_loads("loads"),
|
||||
s_stores("stores");
|
||||
|
||||
void HardwareCounter::getPerfEvents(PerfEventCallback f, void* data) {
|
||||
f(s_instructions, getInstructionCount(), data);
|
||||
if (!m_countersSet) {
|
||||
f(s_loads, getLoadCount(), data);
|
||||
f(s_stores, getStoreCount(), data);
|
||||
}
|
||||
for (unsigned i = 0; i < m_counters.size(); i++) {
|
||||
f(m_counters[i]->m_desc, m_counters[i]->read(), data);
|
||||
}
|
||||
}
|
||||
|
||||
void HardwareCounter::GetPerfEvents(PerfEventCallback f, void* data) {
|
||||
s_counter->getPerfEvents(f, data);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
|
||||
#else // NO_HARDWARE_COUNTERS
|
||||
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HardwareCounter HardwareCounter::s_counter;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
#endif // NO_HARDWARE_COUNTERS
|
||||
@@ -1,108 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef incl_HPHP_UTIL_HARDWARE_COUNTER_H_
|
||||
#define incl_HPHP_UTIL_HARDWARE_COUNTER_H_
|
||||
|
||||
#include "thread-local.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef NO_HARDWARE_COUNTERS
|
||||
|
||||
class InstructionCounter;
|
||||
class LoadCounter;
|
||||
class StoreCounter;
|
||||
|
||||
struct PerfTable {
|
||||
const char* name;
|
||||
uint32_t type;
|
||||
uint64_t config;
|
||||
};
|
||||
|
||||
class HardwareCounterImpl;
|
||||
|
||||
class HardwareCounter {
|
||||
public:
|
||||
HardwareCounter();
|
||||
~HardwareCounter();
|
||||
|
||||
static void Reset();
|
||||
static int64_t GetInstructionCount();
|
||||
static int64_t GetLoadCount();
|
||||
static int64_t GetStoreCount();
|
||||
static bool SetPerfEvents(std::string events);
|
||||
static void IncInstructionCount(int64_t amount);
|
||||
static void IncLoadCount(int64_t amount);
|
||||
static void IncStoreCount(int64_t amount);
|
||||
|
||||
typedef void (*PerfEventCallback)(const std::string&, int64_t, void*);
|
||||
static void GetPerfEvents(PerfEventCallback f, void* data);
|
||||
static void ClearPerfEvents();
|
||||
static void Init(bool enable, const std::string& events, bool subProc);
|
||||
static DECLARE_THREAD_LOCAL_NO_CHECK(HardwareCounter, s_counter);
|
||||
bool m_countersSet{false};
|
||||
private:
|
||||
void reset();
|
||||
int64_t getInstructionCount();
|
||||
int64_t getLoadCount();
|
||||
int64_t getStoreCount();
|
||||
bool eventExists(const char* event);
|
||||
bool addPerfEvent(const char* event);
|
||||
bool setPerfEvents(std::string events);
|
||||
void getPerfEvents(PerfEventCallback f, void* data);
|
||||
void clearPerfEvents();
|
||||
|
||||
std::unique_ptr<InstructionCounter> m_instructionCounter;
|
||||
std::unique_ptr<LoadCounter> m_loadCounter;
|
||||
std::unique_ptr<StoreCounter> m_storeCounter;
|
||||
std::vector<std::unique_ptr<HardwareCounterImpl>> m_counters;
|
||||
};
|
||||
|
||||
#else // NO_HARDWARE_COUNTERS
|
||||
|
||||
/* Stub implementation for platforms without hardware counters (non-linux)
|
||||
* This mock class pretends to track performance events, but just returns
|
||||
* static values, so it doesn't even need to worry about thread safety
|
||||
* for the one static instance of itself.
|
||||
*/
|
||||
class HardwareCounter {
|
||||
public:
|
||||
HardwareCounter() : m_countersSet(false) { }
|
||||
~HardwareCounter() { }
|
||||
|
||||
static void Reset() { }
|
||||
static int64_t GetInstructionCount() { return 0; }
|
||||
static int64_t GetLoadCount() { return 0; }
|
||||
static int64_t GetStoreCount() { return 0; }
|
||||
static bool SetPerfEvents(folly::StringPiece events) { return false; }
|
||||
static void IncInstructionCount(int64_t amount) {}
|
||||
static void IncLoadCount(int64_t amount) {}
|
||||
static void IncStoreCount(int64_t amount) {}
|
||||
typedef void (*PerfEventCallback)(const std::string&, int64_t, void*);
|
||||
static void GetPerfEvents(PerfEventCallback f, void* data) { }
|
||||
static void ClearPerfEvents() { }
|
||||
static void Init(bool enable, const std::string& events, bool subProc) {}
|
||||
|
||||
// Normally exposed by DECLARE_THREAD_LOCAL_NO_CHECK
|
||||
void getCheck() { }
|
||||
void destroy() { }
|
||||
static HardwareCounter s_counter;
|
||||
bool m_countersSet;
|
||||
};
|
||||
|
||||
#endif // NO_HARDWARE_COUNTERS
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,202 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <JavaScriptCore/JavaScript.h>
|
||||
|
||||
#include "hardware-counter.h"
|
||||
|
||||
using HPHP::HardwareCounter;
|
||||
|
||||
void add_native_hook(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef obj,
|
||||
const char *name,
|
||||
JSObjectCallAsFunctionCallback hook
|
||||
) {
|
||||
JSStringRef jsName = JSStringCreateWithUTF8CString(name);
|
||||
JSObjectSetProperty(
|
||||
ctx,
|
||||
obj,
|
||||
jsName,
|
||||
JSObjectMakeFunctionWithCallback(ctx, jsName, hook),
|
||||
kJSPropertyAttributeNone,
|
||||
NULL
|
||||
);
|
||||
JSStringRelease(jsName);
|
||||
}
|
||||
|
||||
static void fprint_value(
|
||||
FILE *file,
|
||||
JSContextRef context,
|
||||
JSValueRef obj
|
||||
) {
|
||||
JSStringRef jsStr = JSValueToStringCopy(context, obj, NULL);
|
||||
size_t size = JSStringGetMaximumUTF8CStringSize(jsStr);
|
||||
char *str = (char *) calloc(
|
||||
size,
|
||||
1
|
||||
);
|
||||
JSStringGetUTF8CString(
|
||||
jsStr,
|
||||
str,
|
||||
size
|
||||
);
|
||||
JSStringRelease(jsStr);
|
||||
fprintf(file, "%s", str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
static JSValueRef js_print(
|
||||
JSContextRef context,
|
||||
JSObjectRef object,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception
|
||||
) {
|
||||
for (int i = 0; i < argumentCount; i++) {
|
||||
if (i != 0) {
|
||||
printf(" ");
|
||||
}
|
||||
fprint_value(stdout, context, arguments[i]);
|
||||
}
|
||||
printf("\n");
|
||||
return JSValueMakeUndefined(context);
|
||||
}
|
||||
|
||||
static JSValueRef js_perf_counters_init(
|
||||
JSContextRef context,
|
||||
JSObjectRef object,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception
|
||||
) {
|
||||
// TODO: Allow customizing recorded events
|
||||
bool enable = true;
|
||||
std::string events = "";
|
||||
bool recordSubprocesses = false;
|
||||
HardwareCounter::Init(enable, events, recordSubprocesses);
|
||||
HardwareCounter::s_counter.getCheck();
|
||||
|
||||
return JSValueMakeUndefined(context);
|
||||
}
|
||||
|
||||
static JSValueRef js_perf_counters_get_counters(
|
||||
JSContextRef context,
|
||||
JSObjectRef object,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception
|
||||
) {
|
||||
JSObjectRef result = JSObjectMake(context, NULL, NULL);
|
||||
std::pair<JSContextRef, JSObjectRef> pair(context, result);
|
||||
|
||||
HardwareCounter::GetPerfEvents(
|
||||
[](const std::string& key, int64_t value, void* data) {
|
||||
std::pair<JSContextRef, JSObjectRef>& pair =
|
||||
*reinterpret_cast<std::pair<JSContextRef, JSObjectRef>*>(data);
|
||||
JSContextRef context = pair.first;
|
||||
JSObjectRef result = pair.second;
|
||||
|
||||
JSObjectSetProperty(
|
||||
context,
|
||||
result,
|
||||
JSStringCreateWithUTF8CString(key.c_str()),
|
||||
JSValueMakeNumber(context, value),
|
||||
kJSPropertyAttributeNone,
|
||||
NULL
|
||||
);
|
||||
},
|
||||
&pair);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: jsc-runner file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *filename = argv[1];
|
||||
std::ifstream ifs(filename);
|
||||
if (ifs.fail()) {
|
||||
std::cerr << "Error opening \"" << filename << "\": " << strerror(errno) << "\n";
|
||||
exit(1);
|
||||
}
|
||||
std::string script(
|
||||
(std::istreambuf_iterator<char>(ifs)),
|
||||
(std::istreambuf_iterator<char>())
|
||||
);
|
||||
JSStringRef jsScript = JSStringCreateWithUTF8CString(script.c_str());
|
||||
JSStringRef jsURL = JSStringCreateWithUTF8CString(argv[1]);
|
||||
|
||||
JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
|
||||
add_native_hook(
|
||||
ctx,
|
||||
JSContextGetGlobalObject(ctx),
|
||||
"print",
|
||||
js_print
|
||||
);
|
||||
|
||||
JSObjectRef jsPerfCounters = JSObjectMake(ctx, NULL, NULL);
|
||||
add_native_hook(
|
||||
ctx,
|
||||
jsPerfCounters,
|
||||
"init",
|
||||
js_perf_counters_init
|
||||
);
|
||||
add_native_hook(
|
||||
ctx,
|
||||
jsPerfCounters,
|
||||
"getCounters",
|
||||
js_perf_counters_get_counters
|
||||
);
|
||||
JSObjectSetProperty(
|
||||
ctx,
|
||||
JSContextGetGlobalObject(ctx),
|
||||
JSStringCreateWithUTF8CString("PerfCounters"),
|
||||
jsPerfCounters,
|
||||
kJSPropertyAttributeNone,
|
||||
NULL
|
||||
);
|
||||
|
||||
JSValueRef jsError = NULL;
|
||||
JSValueRef result = JSEvaluateScript(
|
||||
ctx,
|
||||
jsScript,
|
||||
NULL,
|
||||
jsURL,
|
||||
0,
|
||||
&jsError
|
||||
);
|
||||
if (!result) {
|
||||
fprintf(stderr, "Exception: ");
|
||||
fprint_value(stderr, ctx, jsError);
|
||||
fprintf(stderr, "\n");
|
||||
JSStringRef jsStackStr = JSStringCreateWithUTF8CString("stack");
|
||||
if (JSValueIsObject(ctx, jsError)) {
|
||||
JSValueRef jsStack = JSObjectGetProperty(ctx, (JSObjectRef)jsError, jsStackStr, NULL);
|
||||
JSStringRelease(jsStackStr);
|
||||
fprint_value(stderr, ctx, jsStack);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
JSGlobalContextRelease(ctx);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <node.h>
|
||||
|
||||
#include "hardware-counter.h"
|
||||
|
||||
namespace PerfCounters {
|
||||
|
||||
using HPHP::HardwareCounter;
|
||||
|
||||
void Init(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
// TODO: Allow customizing recorded events
|
||||
bool enable = true;
|
||||
std::string events = "";
|
||||
bool recordSubprocesses = false;
|
||||
HardwareCounter::Init(enable, events, recordSubprocesses);
|
||||
HardwareCounter::s_counter.getCheck();
|
||||
}
|
||||
|
||||
void GetCounters(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::Isolate* isolate = args.GetIsolate();
|
||||
v8::Local<v8::Object> obj = v8::Object::New(isolate);
|
||||
std::pair<v8::Isolate*, v8::Local<v8::Object>> pair(isolate, obj);
|
||||
|
||||
HardwareCounter::GetPerfEvents(
|
||||
[](const std::string& key, int64_t value, void* data) {
|
||||
std::pair<v8::Isolate*, v8::Local<v8::Object>>& pair =
|
||||
*reinterpret_cast<std::pair<v8::Isolate*, v8::Local<v8::Object>>*>(data);
|
||||
v8::Isolate* isolate = pair.first;
|
||||
v8::Local<v8::Object> obj = pair.second;
|
||||
obj->Set(
|
||||
v8::String::NewFromUtf8(isolate, key.c_str()),
|
||||
v8::Number::New(isolate, value)
|
||||
);
|
||||
},
|
||||
&pair);
|
||||
|
||||
args.GetReturnValue().Set(obj);
|
||||
}
|
||||
|
||||
void InitModule(v8::Local<v8::Object> exports) {
|
||||
NODE_SET_METHOD(exports, "init", Init);
|
||||
NODE_SET_METHOD(exports, "getCounters", GetCounters);
|
||||
}
|
||||
|
||||
NODE_MODULE(perfcounters, InitModule)
|
||||
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef incl_HPHP_PORTABILITY_H_
|
||||
#define incl_HPHP_PORTABILITY_H_
|
||||
|
||||
// From folly/Likely.h
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
#define LIKELY(x) (__builtin_expect((x), 1))
|
||||
#define UNLIKELY(x) (__builtin_expect((x), 0))
|
||||
#else
|
||||
#define LIKELY(x) (x)
|
||||
#define UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Various macros to make certain things conditional on either
|
||||
* compiler or architecture.
|
||||
*
|
||||
* Currently we don't *really* compile on anything other than gcc or
|
||||
* sometimes clang, and there are some parts of the code using
|
||||
* __attribute__ stuff directly, but some things go through these
|
||||
* macros to make it maybe easier to change later.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO: does clang define __GNUC__ ?
|
||||
#ifndef __GNUC__
|
||||
# define __attribute__(x)
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef ATTRIBUTE_UNUSED
|
||||
# undef ATTRIBUTE_UNUSED
|
||||
#endif
|
||||
#ifdef ATTRIBUTE_NORETURN
|
||||
# undef ATTRIBUTE_NORETURN
|
||||
#endif
|
||||
#ifdef ATTRIBUTE_PRINTF
|
||||
# undef ATTRIBUTE_PRINTF
|
||||
#endif
|
||||
#ifdef ATTRIBUTE_PRINTF_STRING
|
||||
# undef ATTRIBUTE_PRINTF_STRING
|
||||
#endif
|
||||
|
||||
#define ATTRIBUTE_PRINTF_STRING FOLLY_PRINTF_FORMAT
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define ATTRIBUTE_NORETURN __declspec(noreturn)
|
||||
#define ATTRIBUTE_PRINTF(a1, a2)
|
||||
#ifndef __thread
|
||||
# define __thread __declspec(thread)
|
||||
#endif
|
||||
#define ATTRIBUTE_UNUSED
|
||||
|
||||
#define ALWAYS_INLINE __forceinline
|
||||
#define EXTERNALLY_VISIBLE
|
||||
#define FLATTEN
|
||||
#define NEVER_INLINE __declspec(noinline)
|
||||
#define UNUSED
|
||||
#else
|
||||
#define ATTRIBUTE_NORETURN __attribute__((__noreturn__))
|
||||
#define ATTRIBUTE_PRINTF(a1, a2) \
|
||||
__attribute__((__format__ (__printf__, a1, a2)))
|
||||
#define ATTRIBUTE_UNUSED __attribute__((__unused__))
|
||||
|
||||
#define ALWAYS_INLINE inline __attribute__((__always_inline__))
|
||||
#define EXTERNALLY_VISIBLE __attribute__((__externally_visible__))
|
||||
#define FLATTEN __attribute__((__flatten__))
|
||||
#define NEVER_INLINE __attribute__((__noinline__))
|
||||
#define UNUSED __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
# define DEBUG_ONLY /* nop */
|
||||
#else
|
||||
# define DEBUG_ONLY UNUSED
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We need to keep some unreferenced functions from being removed by
|
||||
* the linker. There is no compile time mechanism for doing this, but
|
||||
* by putting them in the same section as some other, referenced function
|
||||
* in the same file, we can keep them around.
|
||||
*
|
||||
* So this macro should be used to mark at least one function that is
|
||||
* referenced, and other functions that are not referenced in the same
|
||||
* file.
|
||||
*
|
||||
* Note: this may not work properly with LTO. We'll revisit when/if we
|
||||
* move to it.
|
||||
*/
|
||||
#ifndef __APPLE__
|
||||
# define KEEP_SECTION \
|
||||
__attribute__((__section__(".text.keep")))
|
||||
#else
|
||||
# define KEEP_SECTION \
|
||||
__attribute__((__section__(".text,.text.keep")))
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// OS X has a macro "isset" defined in this header. Force the include so we can
|
||||
// make sure the macro gets undef'd. (I think this also applies to BSD, but we
|
||||
// can cross that road when we come to it.)
|
||||
# include <sys/param.h>
|
||||
# ifdef isset
|
||||
# undef isset
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
# if defined(__clang__)
|
||||
# define DECLARE_FRAME_POINTER(fp) \
|
||||
ActRec* fp; \
|
||||
asm volatile("mov %%rbp, %0" : "=r" (fp) ::)
|
||||
# else
|
||||
# define DECLARE_FRAME_POINTER(fp) register ActRec* fp asm("rbp");
|
||||
# endif
|
||||
|
||||
#elif defined(_M_X64)
|
||||
|
||||
// TODO: FIXME! Without this implemented properly, the JIT
|
||||
// will fail "pretty spectacularly".
|
||||
# define DECLARE_FRAME_POINTER(fp) \
|
||||
always_assert(false); \
|
||||
register ActRec* fp = nullptr;
|
||||
|
||||
#elif defined(__AARCH64EL__)
|
||||
|
||||
# if defined(__clang__)
|
||||
# error Clang implementation not done for ARM
|
||||
# endif
|
||||
# define DECLARE_FRAME_POINTER(fp) register ActRec* fp asm("x29");
|
||||
|
||||
#elif defined(__powerpc64__)
|
||||
|
||||
# if defined(__clang__)
|
||||
# error Clang implementation not done for PPC64
|
||||
# endif
|
||||
# define DECLARE_FRAME_POINTER(fp) register ActRec* fp = (ActRec*) __builtin_frame_address(0);
|
||||
|
||||
#else
|
||||
|
||||
# error What are the stack and frame pointers called on your architecture?
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// We reserve the exit status 127 to signal a failure in the
|
||||
// interpreter. 127 is a valid exit code on all reasonable
|
||||
// architectures: POSIX requires at least 8 unsigned bits and
|
||||
// Windows 32 signed bits.
|
||||
#define HPHP_EXIT_FAILURE 127
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if FACEBOOK
|
||||
// Linking in libbfd is a gigantic PITA. If you want this yourself in a non-FB
|
||||
// build, feel free to define HAVE_LIBBFD and specify the right options to link
|
||||
// in libbfd.a in the extra C++ options.
|
||||
#define HAVE_LIBBFD 1
|
||||
#endif
|
||||
|
||||
#ifndef PACKAGE
|
||||
// The value doesn't matter, but it must be defined before you include
|
||||
// bfd.h
|
||||
#define PACKAGE "hhvm"
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "thread-local.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <link.h>
|
||||
#include <asm/prctl.h>
|
||||
#include <sys/prctl.h>
|
||||
extern "C" {
|
||||
extern int arch_prctl(int, unsigned long*);
|
||||
}
|
||||
#endif //__linux__
|
||||
|
||||
namespace HPHP {
|
||||
|
||||
#ifdef USE_GCC_FAST_TLS
|
||||
|
||||
void ThreadLocalManager::OnThreadExit(void* p) {
|
||||
auto list = getList(p);
|
||||
p = list->head;
|
||||
delete list;
|
||||
while (p != nullptr) {
|
||||
auto* pNode = static_cast<ThreadLocalNode<void>*>(p);
|
||||
if (pNode->m_on_thread_exit_fn) {
|
||||
pNode->m_on_thread_exit_fn(p);
|
||||
}
|
||||
p = pNode->m_next;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadLocalManager::PushTop(void* nodePtr, size_t nodeSize) {
|
||||
auto& node = *static_cast<ThreadLocalNode<void>*>(nodePtr);
|
||||
auto key = GetManager().m_key;
|
||||
auto list = getList(pthread_getspecific(key));
|
||||
if (UNLIKELY(!list)) {
|
||||
ThreadLocalSetValue(key, list = new ThreadLocalList);
|
||||
}
|
||||
node.m_next = list->head;
|
||||
node.m_size = nodeSize;
|
||||
list->head = node.m_next;
|
||||
}
|
||||
|
||||
ThreadLocalManager& ThreadLocalManager::GetManager() {
|
||||
static ThreadLocalManager m;
|
||||
return m;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
ThreadLocalManager::ThreadLocalList::ThreadLocalList() {
|
||||
pthread_t self = pthread_self();
|
||||
handler.__routine = ThreadLocalManager::OnThreadExit;
|
||||
handler.__arg = this;
|
||||
handler.__next = self->__cleanup_stack;
|
||||
self->__cleanup_stack = &handler;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
static int visit_phdr(dl_phdr_info* info, size_t, void*) {
|
||||
for (size_t i = 0, n = info->dlpi_phnum; i < n; ++i) {
|
||||
const auto& hdr = info->dlpi_phdr[i];
|
||||
auto addr = info->dlpi_addr + hdr.p_vaddr;
|
||||
if (addr < 0x100000000LL && hdr.p_type == PT_TLS) {
|
||||
// found the main thread-local section
|
||||
assert(int(hdr.p_memsz) == hdr.p_memsz); // ensure no truncation
|
||||
return hdr.p_memsz;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::pair<void*,size_t> getCppTdata() {
|
||||
uintptr_t addr;
|
||||
if (!arch_prctl(ARCH_GET_FS, &addr)) {
|
||||
// fs points to the end of the threadlocal area.
|
||||
size_t size = dl_iterate_phdr(&visit_phdr, nullptr);
|
||||
return {(void*)(addr - size), size};
|
||||
}
|
||||
return {nullptr, 0};
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// how do you find the thread local section on your system?
|
||||
std::pair<void*,size_t> getCppTdata() {
|
||||
return {nullptr, 0};
|
||||
}
|
||||
|
||||
#endif //__linux__
|
||||
|
||||
}
|
||||
@@ -1,760 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef incl_HPHP_THREAD_LOCAL_H_
|
||||
#define incl_HPHP_THREAD_LOCAL_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "portability.h"
|
||||
|
||||
namespace HPHP {
|
||||
|
||||
// return the location of the current thread's tdata section
|
||||
std::pair<void*,size_t> getCppTdata();
|
||||
|
||||
inline uintptr_t tlsBase() {
|
||||
uintptr_t retval;
|
||||
#if defined(__x86_64__)
|
||||
asm ("movq %%fs:0, %0" : "=r" (retval));
|
||||
#elif defined(__AARCH64EL__)
|
||||
// mrs == "move register <-- system"
|
||||
// tpidr_el0 == "thread process id register for exception level 0"
|
||||
asm ("mrs %0, tpidr_el0" : "=r" (retval));
|
||||
#elif defined (__powerpc64__)
|
||||
asm ("xor %0,%0,%0\n\t"
|
||||
"or %0,%0,13\n\t"
|
||||
: "=r" (retval));
|
||||
#elif defined(_M_X64)
|
||||
retval = (uintptr_t)_readfsbase_u64();
|
||||
retval = *(uintptr_t*)(retval + 88);
|
||||
#else
|
||||
# error How do you access thread-local storage on this machine?
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// gcc >= 4.3.0 supports the '__thread' keyword for thread locals
|
||||
//
|
||||
// Clang seems to have added this feature, or at the very least it is ignoring
|
||||
// __thread keyword and compiling anyway
|
||||
//
|
||||
// On OSX, gcc does emulate TLS but in a manner that invalidates assumptions
|
||||
// we have made about __thread and makes accessing thread-local variables in a
|
||||
// JIT-friendly fashion difficult (as the compiler is doing a lot of magic that
|
||||
// is not contractual or documented that we would need to duplicate in emitted
|
||||
// code) so for now we're not going to use it. One possibility if we really
|
||||
// want to do this is to generate functions that access variables of interest
|
||||
// in ThreadLocal* (all of them are NoCheck right now) and use the bytes of
|
||||
// gcc's compiled functions to find the values we would need to pass to
|
||||
// __emutls_get_address.
|
||||
//
|
||||
// icc 13.0.0 appears to support it as well but we end up with
|
||||
// assembler warnings of unknown importance about incorrect section
|
||||
// types
|
||||
//
|
||||
// __thread on cygwin and mingw uses pthreads emulation not native tls so
|
||||
// the emulation for thread local must be used as well
|
||||
//
|
||||
// So we use __thread on gcc, icc and clang, unless we are on OSX. On OSX, we
|
||||
// use our own emulation. Use the DECLARE_THREAD_LOCAL() and
|
||||
// IMPLEMENT_THREAD_LOCAL() macros to access either __thread or the emulation
|
||||
// as appropriate.
|
||||
|
||||
#if !defined(NO_TLS) && \
|
||||
!defined(__CYGWIN__) && !defined(__MINGW__) && \
|
||||
((__llvm__ && __clang__) || \
|
||||
__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || \
|
||||
__INTEL_COMPILER || defined(_MSC_VER))
|
||||
#define USE_GCC_FAST_TLS
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// helper
|
||||
|
||||
inline void ThreadLocalCheckReturn(int ret, const char *funcName) {
|
||||
if (ret != 0) {
|
||||
// This is used from global constructors so the safest thing to do is just
|
||||
// print to stderr and exit().
|
||||
fprintf(stderr, "%s returned %d", funcName, ret);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ThreadLocalCreateKey(pthread_key_t *key, void (*del)(void*)) {
|
||||
int ret = pthread_key_create(key, del);
|
||||
ThreadLocalCheckReturn(ret, "pthread_key_create");
|
||||
}
|
||||
|
||||
inline void ThreadLocalSetValue(pthread_key_t key, const void* value) {
|
||||
int ret = pthread_setspecific(key, value);
|
||||
ThreadLocalCheckReturn(ret, "pthread_setspecific");
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
typedef struct __darwin_pthread_handler_rec darwin_pthread_handler;
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* A thread-local object is a "global" object within a thread. This is useful
|
||||
* for writing apartment-threaded code, where nothing is actually shared
|
||||
* between different threads (hence no locking) but those variables are not
|
||||
* on stack in local scope. To use it, just do something like this,
|
||||
*
|
||||
* IMPLEMENT_THREAD_LOCAL(MyClass, static_object);
|
||||
* static_object->data_ = ...;
|
||||
* static_object->doSomething();
|
||||
*
|
||||
* IMPLEMENT_THREAD_LOCAL(int, static_number);
|
||||
* int value = *static_number;
|
||||
*
|
||||
* So, syntax-wise it's similar to pointers. The type parameter can be a
|
||||
* primitive types. If it's a class, there has to be a default constructor.
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(USE_GCC_FAST_TLS)
|
||||
|
||||
/**
|
||||
* We keep a linked list of destructors in ThreadLocalManager to be called on
|
||||
* thread exit. ThreadLocalNode is a node in this list.
|
||||
*/
|
||||
template <typename T>
|
||||
struct ThreadLocalNode {
|
||||
T * m_p;
|
||||
void (*m_on_thread_exit_fn)(void * p);
|
||||
void * m_next;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
struct ThreadLocalManager {
|
||||
template<class T>
|
||||
static void PushTop(ThreadLocalNode<T>& node) {
|
||||
PushTop(&node, sizeof(T));
|
||||
}
|
||||
template<class F> void scan(F& mark) const;
|
||||
|
||||
private:
|
||||
static void PushTop(void* node, size_t size);
|
||||
struct ThreadLocalList {
|
||||
void* head{nullptr};
|
||||
#ifdef __APPLE__
|
||||
ThreadLocalList();
|
||||
darwin_pthread_handler handler;
|
||||
#endif
|
||||
};
|
||||
static ThreadLocalList* getList(void* p) {
|
||||
return static_cast<ThreadLocalList*>(p);
|
||||
}
|
||||
ThreadLocalManager() : m_key(0) {
|
||||
#ifdef __APPLE__
|
||||
ThreadLocalCreateKey(&m_key, nullptr);
|
||||
#else
|
||||
ThreadLocalCreateKey(&m_key, ThreadLocalManager::OnThreadExit);
|
||||
#endif
|
||||
};
|
||||
static void OnThreadExit(void *p);
|
||||
pthread_key_t m_key;
|
||||
|
||||
static ThreadLocalManager& GetManager();
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ThreadLocal allocates by calling new without parameters and frees by calling
|
||||
// delete
|
||||
|
||||
template<typename T>
|
||||
void ThreadLocalOnThreadExit(void * p) {
|
||||
ThreadLocalNode<T> * pNode = (ThreadLocalNode<T>*)p;
|
||||
delete pNode->m_p;
|
||||
pNode->m_p = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* The USE_GCC_FAST_TLS implementation of ThreadLocal is just a lazy-initialized
|
||||
* pointer wrapper. In this case, we have one ThreadLocal object per thread.
|
||||
*/
|
||||
template<typename T>
|
||||
struct ThreadLocal {
|
||||
T *get() const {
|
||||
if (m_node.m_p == nullptr) {
|
||||
const_cast<ThreadLocal<T>*>(this)->create();
|
||||
}
|
||||
return m_node.m_p;
|
||||
}
|
||||
|
||||
NEVER_INLINE void create();
|
||||
|
||||
bool isNull() const { return m_node.m_p == nullptr; }
|
||||
|
||||
void destroy() {
|
||||
delete m_node.m_p;
|
||||
m_node.m_p = nullptr;
|
||||
}
|
||||
|
||||
void nullOut() {
|
||||
m_node.m_p = nullptr;
|
||||
}
|
||||
|
||||
T *operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
ThreadLocalNode<T> m_node;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void ThreadLocal<T>::create() {
|
||||
if (m_node.m_on_thread_exit_fn == nullptr) {
|
||||
m_node.m_on_thread_exit_fn = ThreadLocalOnThreadExit<T>;
|
||||
ThreadLocalManager::PushTop(m_node);
|
||||
}
|
||||
assert(m_node.m_p == nullptr);
|
||||
m_node.m_p = new T();
|
||||
}
|
||||
|
||||
/**
|
||||
* ThreadLocalNoCheck is a pointer wrapper like ThreadLocal, except that it is
|
||||
* explicitly initialized with getCheck(), rather than being initialized when
|
||||
* it is first dereferenced.
|
||||
*/
|
||||
template<typename T>
|
||||
struct ThreadLocalNoCheck {
|
||||
NEVER_INLINE T *getCheck() const;
|
||||
T* getNoCheck() const {
|
||||
assert(m_node.m_p);
|
||||
return m_node.m_p;
|
||||
}
|
||||
|
||||
NEVER_INLINE void create();
|
||||
|
||||
bool isNull() const { return m_node.m_p == nullptr; }
|
||||
|
||||
void destroy() {
|
||||
delete m_node.m_p;
|
||||
m_node.m_p = nullptr;
|
||||
}
|
||||
|
||||
T *operator->() const {
|
||||
return getNoCheck();
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
return *getNoCheck();
|
||||
}
|
||||
|
||||
ThreadLocalNode<T> m_node;
|
||||
private:
|
||||
void setNull() { m_node.m_p = nullptr; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void ThreadLocalNoCheck<T>::create() {
|
||||
if (m_node.m_on_thread_exit_fn == nullptr) {
|
||||
m_node.m_on_thread_exit_fn = ThreadLocalOnThreadExit<T>;
|
||||
ThreadLocalManager::PushTop(m_node);
|
||||
}
|
||||
assert(m_node.m_p == nullptr);
|
||||
m_node.m_p = new T();
|
||||
}
|
||||
template<typename T>
|
||||
T *ThreadLocalNoCheck<T>::getCheck() const {
|
||||
if (m_node.m_p == nullptr) {
|
||||
const_cast<ThreadLocalNoCheck<T>*>(this)->create();
|
||||
}
|
||||
return m_node.m_p;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Singleton thread-local storage for T
|
||||
|
||||
template<typename T>
|
||||
void ThreadLocalSingletonOnThreadExit(void *obj) {
|
||||
T::OnThreadExit((T*)obj);
|
||||
}
|
||||
|
||||
// ThreadLocalSingleton has NoCheck property
|
||||
template <typename T>
|
||||
class ThreadLocalSingleton {
|
||||
public:
|
||||
ThreadLocalSingleton() { s_inited = true; }
|
||||
|
||||
NEVER_INLINE static T *getCheck();
|
||||
|
||||
static T* getNoCheck() {
|
||||
assert(s_inited);
|
||||
assert(s_singleton == (T*)&s_storage);
|
||||
return (T*)&s_storage;
|
||||
}
|
||||
|
||||
static bool isNull() { return s_singleton == nullptr; }
|
||||
|
||||
static void destroy() {
|
||||
assert(!s_singleton || s_singleton == (T*)&s_storage);
|
||||
T* p = s_singleton;
|
||||
if (p) {
|
||||
T::Delete(p);
|
||||
s_singleton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
T *operator->() const {
|
||||
return getNoCheck();
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
return *getNoCheck();
|
||||
}
|
||||
|
||||
private:
|
||||
static __thread T *s_singleton;
|
||||
typedef typename std::aligned_storage<sizeof(T), sizeof(void*)>::type
|
||||
StorageType;
|
||||
static __thread StorageType s_storage;
|
||||
static bool s_inited; // no-fast-TLS requires construction so be consistent
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool ThreadLocalSingleton<T>::s_inited = false;
|
||||
|
||||
template<typename T>
|
||||
T *ThreadLocalSingleton<T>::getCheck() {
|
||||
assert(s_inited);
|
||||
if (!s_singleton) {
|
||||
T* p = (T*) &s_storage;
|
||||
T::Create(p);
|
||||
s_singleton = p;
|
||||
}
|
||||
return s_singleton;
|
||||
}
|
||||
|
||||
template<typename T> __thread T *ThreadLocalSingleton<T>::s_singleton;
|
||||
template<typename T> __thread typename ThreadLocalSingleton<T>::StorageType
|
||||
ThreadLocalSingleton<T>::s_storage;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// some classes don't need new/delete at all
|
||||
|
||||
template<typename T, bool throwOnNull = true>
|
||||
struct ThreadLocalProxy {
|
||||
T *get() const {
|
||||
if (m_p == nullptr && throwOnNull) {
|
||||
throw std::runtime_error("ThreadLocalProxy::get() called before set()");
|
||||
}
|
||||
return m_p;
|
||||
}
|
||||
|
||||
void set(T* obj) {
|
||||
m_p = obj;
|
||||
}
|
||||
|
||||
bool isNull() const { return m_p == nullptr; }
|
||||
|
||||
void destroy() {
|
||||
m_p = nullptr;
|
||||
}
|
||||
|
||||
T *operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
T * m_p;
|
||||
};
|
||||
|
||||
/*
|
||||
* How to use the thread-local macros:
|
||||
*
|
||||
* Use DECLARE_THREAD_LOCAL to declare a *static* class field as thread local:
|
||||
* class SomeClass {
|
||||
* static DECLARE_THREAD_LOCAL(SomeFieldType, f);
|
||||
* }
|
||||
*
|
||||
* Use IMPLEMENT_THREAD_LOCAL in the cpp file to implement the field:
|
||||
* IMPLEMENT_THREAD_LOCAL(SomeFieldType, SomeClass::f);
|
||||
*
|
||||
* Remember: *Never* write IMPLEMENT_THREAD_LOCAL in a header file.
|
||||
*/
|
||||
|
||||
#define DECLARE_THREAD_LOCAL(T, f) \
|
||||
__thread HPHP::ThreadLocal<T> f
|
||||
#define IMPLEMENT_THREAD_LOCAL(T, f) \
|
||||
__thread HPHP::ThreadLocal<T> f
|
||||
|
||||
#define DECLARE_THREAD_LOCAL_NO_CHECK(T, f) \
|
||||
__thread HPHP::ThreadLocalNoCheck<T> f
|
||||
#define IMPLEMENT_THREAD_LOCAL_NO_CHECK(T, f) \
|
||||
__thread HPHP::ThreadLocalNoCheck<T> f
|
||||
|
||||
#define DECLARE_THREAD_LOCAL_PROXY(T, N, f) \
|
||||
__thread HPHP::ThreadLocalProxy<T, N> f
|
||||
#define IMPLEMENT_THREAD_LOCAL_PROXY(T, N, f) \
|
||||
__thread HPHP::ThreadLocalProxy<T, N> f
|
||||
|
||||
#else /* USE_GCC_FAST_TLS */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ThreadLocal allocates by calling new() without parameters
|
||||
|
||||
template<typename T>
|
||||
void ThreadLocalOnThreadExit(void *p) {
|
||||
delete (T*)p;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
// The __thread variables in class T will be freed when pthread calls
|
||||
// the destructor function on Mac. We can register a handler in
|
||||
// pthread_t->__cleanup_stack similar to pthread_cleanup_push(). The handler
|
||||
// will be called earlier so the __thread variables will still exist in the
|
||||
// handler when the thread exits.
|
||||
//
|
||||
// See the details at:
|
||||
// https://github.com/facebook/hhvm/issues/4444#issuecomment-92497582
|
||||
typedef struct __darwin_pthread_handler_rec darwin_pthread_handler;
|
||||
|
||||
template<typename T>
|
||||
void ThreadLocalOnThreadCleanup(void *key) {
|
||||
void *obj = pthread_getspecific((pthread_key_t)key);
|
||||
if (obj) {
|
||||
ThreadLocalOnThreadExit<T>(obj);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ThreadLocalSetCleanupHandler(pthread_key_t cleanup_key,
|
||||
pthread_key_t key,
|
||||
void (*del)(void*)) {
|
||||
// Prevent from adding the handler for multiple times.
|
||||
darwin_pthread_handler *handler =
|
||||
(darwin_pthread_handler*)pthread_getspecific(cleanup_key);
|
||||
if (handler)
|
||||
return;
|
||||
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
handler = new darwin_pthread_handler();
|
||||
handler->__routine = del;
|
||||
handler->__arg = (void*)key;
|
||||
handler->__next = self->__cleanup_stack;
|
||||
self->__cleanup_stack = handler;
|
||||
|
||||
ThreadLocalSetValue(cleanup_key, handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This is the emulation version of ThreadLocal. In this case, the ThreadLocal
|
||||
* object is a true global, and the get() method returns a thread-dependent
|
||||
* pointer from pthread's thread-specific data management.
|
||||
*/
|
||||
template<typename T>
|
||||
class ThreadLocal {
|
||||
public:
|
||||
/**
|
||||
* Constructor that has to be called from a thread-neutral place.
|
||||
*/
|
||||
ThreadLocal() : m_key(0) {
|
||||
#ifdef __APPLE__
|
||||
ThreadLocalCreateKey(&m_key, nullptr);
|
||||
ThreadLocalCreateKey(&m_cleanup_key,
|
||||
ThreadLocalOnThreadExit<darwin_pthread_handler>);
|
||||
#else
|
||||
ThreadLocalCreateKey(&m_key, ThreadLocalOnThreadExit<T>);
|
||||
#endif
|
||||
}
|
||||
|
||||
T *get() const {
|
||||
T *obj = (T*)pthread_getspecific(m_key);
|
||||
if (obj == nullptr) {
|
||||
obj = new T();
|
||||
ThreadLocalSetValue(m_key, obj);
|
||||
#ifdef __APPLE__
|
||||
ThreadLocalSetCleanupHandler(m_cleanup_key, m_key,
|
||||
ThreadLocalOnThreadCleanup<T>);
|
||||
#endif
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
|
||||
|
||||
void destroy() {
|
||||
delete (T*)pthread_getspecific(m_key);
|
||||
ThreadLocalSetValue(m_key, nullptr);
|
||||
}
|
||||
|
||||
void nullOut() {
|
||||
ThreadLocalSetValue(m_key, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access object's member or method through this operator overload.
|
||||
*/
|
||||
T *operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_key_t m_key;
|
||||
|
||||
#ifdef __APPLE__
|
||||
pthread_key_t m_cleanup_key;
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ThreadLocalNoCheck {
|
||||
public:
|
||||
/**
|
||||
* Constructor that has to be called from a thread-neutral place.
|
||||
*/
|
||||
ThreadLocalNoCheck() : m_key(0) {
|
||||
#ifdef __APPLE__
|
||||
ThreadLocalCreateKey(&m_key, nullptr);
|
||||
ThreadLocalCreateKey(&m_cleanup_key,
|
||||
ThreadLocalOnThreadExit<darwin_pthread_handler>);
|
||||
#else
|
||||
ThreadLocalCreateKey(&m_key, ThreadLocalOnThreadExit<T>);
|
||||
#endif
|
||||
}
|
||||
|
||||
NEVER_INLINE T *getCheck() const;
|
||||
|
||||
T* getNoCheck() const {
|
||||
T *obj = (T*)pthread_getspecific(m_key);
|
||||
assert(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
|
||||
|
||||
void destroy() {
|
||||
delete (T*)pthread_getspecific(m_key);
|
||||
ThreadLocalSetValue(m_key, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access object's member or method through this operator overload.
|
||||
*/
|
||||
T *operator->() const {
|
||||
return getNoCheck();
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
return *getNoCheck();
|
||||
}
|
||||
|
||||
public:
|
||||
void setNull() { ThreadLocalSetValue(m_key, nullptr); }
|
||||
pthread_key_t m_key;
|
||||
|
||||
#ifdef __APPLE__
|
||||
pthread_key_t m_cleanup_key;
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T *ThreadLocalNoCheck<T>::getCheck() const {
|
||||
T *obj = (T*)pthread_getspecific(m_key);
|
||||
if (obj == nullptr) {
|
||||
obj = new T();
|
||||
ThreadLocalSetValue(m_key, obj);
|
||||
#ifdef __APPLE__
|
||||
ThreadLocalSetCleanupHandler(m_cleanup_key, m_key,
|
||||
ThreadLocalOnThreadCleanup<T>);
|
||||
#endif
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Singleton thread-local storage for T
|
||||
|
||||
template<typename T>
|
||||
void ThreadLocalSingletonOnThreadExit(void *obj) {
|
||||
T::OnThreadExit((T*)obj);
|
||||
free(obj);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
template<typename T>
|
||||
void ThreadLocalSingletonOnThreadCleanup(void *key) {
|
||||
void *obj = pthread_getspecific((pthread_key_t)key);
|
||||
if (obj) {
|
||||
ThreadLocalSingletonOnThreadExit<T>(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ThreadLocalSingleton has NoCheck property
|
||||
template<typename T>
|
||||
class ThreadLocalSingleton {
|
||||
public:
|
||||
ThreadLocalSingleton() { getKey(); }
|
||||
|
||||
NEVER_INLINE static T *getCheck();
|
||||
static T* getNoCheck() {
|
||||
assert(s_inited);
|
||||
T *obj = (T*)pthread_getspecific(s_key);
|
||||
assert(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static bool isNull() {
|
||||
return !s_inited || pthread_getspecific(s_key) == nullptr;
|
||||
}
|
||||
|
||||
static void destroy() {
|
||||
void* p = pthread_getspecific(s_key);
|
||||
T::Delete((T*)p);
|
||||
free(p);
|
||||
ThreadLocalSetValue(s_key, nullptr);
|
||||
}
|
||||
|
||||
T *operator->() const {
|
||||
return getNoCheck();
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
return *getNoCheck();
|
||||
}
|
||||
|
||||
private:
|
||||
static pthread_key_t s_key;
|
||||
static bool s_inited; // pthread_key_t has no portable valid sentinel
|
||||
|
||||
#ifdef __APPLE__
|
||||
static pthread_key_t s_cleanup_key;
|
||||
#endif
|
||||
|
||||
static pthread_key_t getKey() {
|
||||
if (!s_inited) {
|
||||
s_inited = true;
|
||||
#ifdef __APPLE__
|
||||
ThreadLocalCreateKey(&s_key, nullptr);
|
||||
ThreadLocalCreateKey(&s_cleanup_key,
|
||||
ThreadLocalOnThreadExit<darwin_pthread_handler>);
|
||||
#else
|
||||
ThreadLocalCreateKey(&s_key, ThreadLocalSingletonOnThreadExit<T>);
|
||||
#endif
|
||||
}
|
||||
return s_key;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T *ThreadLocalSingleton<T>::getCheck() {
|
||||
assert(s_inited);
|
||||
T *obj = (T*)pthread_getspecific(s_key);
|
||||
if (obj == nullptr) {
|
||||
obj = (T*)malloc(sizeof(T));
|
||||
T::Create(obj);
|
||||
ThreadLocalSetValue(s_key, obj);
|
||||
#ifdef __APPLE__
|
||||
ThreadLocalSetCleanupHandler(s_cleanup_key, s_key,
|
||||
ThreadLocalSingletonOnThreadCleanup<T>);
|
||||
#endif
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
pthread_key_t ThreadLocalSingleton<T>::s_key;
|
||||
template<typename T>
|
||||
bool ThreadLocalSingleton<T>::s_inited = false;
|
||||
|
||||
#ifdef __APPLE__
|
||||
template<typename T>
|
||||
pthread_key_t ThreadLocalSingleton<T>::s_cleanup_key;
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// some classes don't need new/delete at all
|
||||
|
||||
template<typename T, bool throwOnNull = true>
|
||||
class ThreadLocalProxy {
|
||||
public:
|
||||
/**
|
||||
* Constructor that has to be called from a thread-neutral place.
|
||||
*/
|
||||
ThreadLocalProxy() : m_key(0) {
|
||||
ThreadLocalCreateKey(&m_key, nullptr);
|
||||
}
|
||||
|
||||
T *get() const {
|
||||
T *obj = (T*)pthread_getspecific(m_key);
|
||||
if (obj == nullptr && throwOnNull) {
|
||||
throw std::runtime_error("ThreadLocalProxy::get() called before set()");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
void set(T* obj) {
|
||||
ThreadLocalSetValue(m_key, obj);
|
||||
}
|
||||
|
||||
bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
|
||||
|
||||
void destroy() {
|
||||
ThreadLocalSetValue(m_key, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access object's member or method through this operator overload.
|
||||
*/
|
||||
T *operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
public:
|
||||
pthread_key_t m_key;
|
||||
};
|
||||
|
||||
/**
|
||||
* The emulation version of the thread-local macros
|
||||
*/
|
||||
#define DECLARE_THREAD_LOCAL(T, f) HPHP::ThreadLocal<T> f
|
||||
#define IMPLEMENT_THREAD_LOCAL(T, f) HPHP::ThreadLocal<T> f
|
||||
|
||||
#define DECLARE_THREAD_LOCAL_NO_CHECK(T, f) HPHP::ThreadLocalNoCheck<T> f
|
||||
#define IMPLEMENT_THREAD_LOCAL_NO_CHECK(T, f) HPHP::ThreadLocalNoCheck<T> f
|
||||
|
||||
#define DECLARE_THREAD_LOCAL_PROXY(T, N, f) HPHP::ThreadLocalProxy<T, N> f
|
||||
#define IMPLEMENT_THREAD_LOCAL_PROXY(T, N, f) HPHP::ThreadLocalProxy<T, N> f
|
||||
|
||||
#endif /* USE_GCC_FAST_TLS */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
#endif // incl_HPHP_THREAD_LOCAL_H_
|
||||
Reference in New Issue
Block a user