time_zone_impl.cc (3745B)
1 // Copyright 2016 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "absl/time/internal/cctz/src/time_zone_impl.h" 16 17 #include <deque> 18 #include <memory> 19 #include <mutex> 20 #include <string> 21 #include <unordered_map> 22 #include <utility> 23 24 #include "absl/base/config.h" 25 #include "absl/time/internal/cctz/src/time_zone_fixed.h" 26 27 namespace absl { 28 ABSL_NAMESPACE_BEGIN 29 namespace time_internal { 30 namespace cctz { 31 32 namespace { 33 34 // time_zone::Impls are linked into a map to support fast lookup by name. 35 using TimeZoneImplByName = 36 std::unordered_map<std::string, const time_zone::Impl*>; 37 TimeZoneImplByName* time_zone_map = nullptr; 38 39 // Mutual exclusion for time_zone_map. 40 std::mutex& TimeZoneMutex() { 41 // This mutex is intentionally "leaked" to avoid the static deinitialization 42 // order fiasco (std::mutex's destructor is not trivial on many platforms). 43 static std::mutex* time_zone_mutex = new std::mutex; 44 return *time_zone_mutex; 45 } 46 47 } // namespace 48 49 time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); } 50 51 bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { 52 const Impl* const utc_impl = UTCImpl(); 53 54 // Check for UTC (which is never a key in time_zone_map). 55 auto offset = seconds::zero(); 56 if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) { 57 *tz = time_zone(utc_impl); 58 return true; 59 } 60 61 // Check whether the time zone has already been loaded. 62 { 63 std::lock_guard<std::mutex> lock(TimeZoneMutex()); 64 if (time_zone_map != nullptr) { 65 TimeZoneImplByName::const_iterator itr = time_zone_map->find(name); 66 if (itr != time_zone_map->end()) { 67 *tz = time_zone(itr->second); 68 return itr->second != utc_impl; 69 } 70 } 71 } 72 73 // Load the new time zone (outside the lock). 74 std::unique_ptr<const Impl> new_impl(new Impl(name)); 75 76 // Add the new time zone to the map. 77 std::lock_guard<std::mutex> lock(TimeZoneMutex()); 78 if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName; 79 const Impl*& impl = (*time_zone_map)[name]; 80 if (impl == nullptr) { // this thread won any load race 81 impl = new_impl->zone_ ? new_impl.release() : utc_impl; 82 } 83 *tz = time_zone(impl); 84 return impl != utc_impl; 85 } 86 87 void time_zone::Impl::ClearTimeZoneMapTestOnly() { 88 std::lock_guard<std::mutex> lock(TimeZoneMutex()); 89 if (time_zone_map != nullptr) { 90 // Existing time_zone::Impl* entries are in the wild, so we can't delete 91 // them. Instead, we move them to a private container, where they are 92 // logically unreachable but not "leaked". Future requests will result 93 // in reloading the data. 94 static auto* cleared = new std::deque<const time_zone::Impl*>; 95 for (const auto& element : *time_zone_map) { 96 cleared->push_back(element.second); 97 } 98 time_zone_map->clear(); 99 } 100 } 101 102 time_zone::Impl::Impl() : name_("UTC"), zone_(TimeZoneIf::UTC()) {} 103 104 time_zone::Impl::Impl(const std::string& name) 105 : name_(name), zone_(TimeZoneIf::Make(name_)) {} 106 107 const time_zone::Impl* time_zone::Impl::UTCImpl() { 108 static const Impl* utc_impl = new Impl; 109 return utc_impl; 110 } 111 112 } // namespace cctz 113 } // namespace time_internal 114 ABSL_NAMESPACE_END 115 } // namespace absl