• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 "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.
TimeZoneMutex()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 
UTC()49 time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); }
50 
LoadTimeZone(const std::string & name,time_zone * tz)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 
ClearTimeZoneMapTestOnly()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 
Impl(const std::string & name)102 time_zone::Impl::Impl(const std::string& name)
103     : name_(name), zone_(TimeZoneIf::Load(name_)) {}
104 
UTCImpl()105 const time_zone::Impl* time_zone::Impl::UTCImpl() {
106   static const Impl* utc_impl = new Impl("UTC");  // never fails
107   return utc_impl;
108 }
109 
110 }  // namespace cctz
111 }  // namespace time_internal
112 ABSL_NAMESPACE_END
113 }  // namespace absl
114