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