Settings.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. #include <folly/experimental/settings/Settings.h>
  17. #include <map>
  18. #include <folly/Synchronized.h>
  19. namespace folly {
  20. namespace settings {
  21. namespace detail {
  22. namespace {
  23. using SettingsMap = std::map<std::string, SettingCoreBase*>;
  24. Synchronized<SettingsMap>& settingsMap() {
  25. static Indestructible<Synchronized<SettingsMap>> map;
  26. return *map;
  27. }
  28. } // namespace
  29. void registerSetting(SettingCoreBase& core) {
  30. if (core.meta().project.empty() ||
  31. core.meta().project.find('_') != std::string::npos) {
  32. throw std::logic_error(
  33. "Setting project must be nonempty and cannot contain underscores: " +
  34. core.meta().project.str());
  35. }
  36. auto fullname = core.meta().project.str() + "_" + core.meta().name.str();
  37. auto mapPtr = settingsMap().wlock();
  38. auto it = mapPtr->find(fullname);
  39. if (it != mapPtr->end()) {
  40. throw std::logic_error("FOLLY_SETTING already exists: " + fullname);
  41. }
  42. mapPtr->emplace(std::move(fullname), &core);
  43. }
  44. } // namespace detail
  45. Optional<SettingMetadata> getSettingsMeta(StringPiece settingName) {
  46. auto mapPtr = detail::settingsMap().rlock();
  47. auto it = mapPtr->find(settingName.str());
  48. if (it == mapPtr->end()) {
  49. return none;
  50. }
  51. return it->second->meta();
  52. }
  53. bool Snapshot::setFromString(
  54. StringPiece settingName,
  55. StringPiece newValue,
  56. StringPiece reason) {
  57. auto mapPtr = detail::settingsMap().rlock();
  58. auto it = mapPtr->find(settingName.str());
  59. if (it == mapPtr->end()) {
  60. return false;
  61. }
  62. it->second->setFromString(newValue, reason, this);
  63. return true;
  64. }
  65. Optional<Snapshot::SettingsInfo> Snapshot::getAsString(
  66. StringPiece settingName) const {
  67. auto mapPtr = detail::settingsMap().rlock();
  68. auto it = mapPtr->find(settingName.str());
  69. if (it == mapPtr->end()) {
  70. return none;
  71. }
  72. return it->second->getAsString(this);
  73. }
  74. bool Snapshot::resetToDefault(StringPiece settingName) {
  75. auto mapPtr = detail::settingsMap().rlock();
  76. auto it = mapPtr->find(settingName.str());
  77. if (it == mapPtr->end()) {
  78. return false;
  79. }
  80. it->second->resetToDefault(this);
  81. return true;
  82. }
  83. void Snapshot::forEachSetting(
  84. const std::function<void(const SettingMetadata&, StringPiece, StringPiece)>&
  85. func) const {
  86. detail::SettingsMap map;
  87. /* Note that this won't hold the lock over the callback, which is
  88. what we want since the user might call other settings:: APIs */
  89. map = *detail::settingsMap().rlock();
  90. for (const auto& kv : map) {
  91. auto value = kv.second->getAsString(this);
  92. func(kv.second->meta(), value.first, value.second);
  93. }
  94. }
  95. namespace detail {
  96. std::atomic<SettingCoreBase::Version> gGlobalVersion_;
  97. auto& getSavedValuesMutex() {
  98. static SharedMutex gSavedValuesMutex;
  99. return gSavedValuesMutex;
  100. }
  101. /* Version -> (count of outstanding snapshots, saved setting values) */
  102. auto& getSavedValues() {
  103. static std::unordered_map<
  104. SettingCoreBase::Version,
  105. std::pair<size_t, std::unordered_map<SettingCoreBase::Key, BoxedValue>>>
  106. gSavedValues;
  107. return gSavedValues;
  108. }
  109. SettingCoreBase::Version nextGlobalVersion() {
  110. return gGlobalVersion_.fetch_add(1) + 1;
  111. }
  112. void saveValueForOutstandingSnapshots(
  113. SettingCoreBase::Key settingKey,
  114. SettingCoreBase::Version version,
  115. const BoxedValue& value) {
  116. SharedMutex::WriteHolder lg(getSavedValuesMutex());
  117. for (auto& it : getSavedValues()) {
  118. if (version <= it.first) {
  119. it.second.second[settingKey] = value;
  120. }
  121. }
  122. }
  123. const BoxedValue* FOLLY_NULLABLE
  124. getSavedValue(SettingCoreBase::Key settingKey, SettingCoreBase::Version at) {
  125. SharedMutex::ReadHolder lg(getSavedValuesMutex());
  126. auto it = getSavedValues().find(at);
  127. if (it != getSavedValues().end()) {
  128. auto jt = it->second.second.find(settingKey);
  129. if (jt != it->second.second.end()) {
  130. return &jt->second;
  131. }
  132. }
  133. return nullptr;
  134. }
  135. SnapshotBase::SnapshotBase() {
  136. SharedMutex::WriteHolder lg(detail::getSavedValuesMutex());
  137. at_ = detail::gGlobalVersion_.load();
  138. auto it = detail::getSavedValues().emplace(
  139. std::piecewise_construct,
  140. std::forward_as_tuple(at_),
  141. std::forward_as_tuple());
  142. ++it.first->second.first;
  143. }
  144. SnapshotBase::~SnapshotBase() {
  145. SharedMutex::WriteHolder lg(detail::getSavedValuesMutex());
  146. auto it = detail::getSavedValues().find(at_);
  147. assert(it != detail::getSavedValues().end());
  148. --it->second.first;
  149. if (!it->second.first) {
  150. detail::getSavedValues().erase(at_);
  151. }
  152. }
  153. } // namespace detail
  154. void Snapshot::publish() {
  155. for (auto& it : snapshotValues_) {
  156. it.second.publish();
  157. }
  158. }
  159. } // namespace settings
  160. } // namespace folly