Export of internal Abseil changes

--
05a099a580753f8e96cee38572e94dcdc079361b by Abseil Team <absl-team@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 405966217

--
c6b81e9ebc183d8389f14ecd091c8bad08cfe0aa by Abseil Team <absl-team@google.com>:

Add `inline_element_size` to hashtablez (so that we can compute the weighted load factors properly e.g., in b/187896534).

PiperOrigin-RevId: 405917711

--
3e3673de4e54e4142c54b09e1644dfa3de4bb296 by Abseil Team <absl-team@google.com>:

align indent of code comment in mutex.h

PiperOrigin-RevId: 405871997

--
2248301a5b14f8d2be5b2e9088f3528a353ea491 by Derek Mauro <dmauro@google.com>:

Internal change

PiperOrigin-RevId: 405639236

--
bc7d3c56fdad3dde4b89324af142529f2afe5f1b by Abseil Team <absl-team@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 405508045

--
66472387276ef02505d99195747be862768bb35b by Laramie Leavitt <lar@google.com>:

Also use uint8_t golden values in randen_test.cc

This makes randen_test, randen_slow_test, and randen_hwaes_test essentially
identical, as is the intent.

PiperOrigin-RevId: 405484423
GitOrigin-RevId: 05a099a580753f8e96cee38572e94dcdc079361b
Change-Id: I3dd5b0cfdb98d6e1ab02266194ba67d15428c2f8
pull/1050/head
Abseil Team 3 years ago committed by Andy Getz
parent f70eadadd7
commit cc413f8b67
  1. 16
      absl/container/BUILD.bazel
  2. 15
      absl/container/CMakeLists.txt
  3. 15
      absl/container/internal/hashtablez_sampler.cc
  4. 9
      absl/container/internal/hashtablez_sampler.h
  5. 19
      absl/container/internal/hashtablez_sampler_test.cc
  6. 2
      absl/container/internal/raw_hash_set.h
  7. 1
      absl/container/internal/raw_hash_set_test.cc
  8. 114
      absl/container/sample_element_size_test.cc
  9. 45
      absl/random/internal/randen_test.cc
  10. 6
      absl/synchronization/mutex.h
  11. 69
      absl/time/internal/cctz/src/time_zone_info.cc
  12. 49
      absl/time/internal/cctz/src/time_zone_lookup.cc
  13. 4
      absl/time/internal/cctz/src/time_zone_lookup_test.cc
  14. 2
      absl/time/internal/cctz/testdata/version
  15. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
  16. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
  17. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji

