blob: 826b96ed4cc7014a6770de362504eff42e9ea2af [file] [log] [blame] [edit]
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
* Copyright (c) 2024, gperftools Contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef THREAD_CACHE_PTR_H_
#define THREAD_CACHE_PTR_H_
#include "config.h"
#include "base/basictypes.h"
#include "base/function_ref.h"
#include "base/spinlock.h"
#include "base/threading.h"
#include "thread_cache.h"
// This module encapsulates tcmalloc's thread cache access. Including
// fast-path access, early access (when process is too young and TLS
// facility isn't set up yet) and emergency malloc mode signaling.
namespace tcmalloc {
// Those platforms are known to do emutls or similar for TLS
// implementation. And so, we have to be more careful especially early
// in process lifetime.
#if __QNXNTO__ || __APPLE__ || __MINGW32__ || _AIX || TCMALLOC_FORCE_BAD_TLS
inline constexpr bool kHaveGoodTLS = false;
#else
// All other platforms are assumed to be great. Known great are
// GNU/Linux (musl too, and android's bionic too, but only most recent
// versions), FreeBSD, NetBSD, Solaris, Windows (but, sadly, not with
// mingw)
inline constexpr bool kHaveGoodTLS = true;
#endif
#if defined(ENABLE_EMERGENCY_MALLOC)
inline constexpr bool kUseEmergencyMalloc = true;
#else
inline constexpr bool kUseEmergencyMalloc = false;
#endif
class ThreadCachePtr {
public:
static bool ThreadCacheKeyIsReady() {
return (tls_key_ != kInvalidTLSKey);
}
static ThreadCache* GetIfPresent() {
if constexpr (kHaveGoodTLS) {
return tls_data_.fast_path_cache;
}
if (PREDICT_FALSE(!ThreadCacheKeyIsReady())) {
return nullptr;
}
return static_cast<ThreadCache*>(GetTlsValue(tls_key_));
}
static void InitThreadCachePtrLate();
static ThreadCachePtr Grab() {
ThreadCache* cache = GetIfPresent();
if (cache) {
return {cache, false};
}
return GetSlow();
}
bool IsEmergencyMallocEnabled() const {
return kUseEmergencyMalloc && is_emergency_malloc_;
}
ThreadCache* get() const { return ptr_; }
ThreadCache& operator*() const { return *ptr_; }
ThreadCache* operator->() const { return ptr_; }
// Cleans up thread's cache pointer and returns what it was. Used by
// TCMallocImplementation::MarkThreadIdle.
static ThreadCache* ReleaseAndClear();
// WithStacktraceScope runs passed function with given arg enabling
// emergency malloc around that call. If emergency malloc for
// current thread is already in effect it passes false to
// stacktrace_allowed argument of `fn'. See malloc_backtrace.cc for
// it's usage.
static void WithStacktraceScope(void (*fn)(bool stacktrace_allowed, void* arg), void* arg);
static void WithStacktraceScope(tcmalloc::FunctionRef<void(bool)> body) {
WithStacktraceScope(body.fn, body.data);
}
private:
friend class SlowTLS;
static ThreadCachePtr GetSlow();
static ThreadCachePtr GetReallySlow();
static void ClearCacheTLS();
ThreadCachePtr(ThreadCache* ptr, bool is_emergency_malloc)
: ptr_(ptr), is_emergency_malloc_(is_emergency_malloc) {
}
struct TLSData {
ThreadCache* fast_path_cache;
};
static inline thread_local TLSData tls_data_ ATTR_INITIAL_EXEC;
static TlsKey tls_key_;
ThreadCache* const ptr_;
const bool is_emergency_malloc_;
};
#if !defined(ENABLE_EMERGENCY_MALLOC)
// Note, the "real" implementation for ENABLE_EMERGENCY_MALLOC case is in .cc
inline ATTRIBUTE_NOINLINE
void ThreadCachePtr::WithStacktraceScope(void (*fn)(bool stacktrace_allowed, void* arg), void* arg) {
fn(true, arg);
// prevent tail-calling fn.
(void)*const_cast<volatile char*>(reinterpret_cast<char *>(arg));
}
#endif // !ENABLE_EMERGENCY_MALLOC
} // namespace tcmalloc
#endif // THREAD_CACHE_PTR_H_