#include <folly/detail/MemoryIdler.h>
#include <climits>
#include <cstdio>
#include <cstring>
#include <utility>
#include <folly/GLog.h>
#include <folly/Portability.h>
#include <folly/ScopeGuard.h>
#include <folly/concurrency/CacheLocality.h>
#include <folly/memory/MallctlHelper.h>
#include <folly/memory/Malloc.h>
#include <folly/portability/GFlags.h>
#include <folly/portability/PThread.h>
#include <folly/portability/SysMman.h>
#include <folly/portability/Unistd.h>
#include <folly/system/Pid.h>
#include <folly/system/ThreadId.h>
FOLLY_GFLAGS_DEFINE_bool(…);
namespace folly {
namespace detail {
AtomicStruct<std::chrono::steady_clock::duration>
MemoryIdler::defaultIdleTimeout(std::chrono::seconds(5));
bool MemoryIdler::isUnmapUnusedStackAvailable() noexcept { … }
void MemoryIdler::flushLocalMallocCaches() { … }
#if defined(__GLIBC__) && defined(__linux__) && !FOLLY_MOBILE && \
!FOLLY_SANITIZE_ADDRESS
static thread_local uintptr_t tls_stackLimit;
static thread_local size_t tls_stackSize;
static size_t pageSize() {
static const size_t s_pageSize = sysconf(_SC_PAGESIZE);
return s_pageSize;
}
static void fetchStackLimits() {
int err;
pthread_attr_t attr;
if ((err = pthread_getattr_np(pthread_self(), &attr))) {
FB_LOG_ONCE(ERROR) << "pthread_getaddr_np failed errno=" << err;
tls_stackSize = 1;
return;
}
SCOPE_EXIT {
pthread_attr_destroy(&attr);
};
void* addr;
size_t rawSize;
if ((err = pthread_attr_getstack(&attr, &addr, &rawSize))) {
FB_LOG_ONCE(ERROR) << "pthread_attr_getstack error " << err;
assert(false);
tls_stackSize = 1;
return;
}
if (rawSize >= (1ULL << 32)) {
FB_LOG_ONCE(ERROR) << "pthread_attr_getstack returned insane stack size "
<< rawSize;
assert(false);
tls_stackSize = 1;
return;
}
assert(addr != nullptr);
assert(
0 < PTHREAD_STACK_MIN &&
rawSize >= static_cast<size_t>(PTHREAD_STACK_MIN));
size_t guardSize;
if (pthread_attr_getguardsize(&attr, &guardSize) != 0) {
guardSize = 0;
}
assert(rawSize > guardSize);
tls_stackLimit = reinterpret_cast<uintptr_t>(addr) + guardSize;
tls_stackSize = rawSize - guardSize;
assert((tls_stackLimit & (pageSize() - 1)) == 0);
}
FOLLY_NOINLINE static uintptr_t getStackPtr() {
char marker;
auto rv = reinterpret_cast<uintptr_t>(&marker);
return rv;
}
void MemoryIdler::unmapUnusedStack(size_t retain) {
if (!isUnmapUnusedStackAvailable()) {
return;
}
if (tls_stackSize == 0) {
fetchStackLimits();
}
if (tls_stackSize <= std::max(static_cast<size_t>(1), retain)) {
return;
}
auto sp = getStackPtr();
assert(sp >= tls_stackLimit);
assert(sp - tls_stackLimit < tls_stackSize);
auto end = (sp - retain) & ~(pageSize() - 1);
if (end <= tls_stackLimit) {
return;
}
size_t len = end - tls_stackLimit;
assert((len & (pageSize() - 1)) == 0);
if (madvise((void*)tls_stackLimit, len, MADV_DONTNEED) != 0) {
PLOG_IF(WARNING, kIsDebug && errno == EINVAL) << "madvise failed";
assert(errno == EAGAIN || errno == ENOMEM || errno == EINVAL);
}
}
#else
void MemoryIdler::unmapUnusedStack(size_t ) { … }
#endif
}
}