@ -876,6 +876,22 @@ cc_test(
],
)
cc_test(
name = "sample_element_size_test",
srcs = ["sample_element_size_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
tags = NOTEST_TAGS_NONMOBILE,
visibility = ["//visibility:private"],
deps = [
":flat_hash_map",
":flat_hash_set",
":node_hash_map",
":node_hash_set",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "btree",
srcs = [

@ -894,3 +894,18 @@ absl_cc_test(
absl::unordered_map_modifiers_test
GTest::gmock_main
)
absl_cc_test(
NAME
sample_element_size_test
SRCS
"sample_element_size_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::flat_hash_map
absl::flat_hash_set
absl::node_hash_map
absl::node_hash_set
GTest::gmock_main
)

@ -55,6 +55,9 @@ HashtablezSampler& GlobalHashtablezSampler() {
return *sampler;
}
// TODO(bradleybear): The comments at this constructors declaration say that the
// fields are not initialized, but this definition does initialize the fields.
// Something needs to be cleaned up.
HashtablezInfo::HashtablezInfo() { PrepareForSampling(); }
HashtablezInfo::~HashtablezInfo() = default;
@ -98,10 +101,12 @@ static bool ShouldForceSampling() {
return state == kForce;
}
HashtablezInfo* SampleSlow(int64_t* next_sample) {
HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) {
if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
*next_sample = 1;
return GlobalHashtablezSampler().Register();
HashtablezInfo* result = GlobalHashtablezSampler().Register();
result->inline_element_size = inline_element_size;
return result;
}
#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@ -123,10 +128,12 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) {
// that case.
if (first) {
if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr;
return SampleSlow(next_sample);
return SampleSlow(next_sample, inline_element_size);
}
return GlobalHashtablezSampler().Register();
HashtablezInfo* result = GlobalHashtablezSampler().Register();
result->inline_element_size = inline_element_size;
return result;
#endif
}

@ -91,6 +91,7 @@ struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> {
absl::Time create_time;
int32_t depth;
void* stack[kMaxStackDepth];
size_t inline_element_size;
};
inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
@ -143,7 +144,7 @@ inline void RecordEraseSlow(HashtablezInfo* info) {
std::memory_order_relaxed);
}
HashtablezInfo* SampleSlow(int64_t* next_sample);
HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size);
void UnsampleSlow(HashtablezInfo* info);
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@ -238,12 +239,14 @@ extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample;
// Returns an RAII sampling handle that manages registration and unregistation
// with the global sampler.
inline HashtablezInfoHandle Sample() {
inline HashtablezInfoHandle Sample(
size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) {
return HashtablezInfoHandle(nullptr);
}
return HashtablezInfoHandle(SampleSlow(&global_next_sample));
return HashtablezInfoHandle(
SampleSlow(&global_next_sample, inline_element_size));
#else
return HashtablezInfoHandle(nullptr);
#endif // !ABSL_PER_THREAD_TLS

@ -78,10 +78,12 @@ HashtablezInfo* Register(HashtablezSampler* s, size_t size) {
TEST(HashtablezInfoTest, PrepareForSampling) {
absl::Time test_start = absl::Now();
const size_t test_element_size = 17;
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
info.inline_element_size = test_element_size;
EXPECT_EQ(info.capacity.load(), 0);
EXPECT_EQ(info.size.load(), 0);
EXPECT_EQ(info.num_erases.load(), 0);
@ -93,6 +95,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
EXPECT_EQ(info.max_reserve.load(), 0);
EXPECT_GE(info.create_time, test_start);
EXPECT_EQ(info.inline_element_size, test_element_size);
info.capacity.store(1, std::memory_order_relaxed);
info.size.store(1, std::memory_order_relaxed);
@ -116,6 +119,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
EXPECT_EQ(info.max_reserve.load(), 0);
EXPECT_EQ(info.inline_element_size, test_element_size);
EXPECT_GE(info.create_time, test_start);
}
@ -154,9 +158,11 @@ TEST(HashtablezInfoTest, RecordInsert) {
}
TEST(HashtablezInfoTest, RecordErase) {
const size_t test_element_size = 29;
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
info.inline_element_size = test_element_size;
EXPECT_EQ(info.num_erases.load(), 0);
EXPECT_EQ(info.size.load(), 0);
RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
@ -164,12 +170,15 @@ TEST(HashtablezInfoTest, RecordErase) {
RecordEraseSlow(&info);
EXPECT_EQ(info.size.load(), 0);
EXPECT_EQ(info.num_erases.load(), 1);
EXPECT_EQ(info.inline_element_size, test_element_size);
}
TEST(HashtablezInfoTest, RecordRehash) {
const size_t test_element_size = 31;
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
info.inline_element_size = test_element_size;
RecordInsertSlow(&info, 0x1, 0);
RecordInsertSlow(&info, 0x2, kProbeLength);
RecordInsertSlow(&info, 0x4, kProbeLength);
@ -188,6 +197,7 @@ TEST(HashtablezInfoTest, RecordRehash) {
EXPECT_EQ(info.total_probe_length.load(), 3);
EXPECT_EQ(info.num_erases.load(), 0);
EXPECT_EQ(info.num_rehashes.load(), 1);
EXPECT_EQ(info.inline_element_size, test_element_size);
}
TEST(HashtablezInfoTest, RecordReservation) {
@ -208,12 +218,13 @@ TEST(HashtablezInfoTest, RecordReservation) {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
TEST(HashtablezSamplerTest, SmallSampleParameter) {
const size_t test_element_size = 31;
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
for (int i = 0; i < 1000; ++i) {
int64_t next_sample = 0;
HashtablezInfo* sample = SampleSlow(&next_sample);
HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
EXPECT_GT(next_sample, 0);
EXPECT_NE(sample, nullptr);
UnsampleSlow(sample);
@ -221,12 +232,13 @@ TEST(HashtablezSamplerTest, SmallSampleParameter) {
}
TEST(HashtablezSamplerTest, LargeSampleParameter) {
const size_t test_element_size = 31;
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
for (int i = 0; i < 1000; ++i) {
int64_t next_sample = 0;
HashtablezInfo* sample = SampleSlow(&next_sample);
HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
EXPECT_GT(next_sample, 0);
EXPECT_NE(sample, nullptr);
UnsampleSlow(sample);
@ -234,13 +246,14 @@ TEST(HashtablezSamplerTest, LargeSampleParameter) {
}
TEST(HashtablezSamplerTest, Sample) {
const size_t test_element_size = 31;
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
int64_t num_sampled = 0;
int64_t total = 0;
double sample_rate = 0.0;
for (int i = 0; i < 1000000; ++i) {
HashtablezInfoHandle h = Sample();
HashtablezInfoHandle h = Sample(test_element_size);
++total;
if (HashtablezInfoHandlePeer::IsSampled(h)) {
++num_sampled;

@ -1643,7 +1643,7 @@ class raw_hash_set {
// bound more carefully.
if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value &&
slots_ == nullptr) {
infoz() = Sample();
infoz() = Sample(sizeof(slot_type));
}
char* mem = static_cast<char*>(Allocate<alignof(slot_type)>(

@ -2075,6 +2075,7 @@ TEST(RawHashSamplerTest, Sample) {
std::memory_order_relaxed)]++;
reservations[info.max_reserve.load(std::memory_order_relaxed)]++;
}
EXPECT_EQ(info.inline_element_size, sizeof(int64_t));
++end_size;
});

@ -0,0 +1,114 @@
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/container/node_hash_map.h"
#include "absl/container/node_hash_set.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
// Create some tables of type `Table`, then look at all the new
// `HashtablezInfo`s to make sure that the `inline_element_size ==
// expected_element_size`. The `inline_element_size` is the amount of memory
// allocated for each slot of a hash table, that is `sizeof(slot_type)`. Add
// the new `HashtablezInfo`s to `preexisting_info`. Store all the new tables
// into `tables`.
template <class Table>
void TestInlineElementSize(
HashtablezSampler& sampler,
// clang-tidy gives a false positive on this declaration. This unordered
// set cannot be flat_hash_set, however, since that would introduce a mutex
// deadlock.
std::unordered_set<const HashtablezInfo*>& preexisting_info, // NOLINT
std::vector<Table>& tables, const typename Table::value_type& elt,
size_t expected_element_size) {
for (int i = 0; i < 10; ++i) {
// We create a new table and must store it somewhere so that when we store
// a pointer to the resulting `HashtablezInfo` into `preexisting_info`
// that we aren't storing a dangling pointer.
tables.emplace_back();
// We must insert an element to get a hashtablez to instantiate.
tables.back().insert(elt);
}
size_t new_count = 0;
sampler.Iterate([&](const HashtablezInfo& info) {
if (preexisting_info.insert(&info).second) {
EXPECT_EQ(info.inline_element_size, expected_element_size);
++new_count;
}
});
// Make sure we actually did get a new hashtablez.
EXPECT_GT(new_count, 0);
}
struct bigstruct {
char a[1000];
friend bool operator==(const bigstruct& x, const bigstruct& y) {
return memcmp(x.a, y.a, sizeof(x.a)) == 0;
}
template <typename H>
friend H AbslHashValue(H h, const bigstruct& c) {
return H::combine_contiguous(std::move(h), c.a, sizeof(c.a));
}
};
#endif
TEST(FlatHashMap, SampleElementSize) {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
// Enable sampling even if the prod default is off.
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(1);
auto& sampler = GlobalHashtablezSampler();
std::vector<flat_hash_map<int, bigstruct>> flat_map_tables;
std::vector<flat_hash_set<bigstruct>> flat_set_tables;
std::vector<node_hash_map<int, bigstruct>> node_map_tables;
std::vector<node_hash_set<bigstruct>> node_set_tables;
// It takes thousands of new tables after changing the sampling parameters
// before you actually get some instrumentation. And if you must actually
// put something into those tables.
for (int i = 0; i < 10000; ++i) {
flat_map_tables.emplace_back();
flat_map_tables.back()[i] = bigstruct{};
}
// clang-tidy gives a false positive on this declaration. This unordered set
// cannot be a flat_hash_set, however, since that would introduce a mutex
// deadlock.
std::unordered_set<const HashtablezInfo*> preexisting_info; // NOLINT
sampler.Iterate(
[&](const HashtablezInfo& info) { preexisting_info.insert(&info); });
TestInlineElementSize(sampler, preexisting_info, flat_map_tables,
{0, bigstruct{}}, sizeof(int) + sizeof(bigstruct));
TestInlineElementSize(sampler, preexisting_info, node_map_tables,
{0, bigstruct{}}, sizeof(void*));
TestInlineElementSize(sampler, preexisting_info, flat_set_tables, //
bigstruct{}, sizeof(bigstruct));
TestInlineElementSize(sampler, preexisting_info, node_set_tables, //
bigstruct{}, sizeof(void*));
#endif
}
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END
} // namespace absl

@ -23,9 +23,6 @@ namespace {
using absl::random_internal::Randen;
// Local state parameters.
constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t);
TEST(RandenTest, CopyAndMove) {
static_assert(std::is_copy_constructible<Randen>::value,
"Randen must be copy constructible");
@ -41,30 +38,38 @@ TEST(RandenTest, CopyAndMove) {
}
TEST(RandenTest, Default) {
constexpr uint64_t kGolden[] = {
0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977,
0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912,
0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6,
0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a,
0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37,
0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556,
0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42,
0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc,
0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f,
0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3,
0x026ff374c101da7e, 0x811ef0821c3de851,
constexpr uint8_t kGolden[] = {
0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d,
0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3,
0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f,
0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0,
0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff,
0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2,
0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5,
0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c,
0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56,
0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4,
0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91,
0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5,
0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d,
0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58,
0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e,
0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49,
0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0,
0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6,
0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61,
0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35,
0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c,
0x82, 0xf0, 0x1e, 0x81,
};
alignas(16) uint64_t state[kStateSizeT];
alignas(16) uint8_t state[Randen::kStateBytes];
std::memset(state, 0, sizeof(state));
Randen r;
r.Generate(state);
auto id = std::begin(state);
for (const auto& elem : kGolden) {
EXPECT_EQ(elem, *id++);
}
EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state)));
}
} // namespace

@ -778,9 +778,9 @@ class Condition {
//
// Usage to wake T is:
// mu.Lock();
// // process data, possibly establishing C
// if (C) { cv->Signal(); }
// mu.Unlock();
// // process data, possibly establishing C
// if (C) { cv->Signal(); }
// mu.Unlock();
//
// If C may be useful to more than one waiter, use `SignalAll()` instead of
// `Signal()`.

@ -39,6 +39,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <functional>
#include <memory>
#include <sstream>
@ -649,7 +650,7 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
// Open the zoneinfo file.
auto fp = FOpen(path.c_str(), "rb");
if (fp.get() == nullptr) return nullptr;
if (fp == nullptr) return nullptr;
return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(std::move(fp)));
}
@ -674,7 +675,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
"/system/usr/share/zoneinfo/tzdata"}) {
auto fp = FOpen(tzdata, "rb");
if (fp.get() == nullptr) continue;
if (fp == nullptr) continue;
char hbuf[24]; // covers header.zonetab_offset too
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
@ -708,6 +709,69 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
return nullptr;
}
// A zoneinfo source for use inside Fuchsia components. This attempts to
// read zoneinfo files from one of several known paths in a component's
// incoming namespace. [Config data][1] is preferred, but package-specific
// resources are also supported.
//
// Fuchsia's implementation supports `FileZoneInfoSource::Version()`.
//
// [1]:
// https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component
class FuchsiaZoneInfoSource : public FileZoneInfoSource {
public:
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
std::string Version() const override { return version_; }
private:
explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version)
: FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {}
std::string version_;
};
std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open(
const std::string& name) {
// Use of the "file:" prefix is intended for testing purposes only.
const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
// Prefixes where a Fuchsia component might find zoneinfo files,
// in descending order of preference.
const auto kTzdataPrefixes = {
"/config/data/tzdata/",
"/pkg/data/tzdata/",
"/data/tzdata/",
};
const auto kEmptyPrefix = {""};
const bool name_absolute = (pos != name.size() && name[pos] == '/');
const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes;
// Fuchsia builds place zoneinfo files at "<prefix><format><name>".
for (const std::string prefix : prefixes) {
std::string path = prefix;
if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format
path.append(name, pos, std::string::npos);
auto fp = FOpen(path.c_str(), "rb");
if (fp == nullptr) continue;
std::string version;
if (!prefix.empty()) {
// Fuchsia builds place the version in "<prefix>revision.txt".
std::ifstream version_stream(prefix + "revision.txt");
if (version_stream.is_open()) {
// revision.txt should contain no newlines, but to be
// defensive we read just the first line.
std::getline(version_stream, version);
}
}
return std::unique_ptr<ZoneInfoSource>(
new FuchsiaZoneInfoSource(std::move(fp), std::move(version)));
}
return nullptr;
}
} // namespace
bool TimeZoneInfo::Load(const std::string& name) {
@ -725,6 +789,7 @@ bool TimeZoneInfo::Load(const std::string& name) {
name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> {
if (auto z = FileZoneInfoSource::Open(n)) return z;
if (auto z = AndroidZoneInfoSource::Open(n)) return z;
if (auto z = FuchsiaZoneInfoSource::Open(n)) return z;
return nullptr;
});
return zip != nullptr && Load(zip.get());

@ -28,6 +28,13 @@
#include <vector>
#endif
#if defined(__Fuchsia__)
#include <fuchsia/intl/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/sys/cpp/component_context.h>
#include <zircon/types.h>
#endif
#include <cstdlib>
#include <cstring>
#include <string>
@ -140,6 +147,48 @@ time_zone local_time_zone() {
}
CFRelease(tz_default);
#endif
#if defined(__Fuchsia__)
std::string primary_tz;
[&]() {
// Note: We can't use the synchronous FIDL API here because it doesn't
// allow timeouts; if the FIDL call failed, local_time_zone() would never
// return.
const zx::duration kTimeout = zx::msec(500);
// Don't attach to the thread because otherwise the thread's dispatcher
// would be set to null when the loop is destroyed, causing any other FIDL
// code running on the same thread to crash.
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
std::unique_ptr<sys::ComponentContext> context =
sys::ComponentContext::Create();
fuchsia::intl::PropertyProviderHandle handle;
zx_status_t status = context->svc()->Connect(handle.NewRequest());
if (status != ZX_OK) {
return;
}
fuchsia::intl::PropertyProviderPtr intl_provider;
status = intl_provider.Bind(std::move(handle), loop.dispatcher());
if (status != ZX_OK) {
return;
}
intl_provider->GetProfile(
[&loop, &primary_tz](fuchsia::intl::Profile profile) {
if (!profile.time_zones().empty()) {
primary_tz = profile.time_zones()[0].id;
}
loop.Quit();
});
loop.Run(zx::deadline_after(kTimeout));
}();
if (!primary_tz.empty()) {
zone = primary_tz.c_str();
}
#endif
// Allow ${TZ} to override to default zone.
char* tz_env = nullptr;

@ -1027,7 +1027,11 @@ TEST(MakeTime, SysSecondsLimits) {
#endif
const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut);
#if defined(__Fuchsia__)
// Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527).
#else
EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
#endif
#endif
}
}

Loading…
Cancel
Save