Commit fc353ba6 authored by Andrii Grynenko's avatar Andrii Grynenko Committed by facebook-github-bot-0

Improve RequestContext::getStaticContext() perf

Summary: Avoid using folly::ThreadLocal on the fast-path. This uses the fact that it is a static object.

Reviewed By: yfeldblum

Differential Revision: D2873374

fb-gh-sync-id: 978108da0c9e8576fda6849058b0262478c2841e
parent 47241bf8
......@@ -274,6 +274,7 @@ nobase_follyinclude_HEADERS = \
SharedMutex.h \
Singleton.h \
Singleton-inl.h \
SingletonThreadLocal.h \
SmallLocks.h \
small_vector.h \
SocketAddress.h \
......
......@@ -96,7 +96,6 @@ void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
}
void SingletonVault::registrationComplete() {
RequestContext::saveContext();
std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
RWSpinLock::WriteHolder wh(&stateMutex_);
......@@ -214,8 +213,6 @@ void SingletonVault::reenableInstances() {
}
void SingletonVault::scheduleDestroyInstances() {
RequestContext::saveContext();
class SingletonVaultDestructor {
public:
~SingletonVaultDestructor() {
......
......@@ -109,7 +109,6 @@
#include <folly/RWSpinLock.h>
#include <folly/Demangle.h>
#include <folly/Executor.h>
#include <folly/io/async/Request.h>
#include <folly/experimental/ReadMostlySharedPtr.h>
#include <algorithm>
......@@ -609,6 +608,75 @@ class Singleton {
}
};
template <typename T, typename Tag = detail::DefaultTag>
class LeakySingleton {
public:
using CreateFunc = std::function<T*()>;
LeakySingleton() : LeakySingleton([] { return new T(); }) {}
explicit LeakySingleton(CreateFunc createFunc) {
auto& entry = entryInstance();
if (entry.state != State::NotRegistered) {
LOG(FATAL) << "Double registration of singletons of the same "
<< "underlying type; check for multiple definitions "
<< "of type folly::LeakySingleton<" + entry.type_.name() + ">";
}
entry.createFunc = createFunc;
entry.state = State::Dead;
}
static T& get() { return instance(); }
private:
enum class State { NotRegistered, Dead, Living };
struct Entry {
Entry() {}
Entry(const Entry&) = delete;
Entry& operator=(const Entry&) = delete;
std::atomic<State> state{State::NotRegistered};
T* ptr{nullptr};
CreateFunc createFunc;
std::mutex mutex;
detail::TypeDescriptor type_{typeid(T), typeid(Tag)};
};
static Entry& entryInstance() {
static auto entry = new Entry();
return *entry;
}
static T& instance() {
auto& entry = entryInstance();
if (UNLIKELY(entry.state != State::Living)) {
createInstance();
}
return *entry.ptr;
}
static void createInstance() {
auto& entry = entryInstance();
std::lock_guard<std::mutex> lg(entry.mutex);
if (entry.state == State::Living) {
return;
}
if (entry.state == State::NotRegistered) {
auto ptr = SingletonVault::stackTraceGetter().load();
LOG(FATAL) << "Creating instance for unregistered singleton: "
<< entry.type_.name() << "\n"
<< "Stacktrace:"
<< "\n" << (ptr ? (*ptr)() : "(not available)");
}
entry.ptr = entry.createFunc();
entry.state = State::Living;
}
};
}
#include <folly/Singleton-inl.h>
/*
* Copyright 2016 Facebook, Inc.
*
* 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.
*/
#pragma once
#include <folly/ThreadLocal.h>
#include <folly/Singleton.h>
namespace folly {
template <typename T, typename Tag = detail::DefaultTag>
class SingletonThreadLocal {
public:
using CreateFunc = std::function<T*(void)>;
SingletonThreadLocal() : SingletonThreadLocal([]() { return new T(); }) {}
explicit SingletonThreadLocal(CreateFunc createFunc)
: singleton_([createFunc = std::move(createFunc)]() mutable {
return new ThreadLocalT([createFunc =
std::move(createFunc)]() mutable {
return new Wrapper(std::unique_ptr<T>(createFunc()));
});
}) {}
static T& get() {
#ifdef FOLLY_TLS
*localPtr() = nullptr;
if (UNLIKELY(*localPtr() == nullptr)) {
*localPtr() = &(**SingletonT::get());
}
return **localPtr();
#else
return **SingletonT::get();
#endif
}
private:
#ifdef FOLLY_TLS
static T** localPtr() {
static FOLLY_TLS T* localPtr = nullptr;
return &localPtr;
}
#endif
class Wrapper {
public:
explicit Wrapper(std::unique_ptr<T> t) : t_(std::move(t)) {}
~Wrapper() {
#ifdef FOLLY_TLS
*localPtr() = nullptr;
#endif
}
T& operator*() { return *t_; }
private:
std::unique_ptr<T> t_;
};
using ThreadLocalT = ThreadLocal<Wrapper>;
using SingletonT = LeakySingleton<ThreadLocalT, Tag>;
SingletonT singleton_;
};
}
......@@ -25,6 +25,7 @@
#include <glog/logging.h>
#include <folly/ThreadLocal.h>
#include <folly/RWSpinLock.h>
#include <folly/SingletonThreadLocal.h>
namespace folly {
......@@ -129,15 +130,10 @@ class RequestContext {
}
private:
// Used to solve static destruction ordering issue. Any static object
// that uses RequestContext must call this function in its constructor.
//
// See below link for more details.
// http://stackoverflow.com/questions/335369/
// finding-c-static-initialization-order-problems#335746
static std::shared_ptr<RequestContext> &getStaticContext() {
static folly::ThreadLocal<std::shared_ptr<RequestContext> > context;
return *context;
static std::shared_ptr<RequestContext>& getStaticContext() {
using SingletonT = SingletonThreadLocal<std::shared_ptr<RequestContext>>;
static SingletonT singleton;
return singleton.get();
}
folly::RWSpinLock lock;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment