Fix licenses
[folly.git] / folly / io / async / Request.h
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements. See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership. The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License. You may obtain a copy of the License at
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied. See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21 #pragma once
22
23 #include <map>
24 #include <memory>
25 #include <glog/logging.h>
26 #include <folly/ThreadLocal.h>
27 #include <folly/RWSpinLock.h>
28
29 /**
30  * In many cases this header is included as a
31  * dependency to libraries which do not need
32  * command line flags. GFLAGS is a large binary
33  * and thus we do this so that a library which
34  * is size sensitive doesn't have to pull in
35  * GFLAGS if it doesn't want to.
36  */
37 #ifndef NO_LIB_GFLAGS
38   #include <gflags/gflags.h>
39   DECLARE_bool(enable_request_context);
40 #endif
41
42 namespace folly {
43
44 #ifdef NO_LIB_GFLAGS
45   extern bool FLAGS_enable_request_context;
46 #endif
47
48 // Some request context that follows an async request through a process
49 // Everything in the context must be thread safe
50
51 class RequestData {
52  public:
53   virtual ~RequestData() {}
54 };
55
56 class RequestContext;
57
58 // If you do not call create() to create a unique request context,
59 // this default request context will always be returned, and is never
60 // copied between threads.
61 extern RequestContext* defaultContext;
62
63 class RequestContext {
64  public:
65   // Create a unique requext context for this request.
66   // It will be passed between queues / threads (where implemented),
67   // so it should be valid for the lifetime of the request.
68   static bool create() {
69     if(!FLAGS_enable_request_context) {
70       return false;
71     }
72     bool prev = getStaticContext().get() != nullptr;
73     getStaticContext().reset(new std::shared_ptr<RequestContext>(
74                      std::make_shared<RequestContext>()));
75     return prev;
76   }
77
78   // Get the current context.
79   static RequestContext* get() {
80     if (!FLAGS_enable_request_context ||
81         getStaticContext().get() == nullptr) {
82       if (defaultContext == nullptr) {
83         defaultContext = new RequestContext;
84       }
85       return defaultContext;
86     }
87     return getStaticContext().get()->get();
88   }
89
90   // The following API may be used to set per-request data in a thread-safe way.
91   // This access is still performance sensitive, so please ask if you need help
92   // profiling any use of these functions.
93   void setContextData(
94     const std::string& val, std::unique_ptr<RequestData> data) {
95     if (!FLAGS_enable_request_context) {
96       return;
97     }
98
99     folly::RWSpinLock::WriteHolder guard(lock);
100     if (data_.find(val) != data_.end()) {
101       LOG_FIRST_N(WARNING, 1) <<
102         "Called RequestContext::setContextData with data already set";
103
104       data_[val] = nullptr;
105     } else {
106       data_[val] = std::move(data);
107     }
108   }
109
110   bool hasContextData(const std::string& val) {
111     folly::RWSpinLock::ReadHolder guard(lock);
112     return data_.find(val) != data_.end();
113   }
114
115   RequestData* getContextData(const std::string& val) {
116     folly::RWSpinLock::ReadHolder guard(lock);
117     auto r = data_.find(val);
118     if (r == data_.end()) {
119       return nullptr;
120     } else {
121       return r->second.get();
122     }
123   }
124
125   void clearContextData(const std::string& val) {
126     folly::RWSpinLock::WriteHolder guard(lock);
127     data_.erase(val);
128   }
129
130   // The following API is used to pass the context through queues / threads.
131   // saveContext is called to geta shared_ptr to the context, and
132   // setContext is used to reset it on the other side of the queue.
133   //
134   // A shared_ptr is used, because many request may fan out across
135   // multiple threads, or do post-send processing, etc.
136
137   static std::shared_ptr<RequestContext>
138   setContext(std::shared_ptr<RequestContext> ctx) {
139     if (FLAGS_enable_request_context) {
140       std::shared_ptr<RequestContext> old_ctx;
141       if (getStaticContext().get()) {
142         old_ctx = *getStaticContext().get();
143       }
144       if (ctx == nullptr) {
145         getStaticContext().reset(nullptr);
146       } else {
147         getStaticContext().reset(new std::shared_ptr<RequestContext>(ctx));
148       }
149       return old_ctx;
150     }
151     return std::shared_ptr<RequestContext>();
152   }
153
154   static std::shared_ptr<RequestContext> saveContext() {
155     if (!FLAGS_enable_request_context) {
156       return std::shared_ptr<RequestContext>();
157     }
158     if (getStaticContext().get() == nullptr) {
159       return std::shared_ptr<RequestContext>();
160     } else {
161       return *getStaticContext().get();
162     }
163   }
164
165   // Used to solve static destruction ordering issue.  Any static object
166   // that uses RequestContext must call this function in its constructor.
167   //
168   // See below link for more details.
169   // http://stackoverflow.com/questions/335369/
170   // finding-c-static-initialization-order-problems#335746
171   static folly::ThreadLocalPtr<std::shared_ptr<RequestContext>>&
172   getStaticContext() {
173     static folly::ThreadLocalPtr<std::shared_ptr<RequestContext> > context;
174     return context;
175   }
176
177  private:
178   folly::RWSpinLock lock;
179   std::map<std::string, std::unique_ptr<RequestData>> data_;
180 };
181
182 /**
183  * Set the request context for a specific scope. For example,
184  * if you ran a part of a request in another thread you could
185  * use RequestContextGuard to copy apply the request context
186  * inside the other therad.
187  */
188 class RequestContextGuard {
189  public:
190   explicit RequestContextGuard(std::shared_ptr<RequestContext> ctx) {
191     oldctx_ = RequestContext::setContext(std::move(ctx));
192   }
193
194   ~RequestContextGuard() {
195     RequestContext::setContext(std::move(oldctx_));
196   }
197
198  private:
199   std::shared_ptr<RequestContext> oldctx_;
200 };
201
202 }