mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
Bug 1660458 - Improve TLS initialization and usage, remove inline-able function-static init - r=canaltinova
The profiler should guarantee that TLS initializations are done only once (from `profiler_init()`), so there is no need to potentially do it at every TLS access. Instead, the initialization functions set the TLS states once (to `Initialized` or `Unavailable`, depending on the underlying TLS initialization success), and later accesses to this effectively-read-only flag can be done without thread synchronization. This reduces the generated code size. There are also DEBUG-build checks that no accesses are done before initialization; this is theoretically racy, but it's only used to detect too-early accesses in DEBUG builds, which should never happen anyway. Differential Revision: https://phabricator.services.mozilla.com/D89338
This commit is contained in:
parent
5abadeda43
commit
5507ddc4c5
@ -1453,17 +1453,34 @@ Atomic<uint32_t, MemoryOrdering::Relaxed> RacyFeatures::sActiveAndFeatures(0);
|
||||
// profiling stack used by AutoProfilerLabel.
|
||||
class TLSRegisteredThread {
|
||||
public:
|
||||
static bool Init() {
|
||||
// Only one call to MOZ_THREAD_LOCAL::init() is needed, we cache the result
|
||||
// for later calls to Init(), in particular before using get() and set().
|
||||
static const bool ok = AutoProfilerLabel::ProfilingStackOwnerTLS::Init() &&
|
||||
sRegisteredThread.init();
|
||||
return ok;
|
||||
// This should only be called once before any other access.
|
||||
// In this case it's called from `profiler_init()` on the main thread, before
|
||||
// the main thread registers itself.
|
||||
static void Init() {
|
||||
MOZ_ASSERT(sState == State::Uninitialized, "Already initialized");
|
||||
AutoProfilerLabel::ProfilingStackOwnerTLS::Init();
|
||||
MOZ_ASSERT(
|
||||
AutoProfilerLabel::ProfilingStackOwnerTLS::sState !=
|
||||
AutoProfilerLabel::ProfilingStackOwnerTLS::State::Uninitialized,
|
||||
"Unexpected ProfilingStackOwnerTLS::sState after "
|
||||
"ProfilingStackOwnerTLS::Init()");
|
||||
sState =
|
||||
(AutoProfilerLabel::ProfilingStackOwnerTLS::sState ==
|
||||
AutoProfilerLabel::ProfilingStackOwnerTLS::State::Initialized &&
|
||||
sRegisteredThread.init())
|
||||
? State::Initialized
|
||||
: State::Unavailable;
|
||||
}
|
||||
|
||||
static bool IsTLSInited() {
|
||||
MOZ_ASSERT(sState != State::Uninitialized,
|
||||
"TLSRegisteredThread should only be accessed after Init()");
|
||||
return sState == State::Initialized;
|
||||
}
|
||||
|
||||
// Get the entire RegisteredThread. Accesses are guarded by gPSMutex.
|
||||
static class RegisteredThread* RegisteredThread(PSLockRef) {
|
||||
if (!Init()) {
|
||||
if (!IsTLSInited()) {
|
||||
return nullptr;
|
||||
}
|
||||
return sRegisteredThread.get();
|
||||
@ -1471,7 +1488,7 @@ class TLSRegisteredThread {
|
||||
|
||||
// Get only the RacyRegisteredThread. Accesses are not guarded by gPSMutex.
|
||||
static class RacyRegisteredThread* RacyRegisteredThread() {
|
||||
if (!Init()) {
|
||||
if (!IsTLSInited()) {
|
||||
return nullptr;
|
||||
}
|
||||
class RegisteredThread* registeredThread = sRegisteredThread.get();
|
||||
@ -1483,7 +1500,7 @@ class TLSRegisteredThread {
|
||||
// RacyRegisteredThread() can also be used to get the ProfilingStack, but that
|
||||
// is marginally slower because it requires an extra pointer indirection.
|
||||
static ProfilingStack* Stack() {
|
||||
if (!Init()) {
|
||||
if (!IsTLSInited()) {
|
||||
return nullptr;
|
||||
}
|
||||
ProfilingStackOwner* profilingStackOwner =
|
||||
@ -1496,7 +1513,7 @@ class TLSRegisteredThread {
|
||||
|
||||
static void SetRegisteredThreadAndAutoProfilerLabelProfilingStack(
|
||||
PSLockRef, class RegisteredThread* aRegisteredThread) {
|
||||
if (!Init()) {
|
||||
if (!IsTLSInited()) {
|
||||
return;
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(
|
||||
@ -1513,7 +1530,7 @@ class TLSRegisteredThread {
|
||||
// is kept, because the thread may not have unregistered itself yet, so it may
|
||||
// still push/pop labels even after the profiler has shut down.
|
||||
static void ResetRegisteredThread(PSLockRef) {
|
||||
if (!Init()) {
|
||||
if (!IsTLSInited()) {
|
||||
return;
|
||||
}
|
||||
sRegisteredThread.set(nullptr);
|
||||
@ -1522,7 +1539,7 @@ class TLSRegisteredThread {
|
||||
// Reset the AutoProfilerLabels' ProfilingStack, because the thread is
|
||||
// unregistering itself.
|
||||
static void ResetAutoProfilerLabelProfilingStack(PSLockRef) {
|
||||
if (!Init()) {
|
||||
if (!IsTLSInited()) {
|
||||
return;
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(
|
||||
@ -1533,6 +1550,12 @@ class TLSRegisteredThread {
|
||||
}
|
||||
|
||||
private:
|
||||
// Only written once from `profiler_init` calling
|
||||
// `TLSRegisteredThread::Init()`; all reads should only happen after `Init()`,
|
||||
// so there is no need to make it atomic.
|
||||
enum class State { Uninitialized = 0, Initialized, Unavailable };
|
||||
static State sState;
|
||||
|
||||
// This is a non-owning reference to the RegisteredThread;
|
||||
// CorePS::mRegisteredThreads is the owning reference. On thread
|
||||
// deregistration, this reference is cleared and the RegisteredThread is
|
||||
@ -1540,8 +1563,21 @@ class TLSRegisteredThread {
|
||||
static MOZ_THREAD_LOCAL(class RegisteredThread*) sRegisteredThread;
|
||||
};
|
||||
|
||||
// Zero-initialized to State::Uninitialized.
|
||||
/* static */
|
||||
TLSRegisteredThread::State TLSRegisteredThread::sState;
|
||||
|
||||
/* static */
|
||||
MOZ_THREAD_LOCAL(RegisteredThread*) TLSRegisteredThread::sRegisteredThread;
|
||||
|
||||
// Only written once from `profiler_init` (through `TLSRegisteredThread::Init()`
|
||||
// and `AutoProfilerLabel::ProfilingStackOwnerTLS::Init()`); all reads should
|
||||
// only happen after `Init()`, so there is no need to make it atomic.
|
||||
// Zero-initialized to State::Uninitialized.
|
||||
/* static */
|
||||
AutoProfilerLabel::ProfilingStackOwnerTLS::State
|
||||
AutoProfilerLabel::ProfilingStackOwnerTLS::sState;
|
||||
|
||||
// Although you can access a thread's ProfilingStack via
|
||||
// TLSRegisteredThread::sRegisteredThread, we also have a second TLS pointer
|
||||
// directly to the ProfilingStack. Here's why.
|
||||
@ -1563,9 +1599,17 @@ MOZ_THREAD_LOCAL(RegisteredThread*) TLSRegisteredThread::sRegisteredThread;
|
||||
// responsible for destroying the ProfilingStack; Because MOZ_THREAD_LOCAL
|
||||
// doesn't support RefPtr, AddRef&Release are done explicitly in
|
||||
// TLSRegisteredThread.
|
||||
/* static */
|
||||
MOZ_THREAD_LOCAL(ProfilingStackOwner*)
|
||||
AutoProfilerLabel::ProfilingStackOwnerTLS::sProfilingStackOwnerTLS;
|
||||
|
||||
/* static */
|
||||
void AutoProfilerLabel::ProfilingStackOwnerTLS::Init() {
|
||||
MOZ_ASSERT(sState == State::Uninitialized, "Already initialized");
|
||||
sState =
|
||||
sProfilingStackOwnerTLS.init() ? State::Initialized : State::Unavailable;
|
||||
}
|
||||
|
||||
void ProfilingStackOwner::DumpStackAndCrash() const {
|
||||
fprintf(stderr,
|
||||
"ProfilingStackOwner::DumpStackAndCrash() thread id: %d, size: %u\n",
|
||||
@ -3768,7 +3812,7 @@ static ProfilingStack* locked_register_thread(PSLockRef aLock,
|
||||
|
||||
VTUNE_REGISTER_THREAD(aName);
|
||||
|
||||
if (!TLSRegisteredThread::Init()) {
|
||||
if (!TLSRegisteredThread::IsTLSInited()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -3923,6 +3967,9 @@ void profiler_init(void* aStackTop) {
|
||||
PrintUsageThenExit(1); // terminates execution
|
||||
}
|
||||
|
||||
// This must be before any TLS access (e.g.: Thread registration, labels...).
|
||||
TLSRegisteredThread::Init();
|
||||
|
||||
SharedLibraryInfo::Initialize();
|
||||
|
||||
uint32_t features = DefaultFeatures() & AvailableFeatures();
|
||||
@ -5039,7 +5086,7 @@ ProfilingStack* profiler_register_thread(const char* aName,
|
||||
TextMarkerPayload(text, TimeStamp::NowUnfuzzed()), &lock);
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
TLSRegisteredThread::Init(),
|
||||
TLSRegisteredThread::IsTLSInited(),
|
||||
"Thread should not have already been registered without TLS::Init()");
|
||||
MOZ_RELEASE_ASSERT(TLSRegisteredThread::RegisteredThread(lock),
|
||||
"TLS should be set when re-registering thread");
|
||||
@ -5073,7 +5120,7 @@ void profiler_unregister_thread() {
|
||||
FindCurrentThreadRegisteredThread(lock);
|
||||
registeredThread) {
|
||||
MOZ_RELEASE_ASSERT(
|
||||
TLSRegisteredThread::Init(),
|
||||
TLSRegisteredThread::IsTLSInited(),
|
||||
"Thread should not have been registered without TLS::Init()");
|
||||
MOZ_RELEASE_ASSERT(TLSRegisteredThread::RegisteredThread(lock),
|
||||
"TLS should be set when un-registering thread");
|
||||
|
@ -1109,6 +1109,8 @@ void profiler_save_profile_to_file(const char* aFilename);
|
||||
// RAII classes
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class TLSRegisteredThread; // Needed for friendship in ProfilingStackOwnerTLS.
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MOZ_RAII AutoProfilerInit {
|
||||
@ -1261,29 +1263,33 @@ class MOZ_RAII AutoProfilerLabel {
|
||||
// See the comment on the definition in platform.cpp for details about this.
|
||||
class ProfilingStackOwnerTLS {
|
||||
public:
|
||||
static bool Init() {
|
||||
// Only one call to MOZ_THREAD_LOCAL::init() is needed, we cache the
|
||||
// result for later calls to Init(), in particular before using get() and
|
||||
// set().
|
||||
static const bool ok = sProfilingStackOwnerTLS.init();
|
||||
return ok;
|
||||
}
|
||||
|
||||
static ProfilingStackOwner* Get() {
|
||||
if (!Init()) {
|
||||
MOZ_ASSERT(
|
||||
sState != State::Uninitialized,
|
||||
"ProfilingStackOwnerTLS::Get() should only be called after Init()");
|
||||
if (sState != State::Initialized) {
|
||||
return nullptr;
|
||||
}
|
||||
return sProfilingStackOwnerTLS.get();
|
||||
}
|
||||
|
||||
static void Set(ProfilingStackOwner* aProfilingStackOwner) {
|
||||
MOZ_RELEASE_ASSERT(Init(),
|
||||
"ProfilingStackOwnerTLS::Set() should only be called "
|
||||
"after a successful Init()");
|
||||
MOZ_ASSERT(
|
||||
sState != State::Uninitialized,
|
||||
"ProfilingStackOwnerTLS::Set() should only be called after Init()");
|
||||
MOZ_DIAGNOSTIC_ASSERT(sState == State::Initialized,
|
||||
"ProfilingStackOwnerTLS::Set() should only be "
|
||||
"called after a successful Init()");
|
||||
sProfilingStackOwnerTLS.set(aProfilingStackOwner);
|
||||
}
|
||||
|
||||
private:
|
||||
friend TLSRegisteredThread;
|
||||
static void Init();
|
||||
|
||||
enum class State { Uninitialized = 0, Initialized, Unavailable };
|
||||
static State sState;
|
||||
|
||||
static MOZ_THREAD_LOCAL(ProfilingStackOwner*) sProfilingStackOwnerTLS;
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user