Commit c0efc2be authored by Dave Watson's avatar Dave Watson Committed by Facebook Github Bot

Use hazptr in RequestContext

Summary:
Instead of a reader-writer lock in RequestContext data_, use an allocation + hazptr.
This improves setContext/saveContext perf at the expense of setData.

Differential Revision: D8397670

fbshipit-source-id: 105fd715f1eb09499c88d1a205f019ae01a54f13
parent 6e707da9
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <folly/lang/Assume.h> #include <folly/lang/Assume.h>
#include <folly/lang/Exception.h> #include <folly/lang/Exception.h>
#include <folly/synchronization/MicroSpinLock.h> #include <folly/synchronization/MicroSpinLock.h>
#include <glog/logging.h>
#include <folly/io/async/Request.h> #include <folly/io/async/Request.h>
......
...@@ -21,44 +21,68 @@ ...@@ -21,44 +21,68 @@
#include <folly/MapUtil.h> #include <folly/MapUtil.h>
#include <folly/SingletonThreadLocal.h> #include <folly/SingletonThreadLocal.h>
#include <folly/synchronization/Hazptr.h>
namespace folly { namespace folly {
struct RequestContext::State : hazptr_obj_base<State> {
std::map<std::string, std::shared_ptr<RequestData>> requestData_;
std::set<RequestData*> callbackData_;
};
RequestContext::RequestContext() : state_(new State) {}
RequestContext::~RequestContext() {
auto p = state_.load(std::memory_order_relaxed);
delete p;
}
bool RequestContext::doSetContextData( bool RequestContext::doSetContextData(
const std::string& val, const std::string& val,
std::unique_ptr<RequestData>& data, std::unique_ptr<RequestData>& data,
bool strict) { bool strict) {
auto ulock = state_.ulock(); State* p{nullptr};
bool conflict = false; {
auto it = ulock->requestData_.find(val); std::lock_guard<std::mutex> g(m_);
if (it != ulock->requestData_.end()) { p = state_.load(std::memory_order_acquire);
if (strict) {
return false; bool conflict = false;
} else { auto it = p->requestData_.find(val);
LOG_FIRST_N(WARNING, 1) << "Calling RequestContext::setContextData for " if (it != p->requestData_.end()) {
<< val << " but it is already set"; if (strict) {
conflict = true; return false;
} else {
LOG_FIRST_N(WARNING, 1) << "Calling RequestContext::setContextData for "
<< val << " but it is already set";
conflict = true;
}
} }
}
auto wlock = ulock.moveFromUpgradeToWrite(); auto newstate = new State(*p);
if (conflict) { it = newstate->requestData_.find(val);
if (it->second) {
if (it->second->hasCallback()) { if (conflict) {
it->second->onUnset(); if (it->second) {
wlock->callbackData_.erase(it->second.get()); if (it->second->hasCallback()) {
it->second->onUnset();
newstate->callbackData_.erase(it->second.get());
}
it->second.reset();
} }
it->second.reset(nullptr);
} }
return true;
if (data && data->hasCallback()) {
newstate->callbackData_.insert(data.get());
data->onSet();
}
newstate->requestData_[val] = std::move(data);
state_.store(newstate, std::memory_order_release);
} }
if (data && data->hasCallback()) { if (p) {
wlock->callbackData_.insert(data.get()); p->retire();
data->onSet();
} }
wlock->requestData_[val] = std::move(data);
return true; return true;
} }
...@@ -76,38 +100,49 @@ bool RequestContext::setContextDataIfAbsent( ...@@ -76,38 +100,49 @@ bool RequestContext::setContextDataIfAbsent(
} }
bool RequestContext::hasContextData(const std::string& val) const { bool RequestContext::hasContextData(const std::string& val) const {
return state_.rlock()->requestData_.count(val); hazptr_local<1> hptr;
auto p = hptr[0].get_protected(state_);
return p->requestData_.count(val) != 0;
} }
RequestData* RequestContext::getContextData(const std::string& val) { RequestData* RequestContext::getContextData(const std::string& val) {
const std::unique_ptr<RequestData> dflt{nullptr}; const std::shared_ptr<RequestData> dflt{nullptr};
return get_ref_default(state_.rlock()->requestData_, val, dflt).get(); hazptr_local<1> hptr;
auto p = hptr[0].get_protected(state_);
p->requestData_.count(val);
return get_ref_default(p->requestData_, val, dflt).get();
} }
const RequestData* RequestContext::getContextData( const RequestData* RequestContext::getContextData(
const std::string& val) const { const std::string& val) const {
const std::unique_ptr<RequestData> dflt{nullptr}; const std::shared_ptr<RequestData> dflt{nullptr};
return get_ref_default(state_.rlock()->requestData_, val, dflt).get(); hazptr_local<1> hptr;
auto p = hptr[0].get_protected(state_);
p->requestData_.count(val);
return get_ref_default(p->requestData_, val, dflt).get();
} }
void RequestContext::onSet() { void RequestContext::onSet() {
auto rlock = state_.rlock(); hazptr_holder<> hptr;
for (const auto& data : rlock->callbackData_) { auto p = hptr.get_protected(state_);
for (const auto& data : p->callbackData_) {
data->onSet(); data->onSet();
} }
} }
void RequestContext::onUnset() { void RequestContext::onUnset() {
auto rlock = state_.rlock(); hazptr_holder<> hptr;
for (const auto& data : rlock->callbackData_) { auto p = hptr.get_protected(state_);
for (const auto& data : p->callbackData_) {
data->onUnset(); data->onUnset();
} }
} }
std::shared_ptr<RequestContext> RequestContext::createChild() { std::shared_ptr<RequestContext> RequestContext::createChild() {
hazptr_local<1> hptr;
auto p = hptr[0].get_protected(state_);
auto child = std::make_shared<RequestContext>(); auto child = std::make_shared<RequestContext>();
auto rlock = state_.rlock(); for (const auto& entry : p->requestData_) {
for (const auto& entry : rlock->requestData_) {
auto& key = entry.first; auto& key = entry.first;
auto childData = entry.second->createChild(); auto childData = entry.second->createChild();
if (childData) { if (childData) {
...@@ -118,24 +153,37 @@ std::shared_ptr<RequestContext> RequestContext::createChild() { ...@@ -118,24 +153,37 @@ std::shared_ptr<RequestContext> RequestContext::createChild() {
} }
void RequestContext::clearContextData(const std::string& val) { void RequestContext::clearContextData(const std::string& val) {
std::unique_ptr<RequestData> requestData; std::shared_ptr<RequestData> requestData;
// Delete the RequestData after giving up the wlock just in case one of the // requestData is deleted while lock is not held, in case
// RequestData destructors will try to grab the lock again. // destructor contains calls to RequestContext
State* p{nullptr};
{ {
auto ulock = state_.ulock(); std::lock_guard<std::mutex> g(m_);
auto it = ulock->requestData_.find(val); p = state_.load(std::memory_order_acquire);
if (it == ulock->requestData_.end()) {
auto it = p->requestData_.find(val);
if (it == p->requestData_.end()) {
return; return;
} }
auto wlock = ulock.moveFromUpgradeToWrite(); auto newstate = new State(*p);
it = newstate->requestData_.find(val);
CHECK(it != newstate->requestData_.end());
if (it->second && it->second->hasCallback()) { if (it->second && it->second->hasCallback()) {
it->second->onUnset(); it->second->onUnset();
wlock->callbackData_.erase(it->second.get()); newstate->callbackData_.erase(it->second.get());
} }
requestData = std::move(it->second); requestData = it->second;
wlock->requestData_.erase(it); newstate->requestData_.erase(it);
state_.store(newstate, std::memory_order_release);
}
if (p) {
p->retire();
} }
} }
......
...@@ -16,12 +16,12 @@ ...@@ -16,12 +16,12 @@
#pragma once #pragma once
#include <atomic>
#include <map> #include <map>
#include <memory> #include <memory>
#include <mutex>
#include <set> #include <set>
#include <folly/Synchronized.h>
namespace folly { namespace folly {
// Some request context that follows an async request through a process // Some request context that follows an async request through a process
...@@ -58,6 +58,8 @@ class RequestData { ...@@ -58,6 +58,8 @@ class RequestData {
// copied between threads. // copied between threads.
class RequestContext { class RequestContext {
public: public:
RequestContext();
~RequestContext();
// Create a unique request context for this request. // Create a unique request context for this request.
// It will be passed between queues / threads (where implemented), // It will be passed between queues / threads (where implemented),
// so it should be valid for the lifetime of the request. // so it should be valid for the lifetime of the request.
...@@ -131,11 +133,9 @@ class RequestContext { ...@@ -131,11 +133,9 @@ class RequestContext {
std::unique_ptr<RequestData>& data, std::unique_ptr<RequestData>& data,
bool strict); bool strict);
struct State { struct State;
std::map<std::string, std::unique_ptr<RequestData>> requestData_; std::atomic<State*> state_;
std::set<RequestData*> callbackData_; std::mutex m_;
};
folly::Synchronized<State> state_;
}; };
class RequestContextScopeGuard { class RequestContextScopeGuard {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <future> #include <future>
#include <folly/Executor.h> #include <folly/Executor.h>
#include <folly/Synchronized.h>
#include <folly/io/async/EventBase.h> #include <folly/io/async/EventBase.h>
#include <folly/synchronization/Baton.h> #include <folly/synchronization/Baton.h>
......
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