public:
explicit TestData(int data) : data_(data) {}
~TestData() override {}
+
+ bool hasCallback() override {
+ return false;
+ }
+
int data_;
};
public:
explicit TestData(int data) : data_(data) {}
~TestData() override {}
+
+ bool hasCallback() override {
+ return false;
+ }
+
int data_;
};
struct MyRequestData : RequestData {
MyRequestData(bool value = false) : value(value) {}
+
+ bool hasCallback() override {
+ return false;
+ }
+
bool value;
};
std::unique_ptr<RequestEventBase>(new RequestEventBase(eb)));
}
+ bool hasCallback() override {
+ return false;
+ }
+
private:
explicit RequestEventBase(EventBase* eb) : eb_(eb) {}
EventBase* eb_;
namespace folly {
+bool RequestContext::doSetContextData(
+ const std::string& val,
+ std::unique_ptr<RequestData>& data,
+ bool strict) {
+ auto ulock = state_.ulock();
+
+ bool conflict = false;
+ auto it = ulock->requestData_.find(val);
+ if (it != ulock->requestData_.end()) {
+ if (strict) {
+ return false;
+ } else {
+ LOG_FIRST_N(WARNING, 1) << "Calling RequestContext::setContextData for "
+ << val << " but it is already set";
+ conflict = true;
+ }
+ }
+
+ auto wlock = ulock.moveFromUpgradeToWrite();
+ if (conflict) {
+ if (it->second) {
+ if (it->second->hasCallback()) {
+ wlock->callbackData_.erase(it->second.get());
+ }
+ it->second.reset(nullptr);
+ }
+ return true;
+ }
+
+ if (data && data->hasCallback()) {
+ wlock->callbackData_.insert(data.get());
+ }
+ wlock->requestData_[val] = std::move(data);
+
+ return true;
+}
+
void RequestContext::setContextData(
const std::string& val,
std::unique_ptr<RequestData> data) {
- auto wlock = data_.wlock();
- if (wlock->count(val)) {
- LOG_FIRST_N(WARNING, 1)
- << "Called RequestContext::setContextData with data already set";
-
- (*wlock)[val] = nullptr;
- } else {
- (*wlock)[val] = std::move(data);
- }
+ doSetContextData(val, data, false /* strict */);
}
bool RequestContext::setContextDataIfAbsent(
const std::string& val,
std::unique_ptr<RequestData> data) {
- auto ulock = data_.ulock();
- if (ulock->count(val)) {
- return false;
- }
-
- auto wlock = ulock.moveFromUpgradeToWrite();
- (*wlock)[val] = std::move(data);
- return true;
+ return doSetContextData(val, data, true /* strict */);
}
bool RequestContext::hasContextData(const std::string& val) const {
- return data_.rlock()->count(val);
+ return state_.rlock()->requestData_.count(val);
}
RequestData* RequestContext::getContextData(const std::string& val) {
const std::unique_ptr<RequestData> dflt{nullptr};
- return get_ref_default(*data_.rlock(), val, dflt).get();
+ return get_ref_default(state_.rlock()->requestData_, val, dflt).get();
}
const RequestData* RequestContext::getContextData(
const std::string& val) const {
const std::unique_ptr<RequestData> dflt{nullptr};
- return get_ref_default(*data_.rlock(), val, dflt).get();
+ return get_ref_default(state_.rlock()->requestData_, val, dflt).get();
}
void RequestContext::onSet() {
- auto rlock = data_.rlock();
- for (auto const& ent : *rlock) {
- if (auto& data = ent.second) {
- data->onSet();
- }
+ auto rlock = state_.rlock();
+ for (const auto& data : rlock->callbackData_) {
+ data->onSet();
}
}
void RequestContext::onUnset() {
- auto rlock = data_.rlock();
- for (auto const& ent : *rlock) {
- if (auto& data = ent.second) {
- data->onUnset();
- }
+ auto rlock = state_.rlock();
+ for (const auto& data : rlock->callbackData_) {
+ data->onUnset();
}
}
// Delete the RequestData after giving up the wlock just in case one of the
// RequestData destructors will try to grab the lock again.
{
- auto wlock = data_.wlock();
- auto it = wlock->find(val);
- if (it != wlock->end()) {
- requestData = std::move(it->second);
- wlock->erase(it);
+ auto ulock = state_.ulock();
+ auto it = ulock->requestData_.find(val);
+ if (it == ulock->requestData_.end()) {
+ return;
}
+
+ auto wlock = ulock.moveFromUpgradeToWrite();
+ if (it->second && it->second->hasCallback()) {
+ wlock->callbackData_.erase(it->second.get());
+ }
+
+ requestData = std::move(it->second);
+ wlock->requestData_.erase(it);
}
}
#include <map>
#include <memory>
+#include <set>
-#include <folly/SharedMutex.h>
#include <folly/Synchronized.h>
namespace folly {
class RequestData {
public:
virtual ~RequestData() = default;
+
// Avoid calling RequestContext::setContextData, setContextDataIfAbsent, or
// clearContextData from these callbacks. Doing so will cause deadlock. We
// could fix these deadlocks, but only at significant performance penalty, so
// just don't do it!
+
+ virtual bool hasCallback() = 0;
+ // Callback executed when setting RequestContext. Make sure your RequestData
+ // instance overrides the hasCallback method to return true otherwise
+ // the callback will not be executed
virtual void onSet() {}
+ // Callback executed when unsetting RequestContext. Make sure your RequestData
+ // instance overrides the hasCallback method to return true otherwise
+ // the callback will not be executed
virtual void onUnset() {}
};
-class RequestContext;
-
// If you do not call create() to create a unique request context,
// this default request context will always be returned, and is never
// copied between threads.
// Get the current context.
static RequestContext* get();
- // The following API may be used to set per-request data in a thread-safe way.
- // This access is still performance sensitive, so please ask if you need help
- // profiling any use of these functions.
+ // The following APIs are used to add, remove and access RequestData instance
+ // in the RequestContext instance, normally used for per-RequestContext
+ // tracking or callback on set and unset. These APIs are Thread-safe.
+ // These APIs are performance sensitive, so please ask if you need help
+ // profiling any use of these APIs.
+
+ // Add RequestData instance "data" to this RequestContext instance, with
+ // string identifier "val". If the same string identifier has already been
+ // used, will print a warning message for the first time, clear the existing
+ // RequestData instance for "val", and **not** add "data".
void setContextData(
const std::string& val,
std::unique_ptr<RequestData> data);
- // Unlike setContextData, this method does not panic if the key is already
- // present. Returns true iff the new value has been inserted.
+ // Add RequestData instance "data" to this RequestContext instance, with
+ // string identifier "val". If the same string identifier has already been
+ // used, return false and do nothing. Otherwise add "data" and return true.
bool setContextDataIfAbsent(
const std::string& val,
std::unique_ptr<RequestData> data);
+ // Remove the RequestData instance with string identifier "val", if it exists.
+ void clearContextData(const std::string& val);
+
+ // Returns true if and only if the RequestData instance with string identifier
+ // "val" exists in this RequestContext instnace.
bool hasContextData(const std::string& val) const;
+ // Get (constant) raw pointer of the RequestData instance with string
+ // identifier "val" if it exists, otherwise returns null pointer.
RequestData* getContextData(const std::string& val);
const RequestData* getContextData(const std::string& val) const;
void onSet();
void onUnset();
- void clearContextData(const std::string& val);
-
// The following API is used to pass the context through queues / threads.
// saveContext is called to get a shared_ptr to the context, and
// setContext is used to reset it on the other side of the queue.
private:
static std::shared_ptr<RequestContext>& getStaticContext();
- using Data = std::map<std::string, std::unique_ptr<RequestData>>;
- folly::Synchronized<Data, folly::SharedMutex> data_;
+ bool doSetContextData(
+ const std::string& val,
+ std::unique_ptr<RequestData>& data,
+ bool strict);
+
+ struct State {
+ std::map<std::string, std::unique_ptr<RequestData>> requestData_;
+ std::set<RequestData*> callbackData_;
+ };
+ folly::Synchronized<State> state_;
};
class RequestContextScopeGuard {
// Set a RequestContext that was previously captured by saveContext(). It will
// be automatically reset to the original value when this goes out of scope.
explicit RequestContextScopeGuard(std::shared_ptr<RequestContext> ctx)
- : prev_(RequestContext::setContext(std::move(ctx))) {
- }
+ : prev_(RequestContext::setContext(std::move(ctx))) {}
~RequestContextScopeGuard() {
RequestContext::setContext(std::move(prev_));
public:
explicit TestData(int data) : data_(data) {}
~TestData() override {}
+
+ bool hasCallback() override {
+ return true;
+ }
+
void onSet() override {
set_++;
}
+
void onUnset() override {
unset_++;
}
+
int set_ = 0, unset_ = 0;
int data_;
};
val_, std::make_unique<TestData>(1));
}
- void onSet() override {}
-
- void onUnset() override {}
+ bool hasCallback() override {
+ return false;
+ }
std::string val_;
};