SettingsImpl.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. * Copyright 2018-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. #pragma once
  17. #include <memory>
  18. #include <mutex>
  19. #include <string>
  20. #include <typeindex>
  21. #include <folly/CachelinePadded.h>
  22. #include <folly/Conv.h>
  23. #include <folly/Range.h>
  24. #include <folly/SharedMutex.h>
  25. #include <folly/ThreadLocal.h>
  26. #include <folly/experimental/settings/SettingsMetadata.h>
  27. namespace folly {
  28. namespace settings {
  29. namespace detail {
  30. /**
  31. * Can we store T in a global atomic?
  32. */
  33. template <class T>
  34. struct IsSmallPOD
  35. : std::integral_constant<
  36. bool,
  37. std::is_trivial<T>::value && sizeof(T) <= sizeof(uint64_t)> {};
  38. template <class T>
  39. struct SettingContents {
  40. std::string updateReason;
  41. T value;
  42. template <class... Args>
  43. SettingContents(std::string _reason, Args&&... args)
  44. : updateReason(std::move(_reason)), value(std::forward<Args>(args)...) {}
  45. };
  46. class SnapshotBase;
  47. class SettingCoreBase {
  48. public:
  49. using Key = intptr_t;
  50. using Version = uint64_t;
  51. virtual void setFromString(
  52. StringPiece newValue,
  53. StringPiece reason,
  54. SnapshotBase* snapshot) = 0;
  55. virtual std::pair<std::string, std::string> getAsString(
  56. const SnapshotBase* snapshot) const = 0;
  57. virtual void resetToDefault(SnapshotBase* snapshot) = 0;
  58. virtual const SettingMetadata& meta() const = 0;
  59. virtual ~SettingCoreBase() {}
  60. /**
  61. * Hashable key uniquely identifying this setting in this process
  62. */
  63. Key getKey() const {
  64. return reinterpret_cast<Key>(this);
  65. }
  66. };
  67. void registerSetting(SettingCoreBase& core);
  68. /**
  69. * Returns the monotonically increasing unique positive version.
  70. */
  71. SettingCoreBase::Version nextGlobalVersion();
  72. template <class T>
  73. class SettingCore;
  74. /**
  75. * Type erasure for setting values
  76. */
  77. class BoxedValue {
  78. public:
  79. BoxedValue() = default;
  80. /**
  81. * Stores a value that can be retrieved later
  82. */
  83. template <class T>
  84. explicit BoxedValue(const SettingContents<T>& value)
  85. : value_(std::make_shared<SettingContents<T>>(value)) {}
  86. /**
  87. * Stores a value that can be both retrieved later and optionally
  88. * applied globally
  89. */
  90. template <class T>
  91. BoxedValue(const T& value, StringPiece reason, SettingCore<T>& core)
  92. : value_(std::make_shared<SettingContents<T>>(reason.str(), value)),
  93. publish_([value = value_, &core]() {
  94. auto& contents = BoxedValue::unboxImpl<T>(value.get());
  95. core.set(contents.value, contents.updateReason);
  96. }) {}
  97. /**
  98. * Returns the reference to the stored value
  99. */
  100. template <class T>
  101. const SettingContents<T>& unbox() const {
  102. return BoxedValue::unboxImpl<T>(value_.get());
  103. }
  104. /**
  105. * Applies the stored value globally
  106. */
  107. void publish() {
  108. if (publish_) {
  109. publish_();
  110. }
  111. }
  112. private:
  113. std::shared_ptr<void> value_;
  114. std::function<void()> publish_;
  115. template <class T>
  116. static const SettingContents<T>& unboxImpl(void* value) {
  117. return *static_cast<const SettingContents<T>*>(value);
  118. }
  119. };
  120. /**
  121. * If there are any outstanding snapshots that care about this
  122. * value that's about to be updated, save it to extend its lifetime
  123. */
  124. void saveValueForOutstandingSnapshots(
  125. SettingCoreBase::Key settingKey,
  126. SettingCoreBase::Version version,
  127. const BoxedValue& value);
  128. /**
  129. * @returns a pointer to a saved value at or before the given version
  130. */
  131. const BoxedValue* getSavedValue(
  132. SettingCoreBase::Key key,
  133. SettingCoreBase::Version at);
  134. class SnapshotBase {
  135. public:
  136. /**
  137. * Type that encapsulates the current pair of (to<string>(value), reason)
  138. */
  139. using SettingsInfo = std::pair<std::string, std::string>;
  140. /**
  141. * Apply all settings updates from this snapshot to the global state
  142. * unconditionally.
  143. */
  144. virtual void publish() = 0;
  145. /**
  146. * Look up a setting by name, and update the value from a string
  147. * representation.
  148. *
  149. * @returns True if the setting was successfully updated, false if no setting
  150. * with that name was found.
  151. * @throws std::runtime_error If there's a conversion error.
  152. */
  153. virtual bool setFromString(
  154. StringPiece settingName,
  155. StringPiece newValue,
  156. StringPiece reason) = 0;
  157. /**
  158. * @return If the setting exists, the current setting information.
  159. * Empty Optional otherwise.
  160. */
  161. virtual Optional<SettingsInfo> getAsString(StringPiece settingName) const = 0;
  162. /**
  163. * Reset the value of the setting identified by name to its default value.
  164. * The reason will be set to "default".
  165. *
  166. * @return True if the setting was reset, false if the setting is not found.
  167. */
  168. virtual bool resetToDefault(StringPiece settingName) = 0;
  169. /**
  170. * Iterates over all known settings and calls
  171. * func(meta, to<string>(value), reason) for each.
  172. */
  173. virtual void forEachSetting(
  174. const std::function<
  175. void(const SettingMetadata&, StringPiece, StringPiece)>& func)
  176. const = 0;
  177. virtual ~SnapshotBase();
  178. protected:
  179. detail::SettingCoreBase::Version at_;
  180. std::unordered_map<detail::SettingCoreBase::Key, detail::BoxedValue>
  181. snapshotValues_;
  182. template <typename T>
  183. friend class SettingCore;
  184. SnapshotBase();
  185. template <class T>
  186. const SettingContents<T>& get(const detail::SettingCore<T>& core) const {
  187. auto it = snapshotValues_.find(core.getKey());
  188. if (it != snapshotValues_.end()) {
  189. return it->second.template unbox<T>();
  190. }
  191. auto savedValue = detail::getSavedValue(core.getKey(), at_);
  192. if (savedValue) {
  193. return savedValue->template unbox<T>();
  194. }
  195. return core.getSlow();
  196. }
  197. template <class T>
  198. void set(detail::SettingCore<T>& core, const T& t, StringPiece reason) {
  199. snapshotValues_[core.getKey()] = detail::BoxedValue(t, reason, core);
  200. }
  201. };
  202. template <class T>
  203. std::enable_if_t<std::is_constructible<T, StringPiece>::value, T>
  204. convertOrConstruct(StringPiece newValue) {
  205. return T(newValue);
  206. }
  207. template <class T>
  208. std::enable_if_t<!std::is_constructible<T, StringPiece>::value, T>
  209. convertOrConstruct(StringPiece newValue) {
  210. return to<T>(newValue);
  211. }
  212. template <class T>
  213. class SettingCore : public SettingCoreBase {
  214. public:
  215. using Contents = SettingContents<T>;
  216. void setFromString(
  217. StringPiece newValue,
  218. StringPiece reason,
  219. SnapshotBase* snapshot) override {
  220. set(convertOrConstruct<T>(newValue), reason.str(), snapshot);
  221. }
  222. std::pair<std::string, std::string> getAsString(
  223. const SnapshotBase* snapshot) const override {
  224. auto& contents = snapshot ? snapshot->get(*this) : getSlow();
  225. return std::make_pair(
  226. to<std::string>(contents.value), contents.updateReason);
  227. }
  228. void resetToDefault(SnapshotBase* snapshot) override {
  229. set(defaultValue_, "default", snapshot);
  230. }
  231. const SettingMetadata& meta() const override {
  232. return meta_;
  233. }
  234. /**
  235. * @param trivialStorage must refer to the same location
  236. * as the internal trivialStorage_. This hint will
  237. * generate better inlined code since the address is known
  238. * at compile time at the callsite.
  239. */
  240. std::conditional_t<IsSmallPOD<T>::value, T, const T&> getWithHint(
  241. std::atomic<uint64_t>& trivialStorage) const {
  242. return getImpl(IsSmallPOD<T>(), trivialStorage);
  243. }
  244. const SettingContents<T>& getSlow() const {
  245. return *tlValue();
  246. }
  247. /***
  248. * SmallPOD version: just read the global atomic
  249. */
  250. T getImpl(std::true_type, std::atomic<uint64_t>& trivialStorage) const {
  251. uint64_t v = trivialStorage.load();
  252. T t;
  253. std::memcpy(&t, &v, sizeof(T));
  254. return t;
  255. }
  256. /**
  257. * Non-SmallPOD version: read the thread local shared_ptr
  258. */
  259. const T& getImpl(std::false_type, std::atomic<uint64_t>& /* ignored */)
  260. const {
  261. return const_cast<SettingCore*>(this)->tlValue()->value;
  262. }
  263. void set(const T& t, StringPiece reason, SnapshotBase* snapshot = nullptr) {
  264. /* Check that we can still display it (will throw otherwise) */
  265. to<std::string>(t);
  266. if (snapshot) {
  267. snapshot->set(*this, t, reason);
  268. return;
  269. }
  270. SharedMutex::WriteHolder lg(globalLock_);
  271. if (globalValue_) {
  272. saveValueForOutstandingSnapshots(
  273. getKey(), *settingVersion_, BoxedValue(*globalValue_));
  274. }
  275. globalValue_ = std::make_shared<Contents>(reason.str(), t);
  276. if (IsSmallPOD<T>::value) {
  277. uint64_t v = 0;
  278. std::memcpy(&v, &t, sizeof(T));
  279. trivialStorage_.store(v);
  280. }
  281. *settingVersion_ = nextGlobalVersion();
  282. }
  283. const T& defaultValue() const {
  284. return defaultValue_;
  285. }
  286. SettingCore(
  287. SettingMetadata meta,
  288. T defaultValue,
  289. std::atomic<uint64_t>& trivialStorage)
  290. : meta_(std::move(meta)),
  291. defaultValue_(std::move(defaultValue)),
  292. trivialStorage_(trivialStorage),
  293. localValue_([]() {
  294. return new CachelinePadded<
  295. std::pair<Version, std::shared_ptr<Contents>>>(0, nullptr);
  296. }) {
  297. set(defaultValue_, "default");
  298. registerSetting(*this);
  299. }
  300. private:
  301. SettingMetadata meta_;
  302. const T defaultValue_;
  303. SharedMutex globalLock_;
  304. std::shared_ptr<Contents> globalValue_;
  305. std::atomic<uint64_t>& trivialStorage_;
  306. /* Thread local versions start at 0, this will force a read on first access.
  307. */
  308. CachelinePadded<std::atomic<Version>> settingVersion_{1};
  309. ThreadLocal<CachelinePadded<std::pair<Version, std::shared_ptr<Contents>>>>
  310. localValue_;
  311. FOLLY_ALWAYS_INLINE const std::shared_ptr<Contents>& tlValue() const {
  312. auto& value = **localValue_;
  313. if (LIKELY(value.first == *settingVersion_)) {
  314. return value.second;
  315. }
  316. return tlValueSlow();
  317. }
  318. FOLLY_NOINLINE const std::shared_ptr<Contents>& tlValueSlow() const {
  319. auto& value = **localValue_;
  320. while (value.first < *settingVersion_) {
  321. /* If this destroys the old value, do it without holding the lock */
  322. value.second.reset();
  323. SharedMutex::ReadHolder lg(globalLock_);
  324. value.first = *settingVersion_;
  325. value.second = globalValue_;
  326. }
  327. return value.second;
  328. }
  329. };
  330. } // namespace detail
  331. } // namespace settings
  332. } // namespace folly