Make RequestContext provider overridable in order to save cost of setContext() on...
[folly.git] / folly / io / async / Request.h
1 /*
2  * Copyright 2004-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #pragma once
18
19 #include <map>
20 #include <memory>
21
22 #include <folly/Function.h>
23 #include <folly/SharedMutex.h>
24 #include <folly/Synchronized.h>
25
26 namespace folly {
27
28 // Some request context that follows an async request through a process
29 // Everything in the context must be thread safe
30
31 class RequestData {
32  public:
33   virtual ~RequestData() = default;
34   // Avoid calling RequestContext::setContextData, setContextDataIfAbsent, or
35   // clearContextData from these callbacks. Doing so will cause deadlock. We
36   // could fix these deadlocks, but only at significant performance penalty, so
37   // just don't do it!
38   virtual void onSet() {}
39   virtual void onUnset() {}
40 };
41
42 class RequestContext;
43
44 // If you do not call create() to create a unique request context,
45 // this default request context will always be returned, and is never
46 // copied between threads.
47 class RequestContext {
48  public:
49   using Provider = folly::Function<std::shared_ptr<RequestContext>&()>;
50
51   // Create a unique request context for this request.
52   // It will be passed between queues / threads (where implemented),
53   // so it should be valid for the lifetime of the request.
54   static void create() {
55     setContext(std::make_shared<RequestContext>());
56   }
57
58   // Get the current context.
59   static RequestContext* get();
60
61   // The following API may be used to set per-request data in a thread-safe way.
62   // This access is still performance sensitive, so please ask if you need help
63   // profiling any use of these functions.
64   void setContextData(
65       const std::string& val,
66       std::unique_ptr<RequestData> data);
67
68   // Unlike setContextData, this method does not panic if the key is already
69   // present. Returns true iff the new value has been inserted.
70   bool setContextDataIfAbsent(
71       const std::string& val,
72       std::unique_ptr<RequestData> data);
73
74   bool hasContextData(const std::string& val) const;
75
76   RequestData* getContextData(const std::string& val);
77   const RequestData* getContextData(const std::string& val) const;
78
79   void onSet();
80   void onUnset();
81
82   void clearContextData(const std::string& val);
83
84   // The following API is used to pass the context through queues / threads.
85   // saveContext is called to get a shared_ptr to the context, and
86   // setContext is used to reset it on the other side of the queue.
87   //
88   // Whenever possible, use RequestContextScopeGuard instead of setContext
89   // to make sure that RequestContext is reset to the original value when
90   // we exit the scope.
91   //
92   // A shared_ptr is used, because many request may fan out across
93   // multiple threads, or do post-send processing, etc.
94   static std::shared_ptr<RequestContext> setContext(
95       std::shared_ptr<RequestContext> ctx);
96
97   static std::shared_ptr<RequestContext> saveContext() {
98     return getStaticContext();
99   }
100
101   // This API allows one to override the default behavior of getStaticContext()
102   // by providing a custom RequestContext provider. The old provider is
103   // returned, and the user must restore the old provider via a subsequent call
104   // to setRequestContextProvider() once the new provider is no longer needed.
105   //
106   // Using custom RequestContext providers can be more efficient than having to
107   // setContext() whenever context must be switched. This is especially true in
108   // applications that do not actually use RequestContext, but where library
109   // code must still support RequestContext for other use cases.  See
110   // FiberManager for an example of how a custom RequestContext provider can
111   // reduce calls to setContext().
112   static Provider setRequestContextProvider(Provider f);
113
114  private:
115   static std::shared_ptr<RequestContext>& getStaticContext();
116   static Provider& requestContextProvider();
117
118   using Data = std::map<std::string, std::unique_ptr<RequestData>>;
119   folly::Synchronized<Data, folly::SharedMutex> data_;
120 };
121
122 class RequestContextScopeGuard {
123  private:
124   std::shared_ptr<RequestContext> prev_;
125
126  public:
127   RequestContextScopeGuard(const RequestContextScopeGuard&) = delete;
128   RequestContextScopeGuard& operator=(const RequestContextScopeGuard&) = delete;
129   RequestContextScopeGuard(RequestContextScopeGuard&&) = delete;
130   RequestContextScopeGuard& operator=(RequestContextScopeGuard&&) = delete;
131
132   // Create a new RequestContext and reset to the original value when
133   // this goes out of scope.
134   RequestContextScopeGuard() : prev_(RequestContext::saveContext()) {
135     RequestContext::create();
136   }
137
138   // Set a RequestContext that was previously captured by saveContext(). It will
139   // be automatically reset to the original value when this goes out of scope.
140   explicit RequestContextScopeGuard(std::shared_ptr<RequestContext> ctx)
141       : prev_(RequestContext::setContext(std::move(ctx))) {
142   }
143
144   ~RequestContextScopeGuard() {
145     RequestContext::setContext(std::move(prev_));
146   }
147 };
148 }