Roll chromium_revision c92ed25217..bcf2616e8e (559015:559838)

Change log: c92ed25217..bcf2616e8e
Full diff: c92ed25217..bcf2616e8e

Roll chromium third_party 51c08cf9af..9d65a3cdda
Change log: 51c08cf9af..9d65a3cdda

Changed dependencies:
* src/base: a7a2409f9b..b802985ef4
* src/build: 03f39fd800..fc8308f6b6
* src/buildtools: a9e946f166..94288c26d2
* src/ios: e070a93062..289c450460
* src/testing: f5b31b58c6..a5fce03148
* src/third_party/catapult: https://chromium.googlesource.com/catapult.git/+log/d8600ccc2d..ce9b3742a1
* src/third_party/depot_tools: 8de3800ce5..8fe4d8cbef
* src/third_party/googletest/src: 045e7f9ee4..08d5b1f33a
* src/tools: e024720629..6e6e398687
* src/tools/swarming_client: 88229872dd..833f5ebf89
DEPS diff: c92ed25217..bcf2616e8e/DEPS

No update to Clang.

TBR=buildbot@webrtc.org,
BUG=None
CQ_INCLUDE_TRYBOTS=master.internal.tryserver.corp.webrtc:linux_internal

Change-Id: I22bf301fcec0103a1987a92f95ebf86e324dade7
Reviewed-on: https://webrtc-review.googlesource.com/77625
Reviewed-by: WebRTC Buildbot <buildbot@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23296}
This commit is contained in:
Autoroller 2018-05-18 01:44:20 -07:00 committed by Commit Bot
parent c948fe62fd
commit 460f53bb86
105 changed files with 3393 additions and 656 deletions

24
DEPS
View File

@ -10,12 +10,12 @@ vars = {
'checkout_configuration': 'default', 'checkout_configuration': 'default',
'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration == "default"', 'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration == "default"',
'webrtc_git': 'https://webrtc.googlesource.com', 'webrtc_git': 'https://webrtc.googlesource.com',
'chromium_revision': 'c92ed252174bc9b9ece9d11a83852f4ab8765575', 'chromium_revision': 'bcf2616e8eb74191a038c9ad457344fa0354f420',
'boringssl_git': 'https://boringssl.googlesource.com', 'boringssl_git': 'https://boringssl.googlesource.com',
# Three lines of non-changing comments so that # Three lines of non-changing comments so that
# the commit queue can handle CLs rolling swarming_client # the commit queue can handle CLs rolling swarming_client
# and whatever else without interference from each other. # and whatever else without interference from each other.
'swarming_revision': '88229872dd17e71658fe96763feaa77915d8cbd6', 'swarming_revision': '833f5ebf894be1e3e6d13678d5de8479bf12ff28',
# Three lines of non-changing comments so that # Three lines of non-changing comments so that
# the commit queue can handle CLs rolling BoringSSL # the commit queue can handle CLs rolling BoringSSL
# and whatever else without interference from each other. # and whatever else without interference from each other.
@ -27,7 +27,7 @@ vars = {
# Three lines of non-changing comments so that # Three lines of non-changing comments so that
# the commit queue can handle CLs rolling catapult # the commit queue can handle CLs rolling catapult
# and whatever else without interference from each other. # and whatever else without interference from each other.
'catapult_revision': 'd8600ccc2ddeb2cc43ba6adccea9f687f5f1b2a9', 'catapult_revision': 'ce9b3742a10dc2f4c796a1b26e73155d0bfa674a',
# Three lines of non-changing comments so that # Three lines of non-changing comments so that
# the commit queue can handle CLs rolling libFuzzer # the commit queue can handle CLs rolling libFuzzer
# and whatever else without interference from each other. # and whatever else without interference from each other.
@ -43,17 +43,17 @@ vars = {
# Three lines of non-changing comments so that # Three lines of non-changing comments so that
# the commit queue can handle CLs rolling Chromium third_party # the commit queue can handle CLs rolling Chromium third_party
# and whatever else without interference from each other. # and whatever else without interference from each other.
'chromium_third_party_revision': '51c08cf9af330cc4173d8e37974e49074431d5f4', 'chromium_third_party_revision': '9d65a3cddae9b8340d4c77b1ae9034d4501935b8',
} }
deps = { deps = {
# TODO(kjellander): Move this to be Android-only once the libevent dependency # TODO(kjellander): Move this to be Android-only once the libevent dependency
# in base/third_party/libevent is solved. # in base/third_party/libevent is solved.
'src/base': 'src/base':
Var('chromium_git') + '/chromium/src/base' + '@' + 'a7a2409f9b398da1abc5e7604a974a1fa2ade51d', Var('chromium_git') + '/chromium/src/base' + '@' + 'b802985ef4661601e825640d1a566489cfcb6d8b',
'src/build': 'src/build':
Var('chromium_git') + '/chromium/src/build' + '@' + '03f39fd800ed8fd5fa1a8b43b5963c6fc063475b', Var('chromium_git') + '/chromium/src/build' + '@' + 'fc8308f6b6c6212c6be8d2769c721611d5253b49',
'src/buildtools': 'src/buildtools':
Var('chromium_git') + '/chromium/buildtools.git' + '@' + 'a9e946f166b73f9dc170129f6586a1e68efb0ab3', Var('chromium_git') + '/chromium/buildtools.git' + '@' + '94288c26d2ffe3aec9848c147839afee597acefd',
# Gradle 4.3-rc4. Used for testing Android Studio project generation for WebRTC. # Gradle 4.3-rc4. Used for testing Android Studio project generation for WebRTC.
'src/examples/androidtests/third_party/gradle': { 'src/examples/androidtests/third_party/gradle': {
'url': Var('chromium_git') + '/external/github.com/gradle/gradle.git' + '@' + 'url': Var('chromium_git') + '/external/github.com/gradle/gradle.git' + '@' +
@ -61,11 +61,11 @@ deps = {
'condition': 'checkout_android', 'condition': 'checkout_android',
}, },
'src/ios': { 'src/ios': {
'url': Var('chromium_git') + '/chromium/src/ios' + '@' + 'e070a9306253c327361423e1fce2ae02c419f6ba', 'url': Var('chromium_git') + '/chromium/src/ios' + '@' + '289c450460f6a2faf57d1ee011ef7595691dd25b',
'condition': 'checkout_ios', 'condition': 'checkout_ios',
}, },
'src/testing': 'src/testing':
Var('chromium_git') + '/chromium/src/testing' + '@' + 'f5b31b58c69ca3bbcfe81d8c2028063931f6681e', Var('chromium_git') + '/chromium/src/testing' + '@' + 'a5fce03148805a479c78b685b31f5a2392218ffd',
# This entry is used for chromium third_party rolling into webrtc third_party only. # This entry is used for chromium third_party rolling into webrtc third_party only.
'src/third_party_chromium': { 'src/third_party_chromium': {
'url': Var('chromium_git') + '/chromium/src/third_party' + '@' + Var('chromium_third_party_revision'), 'url': Var('chromium_git') + '/chromium/src/third_party' + '@' + Var('chromium_third_party_revision'),
@ -94,7 +94,7 @@ deps = {
'src/third_party/colorama/src': 'src/third_party/colorama/src':
Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8', Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8',
'src/third_party/depot_tools': 'src/third_party/depot_tools':
Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8de3800ce55ba459ffcbedcfa52ef5e6e59caab6', Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8fe4d8cbef3bff9d615de14d9a414679cf9ca8c3',
'src/third_party/errorprone/lib': { 'src/third_party/errorprone/lib': {
'url': Var('chromium_git') + '/chromium/third_party/errorprone.git' + '@' + '980d49e839aa4984015efed34b0134d4b2c9b6d7', 'url': Var('chromium_git') + '/chromium/third_party/errorprone.git' + '@' + '980d49e839aa4984015efed34b0134d4b2c9b6d7',
'condition': 'checkout_android', 'condition': 'checkout_android',
@ -113,7 +113,7 @@ deps = {
'src/third_party/gtest-parallel': 'src/third_party/gtest-parallel':
Var('chromium_git') + '/external/github.com/google/gtest-parallel' + '@' + 'cb3514a0858be0f66281d892e2242d1073fd75fe', Var('chromium_git') + '/external/github.com/google/gtest-parallel' + '@' + 'cb3514a0858be0f66281d892e2242d1073fd75fe',
'src/third_party/googletest/src': 'src/third_party/googletest/src':
Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + '045e7f9ee4f969ac1a3fe428f79c4b880f0aff43', Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + '08d5b1f33af8c18785fb8ca02792b5fac81e248f',
'src/third_party/icu': { 'src/third_party/icu': {
'url': Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'f61e46dbee9d539a32551493e3bcc1dea92f83ec', 'url': Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'f61e46dbee9d539a32551493e3bcc1dea92f83ec',
}, },
@ -175,7 +175,7 @@ deps = {
'src/third_party/yasm/source/patched-yasm': 'src/third_party/yasm/source/patched-yasm':
Var('chromium_git') + '/chromium/deps/yasm/patched-yasm.git' + '@' + 'b98114e18d8b9b84586b10d24353ab8616d4c5fc', Var('chromium_git') + '/chromium/deps/yasm/patched-yasm.git' + '@' + 'b98114e18d8b9b84586b10d24353ab8616d4c5fc',
'src/tools': 'src/tools':
Var('chromium_git') + '/chromium/src/tools' + '@' + 'e0247206293ec06cc0aee950384e13721cd55d86', Var('chromium_git') + '/chromium/src/tools' + '@' + '6e6e39868794fc2bfeaf64502ad903be79616a0b',
'src/tools/gyp': 'src/tools/gyp':
Var('chromium_git') + '/external/gyp.git' + '@' + 'd61a9397e668fa9843c4aa7da9e79460fe590bfb', Var('chromium_git') + '/external/gyp.git' + '@' + 'd61a9397e668fa9843c4aa7da9e79460fe590bfb',
'src/tools/swarming_client': 'src/tools/swarming_client':

View File

@ -16,6 +16,11 @@
include(CMakeParseArguments) include(CMakeParseArguments)
# The IDE folder for Abseil that will be used if Abseil is included in a CMake
# project that sets
# set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# For example, Visual Studio supports folders.
set(ABSL_IDE_FOLDER Abseil)
# #
# create a library in the absl namespace # create a library in the absl namespace
@ -49,6 +54,8 @@ function(absl_library)
PUBLIC ${ABSL_COMMON_INCLUDE_DIRS} ${ABSL_LIB_PUBLIC_INCLUDE_DIRS} PUBLIC ${ABSL_COMMON_INCLUDE_DIRS} ${ABSL_LIB_PUBLIC_INCLUDE_DIRS}
PRIVATE ${ABSL_LIB_PRIVATE_INCLUDE_DIRS} PRIVATE ${ABSL_LIB_PRIVATE_INCLUDE_DIRS}
) )
# Add all Abseil targets to a a folder in the IDE for organization.
set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER})
if(ABSL_LIB_EXPORT_NAME) if(ABSL_LIB_EXPORT_NAME)
add_library(absl::${ABSL_LIB_EXPORT_NAME} ALIAS ${_NAME}) add_library(absl::${ABSL_LIB_EXPORT_NAME} ALIAS ${_NAME})
@ -93,6 +100,9 @@ function(absl_header_library)
PRIVATE ${ABSL_HO_LIB_PRIVATE_INCLUDE_DIRS} PRIVATE ${ABSL_HO_LIB_PRIVATE_INCLUDE_DIRS}
) )
# Add all Abseil targets to a a folder in the IDE for organization.
set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER})
if(ABSL_HO_LIB_EXPORT_NAME) if(ABSL_HO_LIB_EXPORT_NAME)
add_library(absl::${ABSL_HO_LIB_EXPORT_NAME} ALIAS ${_NAME}) add_library(absl::${ABSL_HO_LIB_EXPORT_NAME} ALIAS ${_NAME})
endif() endif()
@ -139,7 +149,10 @@ function(absl_test)
PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}
) )
add_test(${_NAME}_test ${_NAME}_bin) # Add all Abseil targets to a a folder in the IDE for organization.
set_property(TARGET ${_NAME}_bin PROPERTY FOLDER ${ABSL_IDE_FOLDER})
add_test(${_NAME} ${_NAME}_bin)
endif(BUILD_TESTING) endif(BUILD_TESTING)
endfunction() endfunction()

View File

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 2.8.2)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@ -0,0 +1,32 @@
# Downloads and unpacks googletest at configure time. Based on the instructions
# at https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project
# Download the latest googletest from Github master
configure_file(
${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in
googletest-download/CMakeLists.txt
)
# Configure and build the downloaded googletest source
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download)
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
# Prevent overriding the parent project's compiler/linker settings on Windows
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Add googletest directly to our build. This defines the gtest and gtest_main
# targets.
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
${CMAKE_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)

View File

@ -52,14 +52,42 @@ if(MSVC)
add_definitions(/DNOMINMAX /DWIN32_LEAN_AND_MEAN=1 /D_CRT_SECURE_NO_WARNINGS) add_definitions(/DNOMINMAX /DWIN32_LEAN_AND_MEAN=1 /D_CRT_SECURE_NO_WARNINGS)
endif() endif()
add_subdirectory(googletest)
add_subdirectory(cctz)
add_subdirectory(abseil-cpp) add_subdirectory(abseil-cpp)
add_executable(my_exe source.cpp) add_executable(my_exe source.cpp)
target_link_libraries(my_exe absl::base absl::synchronization absl::strings) target_link_libraries(my_exe absl::base absl::synchronization absl::strings)
``` ```
### Running Abseil Tests with CMake
Use the `-DABSL_RUN_TESTS=ON` flag to run Abseil tests. Note that if the `-DBUILD_TESTING=OFF` flag is passed then Abseil tests will not be run.
You will need to provide Abseil with a Googletest dependency. There are two
options for how to do this:
* Use `-DABSL_USE_GOOGLETEST_HEAD`. This will automatically download the latest
Googletest source into the build directory at configure time. Googletest will
then be compiled directly alongside Abseil's tests.
* Manually integrate Googletest with your build. See
https://github.com/google/googletest/blob/master/googletest/README.md#using-cmake
for more information on using Googletest in a CMake project.
For example, to run just the Abseil tests, you could use this script:
```
cd path/to/abseil-cpp
mkdir build
cd build
cmake -DABSL_USE_GOOGLETEST_HEAD=ON -DABSL_RUN_TESTS=ON ..
make -j
ctest
```
Currently, we only run our tests with CMake in a Linux environment, but we are
working on the rest of our supported platforms. See
https://github.com/abseil/abseil-cpp/projects/1 and
https://github.com/abseil/abseil-cpp/issues/109 for more information.
### Available Abseil CMake Public Targets ### Available Abseil CMake Public Targets
Here's a non-exhaustive list of Abseil CMake public targets: Here's a non-exhaustive list of Abseil CMake public targets:

View File

@ -16,9 +16,6 @@
cmake_minimum_required(VERSION 2.8.12) cmake_minimum_required(VERSION 2.8.12)
project(absl) project(absl)
# enable ctest
include(CTest)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake)
include(GNUInstallDirs) include(GNUInstallDirs)
@ -56,7 +53,6 @@ list(APPEND ABSL_COMMON_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
# -std=X # -std=X
set(CMAKE_CXX_FLAGS "${ABSL_STD_CXX_FLAG} ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "${ABSL_STD_CXX_FLAG} ${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_WARNING_VLA} ${CMAKE_CXX_FLAGS} ")
# -fexceptions # -fexceptions
set(ABSL_EXCEPTIONS_FLAG "${CMAKE_CXX_EXCEPTIONS}") set(ABSL_EXCEPTIONS_FLAG "${CMAKE_CXX_EXCEPTIONS}")
@ -65,12 +61,25 @@ set(ABSL_EXCEPTIONS_FLAG "${CMAKE_CXX_EXCEPTIONS}")
## pthread ## pthread
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
# commented: used only for standalone test option(ABSL_USE_GOOGLETEST_HEAD
# Don't remove these or else CMake CI will break "If ON, abseil will download HEAD from googletest at config time." OFF)
#add_subdirectory(googletest)
option(ABSL_RUN_TESTS "If ON, Abseil tests will be run." OFF)
if(${ABSL_RUN_TESTS})
# enable CTest. This will set BUILD_TESTING to ON unless otherwise specified
# on the command line
include(CTest)
enable_testing()
endif()
## check targets ## check targets
if(BUILD_TESTING) if(BUILD_TESTING)
if(${ABSL_USE_GOOGLETEST_HEAD})
include(CMake/DownloadGTest.cmake)
endif()
check_target(gtest) check_target(gtest)
check_target(gtest_main) check_target(gtest_main)
check_target(gmock) check_target(gmock)

View File

@ -4,7 +4,7 @@ URL: https://github.com/abseil/abseil-cpp
License: Apache 2.0 License: Apache 2.0
License File: LICENSE License File: LICENSE
Version: 0 Version: 0
Revision: af7882601aad93ada881486eeaabc562f1733961 Revision: 30de20488bb88dc22d23521c5c222ec6d924e289
Security Critical: yes Security Critical: yes
Description: Description:

View File

@ -3,11 +3,11 @@ workspace(name = "com_google_absl")
http_archive( http_archive(
name = "bazel_toolchains", name = "bazel_toolchains",
urls = [ urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/r324073.tar.gz", "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/f8847f64e6950e8ab9fde1c0aba768550b0d9ab2.tar.gz",
"https://github.com/bazelbuild/bazel-toolchains/archive/r324073.tar.gz", "https://github.com/bazelbuild/bazel-toolchains/archive/f8847f64e6950e8ab9fde1c0aba768550b0d9ab2.tar.gz",
], ],
strip_prefix = "bazel-toolchains-r324073", strip_prefix = "bazel-toolchains-f8847f64e6950e8ab9fde1c0aba768550b0d9ab2",
sha256 = "71548c0d6cd53eddebbde4fa9962f5395e82645fb9992719e0890505b177f245", sha256 = "794366f51fea224b3656a0b0f8f1518e739748646523a572fcd3d68614a0e670",
) )
# GoogleTest/GoogleMock framework. Used by most unit-tests. # GoogleTest/GoogleMock framework. Used by most unit-tests.
@ -17,6 +17,13 @@ http_archive(
strip_prefix = "googletest-master", strip_prefix = "googletest-master",
) )
# Google benchmark.
http_archive(
name = "com_github_google_benchmark",
urls = ["https://github.com/google/benchmark/archive/master.zip"],
strip_prefix = "benchmark-master",
)
# RE2 regular-expression framework. Used by some unit-tests. # RE2 regular-expression framework. Used by some unit-tests.
http_archive( http_archive(
name = "com_googlesource_code_re2", name = "com_googlesource_code_re2",

View File

@ -41,6 +41,18 @@ cc_test(
], ],
) )
cc_test(
name = "algorithm_benchmark",
srcs = ["equal_benchmark.cc"],
copts = ABSL_TEST_COPTS,
tags = ["benchmark"],
deps = [
":algorithm",
"//absl/base:core_headers",
"@com_github_google_benchmark//:benchmark",
],
)
cc_library( cc_library(
name = "container", name = "container",
hdrs = [ hdrs = [

View File

@ -0,0 +1,128 @@
// Copyright 2017 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
//
// http://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 <cstdint>
#include <cstring>
#include "benchmark/benchmark.h"
#include "absl/algorithm/algorithm.h"
namespace {
// The range of sequence sizes to benchmark.
constexpr int kMinBenchmarkSize = 1024;
constexpr int kMaxBenchmarkSize = 8 * 1024 * 1024;
// A user-defined type for use in equality benchmarks. Note that we expect
// std::memcmp to win for this type: libstdc++'s std::equal only defers to
// memcmp for integral types. This is because it is not straightforward to
// guarantee that std::memcmp would produce a result "as-if" compared by
// operator== for other types (example gotchas: NaN floats, structs with
// padding).
struct EightBits {
explicit EightBits(int /* unused */) : data(0) {}
bool operator==(const EightBits& rhs) const { return data == rhs.data; }
uint8_t data;
};
template <typename T>
void BM_absl_equal_benchmark(benchmark::State& state) {
std::vector<T> xs(state.range(0), T(0));
std::vector<T> ys = xs;
while (state.KeepRunning()) {
const bool same = absl::equal(xs.begin(), xs.end(), ys.begin(), ys.end());
benchmark::DoNotOptimize(same);
}
}
template <typename T>
void BM_std_equal_benchmark(benchmark::State& state) {
std::vector<T> xs(state.range(0), T(0));
std::vector<T> ys = xs;
while (state.KeepRunning()) {
const bool same = std::equal(xs.begin(), xs.end(), ys.begin());
benchmark::DoNotOptimize(same);
}
}
template <typename T>
void BM_memcmp_benchmark(benchmark::State& state) {
std::vector<T> xs(state.range(0), T(0));
std::vector<T> ys = xs;
while (state.KeepRunning()) {
const bool same =
std::memcmp(xs.data(), ys.data(), xs.size() * sizeof(T)) == 0;
benchmark::DoNotOptimize(same);
}
}
// The expectation is that the compiler should be able to elide the equality
// comparison altogether for sufficiently simple types.
template <typename T>
void BM_absl_equal_self_benchmark(benchmark::State& state) {
std::vector<T> xs(state.range(0), T(0));
while (state.KeepRunning()) {
const bool same = absl::equal(xs.begin(), xs.end(), xs.begin(), xs.end());
benchmark::DoNotOptimize(same);
}
}
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint8_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint8_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint8_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint8_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint16_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint16_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint16_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint16_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint32_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint32_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint32_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint32_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint64_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint64_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint64_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint64_t)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, EightBits)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, EightBits)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, EightBits)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, EightBits)
->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
} // namespace
BENCHMARK_MAIN();

View File

@ -147,6 +147,18 @@ cc_library(
], ],
) )
cc_test(
name = "atomic_hook_test",
size = "small",
srcs = ["internal/atomic_hook_test.cc"],
copts = ABSL_TEST_COPTS,
deps = [
":base",
":core_headers",
"@com_google_googletest//:gtest_main",
],
)
cc_test( cc_test(
name = "bit_cast_test", name = "bit_cast_test",
size = "small", size = "small",
@ -393,3 +405,16 @@ cc_test(
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
], ],
) )
cc_test(
name = "thread_identity_benchmark",
srcs = ["internal/thread_identity_benchmark.cc"],
copts = ABSL_TEST_COPTS,
tags = ["benchmark"],
visibility = ["//visibility:private"],
deps = [
":base",
"//absl/synchronization",
"@com_github_google_benchmark//:benchmark",
],
)

View File

@ -99,14 +99,18 @@ absl_library(
if(BUILD_TESTING) if(BUILD_TESTING)
# exception-safety testing library # exception-safety testing library
set(EXCEPTION_SAFETY_TESTING_SRC "internal/exception_safety_testing.cc") set(EXCEPTION_SAFETY_TESTING_SRC
"internal/exception_safety_testing.h"
"internal/exception_safety_testing.cc"
)
set(EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES set(EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES
${ABSL_TEST_COMMON_LIBRARIES} ${ABSL_TEST_COMMON_LIBRARIES}
absl::base absl::base
absl::memory absl::memory
absl::meta absl::meta
absl::strings absl::strings
absl::types absl::optional
gtest
) )
absl_library( absl_library(
@ -116,6 +120,8 @@ absl_library(
${EXCEPTION_SAFETY_TESTING_SRC} ${EXCEPTION_SAFETY_TESTING_SRC}
PUBLIC_LIBRARIES PUBLIC_LIBRARIES
${EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES} ${EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES}
PRIVATE_COMPILE_FLAGS
${ABSL_EXCEPTIONS_FLAG}
) )
endif() endif()
@ -162,6 +168,20 @@ absl_library(
## TESTS ## TESTS
# #
# call once test
set(ATOMIC_HOOK_TEST_SRC "internal/atomic_hook_test.cc")
set(ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES absl::base)
absl_test(
TARGET
atomic_hook_test
SOURCES
${ATOMIC_HOOK_TEST_SRC}
PUBLIC_LIBRARIES
${ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES}
)
# call once test # call once test
set(CALL_ONCE_TEST_SRC "call_once_test.cc") set(CALL_ONCE_TEST_SRC "call_once_test.cc")
set(CALL_ONCE_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization) set(CALL_ONCE_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
@ -344,7 +364,14 @@ absl_test(
#test exceptions_safety_testing_test #test exceptions_safety_testing_test
set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc") set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc")
set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES absl::base absl::memory absl::meta absl::strings absl::optional) set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES
absl::base
absl_base_internal_exception_safety_testing
absl::memory
absl::meta
absl::strings
absl::optional
)
absl_test( absl_test(
TARGET TARGET

View File

@ -52,7 +52,8 @@
// Example: // Example:
// //
// // Enable branches in the Abseil code that are tagged for ASan: // // Enable branches in the Abseil code that are tagged for ASan:
// $ bazel -D ADDRESS_SANITIZER -fsanitize=address *target* // $ bazel build --copt=-DADDRESS_SANITIZER --copt=-fsanitize=address
// --linkopt=-fsanitize=address *target*
// //
// Since these macro names are only supported by GCC and Clang, we only check // Since these macro names are only supported by GCC and Clang, we only check
// for `__GNUC__` (GCC or Clang) and the above macros. // for `__GNUC__` (GCC or Clang) and the above macros.

View File

@ -32,7 +32,7 @@
/* Each function is empty and called (via a macro) only in debug mode. /* Each function is empty and called (via a macro) only in debug mode.
The arguments are captured by dynamic tools at runtime. */ The arguments are captured by dynamic tools at runtime. */
#if ABSL_DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 #if ABSL_DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 && !defined(__native_client__)
#if __has_feature(memory_sanitizer) #if __has_feature(memory_sanitizer)
#include <sanitizer/msan_interface.h> #include <sanitizer/msan_interface.h>

View File

@ -191,16 +191,16 @@
#elif defined(ABSL_ANNOTALYSIS_ENABLED) #elif defined(ABSL_ANNOTALYSIS_ENABLED)
#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ #define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
AbslStaticAnnotateIgnoreReadsBegin(__FILE__, __LINE__) StaticAnnotateIgnoreReadsBegin(__FILE__, __LINE__)
#define ABSL_ANNOTATE_IGNORE_READS_END() \ #define ABSL_ANNOTATE_IGNORE_READS_END() \
AbslStaticAnnotateIgnoreReadsEnd(__FILE__, __LINE__) StaticAnnotateIgnoreReadsEnd(__FILE__, __LINE__)
#define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() \ #define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() \
AbslStaticAnnotateIgnoreWritesBegin(__FILE__, __LINE__) StaticAnnotateIgnoreWritesBegin(__FILE__, __LINE__)
#define ABSL_ANNOTATE_IGNORE_WRITES_END() \ #define ABSL_ANNOTATE_IGNORE_WRITES_END() \
AbslStaticAnnotateIgnoreWritesEnd(__FILE__, __LINE__) StaticAnnotateIgnoreWritesEnd(__FILE__, __LINE__)
#else #else
#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() /* empty */ #define ABSL_ANNOTATE_IGNORE_READS_BEGIN() /* empty */
@ -282,13 +282,13 @@ void AbslAnnotateIgnoreWritesEnd(const char *file, int line);
allows IGNORE_READS_AND_WRITES to work properly. */ allows IGNORE_READS_AND_WRITES to work properly. */
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-function"
static inline void AbslStaticAnnotateIgnoreReadsBegin(const char *file, int line) static inline void StaticAnnotateIgnoreReadsBegin(const char *file, int line)
ABSL_ATTRIBUTE_IGNORE_READS_BEGIN { (void)file; (void)line; } ABSL_ATTRIBUTE_IGNORE_READS_BEGIN { (void)file; (void)line; }
static inline void AbslStaticAnnotateIgnoreReadsEnd(const char *file, int line) static inline void StaticAnnotateIgnoreReadsEnd(const char *file, int line)
ABSL_ATTRIBUTE_IGNORE_READS_END { (void)file; (void)line; } ABSL_ATTRIBUTE_IGNORE_READS_END { (void)file; (void)line; }
static inline void AbslStaticAnnotateIgnoreWritesBegin( static inline void StaticAnnotateIgnoreWritesBegin(
const char *file, int line) { (void)file; (void)line; } const char *file, int line) { (void)file; (void)line; }
static inline void AbslStaticAnnotateIgnoreWritesEnd( static inline void StaticAnnotateIgnoreWritesEnd(
const char *file, int line) { (void)file; (void)line; } const char *file, int line) { (void)file; (void)line; }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif

View File

@ -25,11 +25,13 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
namespace absl { namespace testing {
namespace { namespace {
using ::absl::exceptions_internal::SetCountdown;
using ::absl::exceptions_internal::TestException; using ::testing::exceptions_internal::SetCountdown;
using ::absl::exceptions_internal::UnsetCountdown; using ::testing::exceptions_internal::TestException;
using ::testing::exceptions_internal::UnsetCountdown;
// EXPECT_NO_THROW can't inspect the thrown inspection in general. // EXPECT_NO_THROW can't inspect the thrown inspection in general.
template <typename F> template <typename F>
@ -41,15 +43,7 @@ void ExpectNoThrow(const F& f) {
} }
} }
class ThrowingValueTest : public ::testing::Test { TEST(ThrowingValueTest, Throws) {
protected:
void SetUp() override { UnsetCountdown(); }
private:
ConstructorTracker clouseau_;
};
TEST_F(ThrowingValueTest, Throws) {
SetCountdown(); SetCountdown();
EXPECT_THROW(ThrowingValue<> bomb, TestException); EXPECT_THROW(ThrowingValue<> bomb, TestException);
@ -60,6 +54,8 @@ TEST_F(ThrowingValueTest, Throws) {
ExpectNoThrow([]() { ThrowingValue<> bomb; }); ExpectNoThrow([]() { ThrowingValue<> bomb; });
ExpectNoThrow([]() { ThrowingValue<> bomb; }); ExpectNoThrow([]() { ThrowingValue<> bomb; });
EXPECT_THROW(ThrowingValue<> bomb, TestException); EXPECT_THROW(ThrowingValue<> bomb, TestException);
UnsetCountdown();
} }
// Tests that an operation throws when the countdown is at 0, doesn't throw when // Tests that an operation throws when the countdown is at 0, doesn't throw when
@ -67,7 +63,6 @@ TEST_F(ThrowingValueTest, Throws) {
// ThrowingValue if it throws // ThrowingValue if it throws
template <typename F> template <typename F>
void TestOp(const F& f) { void TestOp(const F& f) {
UnsetCountdown();
ExpectNoThrow(f); ExpectNoThrow(f);
SetCountdown(); SetCountdown();
@ -75,7 +70,7 @@ void TestOp(const F& f) {
UnsetCountdown(); UnsetCountdown();
} }
TEST_F(ThrowingValueTest, ThrowingCtors) { TEST(ThrowingValueTest, ThrowingCtors) {
ThrowingValue<> bomb; ThrowingValue<> bomb;
TestOp([]() { ThrowingValue<> bomb(1); }); TestOp([]() { ThrowingValue<> bomb(1); });
@ -83,14 +78,35 @@ TEST_F(ThrowingValueTest, ThrowingCtors) {
TestOp([&]() { ThrowingValue<> bomb1 = std::move(bomb); }); TestOp([&]() { ThrowingValue<> bomb1 = std::move(bomb); });
} }
TEST_F(ThrowingValueTest, ThrowingAssignment) { TEST(ThrowingValueTest, ThrowingAssignment) {
ThrowingValue<> bomb, bomb1; ThrowingValue<> bomb, bomb1;
TestOp([&]() { bomb = bomb1; }); TestOp([&]() { bomb = bomb1; });
TestOp([&]() { bomb = std::move(bomb1); }); TestOp([&]() { bomb = std::move(bomb1); });
// Test that when assignment throws, the assignment should fail (lhs != rhs)
// and strong guarantee fails (lhs != lhs_copy).
{
ThrowingValue<> lhs(39), rhs(42);
ThrowingValue<> lhs_copy(lhs);
SetCountdown();
EXPECT_THROW(lhs = rhs, TestException);
UnsetCountdown();
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs_copy, lhs);
}
{
ThrowingValue<> lhs(39), rhs(42);
ThrowingValue<> lhs_copy(lhs), rhs_copy(rhs);
SetCountdown();
EXPECT_THROW(lhs = std::move(rhs), TestException);
UnsetCountdown();
EXPECT_NE(lhs, rhs_copy);
EXPECT_NE(lhs_copy, lhs);
}
} }
TEST_F(ThrowingValueTest, ThrowingComparisons) { TEST(ThrowingValueTest, ThrowingComparisons) {
ThrowingValue<> bomb1, bomb2; ThrowingValue<> bomb1, bomb2;
TestOp([&]() { return bomb1 == bomb2; }); TestOp([&]() { return bomb1 == bomb2; });
TestOp([&]() { return bomb1 != bomb2; }); TestOp([&]() { return bomb1 != bomb2; });
@ -100,7 +116,7 @@ TEST_F(ThrowingValueTest, ThrowingComparisons) {
TestOp([&]() { return bomb1 >= bomb2; }); TestOp([&]() { return bomb1 >= bomb2; });
} }
TEST_F(ThrowingValueTest, ThrowingArithmeticOps) { TEST(ThrowingValueTest, ThrowingArithmeticOps) {
ThrowingValue<> bomb1(1), bomb2(2); ThrowingValue<> bomb1(1), bomb2(2);
TestOp([&bomb1]() { +bomb1; }); TestOp([&bomb1]() { +bomb1; });
@ -118,7 +134,7 @@ TEST_F(ThrowingValueTest, ThrowingArithmeticOps) {
TestOp([&]() { bomb1 >> 1; }); TestOp([&]() { bomb1 >> 1; });
} }
TEST_F(ThrowingValueTest, ThrowingLogicalOps) { TEST(ThrowingValueTest, ThrowingLogicalOps) {
ThrowingValue<> bomb1, bomb2; ThrowingValue<> bomb1, bomb2;
TestOp([&bomb1]() { !bomb1; }); TestOp([&bomb1]() { !bomb1; });
@ -126,7 +142,7 @@ TEST_F(ThrowingValueTest, ThrowingLogicalOps) {
TestOp([&]() { bomb1 || bomb2; }); TestOp([&]() { bomb1 || bomb2; });
} }
TEST_F(ThrowingValueTest, ThrowingBitwiseOps) { TEST(ThrowingValueTest, ThrowingBitwiseOps) {
ThrowingValue<> bomb1, bomb2; ThrowingValue<> bomb1, bomb2;
TestOp([&bomb1]() { ~bomb1; }); TestOp([&bomb1]() { ~bomb1; });
@ -135,7 +151,7 @@ TEST_F(ThrowingValueTest, ThrowingBitwiseOps) {
TestOp([&]() { bomb1 ^ bomb2; }); TestOp([&]() { bomb1 ^ bomb2; });
} }
TEST_F(ThrowingValueTest, ThrowingCompoundAssignmentOps) { TEST(ThrowingValueTest, ThrowingCompoundAssignmentOps) {
ThrowingValue<> bomb1(1), bomb2(2); ThrowingValue<> bomb1(1), bomb2(2);
TestOp([&]() { bomb1 += bomb2; }); TestOp([&]() { bomb1 += bomb2; });
@ -149,7 +165,7 @@ TEST_F(ThrowingValueTest, ThrowingCompoundAssignmentOps) {
TestOp([&]() { bomb1 *= bomb2; }); TestOp([&]() { bomb1 *= bomb2; });
} }
TEST_F(ThrowingValueTest, ThrowingStreamOps) { TEST(ThrowingValueTest, ThrowingStreamOps) {
ThrowingValue<> bomb; ThrowingValue<> bomb;
TestOp([&]() { std::cin >> bomb; }); TestOp([&]() { std::cin >> bomb; });
@ -158,7 +174,6 @@ TEST_F(ThrowingValueTest, ThrowingStreamOps) {
template <typename F> template <typename F>
void TestAllocatingOp(const F& f) { void TestAllocatingOp(const F& f) {
UnsetCountdown();
ExpectNoThrow(f); ExpectNoThrow(f);
SetCountdown(); SetCountdown();
@ -166,62 +181,90 @@ void TestAllocatingOp(const F& f) {
UnsetCountdown(); UnsetCountdown();
} }
TEST_F(ThrowingValueTest, ThrowingAllocatingOps) { TEST(ThrowingValueTest, ThrowingAllocatingOps) {
// make_unique calls unqualified operator new, so these exercise the // make_unique calls unqualified operator new, so these exercise the
// ThrowingValue overloads. // ThrowingValue overloads.
TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>>(1); }); TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>>(1); });
TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>[]>(2); }); TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>[]>(2); });
} }
TEST_F(ThrowingValueTest, NonThrowingMoveCtor) { TEST(ThrowingValueTest, NonThrowingMoveCtor) {
ThrowingValue<NoThrow::kMoveCtor> nothrow_ctor; ThrowingValue<TypeSpec::kNoThrowMove> nothrow_ctor;
SetCountdown(); SetCountdown();
ExpectNoThrow([&nothrow_ctor]() { ExpectNoThrow([&nothrow_ctor]() {
ThrowingValue<NoThrow::kMoveCtor> nothrow1 = std::move(nothrow_ctor); ThrowingValue<TypeSpec::kNoThrowMove> nothrow1 = std::move(nothrow_ctor);
}); });
UnsetCountdown();
} }
TEST_F(ThrowingValueTest, NonThrowingMoveAssign) { TEST(ThrowingValueTest, NonThrowingMoveAssign) {
ThrowingValue<NoThrow::kMoveAssign> nothrow_assign1, nothrow_assign2; ThrowingValue<TypeSpec::kNoThrowMove> nothrow_assign1, nothrow_assign2;
SetCountdown(); SetCountdown();
ExpectNoThrow([&nothrow_assign1, &nothrow_assign2]() { ExpectNoThrow([&nothrow_assign1, &nothrow_assign2]() {
nothrow_assign1 = std::move(nothrow_assign2); nothrow_assign1 = std::move(nothrow_assign2);
}); });
UnsetCountdown();
} }
TEST_F(ThrowingValueTest, ThrowingSwap) { TEST(ThrowingValueTest, ThrowingCopyCtor) {
ThrowingValue<> tv;
TestOp([&]() { ThrowingValue<> tv_copy(tv); });
}
TEST(ThrowingValueTest, ThrowingCopyAssign) {
ThrowingValue<> tv1, tv2;
TestOp([&]() { tv1 = tv2; });
}
TEST(ThrowingValueTest, NonThrowingCopyCtor) {
ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_ctor;
SetCountdown();
ExpectNoThrow([&nothrow_ctor]() {
ThrowingValue<TypeSpec::kNoThrowCopy> nothrow1(nothrow_ctor);
});
UnsetCountdown();
}
TEST(ThrowingValueTest, NonThrowingCopyAssign) {
ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_assign1, nothrow_assign2;
SetCountdown();
ExpectNoThrow([&nothrow_assign1, &nothrow_assign2]() {
nothrow_assign1 = nothrow_assign2;
});
UnsetCountdown();
}
TEST(ThrowingValueTest, ThrowingSwap) {
ThrowingValue<> bomb1, bomb2; ThrowingValue<> bomb1, bomb2;
TestOp([&]() { std::swap(bomb1, bomb2); }); TestOp([&]() { std::swap(bomb1, bomb2); });
ThrowingValue<NoThrow::kMoveCtor> bomb3, bomb4;
TestOp([&]() { std::swap(bomb3, bomb4); });
ThrowingValue<NoThrow::kMoveAssign> bomb5, bomb6;
TestOp([&]() { std::swap(bomb5, bomb6); });
} }
TEST_F(ThrowingValueTest, NonThrowingSwap) { TEST(ThrowingValueTest, NonThrowingSwap) {
ThrowingValue<NoThrow::kMoveAssign | NoThrow::kMoveCtor> bomb1, bomb2; ThrowingValue<TypeSpec::kNoThrowMove> bomb1, bomb2;
ExpectNoThrow([&]() { std::swap(bomb1, bomb2); }); ExpectNoThrow([&]() { std::swap(bomb1, bomb2); });
} }
TEST_F(ThrowingValueTest, NonThrowingAllocation) { TEST(ThrowingValueTest, NonThrowingAllocation) {
ThrowingValue<NoThrow::kAllocation>* allocated; ThrowingValue<TypeSpec::kNoThrowNew>* allocated;
ThrowingValue<NoThrow::kAllocation>* array; ThrowingValue<TypeSpec::kNoThrowNew>* array;
ExpectNoThrow([&allocated]() { ExpectNoThrow([&allocated]() {
allocated = new ThrowingValue<NoThrow::kAllocation>(1); allocated = new ThrowingValue<TypeSpec::kNoThrowNew>(1);
delete allocated; delete allocated;
}); });
ExpectNoThrow([&array]() { ExpectNoThrow([&array]() {
array = new ThrowingValue<NoThrow::kAllocation>[2]; array = new ThrowingValue<TypeSpec::kNoThrowNew>[2];
delete[] array; delete[] array;
}); });
} }
TEST_F(ThrowingValueTest, NonThrowingDelete) { TEST(ThrowingValueTest, NonThrowingDelete) {
auto* allocated = new ThrowingValue<>(1); auto* allocated = new ThrowingValue<>(1);
auto* array = new ThrowingValue<>[2]; auto* array = new ThrowingValue<>[2];
@ -229,12 +272,14 @@ TEST_F(ThrowingValueTest, NonThrowingDelete) {
ExpectNoThrow([allocated]() { delete allocated; }); ExpectNoThrow([allocated]() { delete allocated; });
SetCountdown(); SetCountdown();
ExpectNoThrow([array]() { delete[] array; }); ExpectNoThrow([array]() { delete[] array; });
UnsetCountdown();
} }
using Storage = using Storage =
absl::aligned_storage_t<sizeof(ThrowingValue<>), alignof(ThrowingValue<>)>; absl::aligned_storage_t<sizeof(ThrowingValue<>), alignof(ThrowingValue<>)>;
TEST_F(ThrowingValueTest, NonThrowingPlacementDelete) { TEST(ThrowingValueTest, NonThrowingPlacementDelete) {
constexpr int kArrayLen = 2; constexpr int kArrayLen = 2;
// We intentionally create extra space to store the tag allocated by placement // We intentionally create extra space to store the tag allocated by placement
// new[]. // new[].
@ -256,16 +301,19 @@ TEST_F(ThrowingValueTest, NonThrowingPlacementDelete) {
for (int i = 0; i < kArrayLen; ++i) placed_array[i].~ThrowingValue<>(); for (int i = 0; i < kArrayLen; ++i) placed_array[i].~ThrowingValue<>();
ThrowingValue<>::operator delete[](placed_array, &array_buf); ThrowingValue<>::operator delete[](placed_array, &array_buf);
}); });
UnsetCountdown();
} }
TEST_F(ThrowingValueTest, NonThrowingDestructor) { TEST(ThrowingValueTest, NonThrowingDestructor) {
auto* allocated = new ThrowingValue<>(); auto* allocated = new ThrowingValue<>();
SetCountdown(); SetCountdown();
ExpectNoThrow([allocated]() { delete allocated; }); ExpectNoThrow([allocated]() { delete allocated; });
UnsetCountdown();
} }
TEST(ThrowingBoolTest, ThrowingBool) { TEST(ThrowingBoolTest, ThrowingBool) {
UnsetCountdown();
ThrowingBool t = true; ThrowingBool t = true;
// Test that it's contextually convertible to bool // Test that it's contextually convertible to bool
@ -276,15 +324,7 @@ TEST(ThrowingBoolTest, ThrowingBool) {
TestOp([&]() { (void)!t; }); TestOp([&]() { (void)!t; });
} }
class ThrowingAllocatorTest : public ::testing::Test { TEST(ThrowingAllocatorTest, MemoryManagement) {
protected:
void SetUp() override { UnsetCountdown(); }
private:
ConstructorTracker borlu_;
};
TEST_F(ThrowingAllocatorTest, MemoryManagement) {
// Just exercise the memory management capabilities under LSan to make sure we // Just exercise the memory management capabilities under LSan to make sure we
// don't leak. // don't leak.
ThrowingAllocator<int> int_alloc; ThrowingAllocator<int> int_alloc;
@ -293,24 +333,26 @@ TEST_F(ThrowingAllocatorTest, MemoryManagement) {
int* i_array = int_alloc.allocate(2); int* i_array = int_alloc.allocate(2);
int_alloc.deallocate(i_array, 2); int_alloc.deallocate(i_array, 2);
ThrowingAllocator<ThrowingValue<>> ef_alloc; ThrowingAllocator<ThrowingValue<>> tv_alloc;
ThrowingValue<>* efp = ef_alloc.allocate(1); ThrowingValue<>* ptr = tv_alloc.allocate(1);
ef_alloc.deallocate(efp, 1); tv_alloc.deallocate(ptr, 1);
ThrowingValue<>* ef_array = ef_alloc.allocate(2); ThrowingValue<>* tv_array = tv_alloc.allocate(2);
ef_alloc.deallocate(ef_array, 2); tv_alloc.deallocate(tv_array, 2);
} }
TEST_F(ThrowingAllocatorTest, CallsGlobalNew) { TEST(ThrowingAllocatorTest, CallsGlobalNew) {
ThrowingAllocator<ThrowingValue<>, NoThrow::kNoThrow> nothrow_alloc; ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate> nothrow_alloc;
ThrowingValue<>* ptr; ThrowingValue<>* ptr;
SetCountdown(); SetCountdown();
// This will only throw if ThrowingValue::new is called. // This will only throw if ThrowingValue::new is called.
ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); }); ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); });
nothrow_alloc.deallocate(ptr, 1); nothrow_alloc.deallocate(ptr, 1);
UnsetCountdown();
} }
TEST_F(ThrowingAllocatorTest, ThrowingConstructors) { TEST(ThrowingAllocatorTest, ThrowingConstructors) {
ThrowingAllocator<int> int_alloc; ThrowingAllocator<int> int_alloc;
int* ip = nullptr; int* ip = nullptr;
@ -323,22 +365,27 @@ TEST_F(ThrowingAllocatorTest, ThrowingConstructors) {
EXPECT_THROW(int_alloc.construct(ip, 2), TestException); EXPECT_THROW(int_alloc.construct(ip, 2), TestException);
EXPECT_EQ(*ip, 1); EXPECT_EQ(*ip, 1);
int_alloc.deallocate(ip, 1); int_alloc.deallocate(ip, 1);
UnsetCountdown();
} }
TEST_F(ThrowingAllocatorTest, NonThrowingConstruction) { TEST(ThrowingAllocatorTest, NonThrowingConstruction) {
{ {
ThrowingAllocator<int, NoThrow::kNoThrow> int_alloc; ThrowingAllocator<int, AllocSpec::kNoThrowAllocate> int_alloc;
int* ip = nullptr; int* ip = nullptr;
SetCountdown(); SetCountdown();
ExpectNoThrow([&]() { ip = int_alloc.allocate(1); }); ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
SetCountdown(); SetCountdown();
ExpectNoThrow([&]() { int_alloc.construct(ip, 2); }); ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
EXPECT_EQ(*ip, 2); EXPECT_EQ(*ip, 2);
int_alloc.deallocate(ip, 1); int_alloc.deallocate(ip, 1);
UnsetCountdown();
} }
UnsetCountdown();
{ {
ThrowingAllocator<int> int_alloc; ThrowingAllocator<int> int_alloc;
int* ip = nullptr; int* ip = nullptr;
@ -348,37 +395,45 @@ TEST_F(ThrowingAllocatorTest, NonThrowingConstruction) {
int_alloc.deallocate(ip, 1); int_alloc.deallocate(ip, 1);
} }
UnsetCountdown();
{ {
ThrowingAllocator<ThrowingValue<NoThrow::kIntCtor>, NoThrow::kNoThrow> ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate>
ef_alloc; nothrow_alloc;
ThrowingValue<NoThrow::kIntCtor>* efp; ThrowingValue<>* ptr;
SetCountdown(); SetCountdown();
ExpectNoThrow([&]() { efp = ef_alloc.allocate(1); }); ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); });
SetCountdown(); SetCountdown();
ExpectNoThrow([&]() { ef_alloc.construct(efp, 2); }); ExpectNoThrow(
EXPECT_EQ(efp->Get(), 2); [&]() { nothrow_alloc.construct(ptr, 2, testing::nothrow_ctor); });
ef_alloc.destroy(efp);
ef_alloc.deallocate(efp, 1); EXPECT_EQ(ptr->Get(), 2);
nothrow_alloc.destroy(ptr);
nothrow_alloc.deallocate(ptr, 1);
UnsetCountdown();
} }
UnsetCountdown();
{ {
ThrowingAllocator<int> a; ThrowingAllocator<int> a;
SetCountdown(); SetCountdown();
ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = a; }); ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = a; });
SetCountdown(); SetCountdown();
ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = std::move(a); }); ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = std::move(a); });
UnsetCountdown();
} }
} }
TEST_F(ThrowingAllocatorTest, ThrowingAllocatorConstruction) { TEST(ThrowingAllocatorTest, ThrowingAllocatorConstruction) {
ThrowingAllocator<int> a; ThrowingAllocator<int> a;
TestOp([]() { ThrowingAllocator<int> a; }); TestOp([]() { ThrowingAllocator<int> a; });
TestOp([&]() { a.select_on_container_copy_construction(); }); TestOp([&]() { a.select_on_container_copy_construction(); });
} }
TEST_F(ThrowingAllocatorTest, State) { TEST(ThrowingAllocatorTest, State) {
ThrowingAllocator<int> a1, a2; ThrowingAllocator<int> a1, a2;
EXPECT_NE(a1, a2); EXPECT_NE(a1, a2);
@ -390,13 +445,13 @@ TEST_F(ThrowingAllocatorTest, State) {
EXPECT_EQ(a3, a1); EXPECT_EQ(a3, a1);
} }
TEST_F(ThrowingAllocatorTest, InVector) { TEST(ThrowingAllocatorTest, InVector) {
std::vector<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> v; std::vector<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> v;
for (int i = 0; i < 20; ++i) v.push_back({}); for (int i = 0; i < 20; ++i) v.push_back({});
for (int i = 0; i < 20; ++i) v.pop_back(); for (int i = 0; i < 20; ++i) v.pop_back();
} }
TEST_F(ThrowingAllocatorTest, InList) { TEST(ThrowingAllocatorTest, InList) {
std::list<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> l; std::list<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> l;
for (int i = 0; i < 20; ++i) l.push_back({}); for (int i = 0; i < 20; ++i) l.push_back({});
for (int i = 0; i < 20; ++i) l.pop_back(); for (int i = 0; i < 20; ++i) l.pop_back();
@ -443,15 +498,15 @@ TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) {
// Test that providing operation and inveriants still does not allow for the // Test that providing operation and inveriants still does not allow for the
// the invocation of .Test() and .Test(op) because it lacks a factory // the invocation of .Test() and .Test(op) because it lacks a factory
auto without_fac = auto without_fac =
absl::MakeExceptionSafetyTester().WithOperation(op).WithInvariants( testing::MakeExceptionSafetyTester().WithOperation(op).WithInvariants(
inv, absl::strong_guarantee); inv, testing::strong_guarantee);
EXPECT_FALSE(HasNullaryTest(without_fac)); EXPECT_FALSE(HasNullaryTest(without_fac));
EXPECT_FALSE(HasUnaryTest(without_fac)); EXPECT_FALSE(HasUnaryTest(without_fac));
// Test that providing invariants and factory allows the invocation of // Test that providing invariants and factory allows the invocation of
// .Test(op) but does not allow for .Test() because it lacks an operation // .Test(op) but does not allow for .Test() because it lacks an operation
auto without_op = absl::MakeExceptionSafetyTester() auto without_op = testing::MakeExceptionSafetyTester()
.WithInvariants(inv, absl::strong_guarantee) .WithInvariants(inv, testing::strong_guarantee)
.WithFactory(fac); .WithFactory(fac);
EXPECT_FALSE(HasNullaryTest(without_op)); EXPECT_FALSE(HasNullaryTest(without_op));
EXPECT_TRUE(HasUnaryTest(without_op)); EXPECT_TRUE(HasUnaryTest(without_op));
@ -459,7 +514,7 @@ TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) {
// Test that providing operation and factory still does not allow for the // Test that providing operation and factory still does not allow for the
// the invocation of .Test() and .Test(op) because it lacks invariants // the invocation of .Test() and .Test(op) because it lacks invariants
auto without_inv = auto without_inv =
absl::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac); testing::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac);
EXPECT_FALSE(HasNullaryTest(without_inv)); EXPECT_FALSE(HasNullaryTest(without_inv));
EXPECT_FALSE(HasUnaryTest(without_inv)); EXPECT_FALSE(HasUnaryTest(without_inv));
} }
@ -504,28 +559,28 @@ auto example_lambda_invariant = [](ExampleStruct* example_struct) {
// lambdas can all be used with ExceptionSafetyTester // lambdas can all be used with ExceptionSafetyTester
TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) { TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) {
// function reference // function reference
EXPECT_TRUE(absl::MakeExceptionSafetyTester() EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithFactory(ExampleFunctionFactory) .WithFactory(ExampleFunctionFactory)
.WithOperation(ExampleFunctionOperation) .WithOperation(ExampleFunctionOperation)
.WithInvariants(ExampleFunctionInvariant) .WithInvariants(ExampleFunctionInvariant)
.Test()); .Test());
// function pointer // function pointer
EXPECT_TRUE(absl::MakeExceptionSafetyTester() EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithFactory(&ExampleFunctionFactory) .WithFactory(&ExampleFunctionFactory)
.WithOperation(&ExampleFunctionOperation) .WithOperation(&ExampleFunctionOperation)
.WithInvariants(&ExampleFunctionInvariant) .WithInvariants(&ExampleFunctionInvariant)
.Test()); .Test());
// struct // struct
EXPECT_TRUE(absl::MakeExceptionSafetyTester() EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithFactory(example_struct_factory) .WithFactory(example_struct_factory)
.WithOperation(example_struct_operation) .WithOperation(example_struct_operation)
.WithInvariants(example_struct_invariant) .WithInvariants(example_struct_invariant)
.Test()); .Test());
// lambda // lambda
EXPECT_TRUE(absl::MakeExceptionSafetyTester() EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithFactory(example_lambda_factory) .WithFactory(example_lambda_factory)
.WithOperation(example_lambda_operation) .WithOperation(example_lambda_operation)
.WithInvariants(example_lambda_invariant) .WithInvariants(example_lambda_invariant)
@ -553,9 +608,9 @@ struct {
} invoker; } invoker;
auto tester = auto tester =
absl::MakeExceptionSafetyTester().WithOperation(invoker).WithInvariants( testing::MakeExceptionSafetyTester().WithOperation(invoker).WithInvariants(
CheckNonNegativeInvariants); CheckNonNegativeInvariants);
auto strong_tester = tester.WithInvariants(absl::strong_guarantee); auto strong_tester = tester.WithInvariants(testing::strong_guarantee);
struct FailsBasicGuarantee : public NonNegative { struct FailsBasicGuarantee : public NonNegative {
void operator()() { void operator()() {
@ -659,7 +714,7 @@ TEST(ExceptionCheckTest, ModifyingChecker) {
EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{}) EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{})
.WithInvariants(increment) .WithInvariants(increment)
.Test()); .Test());
EXPECT_TRUE(absl::MakeExceptionSafetyTester() EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithInitialValue(HasReset{}) .WithInitialValue(HasReset{})
.WithInvariants(CheckHasResetInvariants) .WithInvariants(CheckHasResetInvariants)
.Test(invoker)); .Test(invoker));
@ -734,7 +789,7 @@ template <typename T>
unsigned char ExhaustivenessTester<T>::successes = 0; unsigned char ExhaustivenessTester<T>::successes = 0;
TEST(ExceptionCheckTest, Exhaustiveness) { TEST(ExceptionCheckTest, Exhaustiveness) {
auto exhaust_tester = absl::MakeExceptionSafetyTester() auto exhaust_tester = testing::MakeExceptionSafetyTester()
.WithInvariants(CheckExhaustivenessTesterInvariants) .WithInvariants(CheckExhaustivenessTesterInvariants)
.WithOperation(invoker); .WithOperation(invoker);
@ -744,7 +799,7 @@ TEST(ExceptionCheckTest, Exhaustiveness) {
EXPECT_TRUE( EXPECT_TRUE(
exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{}) exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{})
.WithInvariants(absl::strong_guarantee) .WithInvariants(testing::strong_guarantee)
.Test()); .Test());
EXPECT_EQ(ExhaustivenessTester<ThrowingValue<>>::successes, 0xF); EXPECT_EQ(ExhaustivenessTester<ThrowingValue<>>::successes, 0xF);
} }
@ -763,7 +818,7 @@ struct LeaksIfCtorThrows : private exceptions_internal::TrackedObject {
int LeaksIfCtorThrows::counter = 0; int LeaksIfCtorThrows::counter = 0;
TEST(ExceptionCheckTest, TestLeakyCtor) { TEST(ExceptionCheckTest, TestLeakyCtor) {
absl::TestThrowingCtor<LeaksIfCtorThrows>(); testing::TestThrowingCtor<LeaksIfCtorThrows>();
EXPECT_EQ(LeaksIfCtorThrows::counter, 1); EXPECT_EQ(LeaksIfCtorThrows::counter, 1);
LeaksIfCtorThrows::counter = 0; LeaksIfCtorThrows::counter = 0;
} }
@ -772,19 +827,28 @@ struct Tracked : private exceptions_internal::TrackedObject {
Tracked() : TrackedObject(ABSL_PRETTY_FUNCTION) {} Tracked() : TrackedObject(ABSL_PRETTY_FUNCTION) {}
}; };
TEST(ConstructorTrackerTest, Pass) { TEST(ConstructorTrackerTest, CreatedBefore) {
ConstructorTracker javert; Tracked a, b, c;
Tracked t; exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
} }
TEST(ConstructorTrackerTest, NotDestroyed) { TEST(ConstructorTrackerTest, CreatedAfter) {
exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
Tracked a, b, c;
}
TEST(ConstructorTrackerTest, NotDestroyedAfter) {
absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage; absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(
{ {
ConstructorTracker gadget; exceptions_internal::ConstructorTracker ct(
exceptions_internal::countdown);
new (&storage) Tracked; new (&storage) Tracked;
}, },
"not destroyed"); "not destroyed");
// Manual destruction of the Tracked instance is not required because
// ~ConstructorTracker() handles that automatically when a leak is found
} }
TEST(ConstructorTrackerTest, DestroyedTwice) { TEST(ConstructorTrackerTest, DestroyedTwice) {
@ -825,4 +889,5 @@ TEST(ThrowingAllocatorTraitsTest, Assignablility) {
} }
} // namespace } // namespace
} // namespace absl
} // namespace testing

View File

@ -21,6 +21,12 @@
#include <cstdint> #include <cstdint>
#include <utility> #include <utility>
#ifdef _MSC_FULL_VER
#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
#else
#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
#endif
namespace absl { namespace absl {
namespace base_internal { namespace base_internal {
@ -29,9 +35,15 @@ class AtomicHook;
// AtomicHook is a helper class, templatized on a raw function pointer type, for // AtomicHook is a helper class, templatized on a raw function pointer type, for
// implementing Abseil customization hooks. It is a callable object that // implementing Abseil customization hooks. It is a callable object that
// dispatches to the registered hook, or performs a no-op (and returns a default // dispatches to the registered hook.
//
// A default constructed object performs a no-op (and returns a default
// constructed object) if no hook has been registered. // constructed object) if no hook has been registered.
// //
// Hooks can be pre-registered via constant initialization, for example,
// ABSL_CONST_INIT static AtomicHook<void(*)()> my_hook(DefaultAction);
// and then changed at runtime via a call to Store().
//
// Reads and writes guarantee memory_order_acquire/memory_order_release // Reads and writes guarantee memory_order_acquire/memory_order_release
// semantics. // semantics.
template <typename ReturnType, typename... Args> template <typename ReturnType, typename... Args>
@ -39,7 +51,19 @@ class AtomicHook<ReturnType (*)(Args...)> {
public: public:
using FnPtr = ReturnType (*)(Args...); using FnPtr = ReturnType (*)(Args...);
constexpr AtomicHook() : hook_(kInitialValue) {} // Constructs an object that by default performs a no-op (and
// returns a default constructed object) when no hook as been registered.
constexpr AtomicHook() : AtomicHook(DummyFunction) {}
// Constructs an object that by default dispatches to/returns the
// pre-registered default_fn when no hook has been registered at runtime.
#if ABSL_HAVE_WORKING_ATOMIC_POINTER
explicit constexpr AtomicHook(FnPtr default_fn)
: hook_(default_fn), default_fn_(default_fn) {}
#else
explicit constexpr AtomicHook(FnPtr default_fn)
: hook_(kUninitialized), default_fn_(default_fn) {}
#endif
// Stores the provided function pointer as the value for this hook. // Stores the provided function pointer as the value for this hook.
// //
@ -86,16 +110,7 @@ class AtomicHook<ReturnType (*)(Args...)> {
// //
// This causes an issue when building with LLVM under Windows. To avoid this, // This causes an issue when building with LLVM under Windows. To avoid this,
// we use a less-efficient, intptr_t-based implementation on Windows. // we use a less-efficient, intptr_t-based implementation on Windows.
#ifdef _MSC_FULL_VER
#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
#else
#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
#endif
#if ABSL_HAVE_WORKING_ATOMIC_POINTER #if ABSL_HAVE_WORKING_ATOMIC_POINTER
static constexpr FnPtr kInitialValue = &DummyFunction;
// Return the stored value, or DummyFunction if no value has been stored. // Return the stored value, or DummyFunction if no value has been stored.
FnPtr DoLoad() const { return hook_.load(std::memory_order_acquire); } FnPtr DoLoad() const { return hook_.load(std::memory_order_acquire); }
@ -103,10 +118,9 @@ class AtomicHook<ReturnType (*)(Args...)> {
// stored to this object. // stored to this object.
bool DoStore(FnPtr fn) { bool DoStore(FnPtr fn) {
assert(fn); assert(fn);
FnPtr expected = DummyFunction; FnPtr expected = default_fn_;
hook_.compare_exchange_strong(expected, fn, std::memory_order_acq_rel, const bool store_succeeded = hook_.compare_exchange_strong(
std::memory_order_acquire); expected, fn, std::memory_order_acq_rel, std::memory_order_acquire);
const bool store_succeeded = (expected == DummyFunction);
const bool same_value_already_stored = (expected == fn); const bool same_value_already_stored = (expected == fn);
return store_succeeded || same_value_already_stored; return store_succeeded || same_value_already_stored;
} }
@ -114,15 +128,15 @@ class AtomicHook<ReturnType (*)(Args...)> {
std::atomic<FnPtr> hook_; std::atomic<FnPtr> hook_;
#else // !ABSL_HAVE_WORKING_ATOMIC_POINTER #else // !ABSL_HAVE_WORKING_ATOMIC_POINTER
// Use a sentinel value unlikely to be the address of an actual function. // Use a sentinel value unlikely to be the address of an actual function.
static constexpr intptr_t kInitialValue = 0; static constexpr intptr_t kUninitialized = 0;
static_assert(sizeof(intptr_t) >= sizeof(FnPtr), static_assert(sizeof(intptr_t) >= sizeof(FnPtr),
"intptr_t can't contain a function pointer"); "intptr_t can't contain a function pointer");
FnPtr DoLoad() const { FnPtr DoLoad() const {
const intptr_t value = hook_.load(std::memory_order_acquire); const intptr_t value = hook_.load(std::memory_order_acquire);
if (value == 0) { if (value == kUninitialized) {
return DummyFunction; return default_fn_;
} }
return reinterpret_cast<FnPtr>(value); return reinterpret_cast<FnPtr>(value);
} }
@ -130,16 +144,17 @@ class AtomicHook<ReturnType (*)(Args...)> {
bool DoStore(FnPtr fn) { bool DoStore(FnPtr fn) {
assert(fn); assert(fn);
const auto value = reinterpret_cast<intptr_t>(fn); const auto value = reinterpret_cast<intptr_t>(fn);
intptr_t expected = 0; intptr_t expected = kUninitialized;
hook_.compare_exchange_strong(expected, value, std::memory_order_acq_rel, const bool store_succeeded = hook_.compare_exchange_strong(
std::memory_order_acquire); expected, value, std::memory_order_acq_rel, std::memory_order_acquire);
const bool store_succeeded = (expected == 0);
const bool same_value_already_stored = (expected == value); const bool same_value_already_stored = (expected == value);
return store_succeeded || same_value_already_stored; return store_succeeded || same_value_already_stored;
} }
std::atomic<intptr_t> hook_; std::atomic<intptr_t> hook_;
#endif #endif
const FnPtr default_fn_;
}; };
#undef ABSL_HAVE_WORKING_ATOMIC_POINTER #undef ABSL_HAVE_WORKING_ATOMIC_POINTER

View File

@ -0,0 +1,70 @@
// 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
//
// http://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 "absl/base/internal/atomic_hook.h"
#include "gtest/gtest.h"
#include "absl/base/attributes.h"
namespace {
int value = 0;
void TestHook(int x) { value = x; }
TEST(AtomicHookTest, NoDefaultFunction) {
ABSL_CONST_INIT static absl::base_internal::AtomicHook<void(*)(int)> hook;
value = 0;
// Test the default DummyFunction.
EXPECT_TRUE(hook.Load() == nullptr);
EXPECT_EQ(value, 0);
hook(1);
EXPECT_EQ(value, 0);
// Test a stored hook.
hook.Store(TestHook);
EXPECT_TRUE(hook.Load() == TestHook);
EXPECT_EQ(value, 0);
hook(1);
EXPECT_EQ(value, 1);
// Calling Store() with the same hook should not crash.
hook.Store(TestHook);
EXPECT_TRUE(hook.Load() == TestHook);
EXPECT_EQ(value, 1);
hook(2);
EXPECT_EQ(value, 2);
}
TEST(AtomicHookTest, WithDefaultFunction) {
// Set the default value to TestHook at compile-time.
ABSL_CONST_INIT static absl::base_internal::AtomicHook<void (*)(int)> hook(
TestHook);
value = 0;
// Test the default value is TestHook.
EXPECT_TRUE(hook.Load() == TestHook);
EXPECT_EQ(value, 0);
hook(1);
EXPECT_EQ(value, 1);
// Calling Store() with the same hook should not crash.
hook.Store(TestHook);
EXPECT_TRUE(hook.Load() == TestHook);
EXPECT_EQ(value, 1);
hook(2);
EXPECT_EQ(value, 2);
}
} // namespace

View File

@ -52,7 +52,7 @@
// SYS_mmap and SYS_munmap are not defined in Android. // SYS_mmap and SYS_munmap are not defined in Android.
#ifdef __BIONIC__ #ifdef __BIONIC__
extern "C" void* __mmap2(void*, size_t, int, int, int, long); extern "C" void* __mmap2(void*, size_t, int, int, int, size_t);
#if defined(__NR_mmap) && !defined(SYS_mmap) #if defined(__NR_mmap) && !defined(SYS_mmap)
#define SYS_mmap __NR_mmap #define SYS_mmap __NR_mmap
#endif #endif

View File

@ -17,9 +17,14 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
namespace absl { namespace testing {
exceptions_internal::NoThrowTag no_throw_ctor; exceptions_internal::NoThrowTag nothrow_ctor;
bool nothrow_guarantee(const void*) {
return ::testing::AssertionFailure()
<< "Exception thrown violating NoThrow Guarantee";
}
exceptions_internal::StrongGuaranteeTagType strong_guarantee; exceptions_internal::StrongGuaranteeTagType strong_guarantee;
namespace exceptions_internal { namespace exceptions_internal {
@ -37,5 +42,7 @@ testing::AssertionResult FailureMessage(const TestException& e,
int countdown) noexcept { int countdown) noexcept {
return testing::AssertionFailure() << "Exception thrown from " << e.what(); return testing::AssertionFailure() << "Exception thrown from " << e.what();
} }
} // namespace exceptions_internal } // namespace exceptions_internal
} // namespace absl
} // namespace testing

View File

@ -35,40 +35,36 @@
#include "absl/strings/substitute.h" #include "absl/strings/substitute.h"
#include "absl/types/optional.h" #include "absl/types/optional.h"
namespace absl { namespace testing {
struct ConstructorTracker; enum class TypeSpec;
enum class AllocSpec;
// A configuration enum for Throwing*. Operations whose flags are set will constexpr TypeSpec operator|(TypeSpec a, TypeSpec b) {
// throw, everything else won't. This isn't meant to be exhaustive, more flags using T = absl::underlying_type_t<TypeSpec>;
// can always be made in the future. return static_cast<TypeSpec>(static_cast<T>(a) | static_cast<T>(b));
enum class NoThrow : uint8_t {
kNone = 0,
kMoveCtor = 1,
kMoveAssign = 1 << 1,
kAllocation = 1 << 2,
kIntCtor = 1 << 3,
kNoThrow = static_cast<uint8_t>(-1)
};
constexpr NoThrow operator|(NoThrow a, NoThrow b) {
using T = absl::underlying_type_t<NoThrow>;
return static_cast<NoThrow>(static_cast<T>(a) | static_cast<T>(b));
} }
constexpr NoThrow operator&(NoThrow a, NoThrow b) { constexpr TypeSpec operator&(TypeSpec a, TypeSpec b) {
using T = absl::underlying_type_t<NoThrow>; using T = absl::underlying_type_t<TypeSpec>;
return static_cast<NoThrow>(static_cast<T>(a) & static_cast<T>(b)); return static_cast<TypeSpec>(static_cast<T>(a) & static_cast<T>(b));
}
constexpr AllocSpec operator|(AllocSpec a, AllocSpec b) {
using T = absl::underlying_type_t<AllocSpec>;
return static_cast<AllocSpec>(static_cast<T>(a) | static_cast<T>(b));
}
constexpr AllocSpec operator&(AllocSpec a, AllocSpec b) {
using T = absl::underlying_type_t<AllocSpec>;
return static_cast<AllocSpec>(static_cast<T>(a) & static_cast<T>(b));
} }
namespace exceptions_internal { namespace exceptions_internal {
struct NoThrowTag {}; struct NoThrowTag {};
struct StrongGuaranteeTagType {}; struct StrongGuaranteeTagType {};
constexpr bool ThrowingAllowed(NoThrow flags, NoThrow flag) {
return !static_cast<bool>(flags & flag);
}
// A simple exception class. We throw this so that test code can catch // A simple exception class. We throw this so that test code can catch
// exceptions specifically thrown by ThrowingValue. // exceptions specifically thrown by ThrowingValue.
class TestException { class TestException {
@ -105,6 +101,8 @@ void MaybeThrow(absl::string_view msg, bool throw_bad_alloc = false);
testing::AssertionResult FailureMessage(const TestException& e, testing::AssertionResult FailureMessage(const TestException& e,
int countdown) noexcept; int countdown) noexcept;
class ConstructorTracker;
class TrackedObject { class TrackedObject {
public: public:
TrackedObject(const TrackedObject&) = delete; TrackedObject(const TrackedObject&) = delete;
@ -112,31 +110,61 @@ class TrackedObject {
protected: protected:
explicit TrackedObject(const char* child_ctor) { explicit TrackedObject(const char* child_ctor) {
if (!GetAllocs().emplace(this, child_ctor).second) { if (!GetInstanceMap().emplace(this, child_ctor).second) {
ADD_FAILURE() << "Object at address " << static_cast<void*>(this) ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
<< " re-constructed in ctor " << child_ctor; << " re-constructed in ctor " << child_ctor;
} }
} }
static std::unordered_map<TrackedObject*, absl::string_view>& GetAllocs() {
static auto* m =
new std::unordered_map<TrackedObject*, absl::string_view>();
return *m;
}
~TrackedObject() noexcept { ~TrackedObject() noexcept {
if (GetAllocs().erase(this) == 0) { if (GetInstanceMap().erase(this) == 0) {
ADD_FAILURE() << "Object at address " << static_cast<void*>(this) ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
<< " destroyed improperly"; << " destroyed improperly";
} }
} }
friend struct ::absl::ConstructorTracker; private:
using InstanceMap = std::unordered_map<TrackedObject*, absl::string_view>;
static InstanceMap& GetInstanceMap() {
static auto* instance_map = new InstanceMap();
return *instance_map;
}
friend class ConstructorTracker;
};
// Inspects the constructions and destructions of anything inheriting from
// TrackedObject. This allows us to safely "leak" TrackedObjects, as
// ConstructorTracker will destroy everything left over in its destructor.
class ConstructorTracker {
public:
explicit ConstructorTracker(int c)
: init_count_(c), init_instances_(TrackedObject::GetInstanceMap()) {}
~ConstructorTracker() {
auto& cur_instances = TrackedObject::GetInstanceMap();
for (auto it = cur_instances.begin(); it != cur_instances.end();) {
if (init_instances_.count(it->first) == 0) {
ADD_FAILURE() << "Object at address " << static_cast<void*>(it->first)
<< " constructed from " << it->second
<< " where the exception countdown was set to "
<< init_count_ << " was not destroyed";
// Erasing an item inside an unordered_map invalidates the existing
// iterator. A new one is returned for iteration to continue.
it = cur_instances.erase(it);
} else {
++it;
}
}
}
private:
int init_count_;
TrackedObject::InstanceMap init_instances_;
}; };
template <typename Factory, typename Operation, typename Invariant> template <typename Factory, typename Operation, typename Invariant>
absl::optional<testing::AssertionResult> TestSingleInvariantAtCountdownImpl( absl::optional<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
const Factory& factory, const Operation& operation, int count, const Factory& factory, Operation operation, int count,
const Invariant& invariant) { const Invariant& invariant) {
auto t_ptr = factory(); auto t_ptr = factory();
absl::optional<testing::AssertionResult> current_res; absl::optional<testing::AssertionResult> current_res;
@ -199,7 +227,9 @@ inline absl::optional<testing::AssertionResult> TestAllInvariantsAtCountdown(
} // namespace exceptions_internal } // namespace exceptions_internal
extern exceptions_internal::NoThrowTag no_throw_ctor; extern exceptions_internal::NoThrowTag nothrow_ctor;
bool nothrow_guarantee(const void*);
extern exceptions_internal::StrongGuaranteeTagType strong_guarantee; extern exceptions_internal::StrongGuaranteeTagType strong_guarantee;
// A test class which is convertible to bool. The conversion can be // A test class which is convertible to bool. The conversion can be
@ -216,47 +246,71 @@ class ThrowingBool {
bool b_; bool b_;
}; };
// A testing class instrumented to throw an exception at a controlled time. /*
// * Configuration enum for the ThrowingValue type that defines behavior for the
// ThrowingValue implements a slightly relaxed version of the Regular concept -- * lifetime of the instance. Use testing::nothrow_ctor to prevent the integer
// that is it's a value type with the expected semantics. It also implements * constructor from throwing.
// arithmetic operations. It doesn't implement member and pointer operators *
// like operator-> or operator[]. * kEverythingThrows: Every operation can throw an exception
// * kNoThrowCopy: Copy construction and copy assignment will not throw
// ThrowingValue can be instrumented to have certain operations be noexcept by * kNoThrowMove: Move construction and move assignment will not throw
// using compile-time bitfield flag template arguments. That is, to make an * kNoThrowNew: Overloaded operators new and new[] will not throw
// ThrowingValue which has a noexcept move constructor and noexcept move */
// assignment, use enum class TypeSpec {
// ThrowingValue<absl::NoThrow::kMoveCtor | absl::NoThrow::kMoveAssign>. kEverythingThrows = 0,
template <NoThrow Flags = NoThrow::kNone> kNoThrowCopy = 1,
kNoThrowMove = 1 << 1,
kNoThrowNew = 1 << 2,
};
/*
* A testing class instrumented to throw an exception at a controlled time.
*
* ThrowingValue implements a slightly relaxed version of the Regular concept --
* that is it's a value type with the expected semantics. It also implements
* arithmetic operations. It doesn't implement member and pointer operators
* like operator-> or operator[].
*
* ThrowingValue can be instrumented to have certain operations be noexcept by
* using compile-time bitfield template arguments. That is, to make an
* ThrowingValue which has noexcept move construction/assignment and noexcept
* copy construction/assignment, use the following:
* ThrowingValue<testing::kNoThrowMove | testing::kNoThrowCopy> my_thrwr{val};
*/
template <TypeSpec Spec = TypeSpec::kEverythingThrows>
class ThrowingValue : private exceptions_internal::TrackedObject { class ThrowingValue : private exceptions_internal::TrackedObject {
static constexpr bool IsSpecified(TypeSpec spec) {
return static_cast<bool>(Spec & spec);
}
static constexpr int kBadValue = 938550620;
public: public:
ThrowingValue() : TrackedObject(ABSL_PRETTY_FUNCTION) { ThrowingValue() : TrackedObject(ABSL_PRETTY_FUNCTION) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
dummy_ = 0; dummy_ = 0;
} }
ThrowingValue(const ThrowingValue& other) ThrowingValue(const ThrowingValue& other) noexcept(
IsSpecified(TypeSpec::kNoThrowCopy))
: TrackedObject(ABSL_PRETTY_FUNCTION) { : TrackedObject(ABSL_PRETTY_FUNCTION) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
}
dummy_ = other.dummy_; dummy_ = other.dummy_;
} }
ThrowingValue(ThrowingValue&& other) noexcept( ThrowingValue(ThrowingValue&& other) noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveCtor)) IsSpecified(TypeSpec::kNoThrowMove))
: TrackedObject(ABSL_PRETTY_FUNCTION) { : TrackedObject(ABSL_PRETTY_FUNCTION) {
if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveCtor)) { if (!IsSpecified(TypeSpec::kNoThrowMove)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
} }
dummy_ = other.dummy_; dummy_ = other.dummy_;
} }
explicit ThrowingValue(int i) noexcept( explicit ThrowingValue(int i) : TrackedObject(ABSL_PRETTY_FUNCTION) {
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kIntCtor)) exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
: TrackedObject(ABSL_PRETTY_FUNCTION) {
if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kIntCtor)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
}
dummy_ = i; dummy_ = i;
} }
@ -266,15 +320,20 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
// absl expects nothrow destructors // absl expects nothrow destructors
~ThrowingValue() noexcept = default; ~ThrowingValue() noexcept = default;
ThrowingValue& operator=(const ThrowingValue& other) { ThrowingValue& operator=(const ThrowingValue& other) noexcept(
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); IsSpecified(TypeSpec::kNoThrowCopy)) {
dummy_ = kBadValue;
if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
}
dummy_ = other.dummy_; dummy_ = other.dummy_;
return *this; return *this;
} }
ThrowingValue& operator=(ThrowingValue&& other) noexcept( ThrowingValue& operator=(ThrowingValue&& other) noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveAssign)) { IsSpecified(TypeSpec::kNoThrowMove)) {
if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveAssign)) { dummy_ = kBadValue;
if (!IsSpecified(TypeSpec::kNoThrowMove)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
} }
dummy_ = other.dummy_; dummy_ = other.dummy_;
@ -284,22 +343,22 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
// Arithmetic Operators // Arithmetic Operators
ThrowingValue operator+(const ThrowingValue& other) const { ThrowingValue operator+(const ThrowingValue& other) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ + other.dummy_, no_throw_ctor); return ThrowingValue(dummy_ + other.dummy_, nothrow_ctor);
} }
ThrowingValue operator+() const { ThrowingValue operator+() const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_, no_throw_ctor); return ThrowingValue(dummy_, nothrow_ctor);
} }
ThrowingValue operator-(const ThrowingValue& other) const { ThrowingValue operator-(const ThrowingValue& other) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ - other.dummy_, no_throw_ctor); return ThrowingValue(dummy_ - other.dummy_, nothrow_ctor);
} }
ThrowingValue operator-() const { ThrowingValue operator-() const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(-dummy_, no_throw_ctor); return ThrowingValue(-dummy_, nothrow_ctor);
} }
ThrowingValue& operator++() { ThrowingValue& operator++() {
@ -310,7 +369,7 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
ThrowingValue operator++(int) { ThrowingValue operator++(int) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
auto out = ThrowingValue(dummy_, no_throw_ctor); auto out = ThrowingValue(dummy_, nothrow_ctor);
++dummy_; ++dummy_;
return out; return out;
} }
@ -323,34 +382,34 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
ThrowingValue operator--(int) { ThrowingValue operator--(int) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
auto out = ThrowingValue(dummy_, no_throw_ctor); auto out = ThrowingValue(dummy_, nothrow_ctor);
--dummy_; --dummy_;
return out; return out;
} }
ThrowingValue operator*(const ThrowingValue& other) const { ThrowingValue operator*(const ThrowingValue& other) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ * other.dummy_, no_throw_ctor); return ThrowingValue(dummy_ * other.dummy_, nothrow_ctor);
} }
ThrowingValue operator/(const ThrowingValue& other) const { ThrowingValue operator/(const ThrowingValue& other) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ / other.dummy_, no_throw_ctor); return ThrowingValue(dummy_ / other.dummy_, nothrow_ctor);
} }
ThrowingValue operator%(const ThrowingValue& other) const { ThrowingValue operator%(const ThrowingValue& other) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ % other.dummy_, no_throw_ctor); return ThrowingValue(dummy_ % other.dummy_, nothrow_ctor);
} }
ThrowingValue operator<<(int shift) const { ThrowingValue operator<<(int shift) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ << shift, no_throw_ctor); return ThrowingValue(dummy_ << shift, nothrow_ctor);
} }
ThrowingValue operator>>(int shift) const { ThrowingValue operator>>(int shift) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ >> shift, no_throw_ctor); return ThrowingValue(dummy_ >> shift, nothrow_ctor);
} }
// Comparison Operators // Comparison Operators
@ -406,22 +465,22 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
// Bitwise Logical Operators // Bitwise Logical Operators
ThrowingValue operator~() const { ThrowingValue operator~() const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(~dummy_, no_throw_ctor); return ThrowingValue(~dummy_, nothrow_ctor);
} }
ThrowingValue operator&(const ThrowingValue& other) const { ThrowingValue operator&(const ThrowingValue& other) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ & other.dummy_, no_throw_ctor); return ThrowingValue(dummy_ & other.dummy_, nothrow_ctor);
} }
ThrowingValue operator|(const ThrowingValue& other) const { ThrowingValue operator|(const ThrowingValue& other) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ | other.dummy_, no_throw_ctor); return ThrowingValue(dummy_ | other.dummy_, nothrow_ctor);
} }
ThrowingValue operator^(const ThrowingValue& other) const { ThrowingValue operator^(const ThrowingValue& other) const {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return ThrowingValue(dummy_ ^ other.dummy_, no_throw_ctor); return ThrowingValue(dummy_ ^ other.dummy_, nothrow_ctor);
} }
// Compound Assignment operators // Compound Assignment operators
@ -503,8 +562,8 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
// Args.. allows us to overload regular and placement new in one shot // Args.. allows us to overload regular and placement new in one shot
template <typename... Args> template <typename... Args>
static void* operator new(size_t s, Args&&... args) noexcept( static void* operator new(size_t s, Args&&... args) noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { IsSpecified(TypeSpec::kNoThrowNew)) {
if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { if (!IsSpecified(TypeSpec::kNoThrowNew)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
} }
return ::operator new(s, std::forward<Args>(args)...); return ::operator new(s, std::forward<Args>(args)...);
@ -512,8 +571,8 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
template <typename... Args> template <typename... Args>
static void* operator new[](size_t s, Args&&... args) noexcept( static void* operator new[](size_t s, Args&&... args) noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { IsSpecified(TypeSpec::kNoThrowNew)) {
if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { if (!IsSpecified(TypeSpec::kNoThrowNew)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
} }
return ::operator new[](s, std::forward<Args>(args)...); return ::operator new[](s, std::forward<Args>(args)...);
@ -551,20 +610,35 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
}; };
// While not having to do with exceptions, explicitly delete comma operator, to // While not having to do with exceptions, explicitly delete comma operator, to
// make sure we don't use it on user-supplied types. // make sure we don't use it on user-supplied types.
template <NoThrow N, typename T> template <TypeSpec Spec, typename T>
void operator,(const ThrowingValue<N>& ef, T&& t) = delete; void operator,(const ThrowingValue<Spec>&, T&&) = delete;
template <NoThrow N, typename T> template <TypeSpec Spec, typename T>
void operator,(T&& t, const ThrowingValue<N>& ef) = delete; void operator,(T&&, const ThrowingValue<Spec>&) = delete;
// An allocator type which is instrumented to throw at a controlled time, or not /*
// to throw, using NoThrow. The supported settings are the default of every * Configuration enum for the ThrowingAllocator type that defines behavior for
// function which is allowed to throw in a conforming allocator possibly * the lifetime of the instance.
// throwing, or nothing throws, in line with the ABSL_ALLOCATOR_THROWS *
// configuration macro. * kEverythingThrows: Calls to the member functions may throw
template <typename T, NoThrow Flags = NoThrow::kNone> * kNoThrowAllocate: Calls to the member functions will not throw
*/
enum class AllocSpec {
kEverythingThrows = 0,
kNoThrowAllocate = 1,
};
/*
* An allocator type which is instrumented to throw at a controlled time, or not
* to throw, using AllocSpec. The supported settings are the default of every
* function which is allowed to throw in a conforming allocator possibly
* throwing, or nothing throws, in line with the ABSL_ALLOCATOR_THROWS
* configuration macro.
*/
template <typename T, AllocSpec Spec = AllocSpec::kEverythingThrows>
class ThrowingAllocator : private exceptions_internal::TrackedObject { class ThrowingAllocator : private exceptions_internal::TrackedObject {
static_assert(Flags == NoThrow::kNone || Flags == NoThrow::kNoThrow, static constexpr bool IsSpecified(AllocSpec spec) {
"Invalid flag"); return static_cast<bool>(Spec & spec);
}
public: public:
using pointer = T*; using pointer = T*;
@ -577,7 +651,8 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
using size_type = size_t; using size_type = size_t;
using difference_type = ptrdiff_t; using difference_type = ptrdiff_t;
using is_nothrow = std::integral_constant<bool, Flags == NoThrow::kNoThrow>; using is_nothrow =
std::integral_constant<bool, Spec == AllocSpec::kNoThrowAllocate>;
using propagate_on_container_copy_assignment = std::true_type; using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type; using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type; using propagate_on_container_swap = std::true_type;
@ -589,8 +664,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
} }
template <typename U> template <typename U>
ThrowingAllocator( // NOLINT ThrowingAllocator(const ThrowingAllocator<U, Spec>& other) noexcept // NOLINT
const ThrowingAllocator<U, Flags>& other) noexcept
: TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {} : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {}
// According to C++11 standard [17.6.3.5], Table 28, the move/copy ctors of // According to C++11 standard [17.6.3.5], Table 28, the move/copy ctors of
@ -599,8 +673,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
: TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {} : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {}
template <typename U> template <typename U>
ThrowingAllocator( // NOLINT ThrowingAllocator(ThrowingAllocator<U, Spec>&& other) noexcept // NOLINT
ThrowingAllocator<U, Flags>&& other) noexcept
: TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(std::move(other.State())) {} : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(std::move(other.State())) {}
ThrowingAllocator(ThrowingAllocator&& other) noexcept ThrowingAllocator(ThrowingAllocator&& other) noexcept
@ -615,29 +688,30 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
template <typename U> template <typename U>
ThrowingAllocator& operator=( ThrowingAllocator& operator=(
const ThrowingAllocator<U, Flags>& other) noexcept { const ThrowingAllocator<U, Spec>& other) noexcept {
dummy_ = other.State(); dummy_ = other.State();
return *this; return *this;
} }
template <typename U> template <typename U>
ThrowingAllocator& operator=(ThrowingAllocator<U, Flags>&& other) noexcept { ThrowingAllocator& operator=(ThrowingAllocator<U, Spec>&& other) noexcept {
dummy_ = std::move(other.State()); dummy_ = std::move(other.State());
return *this; return *this;
} }
template <typename U> template <typename U>
struct rebind { struct rebind {
using other = ThrowingAllocator<U, Flags>; using other = ThrowingAllocator<U, Spec>;
}; };
pointer allocate(size_type n) noexcept( pointer allocate(size_type n) noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { IsSpecified(AllocSpec::kNoThrowAllocate)) {
ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
return static_cast<pointer>(::operator new(n * sizeof(T))); return static_cast<pointer>(::operator new(n * sizeof(T)));
} }
pointer allocate(size_type n, const_void_pointer) noexcept( pointer allocate(size_type n, const_void_pointer) noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { IsSpecified(AllocSpec::kNoThrowAllocate)) {
return allocate(n); return allocate(n);
} }
@ -648,7 +722,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
template <typename U, typename... Args> template <typename U, typename... Args>
void construct(U* ptr, Args&&... args) noexcept( void construct(U* ptr, Args&&... args) noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { IsSpecified(AllocSpec::kNoThrowAllocate)) {
ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
::new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...); ::new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...);
} }
@ -664,23 +738,23 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
} }
ThrowingAllocator select_on_container_copy_construction() noexcept( ThrowingAllocator select_on_container_copy_construction() noexcept(
!exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { IsSpecified(AllocSpec::kNoThrowAllocate)) {
auto& out = *this; auto& out = *this;
ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
return out; return out;
} }
template <typename U> template <typename U>
bool operator==(const ThrowingAllocator<U, Flags>& other) const noexcept { bool operator==(const ThrowingAllocator<U, Spec>& other) const noexcept {
return dummy_ == other.dummy_; return dummy_ == other.dummy_;
} }
template <typename U> template <typename U>
bool operator!=(const ThrowingAllocator<U, Flags>& other) const noexcept { bool operator!=(const ThrowingAllocator<U, Spec>& other) const noexcept {
return dummy_ != other.dummy_; return dummy_ != other.dummy_;
} }
template <typename U, NoThrow B> template <typename, AllocSpec>
friend class ThrowingAllocator; friend class ThrowingAllocator;
private: private:
@ -694,7 +768,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
} }
void ReadStateAndMaybeThrow(absl::string_view msg) const { void ReadStateAndMaybeThrow(absl::string_view msg) const {
if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { if (!IsSpecified(AllocSpec::kNoThrowAllocate)) {
exceptions_internal::MaybeThrow( exceptions_internal::MaybeThrow(
absl::Substitute("Allocator id $0 threw from $1", *dummy_, msg)); absl::Substitute("Allocator id $0 threw from $1", *dummy_, msg));
} }
@ -704,40 +778,24 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
std::shared_ptr<const int> dummy_; std::shared_ptr<const int> dummy_;
}; };
template <typename T, NoThrow Throws> template <typename T, AllocSpec Spec>
int ThrowingAllocator<T, Throws>::next_id_ = 0; int ThrowingAllocator<T, Spec>::next_id_ = 0;
// Inspects the constructions and destructions of anything inheriting from
// TrackedObject. Place this as a member variable in a test fixture to ensure
// that every ThrowingValue was constructed and destroyed correctly. This also
// allows us to safely "leak" TrackedObjects, as ConstructorTracker will destroy
// everything left over in its destructor.
struct ConstructorTracker {
ConstructorTracker() = default;
~ConstructorTracker() {
auto& allocs = exceptions_internal::TrackedObject::GetAllocs();
for (const auto& kv : allocs) {
ADD_FAILURE() << "Object at address " << static_cast<void*>(kv.first)
<< " constructed from " << kv.second << " not destroyed";
}
allocs.clear();
}
};
// Tests for resource leaks by attempting to construct a T using args repeatedly // Tests for resource leaks by attempting to construct a T using args repeatedly
// until successful, using the countdown method. Side effects can then be // until successful, using the countdown method. Side effects can then be
// tested for resource leaks. If a ConstructorTracker is present in the test // tested for resource leaks.
// fixture, then this will also test that memory resources are not leaked as
// long as T allocates TrackedObjects.
template <typename T, typename... Args> template <typename T, typename... Args>
T TestThrowingCtor(Args&&... args) { void TestThrowingCtor(Args&&... args) {
struct Cleanup { struct Cleanup {
~Cleanup() { exceptions_internal::UnsetCountdown(); } ~Cleanup() { exceptions_internal::UnsetCountdown(); }
} c; } c;
for (int count = 0;; ++count) { for (int count = 0;; ++count) {
exceptions_internal::ConstructorTracker ct(count);
exceptions_internal::SetCountdown(count); exceptions_internal::SetCountdown(count);
try { try {
return T(std::forward<Args>(args)...); T temp(std::forward<Args>(args)...);
static_cast<void>(temp);
break;
} catch (const exceptions_internal::TestException&) { } catch (const exceptions_internal::TestException&) {
} }
} }
@ -859,7 +917,7 @@ class ExceptionSafetyTester {
* created in order to get an empty Invariants... list. * created in order to get an empty Invariants... list.
* *
* In addition to passing in custom invariant assertion callbacks, this method * In addition to passing in custom invariant assertion callbacks, this method
* accepts `absl::strong_guarantee` as an argument which checks T instances * accepts `testing::strong_guarantee` as an argument which checks T instances
* post-throw against freshly created T instances via operator== to verify * post-throw against freshly created T instances via operator== to verify
* that any state changes made during the execution of the operation were * that any state changes made during the execution of the operation were
* properly rolled back. * properly rolled back.
@ -920,7 +978,7 @@ class ExceptionSafetyTester {
template <typename, typename, typename...> template <typename, typename, typename...>
friend class ExceptionSafetyTester; friend class ExceptionSafetyTester;
friend ExceptionSafetyTester<> absl::MakeExceptionSafetyTester(); friend ExceptionSafetyTester<> testing::MakeExceptionSafetyTester();
ExceptionSafetyTester() {} ExceptionSafetyTester() {}
@ -934,6 +992,8 @@ class ExceptionSafetyTester {
// Starting from 0 and counting upwards until one of the exit conditions is // Starting from 0 and counting upwards until one of the exit conditions is
// hit... // hit...
for (int count = 0;; ++count) { for (int count = 0;; ++count) {
exceptions_internal::ConstructorTracker ct(count);
// Run the full exception safety test algorithm for the current countdown // Run the full exception safety test algorithm for the current countdown
auto reduced_res = auto reduced_res =
TestAllInvariantsAtCountdown(factory_, selected_operation, count, TestAllInvariantsAtCountdown(factory_, selected_operation, count,
@ -976,6 +1036,6 @@ MakeExceptionSafetyTester() {
return {}; return {};
} }
} // namespace absl } // namespace testing
#endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ #endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_

View File

@ -0,0 +1,40 @@
// Copyright 2017 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
//
// http://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 "benchmark/benchmark.h"
#include "absl/base/internal/thread_identity.h"
#include "absl/synchronization/internal/create_thread_identity.h"
#include "absl/synchronization/internal/per_thread_sem.h"
namespace {
void BM_SafeCurrentThreadIdentity(benchmark::State& state) {
for (auto _ : state) {
benchmark::DoNotOptimize(
absl::synchronization_internal::GetOrCreateCurrentThreadIdentity());
}
}
BENCHMARK(BM_SafeCurrentThreadIdentity);
void BM_UnsafeCurrentThreadIdentity(benchmark::State& state) {
for (auto _ : state) {
benchmark::DoNotOptimize(
absl::base_internal::CurrentThreadIdentityIfPresent());
}
}
BENCHMARK(BM_UnsafeCurrentThreadIdentity);
} // namespace
BENCHMARK_MAIN();

View File

@ -36,21 +36,20 @@
// ABSL_ARRAYSIZE() // ABSL_ARRAYSIZE()
// //
// Returns the # of elements in an array as a compile-time constant, which can // Returns the number of elements in an array as a compile-time constant, which
// be used in defining new arrays. If you use this macro on a pointer by // can be used in defining new arrays. If you use this macro on a pointer by
// mistake, you will get a compile-time error. // mistake, you will get a compile-time error.
// #define ABSL_ARRAYSIZE(array) \
// Note: this template function declaration is used in defining arraysize. (sizeof(::absl::macros_internal::ArraySizeHelper(array)))
// Note that the function doesn't need an implementation, as we only
// use its type.
namespace absl { namespace absl {
namespace macros_internal { namespace macros_internal {
// Note: this internal template function declaration is used by ABSL_ARRAYSIZE.
// The function doesn't need a definition, as we only use its type.
template <typename T, size_t N> template <typename T, size_t N>
auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
} // namespace macros_internal } // namespace macros_internal
} // namespace absl } // namespace absl
#define ABSL_ARRAYSIZE(array) \
(sizeof(::absl::macros_internal::ArraySizeHelper(array)))
// kLinkerInitialized // kLinkerInitialized
// //

View File

@ -86,7 +86,7 @@
// in May, 2010 and includes some functionality used in Google software // in May, 2010 and includes some functionality used in Google software
// (for instance pthread_setname_np): // (for instance pthread_setname_np):
// https://sourceware.org/ml/libc-alpha/2010-05/msg00000.html // https://sourceware.org/ml/libc-alpha/2010-05/msg00000.html
#ifdef __GLIBC_PREREQ #if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
#if !__GLIBC_PREREQ(2, 12) #if !__GLIBC_PREREQ(2, 12)
#error "Minimum required version of glibc is 2.12." #error "Minimum required version of glibc is 2.12."
#endif #endif

View File

@ -62,6 +62,17 @@ cc_test(
], ],
) )
cc_test(
name = "fixed_array_benchmark",
srcs = ["fixed_array_benchmark.cc"],
copts = ABSL_TEST_COPTS + ["$(STACK_FRAME_UNLIMITED)"],
tags = ["benchmark"],
deps = [
":fixed_array",
"@com_github_google_benchmark//:benchmark",
],
)
cc_library( cc_library(
name = "inlined_vector", name = "inlined_vector",
hdrs = ["inlined_vector.h"], hdrs = ["inlined_vector.h"],
@ -106,6 +117,19 @@ cc_test(
], ],
) )
cc_test(
name = "inlined_vector_benchmark",
srcs = ["inlined_vector_benchmark.cc"],
copts = ABSL_TEST_COPTS,
tags = ["benchmark"],
deps = [
":inlined_vector",
"//absl/base",
"//absl/strings",
"@com_github_google_benchmark//:benchmark",
],
)
cc_library( cc_library(
name = "test_instance_tracker", name = "test_instance_tracker",
testonly = 1, testonly = 1,

View File

@ -0,0 +1,68 @@
// Copyright 2017 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
//
// http://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 "absl/container/fixed_array.h"
#include <stddef.h>
#include <string>
#include "benchmark/benchmark.h"
namespace {
// For benchmarking -- simple class with constructor and destructor that
// set an int to a constant..
class SimpleClass {
public:
SimpleClass() : i(3) { }
~SimpleClass() { i = 0; }
private:
int i;
};
template <typename C, size_t stack_size>
void BM_FixedArray(benchmark::State& state) {
const int size = state.range(0);
for (auto _ : state) {
absl::FixedArray<C, stack_size> fa(size);
benchmark::DoNotOptimize(fa.data());
}
}
BENCHMARK_TEMPLATE(BM_FixedArray, char, absl::kFixedArrayUseDefault)
->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, char, 0)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, char, 1)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, char, 16)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, char, 256)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, char, 65536)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, absl::kFixedArrayUseDefault)
->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 0)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 1)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 16)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 256)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 65536)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, absl::kFixedArrayUseDefault)
->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 0)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 1)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 16)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 256)->Range(0, 1 << 16);
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 65536)->Range(0, 1 << 16);
} // namespace
BENCHMARK_MAIN();

View File

@ -0,0 +1,376 @@
// Copyright 2017 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
//
// http://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 "absl/container/inlined_vector.h"
#include <string>
#include <vector>
#include "benchmark/benchmark.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/strings/str_cat.h"
namespace {
using IntVec = absl::InlinedVector<int, 8>;
void BM_InlinedVectorFill(benchmark::State& state) {
const int len = state.range(0);
for (auto _ : state) {
IntVec v;
for (int i = 0; i < len; i++) {
v.push_back(i);
}
}
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
}
BENCHMARK(BM_InlinedVectorFill)->Range(0, 1024);
void BM_InlinedVectorFillRange(benchmark::State& state) {
const int len = state.range(0);
std::unique_ptr<int[]> ia(new int[len]);
for (int i = 0; i < len; i++) {
ia[i] = i;
}
for (auto _ : state) {
IntVec v(ia.get(), ia.get() + len);
benchmark::DoNotOptimize(v);
}
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
}
BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024);
void BM_StdVectorFill(benchmark::State& state) {
const int len = state.range(0);
for (auto _ : state) {
std::vector<int> v;
for (int i = 0; i < len; i++) {
v.push_back(i);
}
}
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
}
BENCHMARK(BM_StdVectorFill)->Range(0, 1024);
bool StringRepresentedInline(std::string s) {
const char* chars = s.data();
std::string s1 = std::move(s);
return s1.data() != chars;
}
void BM_InlinedVectorFillString(benchmark::State& state) {
const int len = state.range(0);
std::string strings[4] = {"a quite long string",
"another long string",
"012345678901234567",
"to cause allocation"};
for (auto _ : state) {
absl::InlinedVector<std::string, 8> v;
for (int i = 0; i < len; i++) {
v.push_back(strings[i & 3]);
}
}
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
}
BENCHMARK(BM_InlinedVectorFillString)->Range(0, 1024);
void BM_StdVectorFillString(benchmark::State& state) {
const int len = state.range(0);
std::string strings[4] = {"a quite long string",
"another long string",
"012345678901234567",
"to cause allocation"};
for (auto _ : state) {
std::vector<std::string> v;
for (int i = 0; i < len; i++) {
v.push_back(strings[i & 3]);
}
}
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
// The purpose of the benchmark is to verify that inlined vector is
// efficient when moving is more efficent than copying. To do so, we
// use strings that are larger than the small std::string optimization.
ABSL_RAW_CHECK(!StringRepresentedInline(strings[0]),
"benchmarked with strings that are too small");
}
BENCHMARK(BM_StdVectorFillString)->Range(0, 1024);
struct Buffer { // some arbitrary structure for benchmarking.
char* base;
int length;
int capacity;
void* user_data;
};
void BM_InlinedVectorTenAssignments(benchmark::State& state) {
const int len = state.range(0);
using BufferVec = absl::InlinedVector<Buffer, 2>;
BufferVec src;
src.resize(len);
BufferVec dst;
for (auto _ : state) {
for (int i = 0; i < 10; ++i) {
dst = src;
}
}
}
BENCHMARK(BM_InlinedVectorTenAssignments)
->Arg(0)->Arg(1)->Arg(2)->Arg(3)->Arg(4)->Arg(20);
void BM_CreateFromContainer(benchmark::State& state) {
for (auto _ : state) {
absl::InlinedVector<int, 4> x(absl::InlinedVector<int, 4>{1, 2, 3});
benchmark::DoNotOptimize(x);
}
}
BENCHMARK(BM_CreateFromContainer);
struct LargeCopyableOnly {
LargeCopyableOnly() : d(1024, 17) {}
LargeCopyableOnly(const LargeCopyableOnly& o) = default;
LargeCopyableOnly& operator=(const LargeCopyableOnly& o) = default;
std::vector<int> d;
};
struct LargeCopyableSwappable {
LargeCopyableSwappable() : d(1024, 17) {}
LargeCopyableSwappable(const LargeCopyableSwappable& o) = default;
LargeCopyableSwappable(LargeCopyableSwappable&& o) = delete;
LargeCopyableSwappable& operator=(LargeCopyableSwappable o) {
using std::swap;
swap(*this, o);
return *this;
}
LargeCopyableSwappable& operator=(LargeCopyableSwappable&& o) = delete;
friend void swap(LargeCopyableSwappable& a, LargeCopyableSwappable& b) {
using std::swap;
swap(a.d, b.d);
}
std::vector<int> d;
};
struct LargeCopyableMovable {
LargeCopyableMovable() : d(1024, 17) {}
// Use implicitly defined copy and move.
std::vector<int> d;
};
struct LargeCopyableMovableSwappable {
LargeCopyableMovableSwappable() : d(1024, 17) {}
LargeCopyableMovableSwappable(const LargeCopyableMovableSwappable& o) =
default;
LargeCopyableMovableSwappable(LargeCopyableMovableSwappable&& o) = default;
LargeCopyableMovableSwappable& operator=(LargeCopyableMovableSwappable o) {
using std::swap;
swap(*this, o);
return *this;
}
LargeCopyableMovableSwappable& operator=(LargeCopyableMovableSwappable&& o) =
default;
friend void swap(LargeCopyableMovableSwappable& a,
LargeCopyableMovableSwappable& b) {
using std::swap;
swap(a.d, b.d);
}
std::vector<int> d;
};
template <typename ElementType>
void BM_SwapElements(benchmark::State& state) {
const int len = state.range(0);
using Vec = absl::InlinedVector<ElementType, 32>;
Vec a(len);
Vec b;
for (auto _ : state) {
using std::swap;
swap(a, b);
}
}
BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableOnly)->Range(0, 1024);
BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableSwappable)->Range(0, 1024);
BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableMovable)->Range(0, 1024);
BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableMovableSwappable)
->Range(0, 1024);
// The following benchmark is meant to track the efficiency of the vector size
// as a function of stored type via the benchmark label. It is not meant to
// output useful sizeof operator performance. The loop is a dummy operation
// to fulfill the requirement of running the benchmark.
template <typename VecType>
void BM_Sizeof(benchmark::State& state) {
int size = 0;
for (auto _ : state) {
VecType vec;
size = sizeof(vec);
}
state.SetLabel(absl::StrCat("sz=", size));
}
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 1>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 4>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 7>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 8>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 1>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 4>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 7>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 8>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 1>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 4>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 7>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 8>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 1>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 4>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 7>);
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 8>);
void BM_InlinedVectorIndexInlined(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
for (auto _ : state) {
for (int i = 0; i < 1000; ++i) {
benchmark::DoNotOptimize(v);
benchmark::DoNotOptimize(v[4]);
}
}
state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorIndexInlined);
void BM_InlinedVectorIndexExternal(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
for (int i = 0; i < 1000; ++i) {
benchmark::DoNotOptimize(v);
benchmark::DoNotOptimize(v[4]);
}
}
state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorIndexExternal);
void BM_StdVectorIndex(benchmark::State& state) {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
for (int i = 0; i < 1000; ++i) {
benchmark::DoNotOptimize(v);
benchmark::DoNotOptimize(v[4]);
}
}
state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_StdVectorIndex);
#define UNROLL_2(x) \
benchmark::DoNotOptimize(x); \
benchmark::DoNotOptimize(x);
#define UNROLL_4(x) UNROLL_2(x) UNROLL_2(x)
#define UNROLL_8(x) UNROLL_4(x) UNROLL_4(x)
#define UNROLL_16(x) UNROLL_8(x) UNROLL_8(x);
void BM_InlinedVectorDataInlined(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
for (auto _ : state) {
UNROLL_16(v.data());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorDataInlined);
void BM_InlinedVectorDataExternal(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
UNROLL_16(v.data());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorDataExternal);
void BM_StdVectorData(benchmark::State& state) {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
UNROLL_16(v.data());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_StdVectorData);
void BM_InlinedVectorSizeInlined(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
for (auto _ : state) {
UNROLL_16(v.size());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorSizeInlined);
void BM_InlinedVectorSizeExternal(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
UNROLL_16(v.size());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorSizeExternal);
void BM_StdVectorSize(benchmark::State& state) {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
UNROLL_16(v.size());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_StdVectorSize);
void BM_InlinedVectorEmptyInlined(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
for (auto _ : state) {
UNROLL_16(v.empty());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorEmptyInlined);
void BM_InlinedVectorEmptyExternal(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
UNROLL_16(v.empty());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorEmptyExternal);
void BM_StdVectorEmpty(benchmark::State& state) {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
UNROLL_16(v.empty());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_StdVectorEmpty);
} // namespace
BENCHMARK_MAIN();

View File

@ -46,6 +46,7 @@ cc_library(
"symbolize.cc", "symbolize.cc",
"symbolize_elf.inc", "symbolize_elf.inc",
"symbolize_unimplemented.inc", "symbolize_unimplemented.inc",
"symbolize_win32.inc",
], ],
hdrs = [ hdrs = [
"internal/symbolize.h", "internal/symbolize.h",
@ -93,6 +94,38 @@ cc_library(
], ],
) )
cc_library(
name = "failure_signal_handler",
srcs = ["failure_signal_handler.cc"],
hdrs = ["failure_signal_handler.h"],
copts = ABSL_DEFAULT_COPTS,
deps = [
":examine_stack",
":stacktrace",
"//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
],
)
cc_test(
name = "failure_signal_handler_test",
srcs = ["failure_signal_handler_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = select({
"//absl:windows": [],
"//conditions:default": ["-pthread"],
}),
deps = [
":failure_signal_handler",
":stacktrace",
":symbolize",
"//absl/base",
"//absl/strings",
"@com_google_googletest//:gtest",
],
)
cc_library( cc_library(
name = "debugging_internal", name = "debugging_internal",
srcs = [ srcs = [

View File

@ -46,6 +46,7 @@ source_set("symbolize") {
"symbolize.cc", "symbolize.cc",
"symbolize_elf.inc", "symbolize_elf.inc",
"symbolize_unimplemented.inc", "symbolize_unimplemented.inc",
"symbolize_win32.inc",
] ]
public = [ public = [
"internal/symbolize.h", "internal/symbolize.h",
@ -83,6 +84,28 @@ source_set("examine_stack") {
] ]
} }
source_set("failure_signal_handler") {
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [
"//build/config/compiler:no_chromium_code",
"//third_party/abseil-cpp:absl_default_cflags_cc",
]
public_configs = [ "//third_party/abseil-cpp:absl_include_config" ]
sources = [
"failure_signal_handler.cc"
]
public = [
"failure_signal_handler.h"
]
deps = [
":examine_stack",
":stacktrace",
"../base",
"../base:config",
"../base:core_headers",
]
}
source_set("debugging_internal") { source_set("debugging_internal") {
configs -= [ "//build/config/compiler:chromium_code" ] configs -= [ "//build/config/compiler:chromium_code" ]
configs += [ configs += [

View File

@ -15,12 +15,14 @@
# #
list(APPEND DEBUGGING_PUBLIC_HEADERS list(APPEND DEBUGGING_PUBLIC_HEADERS
"failure_signal_handler.h"
"leak_check.h" "leak_check.h"
"stacktrace.h" "stacktrace.h"
"symbolize.h" "symbolize.h"
) )
# TODO(cohenjon) The below is all kinds of wrong. Make this match what we do in
# Bazel
list(APPEND DEBUGGING_INTERNAL_HEADERS list(APPEND DEBUGGING_INTERNAL_HEADERS
"internal/address_is_readable.h" "internal/address_is_readable.h"
"internal/demangle.h" "internal/demangle.h"
@ -31,12 +33,16 @@ list(APPEND DEBUGGING_INTERNAL_HEADERS
"internal/vdso_support.h" "internal/vdso_support.h"
) )
list(APPEND DEBUGGING_INTERNAL_SRC
list(APPEND STACKTRACE_SRC
"stacktrace.cc"
"internal/address_is_readable.cc" "internal/address_is_readable.cc"
"internal/elf_mem_image.cc" "internal/elf_mem_image.cc"
"internal/vdso_support.cc" "internal/vdso_support.cc"
)
list(APPEND STACKTRACE_SRC
"stacktrace.cc"
${DEBUGGING_INTERNAL_SRC}
${DEBUGGING_PUBLIC_HEADERS} ${DEBUGGING_PUBLIC_HEADERS}
${DEBUGGING_INTERNAL_HEADERS} ${DEBUGGING_INTERNAL_HEADERS}
) )
@ -45,9 +51,16 @@ list(APPEND SYMBOLIZE_SRC
"symbolize.cc" "symbolize.cc"
"symbolize_elf.inc" "symbolize_elf.inc"
"symbolize_unimplemented.inc" "symbolize_unimplemented.inc"
"symbolize_win32.inc"
"internal/demangle.cc" "internal/demangle.cc"
${DEBUGGING_PUBLIC_HEADERS} ${DEBUGGING_PUBLIC_HEADERS}
${DEBUGGING_INTERNAL_HEADERS} ${DEBUGGING_INTERNAL_HEADERS}
${DEBUGGING_INTERNAL_SRC}
)
list(APPEND FAILURE_SIGNAL_HANDLER_SRC
"failure_signal_handler.cc"
${DEBUGGING_PUBLIC_HEADERS}
) )
list(APPEND EXAMINE_STACK_SRC list(APPEND EXAMINE_STACK_SRC
@ -70,10 +83,24 @@ absl_library(
absl_symbolize absl_symbolize
SOURCES SOURCES
${SYMBOLIZE_SRC} ${SYMBOLIZE_SRC}
PUBLIC_LIBRARIES
absl::base
absl_malloc_internal
EXPORT_NAME EXPORT_NAME
symbolize symbolize
) )
absl_library(
TARGET
absl_failure_signal_handler
SOURCES
${FAILURE_SIGNAL_HANDLER_SRC}
PUBLIC_LIBRARIES
absl_base absl::examine_stack absl::stacktrace absl_synchronization
EXPORT_NAME
failure_signal_handler
)
# Internal-only. Projects external to Abseil should not depend # Internal-only. Projects external to Abseil should not depend
# directly on this library. # directly on this library.
absl_library( absl_library(
@ -117,13 +144,9 @@ absl_header_library(
## TESTS ## TESTS
# #
list(APPEND DEBUGGING_INTERNAL_TEST_HEADERS
"internal/stack_consumption.h"
)
list(APPEND STACK_CONSUMPTION_SRC list(APPEND STACK_CONSUMPTION_SRC
"internal/stack_consumption.cc" "internal/stack_consumption.cc"
${DEBUGGING_INTERNAL_TEST_HEADERS} "internal/stack_consumption.h"
) )
absl_library( absl_library(
@ -137,10 +160,13 @@ absl_test(
TARGET TARGET
absl_stack_consumption_test absl_stack_consumption_test
SOURCES SOURCES
${STACK_CONSUMPTION_SRC} "internal/stack_consumption_test.cc"
PUBLIC_LIBRARIES
absl_stack_consumption
absl::base
) )
list(APPEND DEMANGLE_TEST_SRC "demangle_test.cc") list(APPEND DEMANGLE_TEST_SRC "internal/demangle_test.cc")
absl_test( absl_test(
TARGET TARGET
@ -159,7 +185,23 @@ absl_test(
SOURCES SOURCES
${SYMBOLIZE_TEST_SRC} ${SYMBOLIZE_TEST_SRC}
PUBLIC_LIBRARIES PUBLIC_LIBRARIES
absl_symbolize absl_stack_consumption absl::base absl::memory absl_symbolize absl_stack_consumption
)
list(APPEND FAILURE_SIGNAL_HANDLER_TEST_SRC "failure_signal_handler_test.cc")
absl_test(
TARGET
failure_signal_handler_test
SOURCES
${FAILURE_SIGNAL_HANDLER_TEST_SRC}
PUBLIC_LIBRARIES
absl_examine_stack
absl_failure_signal_handler
absl_stacktrace
absl_symbolize
absl::base
absl::strings
) )
# test leak_check_test # test leak_check_test

View File

@ -0,0 +1,356 @@
//
// 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
//
// http://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 "absl/debugging/failure_signal_handler.h"
#include "absl/base/config.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#ifdef ABSL_HAVE_MMAP
#include <sys/mman.h>
#endif
#include <algorithm>
#include <atomic>
#include <cerrno>
#include <csignal>
#include <cstdio>
#include <cstring>
#include <ctime>
#include "absl/base/attributes.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/sysinfo.h"
#include "absl/debugging/internal/examine_stack.h"
#include "absl/debugging/stacktrace.h"
#ifndef _WIN32
#define ABSL_HAVE_SIGACTION
#endif
namespace absl {
ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options;
// Resets the signal handler for signo to the default action for that
// signal, then raises the signal.
static void RaiseToDefaultHandler(int signo) {
signal(signo, SIG_DFL);
raise(signo);
}
struct FailureSignalData {
const int signo;
const char* const as_string;
#ifdef ABSL_HAVE_SIGACTION
struct sigaction previous_action;
// StructSigaction is used to silence -Wmissing-field-initializers.
using StructSigaction = struct sigaction;
#define FSD_PREVIOUS_INIT FailureSignalData::StructSigaction()
#else
void (*previous_handler)(int);
#define FSD_PREVIOUS_INIT SIG_DFL
#endif
};
ABSL_CONST_INIT static FailureSignalData failure_signal_data[] = {
{SIGSEGV, "SIGSEGV", FSD_PREVIOUS_INIT},
{SIGILL, "SIGILL", FSD_PREVIOUS_INIT},
{SIGFPE, "SIGFPE", FSD_PREVIOUS_INIT},
{SIGABRT, "SIGABRT", FSD_PREVIOUS_INIT},
{SIGTERM, "SIGTERM", FSD_PREVIOUS_INIT},
#ifndef _WIN32
{SIGBUS, "SIGBUS", FSD_PREVIOUS_INIT},
{SIGTRAP, "SIGTRAP", FSD_PREVIOUS_INIT},
#endif
};
#undef FSD_PREVIOUS_INIT
static void RaiseToPreviousHandler(int signo) {
// Search for the previous handler.
for (const auto& it : failure_signal_data) {
if (it.signo == signo) {
#ifdef ABSL_HAVE_SIGACTION
sigaction(signo, &it.previous_action, nullptr);
#else
signal(signo, it.previous_handler);
#endif
raise(signo);
return;
}
}
// Not found, use the default handler.
RaiseToDefaultHandler(signo);
}
namespace debugging_internal {
const char* FailureSignalToString(int signo) {
for (const auto& it : failure_signal_data) {
if (it.signo == signo) {
return it.as_string;
}
}
return "";
}
} // namespace debugging_internal
#ifndef _WIN32
static bool SetupAlternateStackOnce() {
const size_t page_mask = getpagesize() - 1;
size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
// Account for sanitizer instrumentation requiring additional stack space.
stack_size *= 5;
#endif
stack_t sigstk;
memset(&sigstk, 0, sizeof(sigstk));
sigstk.ss_size = stack_size;
#ifdef ABSL_HAVE_MMAP
#ifndef MAP_STACK
#define MAP_STACK 0
#endif
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
#define MAP_ANONYMOUS MAP_ANON
#endif
sigstk.ss_sp = mmap(nullptr, sigstk.ss_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
if (sigstk.ss_sp == MAP_FAILED) {
ABSL_RAW_LOG(FATAL, "mmap() for alternate signal stack failed");
}
#else
sigstk.ss_sp = malloc(sigstk.ss_size);
if (sigstk.ss_sp == nullptr) {
ABSL_RAW_LOG(FATAL, "malloc() for alternate signal stack failed");
}
#endif
if (sigaltstack(&sigstk, nullptr) != 0) {
ABSL_RAW_LOG(FATAL, "sigaltstack() failed with errno=%d", errno);
}
return true;
}
#endif
// Sets up an alternate stack for signal handlers once.
// Returns the appropriate flag for sig_action.sa_flags
// if the system supports using an alternate stack.
static int MaybeSetupAlternateStack() {
#ifndef _WIN32
ABSL_ATTRIBUTE_UNUSED static const bool kOnce = SetupAlternateStackOnce();
return SA_ONSTACK;
#else
return 0;
#endif
}
#ifdef ABSL_HAVE_SIGACTION
static void InstallOneFailureHandler(FailureSignalData* data,
void (*handler)(int, siginfo_t*, void*)) {
struct sigaction act;
memset(&act, 0, sizeof(act));
sigemptyset(&act.sa_mask);
act.sa_flags |= SA_SIGINFO;
// SA_NODEFER is required to handle SIGABRT from
// ImmediateAbortSignalHandler().
act.sa_flags |= SA_NODEFER;
if (fsh_options.use_alternate_stack) {
act.sa_flags |= MaybeSetupAlternateStack();
}
act.sa_sigaction = handler;
ABSL_RAW_CHECK(sigaction(data->signo, &act, &data->previous_action) == 0,
"sigaction() failed");
}
#else
static void InstallOneFailureHandler(FailureSignalData* data,
void (*handler)(int)) {
data->previous_handler = signal(data->signo, handler);
ABSL_RAW_CHECK(data->previous_handler != SIG_ERR, "signal() failed");
}
#endif
static void WriteToStderr(const char* data) {
int old_errno = errno;
absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data));
errno = old_errno;
}
static void WriteSignalMessage(int signo, void (*writerfn)(const char*)) {
char buf[64];
const char* const signal_string =
debugging_internal::FailureSignalToString(signo);
if (signal_string != nullptr && signal_string[0] != '\0') {
snprintf(buf, sizeof(buf), "*** %s received at time=%ld ***\n",
signal_string,
static_cast<long>(time(nullptr))); // NOLINT(runtime/int)
} else {
snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld ***\n",
signo, static_cast<long>(time(nullptr))); // NOLINT(runtime/int)
}
writerfn(buf);
}
// `void*` might not be big enough to store `void(*)(const char*)`.
struct WriterFnStruct {
void (*writerfn)(const char*);
};
// Many of the absl::debugging_internal::Dump* functions in
// examine_stack.h take a writer function pointer that has a void* arg
// for historical reasons. failure_signal_handler_writer only takes a
// data pointer. This function converts between these types.
static void WriterFnWrapper(const char* data, void* arg) {
static_cast<WriterFnStruct*>(arg)->writerfn(data);
}
// Convenient wrapper around DumpPCAndFrameSizesAndStackTrace() for signal
// handlers. "noinline" so that GetStackFrames() skips the top-most stack
// frame for this function.
ABSL_ATTRIBUTE_NOINLINE static void WriteStackTrace(
void* ucontext, bool symbolize_stacktrace,
void (*writerfn)(const char*, void*), void* writerfn_arg) {
constexpr int kNumStackFrames = 32;
void* stack[kNumStackFrames];
int frame_sizes[kNumStackFrames];
int min_dropped_frames;
int depth = absl::GetStackFramesWithContext(
stack, frame_sizes, kNumStackFrames,
1, // Do not include this function in stack trace.
ucontext, &min_dropped_frames);
absl::debugging_internal::DumpPCAndFrameSizesAndStackTrace(
absl::debugging_internal::GetProgramCounter(ucontext), stack, frame_sizes,
depth, min_dropped_frames, symbolize_stacktrace, writerfn, writerfn_arg);
}
// Called by FailureSignalHandler() to write the failure info. It is
// called once with writerfn set to WriteToStderr() and then possibly
// with writerfn set to the user provided function.
static void WriteFailureInfo(int signo, void* ucontext,
void (*writerfn)(const char*)) {
WriterFnStruct writerfn_struct{writerfn};
WriteSignalMessage(signo, writerfn);
WriteStackTrace(ucontext, fsh_options.symbolize_stacktrace, WriterFnWrapper,
&writerfn_struct);
}
// absl::SleepFor() can't be used here since AbslInternalSleepFor()
// may be overridden to do something that isn't async-signal-safe on
// some platforms.
static void PortableSleepForSeconds(int seconds) {
#ifdef _WIN32
Sleep(seconds * 1000);
#else
struct timespec sleep_time;
sleep_time.tv_sec = seconds;
sleep_time.tv_nsec = 0;
while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {}
#endif
}
#ifdef ABSL_HAVE_ALARM
// FailureSignalHandler() installs this as a signal handler for
// SIGALRM, then sets an alarm to be delivered to the program after a
// set amount of time. If FailureSignalHandler() hangs for more than
// the alarm timeout, ImmediateAbortSignalHandler() will abort the
// program.
static void ImmediateAbortSignalHandler(int) {
RaiseToDefaultHandler(SIGABRT);
}
#endif
// absl::base_internal::GetTID() returns pid_t on most platforms, but
// returns absl::base_internal::pid_t on Windows.
using GetTidType = decltype(absl::base_internal::GetTID());
ABSL_CONST_INIT static std::atomic<GetTidType> failed_tid(0);
#ifndef ABSL_HAVE_SIGACTION
static void FailureSignalHandler(int signo) {
void* ucontext = nullptr;
#else
static void FailureSignalHandler(int signo, siginfo_t*,
void* ucontext) {
#endif
const GetTidType this_tid = absl::base_internal::GetTID();
GetTidType previous_failed_tid = 0;
if (!failed_tid.compare_exchange_strong(
previous_failed_tid, static_cast<intptr_t>(this_tid),
std::memory_order_acq_rel, std::memory_order_relaxed)) {
ABSL_RAW_LOG(
ERROR,
"Signal %d raised at PC=%p while already in FailureSignalHandler()",
signo, absl::debugging_internal::GetProgramCounter(ucontext));
if (this_tid != previous_failed_tid) {
// Another thread is already in FailureSignalHandler(), so wait
// a bit for it to finish. If the other thread doesn't kill us,
// we do so after sleeping.
PortableSleepForSeconds(3);
RaiseToDefaultHandler(signo);
// The recursively raised signal may be blocked until we return.
return;
}
}
#ifdef ABSL_HAVE_ALARM
// Set an alarm to abort the program in case this code hangs or deadlocks.
if (fsh_options.alarm_on_failure_secs > 0) {
alarm(0); // Cancel any existing alarms.
signal(SIGALRM, ImmediateAbortSignalHandler);
alarm(fsh_options.alarm_on_failure_secs);
}
#endif
// First write to stderr.
WriteFailureInfo(signo, ucontext, WriteToStderr);
// Riskier code (because it is less likely to be async-signal-safe)
// goes after this point.
if (fsh_options.writerfn != nullptr) {
WriteFailureInfo(signo, ucontext, fsh_options.writerfn);
}
if (fsh_options.call_previous_handler) {
RaiseToPreviousHandler(signo);
} else {
RaiseToDefaultHandler(signo);
}
}
void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) {
fsh_options = options;
for (auto& it : failure_signal_data) {
InstallOneFailureHandler(&it, FailureSignalHandler);
}
}
} // namespace absl

View File

@ -0,0 +1,117 @@
// 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
//
// http://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.
//
// -----------------------------------------------------------------------------
// File: failure_signal_handler.h
// -----------------------------------------------------------------------------
//
// This file configures the Abseil *failure signal handler* to capture and dump
// useful debugging information (such as a stacktrace) upon program failure.
//
// To use the failure signal handler, call `absl::InstallFailureSignalHandler()`
// very early in your program, usually in the first few lines of main():
//
// int main(int argc, char** argv) {
// // Initialize the symbolizer to get a human-readable stack trace
// absl::InitializeSymbolizer(argv[0]);
//
// absl::FailureSignalHandlerOptions options;
// absl::InstallFailureSignalHandler(options);
// DoSomethingInteresting();
// return 0;
// }
//
// Any program that raises a fatal signal (such as `SIGSEGV`, `SIGILL`,
// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP`) will call the
// installed failure signal handler and provide debugging information to stderr.
//
// Note that you should *not* install the Abseil failure signal handler more
// than once. You may, of course, have another (non-Abseil) failure signal
// handler installed (which would be triggered if Abseil's failure signal
// handler sets `call_previous_handler` to `true`).
#ifndef ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
#define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
namespace absl {
// FailureSignalHandlerOptions
//
// Struct for holding `absl::InstallFailureSignalHandler()` configuration
// options.
struct FailureSignalHandlerOptions {
// If true, try to symbolize the stacktrace emitted on failure, provided that
// you have initialized a symbolizer for that purpose. (See symbolize.h for
// more information.)
bool symbolize_stacktrace = true;
// If true, try to run signal handlers on an alternate stack (if supported on
// the given platform). An alternate stack is useful for program crashes due
// to a stack overflow; by running on a alternate stack, the signal handler
// may run even when normal stack space has been exausted. The downside of
// using an alternate stack is that extra memory for the alternate stack needs
// to be pre-allocated.
bool use_alternate_stack = true;
// If positive, indicates the number of seconds after which the failure signal
// handler is invoked to abort the program. Setting such an alarm is useful in
// cases where the failure signal handler itself may become hung or
// deadlocked.
int alarm_on_failure_secs = 3;
// If true, call the previously registered signal handler for the signal that
// was received (if one was registered) after the existing signal handler
// runs. This mechanism can be used to chain signal handlers together.
//
// If false, the signal is raised to the default handler for that signal
// (which normally terminates the program).
//
// IMPORTANT: If true, the chained fatal signal handlers must not try to
// recover from the fatal signal. Instead, they should terminate the program
// via some mechanism, like raising the default handler for the signal, or by
// calling `_exit()`. Note that the failure signal handler may put parts of
// the Abseil library into a state from which they cannot recover.
bool call_previous_handler = false;
// If non-null, indicates a pointer to a callback function that will be called
// upon failure, with a std::string argument containing failure data. This function
// may be used as a hook to write failure data to a secondary location, such
// as a log file. This function may also be called with null data, as a hint
// to flush any buffered data before the program may be terminated. Consider
// flushing any buffered data in all calls to this function.
//
// Since this function runs within a signal handler, it should be
// async-signal-safe if possible.
// See http://man7.org/linux/man-pages/man7/signal-safety.7.html
void (*writerfn)(const char*) = nullptr;
};
// InstallFailureSignalHandler()
//
// Installs a signal handler for the common failure signals `SIGSEGV`, `SIGILL`,
// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP` (provided they exist
// on the given platform). The failure signal handler dumps program failure data
// useful for debugging in an unspecified format to stderr. This data may
// include the program counter, a stacktrace, and register information on some
// systems; do not rely on an exact format for the output, as it is subject to
// change.
void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options);
namespace debugging_internal {
const char* FailureSignalToString(int signo);
} // namespace debugging_internal
} // namespace absl
#endif // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_

View File

@ -0,0 +1,155 @@
//
// 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
//
// http://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 "absl/debugging/failure_signal_handler.h"
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
namespace {
#if GTEST_HAS_DEATH_TEST
// For the parameterized death tests. GetParam() returns the signal number.
using FailureSignalHandlerDeathTest = ::testing::TestWithParam<int>;
// This function runs in a fork()ed process on most systems.
void InstallHandlerAndRaise(int signo) {
absl::InstallFailureSignalHandler(absl::FailureSignalHandlerOptions());
raise(signo);
}
TEST_P(FailureSignalHandlerDeathTest, AbslFailureSignal) {
const int signo = GetParam();
std::string exit_regex = absl::StrCat(
"\\*\\*\\* ", absl::debugging_internal::FailureSignalToString(signo),
" received at time=");
#ifndef _WIN32
EXPECT_EXIT(InstallHandlerAndRaise(signo), testing::KilledBySignal(signo),
exit_regex);
#else
// Windows doesn't have testing::KilledBySignal().
EXPECT_DEATH(InstallHandlerAndRaise(signo), exit_regex);
#endif
}
ABSL_CONST_INIT FILE* error_file = nullptr;
void WriteToErrorFile(const char* msg) {
if (msg != nullptr) {
ABSL_RAW_CHECK(fwrite(msg, strlen(msg), 1, error_file) == 1,
"fwrite() failed");
}
ABSL_RAW_CHECK(fflush(error_file) == 0, "fflush() failed");
}
std::string GetTmpDir() {
// TEST_TMPDIR is set by Bazel. Try the others when not running under Bazel.
static const char* const kTmpEnvVars[] = {"TEST_TMPDIR", "TMPDIR", "TEMP",
"TEMPDIR", "TMP"};
for (const char* const var : kTmpEnvVars) {
const char* tmp_dir = std::getenv(var);
if (tmp_dir != nullptr) {
return tmp_dir;
}
}
// Try something reasonable.
return "/tmp";
}
// This function runs in a fork()ed process on most systems.
void InstallHandlerWithWriteToFileAndRaise(const char* file, int signo) {
error_file = fopen(file, "w");
ABSL_RAW_CHECK(error_file != nullptr, "Failed create error_file");
absl::FailureSignalHandlerOptions options;
options.writerfn = WriteToErrorFile;
absl::InstallFailureSignalHandler(options);
raise(signo);
}
TEST_P(FailureSignalHandlerDeathTest, AbslFatalSignalsWithWriterFn) {
const int signo = GetParam();
std::string tmp_dir = GetTmpDir();
std::string file = absl::StrCat(tmp_dir, "/signo_", signo);
std::string exit_regex = absl::StrCat(
"\\*\\*\\* ", absl::debugging_internal::FailureSignalToString(signo),
" received at time=");
#ifndef _WIN32
EXPECT_EXIT(InstallHandlerWithWriteToFileAndRaise(file.c_str(), signo),
testing::KilledBySignal(signo), exit_regex);
#else
// Windows doesn't have testing::KilledBySignal().
EXPECT_DEATH(InstallHandlerWithWriteToFileAndRaise(file.c_str(), signo),
exit_regex);
#endif
// Open the file in this process and check its contents.
std::fstream error_output(file);
ASSERT_TRUE(error_output.is_open()) << file;
std::string error_line;
std::getline(error_output, error_line);
EXPECT_TRUE(absl::StartsWith(
error_line,
absl::StrCat("*** ",
absl::debugging_internal::FailureSignalToString(signo),
" received at ")));
if (absl::debugging_internal::StackTraceWorksForTest()) {
std::getline(error_output, error_line);
EXPECT_TRUE(absl::StartsWith(error_line, "PC: "));
}
}
constexpr int kFailureSignals[] = {
SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGTERM,
#ifndef _WIN32
SIGBUS, SIGTRAP,
#endif
};
std::string SignalParamToString(const ::testing::TestParamInfo<int>& info) {
std::string result = absl::debugging_internal::FailureSignalToString(info.param);
if (result.empty()) {
result = absl::StrCat(info.param);
}
return result;
}
INSTANTIATE_TEST_CASE_P(AbslDeathTest, FailureSignalHandlerDeathTest,
::testing::ValuesIn(kFailureSignals),
SignalParamToString);
#endif // GTEST_HAS_DEATH_TEST
} // namespace
int main(int argc, char** argv) {
absl::InitializeSymbolizer(argv[0]);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -121,7 +121,7 @@ const void *ElfMemImage::GetSymAddr(const ElfW(Sym) *sym) const {
return reinterpret_cast<const void *>(sym->st_value); return reinterpret_cast<const void *>(sym->st_value);
} }
ABSL_RAW_CHECK(link_base_ < sym->st_value, "symbol out of range"); ABSL_RAW_CHECK(link_base_ < sym->st_value, "symbol out of range");
return GetTableElement<char>(ehdr_, 0, 1, sym->st_value) - link_base_; return GetTableElement<char>(ehdr_, 0, 1, sym->st_value - link_base_);
} }
const ElfW(Verdef) *ElfMemImage::GetVerdef(int index) const { const ElfW(Verdef) *ElfMemImage::GetVerdef(int index) const {
@ -161,10 +161,6 @@ void ElfMemImage::Init(const void *base) {
if (!base) { if (!base) {
return; return;
} }
const intptr_t base_as_uintptr_t = reinterpret_cast<uintptr_t>(base);
// Fake VDSO has low bit set.
const bool fake_vdso = ((base_as_uintptr_t & 1) != 0);
base = reinterpret_cast<const void *>(base_as_uintptr_t & ~1);
const char *const base_as_char = reinterpret_cast<const char *>(base); const char *const base_as_char = reinterpret_cast<const char *>(base);
if (base_as_char[EI_MAG0] != ELFMAG0 || base_as_char[EI_MAG1] != ELFMAG1 || if (base_as_char[EI_MAG0] != ELFMAG0 || base_as_char[EI_MAG1] != ELFMAG1 ||
base_as_char[EI_MAG2] != ELFMAG2 || base_as_char[EI_MAG3] != ELFMAG3) { base_as_char[EI_MAG2] != ELFMAG2 || base_as_char[EI_MAG3] != ELFMAG3) {
@ -224,21 +220,7 @@ void ElfMemImage::Init(const void *base) {
reinterpret_cast<ElfW(Dyn) *>(dynamic_program_header->p_vaddr + reinterpret_cast<ElfW(Dyn) *>(dynamic_program_header->p_vaddr +
relocation); relocation);
for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) { for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) {
ElfW(Xword) value = dynamic_entry->d_un.d_val; const ElfW(Xword) value = dynamic_entry->d_un.d_val + relocation;
if (fake_vdso) {
// A complication: in the real VDSO, dynamic entries are not relocated
// (it wasn't loaded by a dynamic loader). But when testing with a
// "fake" dlopen()ed vdso library, the loader relocates some (but
// not all!) of them before we get here.
if (dynamic_entry->d_tag == DT_VERDEF) {
// The only dynamic entry (of the ones we care about) libc-2.3.6
// loader doesn't relocate.
value += relocation;
}
} else {
// Real VDSO. Everything needs to be relocated.
value += relocation;
}
switch (dynamic_entry->d_tag) { switch (dynamic_entry->d_tag) {
case DT_HASH: case DT_HASH:
hash_ = reinterpret_cast<ElfW(Word) *>(value); hash_ = reinterpret_cast<ElfW(Word) *>(value);

View File

@ -14,6 +14,7 @@
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include "absl/base/attributes.h"
#include "absl/debugging/internal/address_is_readable.h" #include "absl/debugging/internal/address_is_readable.h"
#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems #include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems
#include "absl/debugging/stacktrace.h" #include "absl/debugging/stacktrace.h"
@ -24,7 +25,7 @@ static const uintptr_t kUnknownFrameSize = 0;
// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. // Returns the address of the VDSO __kernel_rt_sigreturn function, if present.
static const unsigned char* GetKernelRtSigreturnAddress() { static const unsigned char* GetKernelRtSigreturnAddress() {
constexpr uintptr_t kImpossibleAddress = 1; constexpr uintptr_t kImpossibleAddress = 1;
static std::atomic<uintptr_t> memoized{kImpossibleAddress}; ABSL_CONST_INIT static std::atomic<uintptr_t> memoized{kImpossibleAddress};
uintptr_t address = memoized.load(std::memory_order_relaxed); uintptr_t address = memoized.load(std::memory_order_relaxed);
if (address != kImpossibleAddress) { if (address != kImpossibleAddress) {
return reinterpret_cast<const unsigned char*>(address); return reinterpret_cast<const unsigned char*>(address);

View File

@ -20,6 +20,7 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "absl/base/port.h" // Needed for string vs std::string
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
#error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set #error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set

View File

@ -41,6 +41,7 @@
#include <atomic> #include <atomic>
#include "absl/base/attributes.h"
#include "absl/debugging/internal/elf_mem_image.h" #include "absl/debugging/internal/elf_mem_image.h"
#ifdef ABSL_HAVE_ELF_MEM_IMAGE #ifdef ABSL_HAVE_ELF_MEM_IMAGE
@ -132,7 +133,7 @@ class VDSOSupport {
// This function pointer may point to InitAndGetCPU, // This function pointer may point to InitAndGetCPU,
// GetCPUViaSyscall, or __vdso_getcpu at different stages of initialization. // GetCPUViaSyscall, or __vdso_getcpu at different stages of initialization.
static std::atomic<GetCpuFn> getcpu_fn_; ABSL_CONST_INIT static std::atomic<GetCpuFn> getcpu_fn_;
friend int GetCPU(void); // Needs access to getcpu_fn_. friend int GetCPU(void); // Needs access to getcpu_fn_.

View File

@ -1,5 +1,4 @@
// // Copyright 2018 The Abseil Authors.
// Copyright 2017 The Abseil Authors.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -13,15 +12,14 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// File: leak_check.h // File: leak_check.h
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// This package contains functions that affect leak checking behavior within // This file contains functions that affect leak checking behavior within
// targets built with the LeakSanitizer (LSan), a memory leak detector that is // targets built with the LeakSanitizer (LSan), a memory leak detector that is
// integrated within the AddressSanitizer (ASan) as an additional component, or // integrated within the AddressSanitizer (ASan) as an additional component, or
// which can be used standalone. LSan and ASan are included or can be provided // which can be used standalone. LSan and ASan are included (or can be provided)
// as additional components for most compilers such as Clang, gcc and MSVC. // as additional components for most compilers such as Clang, gcc and MSVC.
// Note: this leak checking API is not yet supported in MSVC. // Note: this leak checking API is not yet supported in MSVC.
// Leak checking is enabled by default in all ASan builds. // Leak checking is enabled by default in all ASan builds.

View File

@ -1,5 +1,4 @@
// // Copyright 2018 The Abseil Authors.
// Copyright 2017 The Abseil Authors.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -13,26 +12,37 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
// -----------------------------------------------------------------------------
// Routines to extract the current stack trace. These functions are // File: stacktrace.h
// thread-safe and async-signal-safe. // -----------------------------------------------------------------------------
//
// This file contains routines to extract the current stack trace and associated
// stack frames. These functions are thread-safe and async-signal-safe.
//
// Note that stack trace functionality is platform dependent and requires // Note that stack trace functionality is platform dependent and requires
// additional support from the compiler/build system in many cases. (That is, // additional support from the compiler/build system in most cases. (That is,
// this generally only works on platforms/builds that have been specifically // this functionality generally only works on platforms/builds that have been
// configured to support it.) // specifically configured to support it.)
//
// Note: stack traces in Abseil that do not utilize a symbolizer will result in
// frames consisting of function addresses rather than human-readable function
// names. (See symbolize.h for information on symbolizing these values.)
#ifndef ABSL_DEBUGGING_STACKTRACE_H_ #ifndef ABSL_DEBUGGING_STACKTRACE_H_
#define ABSL_DEBUGGING_STACKTRACE_H_ #define ABSL_DEBUGGING_STACKTRACE_H_
namespace absl { namespace absl {
// Skips the most recent "skip_count" stack frames (also skips the // GetStackFrames()
// frame generated for the "absl::GetStackFrames" routine itself), and then //
// records the pc values for up to the next "max_depth" frames in // Records program counter values for up to `max_depth` frames, skipping the
// "result", and the corresponding stack frame sizes in "sizes". // most recent `skip_count` stack frames, and stores their corresponding values
// Returns the number of values recorded in "result"/"sizes". // and sizes in `results` and `sizes` buffers. (Note that the frame generated
// for the `absl::GetStackFrames()` routine itself is also skipped.)
// routine itself.
// //
// Example: // Example:
//
// main() { foo(); } // main() { foo(); }
// foo() { bar(); } // foo() { bar(); }
// bar() { // bar() {
@ -41,41 +51,66 @@ namespace absl {
// int depth = absl::GetStackFrames(result, sizes, 10, 1); // int depth = absl::GetStackFrames(result, sizes, 10, 1);
// } // }
// //
// The absl::GetStackFrames call will skip the frame for "bar". It will // The current stack frame would consist of three function calls: `bar()`,
// return 2 and will produce pc values that map to the following // `foo()`, and then `main()`; however, since the `GetStackFrames()` call sets
// procedures: // `skip_count` to `1`, it will skip the frame for `bar()`, the most recently
// result[0] foo // invoked function call. It will therefore return two program counters and will
// result[1] main // produce values that map to the following function calls:
// (Actually, there may be a few more entries after "main" to account for //
// startup procedures.) // result[0] foo()
// And corresponding stack frame sizes will also be recorded: // result[1] main()
//
// (Note: in practice, a few more entries after `main()` may be added to account
// for startup processes.)
//
// Corresponding stack frame sizes will also be recorded:
//
// sizes[0] 16 // sizes[0] 16
// sizes[1] 16 // sizes[1] 16
// (Stack frame sizes of 16 above are just for illustration purposes.) //
// (Stack frame sizes of `16` above are just for illustration purposes.)
//
// Stack frame sizes of 0 or less indicate that those frame sizes couldn't // Stack frame sizes of 0 or less indicate that those frame sizes couldn't
// be identified. // be identified.
// //
// This routine may return fewer stack frame entries than are // This routine may return fewer stack frame entries than are
// available. Also note that "result" and "sizes" must both be non-null. // available. Also note that `result` and `sizes` must both be non-null.
extern int GetStackFrames(void** result, int* sizes, int max_depth, extern int GetStackFrames(void** result, int* sizes, int max_depth,
int skip_count); int skip_count);
// Same as above, but to be used from a signal handler. The "uc" parameter // GetStackFramesWithContext()
// should be the pointer to ucontext_t which was passed as the 3rd parameter
// to sa_sigaction signal handler. It may help the unwinder to get a
// better stack trace under certain conditions. The "uc" may safely be null.
// //
// If min_dropped_frames is not null, stores in *min_dropped_frames a // Records program counter values obtained from a signal handler. Records
// lower bound on the number of dropped stack frames. The stored value is // program counter values for up to `max_depth` frames, skipping the most recent
// guaranteed to be >= 0. The number of real stack frames is guaranteed to // `skip_count` stack frames, and stores their corresponding values and sizes in
// be >= skip_count + max_depth + *min_dropped_frames. // `results` and `sizes` buffers. (Note that the frame generated for the
// `absl::GetStackFramesWithContext()` routine itself is also skipped.)
//
// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value
// passed to a signal handler registered via the `sa_sigaction` field of a
// `sigaction` struct. (See
// http://man7.org/linux/man-pages/man2/sigaction.2.html.) The `uc` value may
// help a stack unwinder to provide a better stack trace under certain
// conditions. `uc` may safely be null.
//
// The `min_dropped_frames` output parameter, if non-null, points to the
// location to note any dropped stack frames, if any, due to buffer limitations
// or other reasons. (This value will be set to `0` if no frames were dropped.)
// The number of total stack frames is guaranteed to be >= skip_count +
// max_depth + *min_dropped_frames.
extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth, extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
int skip_count, const void* uc, int skip_count, const void* uc,
int* min_dropped_frames); int* min_dropped_frames);
// This is similar to the absl::GetStackFrames routine, except that it returns // GetStackTrace()
// the stack trace only, and not the stack frame sizes as well. //
// Records program counter values for up to `max_depth` frames, skipping the
// most recent `skip_count` stack frames, and stores their corresponding values
// in `results`. Note that this function is similar to `absl::GetStackFrames()`
// except that it returns the stack trace only, and not stack frame sizes.
//
// Example: // Example:
//
// main() { foo(); } // main() { foo(); }
// foo() { bar(); } // foo() { bar(); }
// bar() { // bar() {
@ -84,42 +119,57 @@ extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
// } // }
// //
// This produces: // This produces:
//
// result[0] foo // result[0] foo
// result[1] main // result[1] main
// .... ... // .... ...
// //
// "result" must not be null. // `result` must not be null.
extern int GetStackTrace(void** result, int max_depth, int skip_count); extern int GetStackTrace(void** result, int max_depth, int skip_count);
// Same as above, but to be used from a signal handler. The "uc" parameter // GetStackTraceWithContext()
// should be the pointer to ucontext_t which was passed as the 3rd parameter
// to sa_sigaction signal handler. It may help the unwinder to get a
// better stack trace under certain conditions. The "uc" may safely be null.
// //
// If min_dropped_frames is not null, stores in *min_dropped_frames a // Records program counter values obtained from a signal handler. Records
// lower bound on the number of dropped stack frames. The stored value is // program counter values for up to `max_depth` frames, skipping the most recent
// guaranteed to be >= 0. The number of real stack frames is guaranteed to // `skip_count` stack frames, and stores their corresponding values in
// be >= skip_count + max_depth + *min_dropped_frames. // `results`. (Note that the frame generated for the
// `absl::GetStackFramesWithContext()` routine itself is also skipped.)
//
// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value
// passed to a signal handler registered via the `sa_sigaction` field of a
// `sigaction` struct. (See
// http://man7.org/linux/man-pages/man2/sigaction.2.html.) The `uc` value may
// help a stack unwinder to provide a better stack trace under certain
// conditions. `uc` may safely be null.
//
// The `min_dropped_frames` output parameter, if non-null, points to the
// location to note any dropped stack frames, if any, due to buffer limitations
// or other reasons. (This value will be set to `0` if no frames were dropped.)
// The number of total stack frames is guaranteed to be >= skip_count +
// max_depth + *min_dropped_frames.
extern int GetStackTraceWithContext(void** result, int max_depth, extern int GetStackTraceWithContext(void** result, int max_depth,
int skip_count, const void* uc, int skip_count, const void* uc,
int* min_dropped_frames); int* min_dropped_frames);
// Call this to provide a custom function for unwinding stack frames // SetStackUnwinder()
// that will be used every time someone invokes one of the static //
// Provides a custom function for unwinding stack frames that will be used in
// place of the default stack unwinder when invoking the static
// GetStack{Frames,Trace}{,WithContext}() functions above. // GetStack{Frames,Trace}{,WithContext}() functions above.
// //
// The arguments passed to the unwinder function will match the // The arguments passed to the unwinder function will match the
// arguments passed to absl::GetStackFramesWithContext() except that sizes // arguments passed to `absl::GetStackFramesWithContext()` except that sizes
// will be non-null iff the caller is interested in frame sizes. // will be non-null iff the caller is interested in frame sizes.
// //
// If unwinder is null, we revert to the default stack-tracing behavior. // If unwinder is set to null, we revert to the default stack-tracing behavior.
// //
// **************************************************************** // *****************************************************************************
// WARNINGS // WARNING
// *****************************************************************************
// //
// absl::SetStackUnwinder is not suitable for general purpose use. It is // absl::SetStackUnwinder is not suitable for general purpose use. It is
// provided for custom runtimes. // provided for custom runtimes.
// Some things to watch out for when calling absl::SetStackUnwinder: // Some things to watch out for when calling `absl::SetStackUnwinder()`:
// //
// (a) The unwinder may be called from within signal handlers and // (a) The unwinder may be called from within signal handlers and
// therefore must be async-signal-safe. // therefore must be async-signal-safe.
@ -128,23 +178,31 @@ extern int GetStackTraceWithContext(void** result, int max_depth,
// threads may still be in the process of using that unwinder. // threads may still be in the process of using that unwinder.
// Therefore do not clean up any state that may be needed by an old // Therefore do not clean up any state that may be needed by an old
// unwinder. // unwinder.
// **************************************************************** // *****************************************************************************
extern void SetStackUnwinder(int (*unwinder)(void** pcs, int* sizes, extern void SetStackUnwinder(int (*unwinder)(void** pcs, int* sizes,
int max_depth, int skip_count, int max_depth, int skip_count,
const void* uc, const void* uc,
int* min_dropped_frames)); int* min_dropped_frames));
// Function that exposes built-in stack-unwinding behavior, ignoring // DefaultStackUnwinder()
// any calls to absl::SetStackUnwinder().
// //
// pcs must NOT be null. // Records program counter values of up to `max_depth` frames, skipping the most
// recent `skip_count` stack frames, and stores their corresponding values in
// `pcs`. (Note that the frame generated for this call itself is also skipped.)
// This function acts as a generic stack-unwinder; prefer usage of the more
// specific `GetStack{Trace,Frames}{,WithContext}()` functions above.
// //
// sizes may be null. // If you have set your own stack unwinder (with the `SetStackUnwinder()`
// uc may be null. // function above, you can still get the default stack unwinder by calling
// min_dropped_frames may be null. // `DefaultStackUnwinder()`, which will ignore any previously set stack unwinder
// and use the default one instead.
// //
// The semantics are the same as the corresponding GetStack*() function in the // Because this function is generic, only `pcs` is guaranteed to be non-null
// case where absl::SetStackUnwinder() was never called. Equivalents are: // upon return. It is legal for `sizes`, `uc`, and `min_dropped_frames` to all
// be null when called.
//
// The semantics are the same as the corresponding `GetStack*()` function in the
// case where `absl::SetStackUnwinder()` was never called. Equivalents are:
// //
// null sizes | non-nullptr sizes // null sizes | non-nullptr sizes
// |==========================================================| // |==========================================================|

View File

@ -14,8 +14,15 @@
#include "absl/debugging/symbolize.h" #include "absl/debugging/symbolize.h"
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE)
#include "absl/debugging/symbolize_elf.inc" #include "absl/debugging/symbolize_elf.inc"
#elif defined(_WIN32) && defined(_DEBUG)
// The Windows Symbolizer only works in debug mode. Note that _DEBUG
// is the macro that defines whether or not MS C-Runtime debug info is
// available. Note that the PDB files containing the debug info must
// also be available to the program at runtime for the symbolizer to
// work.
#include "absl/debugging/symbolize_win32.inc"
#else #else
#include "absl/debugging/symbolize_unimplemented.inc" #include "absl/debugging/symbolize_unimplemented.inc"
#endif #endif

View File

@ -11,7 +11,44 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//
// -----------------------------------------------------------------------------
// File: symbolize.h
// -----------------------------------------------------------------------------
//
// This file configures the Abseil symbolizer for use in converting instruction
// pointer addresses (program counters) into human-readable names (function
// calls, etc.) within Abseil code.
//
// The symbolizer may be invoked from several sources:
//
// * Implicitly, through the installation of an Abseil failure signal handler.
// (See failure_signal_handler.h for more information.)
// * By calling `Symbolize()` directly on a program counter you obtain through
// `absl::GetStackTrace()` or `absl::GetStackFrames()`. (See stacktrace.h
// for more information.
// * By calling `Symbolize()` directly on a program counter you obtain through
// other means (which would be platform-dependent).
//
// In all of the above cases, the symbolizer must first be initialized before
// any program counter values can be symbolized. If you are installing a failure
// signal handler, initialize the symbolizer before you do so.
//
// Example:
//
// int main(int argc, char** argv) {
// // Initialize the Symbolizer before installing the failure signal handler
// absl::InitializeSymbolizer(argv[0]);
//
// // Now you may install the failure signal handler
// absl::FailureSignalHandlerOptions options;
// absl::InstallFailureSignalHandler(options);
//
// // Start running your main program
// ...
// return 0;
// }
//
#ifndef ABSL_DEBUGGING_SYMBOLIZE_H_ #ifndef ABSL_DEBUGGING_SYMBOLIZE_H_
#define ABSL_DEBUGGING_SYMBOLIZE_H_ #define ABSL_DEBUGGING_SYMBOLIZE_H_
@ -19,15 +56,40 @@
namespace absl { namespace absl {
// Initializes this module. Symbolize() may fail prior calling this function. // InitializeSymbolizer()
// `argv0` is the path to this program, which is usually obtained in main() //
// though argv[0]. // Initializes the program counter symbolizer, given the path of the program
// (typically obtained through `main()`s `argv[0]`). The Abseil symbolizer
// allows you to read program counters (instruction pointer values) using their
// human-readable names within output such as stack traces.
//
// Example:
//
// int main(int argc, char *argv[]) {
// absl::InitializeSymbolizer(argv[0]);
// // Now you can use the symbolizer
// }
void InitializeSymbolizer(const char* argv0); void InitializeSymbolizer(const char* argv0);
// Symbolizes a program counter. On success, returns true and write the // Symbolize()
// symbol name to "out". The symbol name is demangled if possible //
// (supports symbols generated by GCC 3.x or newer), may be truncated, and // Symbolizes a program counter (instruction pointer value) `pc` and, on
// will be '\0' terminated. Otherwise, returns false. // success, writes the name to `out`. The symbol name is demangled, if possible.
// Note that the symbolized name may be truncated and will be NUL-terminated.
// Demangling is supported for symbols generated by GCC 3.x or newer). Returns
// `false` on failure.
//
// Example:
//
// // Print a program counter and its symbol name.
// static void DumpPCAndSymbol(void *pc) {
// char tmp[1024];
// const char *symbol = "(unknown)";
// if (absl::Symbolize(pc, tmp, sizeof(tmp))) {
// symbol = tmp;
// }
// absl::PrintF("%*p %s\n", pc, symbol);
// }
bool Symbolize(const void *pc, char *out, int out_size); bool Symbolize(const void *pc, char *out, int out_size);
} // namespace absl } // namespace absl

View File

@ -68,7 +68,7 @@ int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text.exit) exit_func() {
return 0; return 0;
} }
int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text) regular_func() { int /*ABSL_ATTRIBUTE_SECTION_VARIABLE(.text)*/ regular_func() {
return 0; return 0;
} }
@ -88,9 +88,7 @@ static volatile bool volatile_bool = false;
// Force the binary to be large enough that a THP .text remap will succeed. // Force the binary to be large enough that a THP .text remap will succeed.
static constexpr size_t kHpageSize = 1 << 21; static constexpr size_t kHpageSize = 1 << 21;
const char kHpageTextPadding[kHpageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE( const char kHpageTextPadding[kHpageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(
".text") = ""; .text) = "";
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
static char try_symbolize_buffer[4096]; static char try_symbolize_buffer[4096];
@ -120,6 +118,8 @@ static const char *TrySymbolize(void *pc) {
return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer)); return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer));
} }
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
TEST(Symbolize, Cached) { TEST(Symbolize, Cached) {
// Compilers should give us pointers to them. // Compilers should give us pointers to them.
EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func))); EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
@ -236,9 +236,9 @@ TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {
const size_t kPageSize = 64 << 10; const size_t kPageSize = 64 << 10;
// We place a read-only symbols into the .text section and verify that we can // We place a read-only symbols into the .text section and verify that we can
// symbolize them and other symbols after remapping them. // symbolize them and other symbols after remapping them.
const char kPadding0[kPageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(".text") = const char kPadding0[kPageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(.text) =
""; "";
const char kPadding1[kPageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(".text") = const char kPadding1[kPageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(.text) =
""; "";
static int FilterElfHeader(struct dl_phdr_info *info, size_t size, void *data) { static int FilterElfHeader(struct dl_phdr_info *info, size_t size, void *data) {
@ -442,6 +442,45 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() {
#endif #endif
} }
#elif defined(_WIN32) && defined(_DEBUG)
TEST(Symbolize, Basics) {
EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
// The name of an internal linkage symbol is not specified; allow either a
// mangled or an unmangled name here.
const char* static_func_symbol = TrySymbolize((void *)(&static_func));
ASSERT_TRUE(static_func_symbol != nullptr);
EXPECT_TRUE(strstr(static_func_symbol, "static_func") != nullptr);
EXPECT_TRUE(nullptr == TrySymbolize(nullptr));
}
TEST(Symbolize, Truncation) {
constexpr char kNonStaticFunc[] = "nonstatic_func";
EXPECT_STREQ("nonstatic_func",
TrySymbolizeWithLimit((void *)(&nonstatic_func),
strlen(kNonStaticFunc) + 1));
EXPECT_STREQ("nonstatic_...",
TrySymbolizeWithLimit((void *)(&nonstatic_func),
strlen(kNonStaticFunc) + 0));
EXPECT_STREQ("nonstatic...",
TrySymbolizeWithLimit((void *)(&nonstatic_func),
strlen(kNonStaticFunc) - 1));
EXPECT_STREQ("n...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 5));
EXPECT_STREQ("...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 4));
EXPECT_STREQ("..", TrySymbolizeWithLimit((void *)(&nonstatic_func), 3));
EXPECT_STREQ(".", TrySymbolizeWithLimit((void *)(&nonstatic_func), 2));
EXPECT_STREQ("", TrySymbolizeWithLimit((void *)(&nonstatic_func), 1));
EXPECT_EQ(nullptr, TrySymbolizeWithLimit((void *)(&nonstatic_func), 0));
}
TEST(Symbolize, SymbolizeWithDemangling) {
const char* result = TrySymbolize((void *)(&Foo::func));
ASSERT_TRUE(result != nullptr);
EXPECT_TRUE(strstr(result, "Foo::func") != nullptr) << result;
}
#else // Symbolizer unimplemented #else // Symbolizer unimplemented
TEST(Symbolize, Unimplemented) { TEST(Symbolize, Unimplemented) {

View File

@ -0,0 +1,74 @@
// 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
//
// http://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.
// See "Retrieving Symbol Information by Address":
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx
#include <windows.h>
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp")
#include <algorithm>
#include <cstring>
#include "absl/base/internal/raw_logging.h"
namespace absl {
static HANDLE process = NULL;
void InitializeSymbolizer(const char *argv0) {
if (process != nullptr) {
return;
}
process = GetCurrentProcess();
// Symbols are not loaded until a reference is made requiring the
// symbols be loaded. This is the fastest, most efficient way to use
// the symbol handler.
SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
if (!SymInitialize(process, nullptr, true)) {
// GetLastError() returns a Win32 DWORD, but we assign to
// unsigned long long to simplify the ABSL_RAW_LOG case below. The uniform
// initialization guarantees this is not a narrowing conversion.
const unsigned long long error{GetLastError()}; // NOLINT(runtime/int)
ABSL_RAW_LOG(FATAL, "SymInitialize() failed: %llu", error);
}
}
bool Symbolize(const void *pc, char *out, int out_size) {
if (out_size <= 0) {
return false;
}
std::aligned_storage<sizeof(SYMBOL_INFO) + MAX_SYM_NAME,
alignof(SYMBOL_INFO)>::type buf;
SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(&buf);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
if (!SymFromAddr(process, reinterpret_cast<DWORD64>(pc), nullptr, symbol)) {
return false;
}
strncpy(out, symbol->Name, out_size);
if (out[out_size - 1] != '\0') {
// strncpy() does not '\0' terminate when it truncates.
static constexpr char kEllipsis[] = "...";
int ellipsis_size =
std::min<int>(sizeof(kEllipsis) - 1, out_size - 1);
memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size);
out[out_size - 1] = '\0';
}
return true;
}
} // namespace absl

View File

@ -32,6 +32,8 @@ list(APPEND TYPE_TRAITS_TEST_SRC
absl_header_library( absl_header_library(
TARGET TARGET
absl_meta absl_meta
PUBLIC_LIBRARIES
absl::base
EXPORT_NAME EXPORT_NAME
meta meta
) )
@ -42,7 +44,8 @@ absl_test(
SOURCES SOURCES
${TYPE_TRAITS_TEST_SRC} ${TYPE_TRAITS_TEST_SRC}
PUBLIC_LIBRARIES PUBLIC_LIBRARIES
${TYPE_TRAITS_TEST_PUBLIC_LIBRARIES} absl::meta absl::base
absl::meta
) )

View File

@ -84,6 +84,7 @@ cc_library(
"internal/utf8.cc", "internal/utf8.cc",
], ],
hdrs = [ hdrs = [
"internal/bits.h",
"internal/char_map.h", "internal/char_map.h",
"internal/ostringstream.h", "internal/ostringstream.h",
"internal/resize_uninitialized.h", "internal/resize_uninitialized.h",

View File

@ -76,6 +76,7 @@ source_set("internal") {
"internal/utf8.cc", "internal/utf8.cc",
] ]
public = [ public = [
"internal/bits.h",
"internal/char_map.h", "internal/char_map.h",
"internal/ostringstream.h", "internal/ostringstream.h",
"internal/resize_uninitialized.h", "internal/resize_uninitialized.h",

View File

@ -31,6 +31,7 @@ list(APPEND STRINGS_PUBLIC_HEADERS
list(APPEND STRINGS_INTERNAL_HEADERS list(APPEND STRINGS_INTERNAL_HEADERS
"internal/bits.h"
"internal/char_map.h" "internal/char_map.h"
"internal/memutil.h" "internal/memutil.h"
"internal/ostringstream.h" "internal/ostringstream.h"

View File

@ -0,0 +1,53 @@
// 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
//
// http://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.
#ifndef ABSL_STRINGS_INTERNAL_BITS_H_
#define ABSL_STRINGS_INTERNAL_BITS_H_
#include <cstdint>
#if defined(_MSC_VER) && defined(_M_X64)
#include <intrin.h>
#pragma intrinsic(_BitScanReverse64)
#endif
namespace absl {
namespace strings_internal {
// Returns the number of leading 0 bits in a 64-bit value.
inline int CountLeadingZeros64(uint64_t n) {
#if defined(__GNUC__)
static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
"__builtin_clzll does not take 64bit arg");
return n == 0 ? 64 : __builtin_clzll(n);
#elif defined(_MSC_VER) && defined(_M_X64)
unsigned long result; // NOLINT(runtime/int)
if (_BitScanReverse64(&result, n)) {
return 63 - result;
}
return 64;
#else
int zeroes = 60;
if (n >> 32) zeroes -= 32, n >>= 32;
if (n >> 16) zeroes -= 16, n >>= 16;
if (n >> 8) zeroes -= 8, n >>= 8;
if (n >> 4) zeroes -= 4, n >>= 4;
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0\0"[n] + zeroes;
#endif
}
} // namespace strings_internal
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_BITS_H_

View File

@ -234,17 +234,19 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
result_size += it->size(); result_size += it->size();
} }
STLStringResizeUninitialized(&result, result_size); if (result_size > 0) {
STLStringResizeUninitialized(&result, result_size);
// Joins strings // Joins strings
char* result_buf = &*result.begin(); char* result_buf = &*result.begin();
memcpy(result_buf, start->data(), start->size()); memcpy(result_buf, start->data(), start->size());
result_buf += start->size(); result_buf += start->size();
for (Iterator it = start; ++it != end;) { for (Iterator it = start; ++it != end;) {
memcpy(result_buf, s.data(), s.size()); memcpy(result_buf, s.data(), s.size());
result_buf += s.size(); result_buf += s.size();
memcpy(result_buf, it->data(), it->size()); memcpy(result_buf, it->data(), it->size());
result_buf += it->size(); result_buf += it->size();
}
} }
} }

View File

@ -43,8 +43,7 @@ namespace absl {
// //
// Returns whether a given std::string `haystack` contains the substring `needle`. // Returns whether a given std::string `haystack` contains the substring `needle`.
inline bool StrContains(absl::string_view haystack, absl::string_view needle) { inline bool StrContains(absl::string_view haystack, absl::string_view needle) {
return static_cast<absl::string_view::size_type>(haystack.find(needle, 0)) != return haystack.find(needle, 0) != haystack.npos;
haystack.npos;
} }
// StartsWith() // StartsWith()

View File

@ -32,6 +32,7 @@
#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/raw_logging.h"
#include "absl/strings/ascii.h" #include "absl/strings/ascii.h"
#include "absl/strings/internal/bits.h"
#include "absl/strings/internal/memutil.h" #include "absl/strings/internal/memutil.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
@ -302,18 +303,6 @@ char* numbers_internal::FastIntToBuffer(int64_t i, char* buffer) {
return numbers_internal::FastIntToBuffer(u, buffer); return numbers_internal::FastIntToBuffer(u, buffer);
} }
// Returns the number of leading 0 bits in a 64-bit value.
// TODO(jorg): Replace with builtin_clzll if available.
// Are we shipping util/bits in absl?
static inline int CountLeadingZeros64(uint64_t n) {
int zeroes = 60;
if (n >> 32) zeroes -= 32, n >>= 32;
if (n >> 16) zeroes -= 16, n >>= 16;
if (n >> 8) zeroes -= 8, n >>= 8;
if (n >> 4) zeroes -= 4, n >>= 4;
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0\0"[n] + zeroes;
}
// Given a 128-bit number expressed as a pair of uint64_t, high half first, // Given a 128-bit number expressed as a pair of uint64_t, high half first,
// return that number multiplied by the given 32-bit value. If the result is // return that number multiplied by the given 32-bit value. If the result is
// too large to fit in a 128-bit number, divide it by 2 until it fits. // too large to fit in a 128-bit number, divide it by 2 until it fits.
@ -351,7 +340,7 @@ static std::pair<uint64_t, uint64_t> Mul32(std::pair<uint64_t, uint64_t> num,
uint64_t bits128_up = (bits96_127 >> 32) + (bits64_127 < bits64_95); uint64_t bits128_up = (bits96_127 >> 32) + (bits64_127 < bits64_95);
if (bits128_up == 0) return {bits64_127, bits0_63}; if (bits128_up == 0) return {bits64_127, bits0_63};
int shift = 64 - CountLeadingZeros64(bits128_up); int shift = 64 - strings_internal::CountLeadingZeros64(bits128_up);
uint64_t lo = (bits0_63 >> shift) + (bits64_127 << (64 - shift)); uint64_t lo = (bits0_63 >> shift) + (bits64_127 << (64 - shift));
uint64_t hi = (bits64_127 >> shift) + (bits128_up << (64 - shift)); uint64_t hi = (bits64_127 >> shift) + (bits128_up << (64 - shift));
return {hi, lo}; return {hi, lo};
@ -382,7 +371,7 @@ static std::pair<uint64_t, uint64_t> PowFive(uint64_t num, int expfive) {
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5}; 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5};
result = Mul32(result, powers_of_five[expfive & 15]); result = Mul32(result, powers_of_five[expfive & 15]);
int shift = CountLeadingZeros64(result.first); int shift = strings_internal::CountLeadingZeros64(result.first);
if (shift != 0) { if (shift != 0) {
result.first = (result.first << shift) + (result.second >> (64 - shift)); result.first = (result.first << shift) + (result.second >> (64 - shift));
result.second = (result.second << shift); result.second = (result.second << shift);

View File

@ -80,7 +80,7 @@ struct AlphaNumBuffer {
// `Dec` conversion and fill character to use. A `kZeroPad2` value, for example, // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
// would produce hexadecimal strings such as "0A","0F" and a 'kSpacePad5' value // would produce hexadecimal strings such as "0A","0F" and a 'kSpacePad5' value
// would produce hexadecimal strings such as " A"," F". // would produce hexadecimal strings such as " A"," F".
enum PadSpec { enum PadSpec : uint8_t {
kNoPad = 1, kNoPad = 1,
kZeroPad2, kZeroPad2,
kZeroPad3, kZeroPad3,

View File

@ -24,7 +24,6 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <ostream> #include <ostream>
#include <set>
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>

View File

@ -22,8 +22,6 @@
#include <ostream> #include <ostream>
#include "absl/strings/internal/memutil.h" #include "absl/strings/internal/memutil.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/match.h"
namespace absl { namespace absl {

View File

@ -36,7 +36,7 @@
namespace absl { namespace absl {
using std::string_view; using std::string_view;
}; } // namespace absl
#else // ABSL_HAVE_STD_STRING_VIEW #else // ABSL_HAVE_STD_STRING_VIEW

View File

@ -94,6 +94,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format,
assert(target == output->data() + output->size()); assert(target == output->data() + output->size());
} }
static const char kHexDigits[] = "0123456789abcdef";
Arg::Arg(const void* value) { Arg::Arg(const void* value) {
static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2, static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2,
"fix sizeof(scratch_)"); "fix sizeof(scratch_)");
@ -102,7 +103,6 @@ Arg::Arg(const void* value) {
} else { } else {
char* ptr = scratch_ + sizeof(scratch_); char* ptr = scratch_ + sizeof(scratch_);
uintptr_t num = reinterpret_cast<uintptr_t>(value); uintptr_t num = reinterpret_cast<uintptr_t>(value);
static const char kHexDigits[] = "0123456789abcdef";
do { do {
*--ptr = kHexDigits[num & 0xf]; *--ptr = kHexDigits[num & 0xf];
num >>= 4; num >>= 4;
@ -113,5 +113,58 @@ Arg::Arg(const void* value) {
} }
} }
// TODO(jorg): Don't duplicate so much code between here and str_cat.cc
Arg::Arg(Hex hex) {
char* const end = &scratch_[numbers_internal::kFastToBufferSize];
char* writer = end;
uint64_t value = hex.value;
do {
*--writer = kHexDigits[value & 0xF];
value >>= 4;
} while (value != 0);
char* beg;
if (end - writer < hex.width) {
beg = end - hex.width;
std::fill_n(beg, writer - beg, hex.fill);
} else {
beg = writer;
}
piece_ = absl::string_view(beg, end - beg);
}
// TODO(jorg): Don't duplicate so much code between here and str_cat.cc
Arg::Arg(Dec dec) {
assert(dec.width <= numbers_internal::kFastToBufferSize);
char* const end = &scratch_[numbers_internal::kFastToBufferSize];
char* const minfill = end - dec.width;
char* writer = end;
uint64_t value = dec.value;
bool neg = dec.neg;
while (value > 9) {
*--writer = '0' + (value % 10);
value /= 10;
}
*--writer = '0' + value;
if (neg) *--writer = '-';
ptrdiff_t fillers = writer - minfill;
if (fillers > 0) {
// Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
// But...: if the fill character is '0', then it's <+/-><fill><digits>
bool add_sign_again = false;
if (neg && dec.fill == '0') { // If filling with '0',
++writer; // ignore the sign we just added
add_sign_again = true; // and re-add the sign later.
}
writer -= fillers;
std::fill_n(writer, fillers, dec.fill);
if (add_sign_again) *--writer = '-';
}
piece_ = absl::string_view(writer, end - writer);
}
} // namespace substitute_internal } // namespace substitute_internal
} // namespace absl } // namespace absl

View File

@ -76,7 +76,7 @@
#include "absl/strings/ascii.h" #include "absl/strings/ascii.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "absl/strings/numbers.h" #include "absl/strings/numbers.h"
#include "absl/strings/str_join.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h" #include "absl/strings/str_split.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/strings/strip.h" #include "absl/strings/strip.h"
@ -113,10 +113,10 @@ class Arg {
// what to do. // what to do.
Arg(char value) // NOLINT(runtime/explicit) Arg(char value) // NOLINT(runtime/explicit)
: piece_(scratch_, 1) { scratch_[0] = value; } : piece_(scratch_, 1) { scratch_[0] = value; }
Arg(short value) // NOLINT(runtime/explicit) Arg(short value) // NOLINT(*)
: piece_(scratch_, : piece_(scratch_,
numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned short value) // NOLINT(runtime/explicit) Arg(unsigned short value) // NOLINT(*)
: piece_(scratch_, : piece_(scratch_,
numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(int value) // NOLINT(runtime/explicit) Arg(int value) // NOLINT(runtime/explicit)
@ -125,16 +125,16 @@ class Arg {
Arg(unsigned int value) // NOLINT(runtime/explicit) Arg(unsigned int value) // NOLINT(runtime/explicit)
: piece_(scratch_, : piece_(scratch_,
numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(long value) // NOLINT(runtime/explicit) Arg(long value) // NOLINT(*)
: piece_(scratch_, : piece_(scratch_,
numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned long value) // NOLINT(runtime/explicit) Arg(unsigned long value) // NOLINT(*)
: piece_(scratch_, : piece_(scratch_,
numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(long long value) // NOLINT(runtime/explicit) Arg(long long value) // NOLINT(*)
: piece_(scratch_, : piece_(scratch_,
numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned long long value) // NOLINT(runtime/explicit) Arg(unsigned long long value) // NOLINT(*)
: piece_(scratch_, : piece_(scratch_,
numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(float value) // NOLINT(runtime/explicit) Arg(float value) // NOLINT(runtime/explicit)
@ -145,6 +145,10 @@ class Arg {
} }
Arg(bool value) // NOLINT(runtime/explicit) Arg(bool value) // NOLINT(runtime/explicit)
: piece_(value ? "true" : "false") {} : piece_(value ? "true" : "false") {}
Arg(Hex hex); // NOLINT(runtime/explicit)
Arg(Dec dec); // NOLINT(runtime/explicit)
// `void*` values, with the exception of `char*`, are printed as // `void*` values, with the exception of `char*`, are printed as
// "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed. // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed.
Arg(const void* value); // NOLINT(runtime/explicit) Arg(const void* value); // NOLINT(runtime/explicit)

View File

@ -43,6 +43,24 @@ TEST(SubstituteTest, Substitute) {
-1234567890, 3234567890U, -1234567890L, 3234567890UL, -1234567890, 3234567890U, -1234567890L, 3234567890UL,
-int64_t{1234567890123456789}, uint64_t{9234567890123456789u})); -int64_t{1234567890123456789}, uint64_t{9234567890123456789u}));
// Hex format
EXPECT_EQ("0 1 f ffff0ffff 0123456789abcdef",
absl::Substitute("$0$1$2$3$4 $5", //
absl::Hex(0), absl::Hex(1, absl::kSpacePad2),
absl::Hex(0xf, absl::kSpacePad2),
absl::Hex(int16_t{-1}, absl::kSpacePad5),
absl::Hex(int16_t{-1}, absl::kZeroPad5),
absl::Hex(0x123456789abcdef, absl::kZeroPad16)));
// Dec format
EXPECT_EQ("0 115 -1-0001 81985529216486895",
absl::Substitute("$0$1$2$3$4 $5", //
absl::Dec(0), absl::Dec(1, absl::kSpacePad2),
absl::Dec(0xf, absl::kSpacePad2),
absl::Dec(int16_t{-1}, absl::kSpacePad5),
absl::Dec(int16_t{-1}, absl::kZeroPad5),
absl::Dec(0x123456789abcdef, absl::kZeroPad16)));
// Pointer. // Pointer.
const int* int_p = reinterpret_cast<const int*>(0x12345); const int* int_p = reinterpret_cast<const int*>(0x12345);
std::string str = absl::Substitute("$0", int_p); std::string str = absl::Substitute("$0", int_p);

View File

@ -77,6 +77,7 @@ cc_library(
"//absl/base:dynamic_annotations", "//absl/base:dynamic_annotations",
"//absl/base:malloc_internal", "//absl/base:malloc_internal",
"//absl/debugging:stacktrace", "//absl/debugging:stacktrace",
"//absl/debugging:symbolize",
"//absl/time", "//absl/time",
], ],
) )

View File

@ -73,6 +73,7 @@ source_set("synchronization") {
"../base:dynamic_annotations", "../base:dynamic_annotations",
"../base:malloc_internal", "../base:malloc_internal",
"../debugging:stacktrace", "../debugging:stacktrace",
"../debugging:symbolize",
"../time", "../time",
] ]
} }

View File

@ -33,7 +33,7 @@ list(APPEND SYNCHRONIZATION_INTERNAL_HEADERS
# syncrhonisation library # synchronization library
list(APPEND SYNCHRONIZATION_SRC list(APPEND SYNCHRONIZATION_SRC
"barrier.cc" "barrier.cc"
"blocking_counter.cc" "blocking_counter.cc"
@ -44,7 +44,8 @@ list(APPEND SYNCHRONIZATION_SRC
"notification.cc" "notification.cc"
"mutex.cc" "mutex.cc"
) )
set(SYNCHRONIZATION_PUBLIC_LIBRARIES absl::base absl::time)
set(SYNCHRONIZATION_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::symbolize absl::time)
absl_library( absl_library(
TARGET TARGET

View File

@ -50,6 +50,7 @@
#include "absl/base/internal/thread_identity.h" #include "absl/base/internal/thread_identity.h"
#include "absl/base/port.h" #include "absl/base/port.h"
#include "absl/debugging/stacktrace.h" #include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
#include "absl/synchronization/internal/graphcycles.h" #include "absl/synchronization/internal/graphcycles.h"
#include "absl/synchronization/internal/per_thread_sem.h" #include "absl/synchronization/internal/per_thread_sem.h"
#include "absl/time/time.h" #include "absl/time/time.h"
@ -111,7 +112,8 @@ ABSL_CONST_INIT absl::base_internal::AtomicHook<
ABSL_CONST_INIT absl::base_internal::AtomicHook< ABSL_CONST_INIT absl::base_internal::AtomicHook<
void (*)(const char *msg, const void *cv)> cond_var_tracer; void (*)(const char *msg, const void *cv)> cond_var_tracer;
ABSL_CONST_INIT absl::base_internal::AtomicHook< ABSL_CONST_INIT absl::base_internal::AtomicHook<
bool (*)(const void *pc, char *out, int out_size)> symbolizer; bool (*)(const void *pc, char *out, int out_size)>
symbolizer(absl::Symbolize);
} // namespace } // namespace

View File

@ -979,6 +979,12 @@ void RegisterCondVarTracer(void (*fn)(const char *msg, const void *cv));
// to 'out.' // to 'out.'
// //
// This has the same memory ordering concerns as RegisterMutexProfiler() above. // This has the same memory ordering concerns as RegisterMutexProfiler() above.
//
// DEPRECATED: The default symbolizer function is absl::Symbolize() and the
// ability to register a different hook for symbolizing stack traces will be
// removed on or after 2023-05-01.
ABSL_DEPRECATED("absl::RegisterSymbolizer() is deprecated and will be removed "
"on or after 2023-05-01")
void RegisterSymbolizer(bool (*fn)(const void *pc, char *out, int out_size)); void RegisterSymbolizer(bool (*fn)(const void *pc, char *out, int out_size));
// EnableMutexInvariantDebugging() // EnableMutexInvariantDebugging()

View File

@ -51,6 +51,7 @@ cc_library(
cc_library( cc_library(
name = "test_util", name = "test_util",
testonly = 1,
srcs = [ srcs = [
"internal/test_util.cc", "internal/test_util.cc",
"internal/zoneinfo.inc", "internal/zoneinfo.inc",
@ -64,6 +65,7 @@ cc_library(
":time", ":time",
"//absl/base", "//absl/base",
"//absl/time/internal/cctz:time_zone", "//absl/time/internal/cctz:time_zone",
"@com_google_googletest//:gtest",
], ],
) )

View File

@ -45,6 +45,7 @@ source_set("time") {
} }
source_set("test_util") { source_set("test_util") {
testonly = true
configs -= [ "//build/config/compiler:chromium_code" ] configs -= [ "//build/config/compiler:chromium_code" ]
configs += [ configs += [
"//build/config/compiler:no_chromium_code", "//build/config/compiler:no_chromium_code",
@ -62,6 +63,8 @@ source_set("test_util") {
":time", ":time",
"../base", "../base",
"../time/internal/cctz:time_zone", "../time/internal/cctz:time_zone",
"//testing/gtest",
"//testing/gmock",
] ]
visibility = [] visibility = []
visibility += [ "../time:*" ] visibility += [ "../time:*" ]

View File

@ -155,8 +155,7 @@ TEST(ParseTime, Basics) {
"2013-06-28 19:08:09 -0800", &t, &err)) "2013-06-28 19:08:09 -0800", &t, &err))
<< err; << err;
absl::Time::Breakdown bd = t.In(absl::FixedTimeZone(-8 * 60 * 60)); absl::Time::Breakdown bd = t.In(absl::FixedTimeZone(-8 * 60 * 60));
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 6, 28, 19, 8, 9, -8 * 60 * 60, false, ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 6, 28, 19, 8, 9, -8 * 60 * 60, false);
"UTC-8");
EXPECT_EQ(absl::ZeroDuration(), bd.subsecond); EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
} }
@ -179,8 +178,7 @@ TEST(ParseTime, WithTimeZone) {
absl::ParseTime("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &t, &e)) absl::ParseTime("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &t, &e))
<< e; << e;
absl::Time::Breakdown bd = t.In(tz); absl::Time::Breakdown bd = t.In(tz);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 6, 28, 19, 8, 9, -7 * 60 * 60, true, ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 6, 28, 19, 8, 9, -7 * 60 * 60, true);
"PDT");
EXPECT_EQ(absl::ZeroDuration(), bd.subsecond); EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
// But the timezone is ignored when a UTC offset is present. // But the timezone is ignored when a UTC offset is present.
@ -188,8 +186,7 @@ TEST(ParseTime, WithTimeZone) {
"2013-06-28 19:08:09 +0800", tz, &t, &e)) "2013-06-28 19:08:09 +0800", tz, &t, &e))
<< e; << e;
bd = t.In(absl::FixedTimeZone(8 * 60 * 60)); bd = t.In(absl::FixedTimeZone(8 * 60 * 60));
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 6, 28, 19, 8, 9, 8 * 60 * 60, false, ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 6, 28, 19, 8, 9, 8 * 60 * 60, false);
"UTC+8");
EXPECT_EQ(absl::ZeroDuration(), bd.subsecond); EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
} }

View File

@ -80,6 +80,7 @@ cc_test(
name = "time_zone_format_test", name = "time_zone_format_test",
size = "small", size = "small",
srcs = ["src/time_zone_format_test.cc"], srcs = ["src/time_zone_format_test.cc"],
data = [":zoneinfo"],
deps = [ deps = [
":civil_time", ":civil_time",
":time_zone", ":time_zone",
@ -91,6 +92,7 @@ cc_test(
name = "time_zone_lookup_test", name = "time_zone_lookup_test",
size = "small", size = "small",
srcs = ["src/time_zone_lookup_test.cc"], srcs = ["src/time_zone_lookup_test.cc"],
data = [":zoneinfo"],
deps = [ deps = [
":civil_time", ":civil_time",
":time_zone", ":time_zone",
@ -103,3 +105,8 @@ cc_test(
### examples ### examples
### binaries ### binaries
filegroup(
name = "zoneinfo",
srcs = glob(["testdata/zoneinfo/**"]),
)

View File

@ -20,8 +20,8 @@
#include <ostream> #include <ostream>
#include <type_traits> #include <type_traits>
// Disable constexpr support unless we are using clang in C++14 mode. // Disable constexpr support unless we are in C++14 mode.
#if __clang__ && __cpp_constexpr >= 201304 #if __cpp_constexpr >= 201304 || _MSC_VER >= 1910
#define CONSTEXPR_D constexpr // data #define CONSTEXPR_D constexpr // data
#define CONSTEXPR_F constexpr // function #define CONSTEXPR_F constexpr // function
#define CONSTEXPR_M constexpr // member #define CONSTEXPR_M constexpr // member

View File

@ -37,7 +37,7 @@ std::string Format(const T& t) {
} // namespace } // namespace
#if __clang__ && __cpp_constexpr >= 201304 #if __cpp_constexpr >= 201304 || _MSC_VER >= 1910
// Construction constexpr tests // Construction constexpr tests
TEST(CivilTime, Normal) { TEST(CivilTime, Normal) {
@ -319,7 +319,7 @@ TEST(CivilTime, YearDay) {
constexpr int yd = get_yearday(cd); constexpr int yd = get_yearday(cd);
static_assert(yd == 28, "YearDay"); static_assert(yd == 28, "YearDay");
} }
#endif // __clang__ && __cpp_constexpr >= 201304 #endif // __cpp_constexpr >= 201304 || _MSC_VER >= 1910
// The remaining tests do not use constexpr. // The remaining tests do not use constexpr.

View File

@ -27,7 +27,7 @@ namespace cctz {
namespace { namespace {
// The prefix used for the internal names of fixed-offset zones. // The prefix used for the internal names of fixed-offset zones.
const char kFixedOffsetPrefix[] = "Fixed/"; const char kFixedOffsetPrefix[] = "Fixed/UTC";
int Parse02d(const char* p) { int Parse02d(const char* p) {
static const char kDigits[] = "0123456789"; static const char kDigits[] = "0123456789";
@ -50,13 +50,11 @@ bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) {
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1; const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
const char* const ep = kFixedOffsetPrefix + prefix_len; const char* const ep = kFixedOffsetPrefix + prefix_len;
if (name.size() != prefix_len + 12) // "<prefix>UTC+99:99:99" if (name.size() != prefix_len + 9) // <prefix>+99:99:99
return false; return false;
if (!std::equal(kFixedOffsetPrefix, ep, name.begin())) if (!std::equal(kFixedOffsetPrefix, ep, name.begin()))
return false; return false;
const char* np = name.data() + prefix_len; const char* np = name.data() + prefix_len;
if (*np++ != 'U' || *np++ != 'T' || *np++ != 'C')
return false;
if (np[0] != '+' && np[0] != '-') if (np[0] != '+' && np[0] != '-')
return false; return false;
if (np[3] != ':' || np[6] != ':') // see note below about large offsets if (np[3] != ':' || np[6] != ':') // see note below about large offsets
@ -97,8 +95,8 @@ std::string FixedOffsetToName(const sys_seconds& offset) {
} }
int hours = minutes / 60; int hours = minutes / 60;
minutes %= 60; minutes %= 60;
char buf[sizeof(kFixedOffsetPrefix) + sizeof("UTC-24:00:00")]; char buf[sizeof(kFixedOffsetPrefix) + sizeof("-24:00:00")];
snprintf(buf, sizeof(buf), "%sUTC%c%02d:%02d:%02d", snprintf(buf, sizeof(buf), "%s%c%02d:%02d:%02d",
kFixedOffsetPrefix, sign, hours, minutes, seconds); kFixedOffsetPrefix, sign, hours, minutes, seconds);
return buf; return buf;
} }
@ -106,22 +104,14 @@ std::string FixedOffsetToName(const sys_seconds& offset) {
std::string FixedOffsetToAbbr(const sys_seconds& offset) { std::string FixedOffsetToAbbr(const sys_seconds& offset) {
std::string abbr = FixedOffsetToName(offset); std::string abbr = FixedOffsetToName(offset);
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1; const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
const char* const ep = kFixedOffsetPrefix + prefix_len; if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99
if (abbr.size() >= prefix_len) { abbr.erase(0, prefix_len); // +99:99:99
if (std::equal(kFixedOffsetPrefix, ep, abbr.begin())) { abbr.erase(6, 1); // +99:9999
abbr.erase(0, prefix_len); abbr.erase(3, 1); // +999999
if (abbr.size() == 12) { // UTC+99:99:99 if (abbr[5] == '0' && abbr[6] == '0') { // +999900
abbr.erase(9, 1); // UTC+99:9999 abbr.erase(5, 2); // +9999
abbr.erase(6, 1); // UTC+999999 if (abbr[3] == '0' && abbr[4] == '0') { // +9900
if (abbr[8] == '0' && abbr[9] == '0') { // UTC+999900 abbr.erase(3, 2); // +99
abbr.erase(8, 2); // UTC+9999
if (abbr[6] == '0' && abbr[7] == '0') { // UTC+9900
abbr.erase(6, 2); // UTC+99
if (abbr[4] == '0') { // UTC+09
abbr.erase(4, 1); // UTC+9
}
}
}
} }
} }
} }

View File

@ -134,6 +134,9 @@ time_zone local_time_zone() {
time_zone tz; time_zone tz;
load_time_zone(name, &tz); // Falls back to UTC. load_time_zone(name, &tz); // Falls back to UTC.
// TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
// arrange for %z to generate "-0000" when we don't know the local
// offset because the load_time_zone() failed and we're using UTC.
return tz; return tz;
} }

View File

@ -415,7 +415,6 @@ const char* const kTimeZoneNames[] = {
"CST6CDT", "CST6CDT",
"Canada/Atlantic", "Canada/Atlantic",
"Canada/Central", "Canada/Central",
"Canada/East-Saskatchewan",
"Canada/Eastern", "Canada/Eastern",
"Canada/Mountain", "Canada/Mountain",
"Canada/Newfoundland", "Canada/Newfoundland",
@ -1119,18 +1118,6 @@ TEST(TimeZoneEdgeCase, AfricaMonrovia) {
auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz); auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT"); ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
tp += seconds(1); tp += seconds(1);
#ifndef TZDATA_2017B_IS_UBIQUITOUS
// The 2017b tzdata release moved the shift from -004430 to +00
// from 1972-05-01 to 1972-01-07, so we temporarily accept both
// outcomes until 2017b is ubiquitous.
if (tz.lookup(tp).offset == -44.5 * 60) {
tp = convert(civil_second(1972, 4, 30, 23, 59, 59), tz);
ExpectTime(tp, tz, 1972, 4, 30, 23, 59, 59, -44.5 * 60, false, "LRT");
tp += seconds(1);
ExpectTime(tp, tz, 1972, 5, 1, 0, 44, 30, 0 * 60, false, "GMT");
return;
}
#endif
ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT"); ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
} }

View File

@ -1 +1 @@
2018d-2-g8d1dac0 2018e-2-g99dd695

View File

@ -26,6 +26,12 @@ namespace cctz = absl::time_internal::cctz;
namespace absl { namespace absl {
namespace time_internal { namespace time_internal {
#if GTEST_USES_SIMPLE_RE
extern const char kZoneAbbrRE[] = ".*"; // just punt
#else
extern const char kZoneAbbrRE[] = "[A-Za-z]{3,4}|[-+][0-9]{2}([0-9]{2})?";
#endif
TimeZone LoadTimeZone(const std::string& name) { TimeZone LoadTimeZone(const std::string& name) {
TimeZone tz; TimeZone tz;
ABSL_RAW_CHECK(LoadTimeZone(name, &tz), name.c_str()); ABSL_RAW_CHECK(LoadTimeZone(name, &tz), name.c_str());

View File

@ -17,6 +17,8 @@
#include <string> #include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/time/time.h" #include "absl/time/time.h"
// This helper is a macro so that failed expectations show up with the // This helper is a macro so that failed expectations show up with the
@ -24,22 +26,26 @@
// //
// This is for internal testing of the Base Time library itself. This is not // This is for internal testing of the Base Time library itself. This is not
// part of a public API. // part of a public API.
#define ABSL_INTERNAL_EXPECT_TIME(bd, y, m, d, h, min, s, off, isdst, zone) \ #define ABSL_INTERNAL_EXPECT_TIME(bd, y, m, d, h, min, s, off, isdst) \
do { \ do { \
EXPECT_EQ(y, bd.year); \ EXPECT_EQ(y, bd.year); \
EXPECT_EQ(m, bd.month); \ EXPECT_EQ(m, bd.month); \
EXPECT_EQ(d, bd.day); \ EXPECT_EQ(d, bd.day); \
EXPECT_EQ(h, bd.hour); \ EXPECT_EQ(h, bd.hour); \
EXPECT_EQ(min, bd.minute); \ EXPECT_EQ(min, bd.minute); \
EXPECT_EQ(s, bd.second); \ EXPECT_EQ(s, bd.second); \
EXPECT_EQ(off, bd.offset); \ EXPECT_EQ(off, bd.offset); \
EXPECT_EQ(isdst, bd.is_dst); \ EXPECT_EQ(isdst, bd.is_dst); \
EXPECT_STREQ(zone, bd.zone_abbr); \ EXPECT_THAT(bd.zone_abbr, \
testing::MatchesRegex(absl::time_internal::kZoneAbbrRE)); \
} while (0) } while (0)
namespace absl { namespace absl {
namespace time_internal { namespace time_internal {
// A regular expression that matches all zone abbreviations (%Z).
extern const char kZoneAbbrRE[];
// Loads the named timezone, but dies on any failure. // Loads the named timezone, but dies on any failure.
absl::TimeZone LoadTimeZone(const std::string& name); absl::TimeZone LoadTimeZone(const std::string& name);

View File

@ -71,7 +71,7 @@ inline absl::Time::Breakdown InfiniteFutureBreakdown() {
bd.yearday = 365; bd.yearday = 365;
bd.offset = 0; bd.offset = 0;
bd.is_dst = false; bd.is_dst = false;
bd.zone_abbr = "-0000"; bd.zone_abbr = "-00";
return bd; return bd;
} }
@ -88,7 +88,7 @@ inline Time::Breakdown InfinitePastBreakdown() {
bd.yearday = 1; bd.yearday = 1;
bd.offset = 0; bd.offset = 0;
bd.is_dst = false; bd.is_dst = false;
bd.zone_abbr = "-0000"; bd.zone_abbr = "-00";
return bd; return bd;
} }

View File

@ -18,6 +18,7 @@
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/time/internal/test_util.h" #include "absl/time/internal/test_util.h"
#include "absl/time/time.h" #include "absl/time/time.h"
@ -32,31 +33,31 @@ TEST(TimeNormCase, SimpleOverflow) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
absl::Time::Breakdown bd = tc.pre.In(utc); absl::Time::Breakdown bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 16, 33, 0, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 16, 33, 0, 0, false);
tc = absl::ConvertDateTime(2013, 11, 15, 16, 59 + 1, 14, utc); tc = absl::ConvertDateTime(2013, 11, 15, 16, 59 + 1, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 17, 0, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 17, 0, 14, 0, false);
tc = absl::ConvertDateTime(2013, 11, 15, 23 + 1, 32, 14, utc); tc = absl::ConvertDateTime(2013, 11, 15, 23 + 1, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 16, 0, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 16, 0, 32, 14, 0, false);
tc = absl::ConvertDateTime(2013, 11, 30 + 1, 16, 32, 14, utc); tc = absl::ConvertDateTime(2013, 11, 30 + 1, 16, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 12, 1, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 12, 1, 16, 32, 14, 0, false);
tc = absl::ConvertDateTime(2013, 12 + 1, 15, 16, 32, 14, utc); tc = absl::ConvertDateTime(2013, 12 + 1, 15, 16, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2014, 1, 15, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2014, 1, 15, 16, 32, 14, 0, false);
} }
TEST(TimeNormCase, SimpleUnderflow) { TEST(TimeNormCase, SimpleUnderflow) {
@ -66,31 +67,31 @@ TEST(TimeNormCase, SimpleUnderflow) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
absl::Time::Breakdown bd = tc.pre.In(utc); absl::Time::Breakdown bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 16, 31, 59, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 16, 31, 59, 0, false);
tc = ConvertDateTime(2013, 11, 15, 16, 0 - 1, 14, utc); tc = ConvertDateTime(2013, 11, 15, 16, 0 - 1, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 15, 59, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 15, 59, 14, 0, false);
tc = ConvertDateTime(2013, 11, 15, 0 - 1, 32, 14, utc); tc = ConvertDateTime(2013, 11, 15, 0 - 1, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 14, 23, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 14, 23, 32, 14, 0, false);
tc = ConvertDateTime(2013, 11, 1 - 1, 16, 32, 14, utc); tc = ConvertDateTime(2013, 11, 1 - 1, 16, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 10, 31, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 10, 31, 16, 32, 14, 0, false);
tc = ConvertDateTime(2013, 1 - 1, 15, 16, 32, 14, utc); tc = ConvertDateTime(2013, 1 - 1, 15, 16, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2012, 12, 15, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2012, 12, 15, 16, 32, 14, 0, false);
} }
TEST(TimeNormCase, MultipleOverflow) { TEST(TimeNormCase, MultipleOverflow) {
@ -99,7 +100,7 @@ TEST(TimeNormCase, MultipleOverflow) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
absl::Time::Breakdown bd = tc.pre.In(utc); absl::Time::Breakdown bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2014, 1, 1, 0, 0, 0, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2014, 1, 1, 0, 0, 0, 0, false);
} }
TEST(TimeNormCase, MultipleUnderflow) { TEST(TimeNormCase, MultipleUnderflow) {
@ -108,7 +109,7 @@ TEST(TimeNormCase, MultipleUnderflow) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
absl::Time::Breakdown bd = tc.pre.In(utc); absl::Time::Breakdown bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 12, 31, 23, 59, 59, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 12, 31, 23, 59, 59, 0, false);
} }
TEST(TimeNormCase, OverflowLimits) { TEST(TimeNormCase, OverflowLimits) {
@ -122,7 +123,7 @@ TEST(TimeNormCase, OverflowLimits) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 185085715, 11, 27, 12, 21, 7, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 185085715, 11, 27, 12, 21, 7, 0, false);
const int kintmin = std::numeric_limits<int>::min(); const int kintmin = std::numeric_limits<int>::min();
tc = absl::ConvertDateTime(0, kintmin, kintmin, kintmin, kintmin, kintmin, tc = absl::ConvertDateTime(0, kintmin, kintmin, kintmin, kintmin, kintmin,
@ -130,8 +131,7 @@ TEST(TimeNormCase, OverflowLimits) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, -185085717, 10, 31, 10, 37, 52, 0, false, ABSL_INTERNAL_EXPECT_TIME(bd, -185085717, 10, 31, 10, 37, 52, 0, false);
"UTC");
const int64_t max_year = std::numeric_limits<int64_t>::max(); const int64_t max_year = std::numeric_limits<int64_t>::max();
tc = absl::ConvertDateTime(max_year, 12, 31, 23, 59, 59, utc); tc = absl::ConvertDateTime(max_year, 12, 31, 23, 59, 59, utc);
@ -154,31 +154,31 @@ TEST(TimeNormCase, ComplexOverflow) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
absl::Time::Breakdown bd = tc.pre.In(utc); absl::Time::Breakdown bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2017, 10, 14, 14, 5, 23, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2017, 10, 14, 14, 5, 23, 0, false);
tc = absl::ConvertDateTime(2013, 11, 15, 16, 32 + 1234567, 14, utc); tc = absl::ConvertDateTime(2013, 11, 15, 16, 32 + 1234567, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2016, 3, 22, 0, 39, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2016, 3, 22, 0, 39, 14, 0, false);
tc = absl::ConvertDateTime(2013, 11, 15, 16 + 123456, 32, 14, utc); tc = absl::ConvertDateTime(2013, 11, 15, 16 + 123456, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2027, 12, 16, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2027, 12, 16, 16, 32, 14, 0, false);
tc = absl::ConvertDateTime(2013, 11, 15 + 1234, 16, 32, 14, utc); tc = absl::ConvertDateTime(2013, 11, 15 + 1234, 16, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2017, 4, 2, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2017, 4, 2, 16, 32, 14, 0, false);
tc = absl::ConvertDateTime(2013, 11 + 123, 15, 16, 32, 14, utc); tc = absl::ConvertDateTime(2013, 11 + 123, 15, 16, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2024, 2, 15, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2024, 2, 15, 16, 32, 14, 0, false);
} }
TEST(TimeNormCase, ComplexUnderflow) { TEST(TimeNormCase, ComplexUnderflow) {
@ -189,37 +189,37 @@ TEST(TimeNormCase, ComplexUnderflow) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
absl::Time::Breakdown bd = tc.pre.In(utc); absl::Time::Breakdown bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 1999, 2, 28, 0, 0, 0, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 1999, 2, 28, 0, 0, 0, 0, false);
tc = absl::ConvertDateTime(2013, 11, 15, 16, 32, 14 - 123456789, utc); tc = absl::ConvertDateTime(2013, 11, 15, 16, 32, 14 - 123456789, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2009, 12, 17, 18, 59, 5, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2009, 12, 17, 18, 59, 5, 0, false);
tc = absl::ConvertDateTime(2013, 11, 15, 16, 32 - 1234567, 14, utc); tc = absl::ConvertDateTime(2013, 11, 15, 16, 32 - 1234567, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2011, 7, 12, 8, 25, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2011, 7, 12, 8, 25, 14, 0, false);
tc = absl::ConvertDateTime(2013, 11, 15, 16 - 123456, 32, 14, utc); tc = absl::ConvertDateTime(2013, 11, 15, 16 - 123456, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 1999, 10, 16, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 1999, 10, 16, 16, 32, 14, 0, false);
tc = absl::ConvertDateTime(2013, 11, 15 - 1234, 16, 32, 14, utc); tc = absl::ConvertDateTime(2013, 11, 15 - 1234, 16, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2010, 6, 30, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2010, 6, 30, 16, 32, 14, 0, false);
tc = absl::ConvertDateTime(2013, 11 - 123, 15, 16, 32, 14, utc); tc = absl::ConvertDateTime(2013, 11 - 123, 15, 16, 32, 14, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2003, 8, 15, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2003, 8, 15, 16, 32, 14, 0, false);
} }
TEST(TimeNormCase, Mishmash) { TEST(TimeNormCase, Mishmash) {
@ -231,14 +231,14 @@ TEST(TimeNormCase, Mishmash) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
absl::Time::Breakdown bd = tc.pre.In(utc); absl::Time::Breakdown bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 1991, 5, 9, 3, 6, 5, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 1991, 5, 9, 3, 6, 5, 0, false);
tc = absl::ConvertDateTime(2013, 11 + 123, 15 - 1234, 16 + 123456, tc = absl::ConvertDateTime(2013, 11 + 123, 15 - 1234, 16 + 123456,
32 - 1234567, 14 + 123456789, utc); 32 - 1234567, 14 + 123456789, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2036, 5, 24, 5, 58, 23, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2036, 5, 24, 5, 58, 23, 0, false);
// Here is a normalization case we got wrong for a while. Because the // Here is a normalization case we got wrong for a while. Because the
// day is converted to "1" within a 400-year (146097-day) period, we // day is converted to "1" within a 400-year (146097-day) period, we
@ -247,7 +247,7 @@ TEST(TimeNormCase, Mishmash) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 1613, 11, 1, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 1613, 11, 1, 16, 32, 14, 0, false);
// Even though the month overflow compensates for the day underflow, // Even though the month overflow compensates for the day underflow,
// this should still be marked as normalized. // this should still be marked as normalized.
@ -255,7 +255,7 @@ TEST(TimeNormCase, Mishmash) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 1, 16, 32, 14, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 1, 16, 32, 14, 0, false);
} }
TEST(TimeNormCase, LeapYears) { TEST(TimeNormCase, LeapYears) {
@ -266,25 +266,25 @@ TEST(TimeNormCase, LeapYears) {
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
absl::Time::Breakdown bd = tc.pre.In(utc); absl::Time::Breakdown bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 3, 1, 0, 0, 0, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 3, 1, 0, 0, 0, 0, false);
tc = absl::ConvertDateTime(2012, 2, 28 + 1, 0, 0, 0, utc); tc = absl::ConvertDateTime(2012, 2, 28 + 1, 0, 0, 0, utc);
EXPECT_FALSE(tc.normalized); EXPECT_FALSE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2012, 2, 29, 0, 0, 0, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2012, 2, 29, 0, 0, 0, 0, false);
tc = absl::ConvertDateTime(2000, 2, 28 + 1, 0, 0, 0, utc); tc = absl::ConvertDateTime(2000, 2, 28 + 1, 0, 0, 0, utc);
EXPECT_FALSE(tc.normalized); EXPECT_FALSE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 2000, 2, 29, 0, 0, 0, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 2000, 2, 29, 0, 0, 0, 0, false);
tc = absl::ConvertDateTime(1900, 2, 28 + 1, 0, 0, 0, utc); tc = absl::ConvertDateTime(1900, 2, 28 + 1, 0, 0, 0, utc);
EXPECT_TRUE(tc.normalized); EXPECT_TRUE(tc.normalized);
EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind); EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
bd = tc.pre.In(utc); bd = tc.pre.In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, 1900, 3, 1, 0, 0, 0, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 1900, 3, 1, 0, 0, 0, 0, false);
} }
// Convert all the days from 1970-1-1 to 1970-1-146097 (aka 2369-12-31) // Convert all the days from 1970-1-1 to 1970-1-146097 (aka 2369-12-31)

View File

@ -85,7 +85,7 @@ TEST(Time, ValueSemantics) {
TEST(Time, UnixEpoch) { TEST(Time, UnixEpoch) {
absl::Time::Breakdown bd = absl::UnixEpoch().In(absl::UTCTimeZone()); absl::Time::Breakdown bd = absl::UnixEpoch().In(absl::UTCTimeZone());
ABSL_INTERNAL_EXPECT_TIME(bd, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); ABSL_INTERNAL_EXPECT_TIME(bd, 1970, 1, 1, 0, 0, 0, 0, false);
EXPECT_EQ(absl::ZeroDuration(), bd.subsecond); EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
EXPECT_EQ(4, bd.weekday); // Thursday EXPECT_EQ(4, bd.weekday); // Thursday
} }
@ -96,14 +96,14 @@ TEST(Time, Breakdown) {
// The Unix epoch as seen in NYC. // The Unix epoch as seen in NYC.
absl::Time::Breakdown bd = t.In(tz); absl::Time::Breakdown bd = t.In(tz);
ABSL_INTERNAL_EXPECT_TIME(bd, 1969, 12, 31, 19, 0, 0, -18000, false, "EST"); ABSL_INTERNAL_EXPECT_TIME(bd, 1969, 12, 31, 19, 0, 0, -18000, false);
EXPECT_EQ(absl::ZeroDuration(), bd.subsecond); EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
EXPECT_EQ(3, bd.weekday); // Wednesday EXPECT_EQ(3, bd.weekday); // Wednesday
// Just before the epoch. // Just before the epoch.
t -= absl::Nanoseconds(1); t -= absl::Nanoseconds(1);
bd = t.In(tz); bd = t.In(tz);
ABSL_INTERNAL_EXPECT_TIME(bd, 1969, 12, 31, 18, 59, 59, -18000, false, "EST"); ABSL_INTERNAL_EXPECT_TIME(bd, 1969, 12, 31, 18, 59, 59, -18000, false);
EXPECT_EQ(absl::Nanoseconds(999999999), bd.subsecond); EXPECT_EQ(absl::Nanoseconds(999999999), bd.subsecond);
EXPECT_EQ(3, bd.weekday); // Wednesday EXPECT_EQ(3, bd.weekday); // Wednesday
@ -112,7 +112,7 @@ TEST(Time, Breakdown) {
t += absl::Hours(18) + absl::Minutes(30) + absl::Seconds(15) + t += absl::Hours(18) + absl::Minutes(30) + absl::Seconds(15) +
absl::Nanoseconds(9); absl::Nanoseconds(9);
bd = t.In(tz); bd = t.In(tz);
ABSL_INTERNAL_EXPECT_TIME(bd, 1977, 6, 28, 14, 30, 15, -14400, true, "EDT"); ABSL_INTERNAL_EXPECT_TIME(bd, 1977, 6, 28, 14, 30, 15, -14400, true);
EXPECT_EQ(8, bd.subsecond / absl::Nanoseconds(1)); EXPECT_EQ(8, bd.subsecond / absl::Nanoseconds(1));
EXPECT_EQ(2, bd.weekday); // Tuesday EXPECT_EQ(2, bd.weekday); // Tuesday
} }
@ -983,16 +983,18 @@ TEST(Time, ConversionSaturation) {
// Checks how Time::In() saturates on infinities. // Checks how Time::In() saturates on infinities.
absl::Time::Breakdown bd = absl::InfiniteFuture().In(utc); absl::Time::Breakdown bd = absl::InfiniteFuture().In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, std::numeric_limits<int64_t>::max(), 12, 31, 23, ABSL_INTERNAL_EXPECT_TIME(bd, std::numeric_limits<int64_t>::max(), 12, 31, 23,
59, 59, 0, false, "-0000"); 59, 59, 0, false);
EXPECT_EQ(absl::InfiniteDuration(), bd.subsecond); EXPECT_EQ(absl::InfiniteDuration(), bd.subsecond);
EXPECT_EQ(4, bd.weekday); // Thursday EXPECT_EQ(4, bd.weekday); // Thursday
EXPECT_EQ(365, bd.yearday); EXPECT_EQ(365, bd.yearday);
EXPECT_STREQ("-00", bd.zone_abbr); // artifact of absl::Time::In()
bd = absl::InfinitePast().In(utc); bd = absl::InfinitePast().In(utc);
ABSL_INTERNAL_EXPECT_TIME(bd, std::numeric_limits<int64_t>::min(), 1, 1, 0, 0, ABSL_INTERNAL_EXPECT_TIME(bd, std::numeric_limits<int64_t>::min(), 1, 1, 0, 0,
0, 0, false, "-0000"); 0, 0, false);
EXPECT_EQ(-absl::InfiniteDuration(), bd.subsecond); EXPECT_EQ(-absl::InfiniteDuration(), bd.subsecond);
EXPECT_EQ(7, bd.weekday); // Sunday EXPECT_EQ(7, bd.weekday); // Sunday
EXPECT_EQ(1, bd.yearday); EXPECT_EQ(1, bd.yearday);
EXPECT_STREQ("-00", bd.zone_abbr); // artifact of absl::Time::In()
// Approach the maximal Time value from below. // Approach the maximal Time value from below.
t = absl::FromDateTime(292277026596, 12, 4, 15, 30, 6, utc); t = absl::FromDateTime(292277026596, 12, 4, 15, 30, 6, utc);
@ -1054,13 +1056,11 @@ TEST(Time, ExtendedConversionSaturation) {
// The maximal time converted in each zone. // The maximal time converted in each zone.
bd = max.In(syd); bd = max.In(syd);
ABSL_INTERNAL_EXPECT_TIME(bd, 292277026596, 12, 5, 2, 30, 7, 39600, true, ABSL_INTERNAL_EXPECT_TIME(bd, 292277026596, 12, 5, 2, 30, 7, 39600, true);
"AEDT");
t = absl::FromDateTime(292277026596, 12, 5, 2, 30, 7, syd); t = absl::FromDateTime(292277026596, 12, 5, 2, 30, 7, syd);
EXPECT_EQ(max, t); EXPECT_EQ(max, t);
bd = max.In(nyc); bd = max.In(nyc);
ABSL_INTERNAL_EXPECT_TIME(bd, 292277026596, 12, 4, 10, 30, 7, -18000, false, ABSL_INTERNAL_EXPECT_TIME(bd, 292277026596, 12, 4, 10, 30, 7, -18000, false);
"EST");
t = absl::FromDateTime(292277026596, 12, 4, 10, 30, 7, nyc); t = absl::FromDateTime(292277026596, 12, 4, 10, 30, 7, nyc);
EXPECT_EQ(max, t); EXPECT_EQ(max, t);

View File

@ -223,3 +223,18 @@ cc_test(
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
], ],
) )
cc_test(
name = "variant_exception_safety_test",
size = "small",
srcs = [
"variant_exception_safety_test.cc",
],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
deps = [
":variant",
"//absl/base:exception_safety_testing",
"//absl/memory",
"@com_google_googletest//:gtest_main",
],
)

View File

@ -29,6 +29,9 @@ absl_header_library(
TARGET TARGET
absl_any absl_any
PUBLIC_LIBRARIES PUBLIC_LIBRARIES
absl::bad_any_cast
absl::base
absl::meta
absl::utility absl::utility
PRIVATE_COMPILE_FLAGS PRIVATE_COMPILE_FLAGS
${ABSL_EXCEPTIONS_FLAG} ${ABSL_EXCEPTIONS_FLAG}
@ -59,7 +62,6 @@ absl_library(
SOURCES SOURCES
${BAD_ANY_CAST_SRC} ${BAD_ANY_CAST_SRC}
PUBLIC_LIBRARIES PUBLIC_LIBRARIES
absl::base absl::any
EXPORT_NAME EXPORT_NAME
bad_any_cast bad_any_cast
) )
@ -76,7 +78,11 @@ absl_library(
SOURCES SOURCES
${OPTIONAL_SRC} ${OPTIONAL_SRC}
PUBLIC_LIBRARIES PUBLIC_LIBRARIES
absl::bad_optional_access
absl::base absl::base
absl::memory
absl::meta
absl::utility
EXPORT_NAME EXPORT_NAME
optional optional
) )
@ -143,7 +149,11 @@ absl_test(
# test any_exception_safety_test # test any_exception_safety_test
set(ANY_EXCEPTION_SAFETY_TEST_SRC "any_exception_safety_test.cc") set(ANY_EXCEPTION_SAFETY_TEST_SRC "any_exception_safety_test.cc")
set(ANY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES absl::any absl::base absl::base_internal_exception_safety_testing) set(ANY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES
absl::any
absl::base
absl_base_internal_exception_safety_testing
)
absl_test( absl_test(
TARGET TARGET

View File

@ -20,21 +20,16 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/internal/exception_safety_testing.h" #include "absl/base/internal/exception_safety_testing.h"
using Thrower = absl::ThrowingValue<>; using Thrower = testing::ThrowingValue<>;
using NoThrowMoveThrower = using NoThrowMoveThrower =
absl::ThrowingValue<absl::NoThrow::kMoveCtor | absl::NoThrow::kMoveAssign>; testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
using ThrowerList = std::initializer_list<Thrower>; using ThrowerList = std::initializer_list<Thrower>;
using ThrowerVec = std::vector<Thrower>; using ThrowerVec = std::vector<Thrower>;
using ThrowingAlloc = absl::ThrowingAllocator<Thrower>; using ThrowingAlloc = testing::ThrowingAllocator<Thrower>;
using ThrowingThrowerVec = std::vector<Thrower, ThrowingAlloc>; using ThrowingThrowerVec = std::vector<Thrower, ThrowingAlloc>;
namespace { namespace {
class AnyExceptionSafety : public ::testing::Test {
private:
absl::ConstructorTracker inspector_;
};
testing::AssertionResult AnyInvariants(absl::any* a) { testing::AssertionResult AnyInvariants(absl::any* a) {
using testing::AssertionFailure; using testing::AssertionFailure;
using testing::AssertionSuccess; using testing::AssertionSuccess;
@ -84,30 +79,33 @@ testing::AssertionResult AnyIsEmpty(absl::any* a) {
<< absl::any_cast<Thrower>(*a).Get(); << absl::any_cast<Thrower>(*a).Get();
} }
TEST_F(AnyExceptionSafety, Ctors) { TEST(AnyExceptionSafety, Ctors) {
Thrower val(1); Thrower val(1);
auto with_val = absl::TestThrowingCtor<absl::any>(val); testing::TestThrowingCtor<absl::any>(val);
auto copy = absl::TestThrowingCtor<absl::any>(with_val);
auto in_place = Thrower copy(val);
absl::TestThrowingCtor<absl::any>(absl::in_place_type_t<Thrower>(), 1); testing::TestThrowingCtor<absl::any>(copy);
auto in_place_list = absl::TestThrowingCtor<absl::any>(
absl::in_place_type_t<ThrowerVec>(), ThrowerList{val}); testing::TestThrowingCtor<absl::any>(absl::in_place_type_t<Thrower>(), 1);
auto in_place_list_again =
absl::TestThrowingCtor<absl::any, testing::TestThrowingCtor<absl::any>(absl::in_place_type_t<ThrowerVec>(),
absl::in_place_type_t<ThrowingThrowerVec>, ThrowerList{val});
ThrowerList, ThrowingAlloc>(
absl::in_place_type_t<ThrowingThrowerVec>(), {val}, ThrowingAlloc()); testing::TestThrowingCtor<absl::any,
absl::in_place_type_t<ThrowingThrowerVec>,
ThrowerList, ThrowingAlloc>(
absl::in_place_type_t<ThrowingThrowerVec>(), {val}, ThrowingAlloc());
} }
TEST_F(AnyExceptionSafety, Assignment) { TEST(AnyExceptionSafety, Assignment) {
auto original = auto original =
absl::any(absl::in_place_type_t<Thrower>(), 1, absl::no_throw_ctor); absl::any(absl::in_place_type_t<Thrower>(), 1, testing::nothrow_ctor);
auto any_is_strong = [original](absl::any* ap) { auto any_is_strong = [original](absl::any* ap) {
return testing::AssertionResult(ap->has_value() && return testing::AssertionResult(ap->has_value() &&
absl::any_cast<Thrower>(original) == absl::any_cast<Thrower>(original) ==
absl::any_cast<Thrower>(*ap)); absl::any_cast<Thrower>(*ap));
}; };
auto any_strong_tester = absl::MakeExceptionSafetyTester() auto any_strong_tester = testing::MakeExceptionSafetyTester()
.WithInitialValue(original) .WithInitialValue(original)
.WithInvariants(AnyInvariants, any_is_strong); .WithInvariants(AnyInvariants, any_is_strong);
@ -129,7 +127,7 @@ TEST_F(AnyExceptionSafety, Assignment) {
return testing::AssertionResult{!ap->has_value()}; return testing::AssertionResult{!ap->has_value()};
}; };
auto strong_empty_any_tester = auto strong_empty_any_tester =
absl::MakeExceptionSafetyTester() testing::MakeExceptionSafetyTester()
.WithInitialValue(absl::any{}) .WithInitialValue(absl::any{})
.WithInvariants(AnyInvariants, empty_any_is_strong); .WithInvariants(AnyInvariants, empty_any_is_strong);
@ -139,16 +137,16 @@ TEST_F(AnyExceptionSafety, Assignment) {
} }
// libstdc++ std::any fails this test // libstdc++ std::any fails this test
#if !defined(ABSL_HAVE_STD_ANY) #if !defined(ABSL_HAVE_STD_ANY)
TEST_F(AnyExceptionSafety, Emplace) { TEST(AnyExceptionSafety, Emplace) {
auto initial_val = auto initial_val =
absl::any{absl::in_place_type_t<Thrower>(), 1, absl::no_throw_ctor}; absl::any{absl::in_place_type_t<Thrower>(), 1, testing::nothrow_ctor};
auto one_tester = absl::MakeExceptionSafetyTester() auto one_tester = testing::MakeExceptionSafetyTester()
.WithInitialValue(initial_val) .WithInitialValue(initial_val)
.WithInvariants(AnyInvariants, AnyIsEmpty); .WithInvariants(AnyInvariants, AnyIsEmpty);
auto emp_thrower = [](absl::any* ap) { ap->emplace<Thrower>(2); }; auto emp_thrower = [](absl::any* ap) { ap->emplace<Thrower>(2); };
auto emp_throwervec = [](absl::any* ap) { auto emp_throwervec = [](absl::any* ap) {
std::initializer_list<Thrower> il{Thrower(2, absl::no_throw_ctor)}; std::initializer_list<Thrower> il{Thrower(2, testing::nothrow_ctor)};
ap->emplace<ThrowerVec>(il); ap->emplace<ThrowerVec>(il);
}; };
auto emp_movethrower = [](absl::any* ap) { auto emp_movethrower = [](absl::any* ap) {

View File

@ -776,10 +776,13 @@ class optional : private optional_internal::optional_data<T>,
// `optional` is empty, behavior is undefined. // `optional` is empty, behavior is undefined.
// //
// If you need myOpt->foo in constexpr, use (*myOpt).foo instead. // If you need myOpt->foo in constexpr, use (*myOpt).foo instead.
const T* operator->() const { return this->pointer(); } const T* operator->() const {
assert(this->engaged_);
return std::addressof(this->data_);
}
T* operator->() { T* operator->() {
assert(this->engaged_); assert(this->engaged_);
return this->pointer(); return std::addressof(this->data_);
} }
// optional::operator*() // optional::operator*()
@ -817,6 +820,12 @@ class optional : private optional_internal::optional_data<T>,
// only if `*this` is empty. // only if `*this` is empty.
constexpr bool has_value() const noexcept { return this->engaged_; } constexpr bool has_value() const noexcept { return this->engaged_; }
// Suppress bogus warning on MSVC: MSVC complains call to reference() after
// throw_bad_optional_access() is unreachable.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4702)
#endif // _MSC_VER
// optional::value() // optional::value()
// //
// Returns a reference to an `optional`s underlying value. The constness // Returns a reference to an `optional`s underlying value. The constness
@ -845,6 +854,9 @@ class optional : private optional_internal::optional_data<T>,
? reference() ? reference()
: (optional_internal::throw_bad_optional_access(), reference())); : (optional_internal::throw_bad_optional_access(), reference()));
} }
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
// optional::value_or() // optional::value_or()
// //
@ -871,10 +883,6 @@ class optional : private optional_internal::optional_data<T>,
} }
private: private:
// Private accessors for internal storage viewed as pointer to T.
const T* pointer() const { return std::addressof(this->data_); }
T* pointer() { return std::addressof(this->data_); }
// Private accessors for internal storage viewed as reference to T. // Private accessors for internal storage viewed as reference to T.
constexpr const T& reference() const { return this->data_; } constexpr const T& reference() const { return this->data_; }
T& reference() { return this->data_; } T& reference() { return this->data_; }
@ -958,7 +966,8 @@ constexpr auto operator==(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x == *y)) { -> decltype(optional_internal::convertible_to_bool(*x == *y)) {
return static_cast<bool>(x) != static_cast<bool>(y) return static_cast<bool>(x) != static_cast<bool>(y)
? false ? false
: static_cast<bool>(x) == false ? true : *x == *y; : static_cast<bool>(x) == false ? true
: static_cast<bool>(*x == *y);
} }
// Returns: If bool(x) != bool(y), true; otherwise, if bool(x) == false, false; // Returns: If bool(x) != bool(y), true; otherwise, if bool(x) == false, false;
@ -968,31 +977,32 @@ constexpr auto operator!=(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x != *y)) { -> decltype(optional_internal::convertible_to_bool(*x != *y)) {
return static_cast<bool>(x) != static_cast<bool>(y) return static_cast<bool>(x) != static_cast<bool>(y)
? true ? true
: static_cast<bool>(x) == false ? false : *x != *y; : static_cast<bool>(x) == false ? false
: static_cast<bool>(*x != *y);
} }
// Returns: If !y, false; otherwise, if !x, true; otherwise *x < *y. // Returns: If !y, false; otherwise, if !x, true; otherwise *x < *y.
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator<(const optional<T>& x, const optional<U>& y) constexpr auto operator<(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x < *y)) { -> decltype(optional_internal::convertible_to_bool(*x < *y)) {
return !y ? false : !x ? true : *x < *y; return !y ? false : !x ? true : static_cast<bool>(*x < *y);
} }
// Returns: If !x, false; otherwise, if !y, true; otherwise *x > *y. // Returns: If !x, false; otherwise, if !y, true; otherwise *x > *y.
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator>(const optional<T>& x, const optional<U>& y) constexpr auto operator>(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x > *y)) { -> decltype(optional_internal::convertible_to_bool(*x > *y)) {
return !x ? false : !y ? true : *x > *y; return !x ? false : !y ? true : static_cast<bool>(*x > *y);
} }
// Returns: If !x, true; otherwise, if !y, false; otherwise *x <= *y. // Returns: If !x, true; otherwise, if !y, false; otherwise *x <= *y.
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator<=(const optional<T>& x, const optional<U>& y) constexpr auto operator<=(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x <= *y)) { -> decltype(optional_internal::convertible_to_bool(*x <= *y)) {
return !x ? true : !y ? false : *x <= *y; return !x ? true : !y ? false : static_cast<bool>(*x <= *y);
} }
// Returns: If !y, true; otherwise, if !x, false; otherwise *x >= *y. // Returns: If !y, true; otherwise, if !x, false; otherwise *x >= *y.
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator>=(const optional<T>& x, const optional<U>& y) constexpr auto operator>=(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x >= *y)) { -> decltype(optional_internal::convertible_to_bool(*x >= *y)) {
return !y ? true : !x ? false : *x >= *y; return !y ? true : !x ? false : static_cast<bool>(*x >= *y);
} }
// Comparison with nullopt [optional.nullops] // Comparison with nullopt [optional.nullops]
@ -1054,62 +1064,62 @@ constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept {
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator==(const optional<T>& x, const U& v) constexpr auto operator==(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x == v)) { -> decltype(optional_internal::convertible_to_bool(*x == v)) {
return static_cast<bool>(x) ? *x == v : false; return static_cast<bool>(x) ? static_cast<bool>(*x == v) : false;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator==(const U& v, const optional<T>& x) constexpr auto operator==(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v == *x)) { -> decltype(optional_internal::convertible_to_bool(v == *x)) {
return static_cast<bool>(x) ? v == *x : false; return static_cast<bool>(x) ? static_cast<bool>(v == *x) : false;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator!=(const optional<T>& x, const U& v) constexpr auto operator!=(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x != v)) { -> decltype(optional_internal::convertible_to_bool(*x != v)) {
return static_cast<bool>(x) ? *x != v : true; return static_cast<bool>(x) ? static_cast<bool>(*x != v) : true;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator!=(const U& v, const optional<T>& x) constexpr auto operator!=(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v != *x)) { -> decltype(optional_internal::convertible_to_bool(v != *x)) {
return static_cast<bool>(x) ? v != *x : true; return static_cast<bool>(x) ? static_cast<bool>(v != *x) : true;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator<(const optional<T>& x, const U& v) constexpr auto operator<(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x < v)) { -> decltype(optional_internal::convertible_to_bool(*x < v)) {
return static_cast<bool>(x) ? *x < v : true; return static_cast<bool>(x) ? static_cast<bool>(*x < v) : true;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator<(const U& v, const optional<T>& x) constexpr auto operator<(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v < *x)) { -> decltype(optional_internal::convertible_to_bool(v < *x)) {
return static_cast<bool>(x) ? v < *x : false; return static_cast<bool>(x) ? static_cast<bool>(v < *x) : false;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator<=(const optional<T>& x, const U& v) constexpr auto operator<=(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x <= v)) { -> decltype(optional_internal::convertible_to_bool(*x <= v)) {
return static_cast<bool>(x) ? *x <= v : true; return static_cast<bool>(x) ? static_cast<bool>(*x <= v) : true;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator<=(const U& v, const optional<T>& x) constexpr auto operator<=(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v <= *x)) { -> decltype(optional_internal::convertible_to_bool(v <= *x)) {
return static_cast<bool>(x) ? v <= *x : false; return static_cast<bool>(x) ? static_cast<bool>(v <= *x) : false;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator>(const optional<T>& x, const U& v) constexpr auto operator>(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x > v)) { -> decltype(optional_internal::convertible_to_bool(*x > v)) {
return static_cast<bool>(x) ? *x > v : false; return static_cast<bool>(x) ? static_cast<bool>(*x > v) : false;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator>(const U& v, const optional<T>& x) constexpr auto operator>(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v > *x)) { -> decltype(optional_internal::convertible_to_bool(v > *x)) {
return static_cast<bool>(x) ? v > *x : true; return static_cast<bool>(x) ? static_cast<bool>(v > *x) : true;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator>=(const optional<T>& x, const U& v) constexpr auto operator>=(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x >= v)) { -> decltype(optional_internal::convertible_to_bool(*x >= v)) {
return static_cast<bool>(x) ? *x >= v : false; return static_cast<bool>(x) ? static_cast<bool>(*x >= v) : false;
} }
template <typename T, typename U> template <typename T, typename U>
constexpr auto operator>=(const U& v, const optional<T>& x) constexpr auto operator>=(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v >= *x)) { -> decltype(optional_internal::convertible_to_bool(v >= *x)) {
return static_cast<bool>(x) ? v >= *x : true; return static_cast<bool>(x) ? static_cast<bool>(v >= *x) : true;
} }
} // namespace absl } // namespace absl

View File

@ -290,7 +290,8 @@ class Span {
constexpr Span(T (&a)[N]) noexcept // NOLINT(runtime/explicit) constexpr Span(T (&a)[N]) noexcept // NOLINT(runtime/explicit)
: Span(a, N) {} : Span(a, N) {}
// Explicit reference constructor for a mutable `Span<T>` type // Explicit reference constructor for a mutable `Span<T>` type. Can be
// replaced with MakeSpan() to infer the type parameter.
template <typename V, typename = EnableIfConvertibleFrom<V>, template <typename V, typename = EnableIfConvertibleFrom<V>,
typename = EnableIfMutableView<V>> typename = EnableIfMutableView<V>>
explicit Span(V& v) noexcept // NOLINT(runtime/references) explicit Span(V& v) noexcept // NOLINT(runtime/references)
@ -458,10 +459,20 @@ class Span {
// Span::subspan() // Span::subspan()
// //
// Returns a `Span` starting at element `pos` and of length `len`, with // Returns a `Span` starting at element `pos` and of length `len`. Both `pos`
// proper bounds checking to ensure `len` does not exceed the ptr+size of the // and `len` are of type `size_type` and thus non-negative. Parameter `pos`
// original array. (Spans whose `len` would point past the end of the array // must be <= size(). Any `len` value that points past the end of the span
// will throw a `std::out_of_range`.) // will be trimmed to at most size() - `pos`. A default `len` value of `npos`
// ensures the returned subspan continues until the end of the span.
//
// Examples:
//
// std::vector<int> vec = {10, 11, 12, 13};
// absl::MakeSpan(vec).subspan(1, 2); // {11, 12}
// absl::MakeSpan(vec).subspan(2, 8); // {12, 13}
// absl::MakeSpan(vec).subspan(1); // {11, 12, 13}
// absl::MakeSpan(vec).subspan(4); // {}
// absl::MakeSpan(vec).subspan(5); // throws std::out_of_range
constexpr Span subspan(size_type pos = 0, size_type len = npos) const { constexpr Span subspan(size_type pos = 0, size_type len = npos) const {
return (pos <= len_) return (pos <= len_)
? Span(ptr_ + pos, span_internal::Min(len_ - pos, len)) ? Span(ptr_ + pos, span_internal::Min(len_ - pos, len))

View File

@ -350,7 +350,7 @@ constexpr const variant_alternative_t<I, variant<Types...>>&& get(
// get_if() // get_if()
// //
// Returns a pointer to the value currently stored within a given variant, if // Returns a pointer to the value currently stored within a given variant, if
// present, using either a unique alternative type amonst the variant's set of // present, using either a unique alternative type amongst the variant's set of
// alternative types, or the variant's index value. If such a value does not // alternative types, or the variant's index value. If such a value does not
// exist, returns `nullptr`. // exist, returns `nullptr`.
// //

View File

@ -0,0 +1,519 @@
// Copyright 2017 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
//
// http://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 "absl/types/variant.h"
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/exception_safety_testing.h"
#include "absl/memory/memory.h"
namespace absl {
namespace {
using ::testing::MakeExceptionSafetyTester;
using ::testing::nothrow_guarantee;
using ::testing::strong_guarantee;
using ::testing::TestThrowingCtor;
using Thrower = testing::ThrowingValue<>;
using CopyNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowCopy>;
using MoveNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
using ThrowingAlloc = testing::ThrowingAllocator<Thrower>;
using ThrowerVec = std::vector<Thrower, ThrowingAlloc>;
using ThrowingVariant =
absl::variant<Thrower, CopyNothrow, MoveNothrow, ThrowerVec>;
struct ConversionException {};
template <class T>
struct ExceptionOnConversion {
operator T() const { // NOLINT
throw ConversionException();
}
};
// Forces a variant into the valueless by exception state.
void ToValuelessByException(ThrowingVariant& v) { // NOLINT
try {
v.emplace<Thrower>();
v.emplace<Thrower>(ExceptionOnConversion<Thrower>());
} catch (ConversionException& /*e*/) {
// This space intentionally left blank.
}
}
// Check that variant is still in a usable state after an exception is thrown.
testing::AssertionResult CheckInvariants(ThrowingVariant* v) {
using testing::AssertionFailure;
using testing::AssertionSuccess;
// Try using the active alternative
if (absl::holds_alternative<Thrower>(*v)) {
auto& t = absl::get<Thrower>(*v);
t = Thrower{-100};
if (t.Get() != -100) {
return AssertionFailure() << "Thrower should be assigned -100";
}
} else if (absl::holds_alternative<ThrowerVec>(*v)) {
auto& tv = absl::get<ThrowerVec>(*v);
tv.clear();
tv.emplace_back(-100);
if (tv.size() != 1 || tv[0].Get() != -100) {
return AssertionFailure() << "ThrowerVec should be {Thrower{-100}}";
}
} else if (absl::holds_alternative<CopyNothrow>(*v)) {
auto& t = absl::get<CopyNothrow>(*v);
t = CopyNothrow{-100};
if (t.Get() != -100) {
return AssertionFailure() << "CopyNothrow should be assigned -100";
}
} else if (absl::holds_alternative<MoveNothrow>(*v)) {
auto& t = absl::get<MoveNothrow>(*v);
t = MoveNothrow{-100};
if (t.Get() != -100) {
return AssertionFailure() << "MoveNothrow should be assigned -100";
}
}
// Try making variant valueless_by_exception
if (!v->valueless_by_exception()) ToValuelessByException(*v);
if (!v->valueless_by_exception()) {
return AssertionFailure() << "Variant should be valueless_by_exception";
}
try {
auto unused = absl::get<Thrower>(*v);
static_cast<void>(unused);
return AssertionFailure() << "Variant should not contain Thrower";
} catch (absl::bad_variant_access) {
} catch (...) {
return AssertionFailure() << "Unexpected exception throw from absl::get";
}
// Try using the variant
v->emplace<Thrower>(100);
if (!absl::holds_alternative<Thrower>(*v) ||
absl::get<Thrower>(*v) != Thrower(100)) {
return AssertionFailure() << "Variant should contain Thrower(100)";
}
v->emplace<ThrowerVec>({Thrower(100)});
if (!absl::holds_alternative<ThrowerVec>(*v) ||
absl::get<ThrowerVec>(*v)[0] != Thrower(100)) {
return AssertionFailure()
<< "Variant should contain ThrowerVec{Thrower(100)}";
}
return AssertionSuccess();
}
Thrower ExpectedThrower() { return Thrower(42); }
ThrowerVec ExpectedThrowerVec() { return {Thrower(100), Thrower(200)}; }
ThrowingVariant ValuelessByException() {
ThrowingVariant v;
ToValuelessByException(v);
return v;
}
ThrowingVariant WithThrower() { return Thrower(39); }
ThrowingVariant WithThrowerVec() {
return ThrowerVec{Thrower(1), Thrower(2), Thrower(3)};
}
ThrowingVariant WithCopyNoThrow() { return CopyNothrow(39); }
ThrowingVariant WithMoveNoThrow() { return MoveNothrow(39); }
TEST(VariantExceptionSafetyTest, DefaultConstructor) {
TestThrowingCtor<ThrowingVariant>();
}
TEST(VariantExceptionSafetyTest, CopyConstructor) {
{
ThrowingVariant v(ExpectedThrower());
TestThrowingCtor<ThrowingVariant>(v);
}
{
ThrowingVariant v(ExpectedThrowerVec());
TestThrowingCtor<ThrowingVariant>(v);
}
{
ThrowingVariant v(ValuelessByException());
TestThrowingCtor<ThrowingVariant>(v);
}
}
TEST(VariantExceptionSafetyTest, MoveConstructor) {
{
ThrowingVariant v(ExpectedThrower());
TestThrowingCtor<ThrowingVariant>(std::move(v));
}
{
ThrowingVariant v(ExpectedThrowerVec());
TestThrowingCtor<ThrowingVariant>(std::move(v));
}
{
ThrowingVariant v(ValuelessByException());
TestThrowingCtor<ThrowingVariant>(std::move(v));
}
}
TEST(VariantExceptionSafetyTest, ValueConstructor) {
TestThrowingCtor<ThrowingVariant>(ExpectedThrower());
TestThrowingCtor<ThrowingVariant>(ExpectedThrowerVec());
}
TEST(VariantExceptionSafetyTest, InPlaceTypeConstructor) {
TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<Thrower>{},
ExpectedThrower());
TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<ThrowerVec>{},
ExpectedThrowerVec());
}
TEST(VariantExceptionSafetyTest, InPlaceIndexConstructor) {
TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<0>{},
ExpectedThrower());
TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<3>{},
ExpectedThrowerVec());
}
TEST(VariantExceptionSafetyTest, CopyAssign) {
// variant& operator=(const variant& rhs);
// Let j be rhs.index()
{
// - neither *this nor rhs holds a value
const ThrowingVariant rhs = ValuelessByException();
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(ValuelessByException())
.WithInvariants(nothrow_guarantee)
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
}
{
// - *this holds a value but rhs does not
const ThrowingVariant rhs = ValuelessByException();
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithInvariants(nothrow_guarantee)
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
}
// - index() == j
{
const ThrowingVariant rhs(ExpectedThrower());
auto tester =
MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test());
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
}
{
const ThrowingVariant rhs(ExpectedThrowerVec());
auto tester =
MakeExceptionSafetyTester()
.WithInitialValue(WithThrowerVec())
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test());
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
}
// libstdc++ std::variant has bugs on copy assignment regarding exception
// safety.
#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
// index() != j
// if is_nothrow_copy_constructible_v<Tj> or
// !is_nothrow_move_constructible<Tj> is true, equivalent to
// emplace<j>(get<j>(rhs))
{
// is_nothrow_copy_constructible_v<Tj> == true
// should not throw because emplace() invokes Tj's copy ctor
// which should not throw.
const ThrowingVariant rhs(CopyNothrow{});
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithInvariants(nothrow_guarantee)
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
}
{
// is_nothrow_copy_constructible<Tj> == false &&
// is_nothrow_move_constructible<Tj> == false
// should provide basic guarantee because emplace() invokes Tj's copy ctor
// which may throw.
const ThrowingVariant rhs(ExpectedThrower());
auto tester =
MakeExceptionSafetyTester()
.WithInitialValue(WithCopyNoThrow())
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
EXPECT_TRUE(tester
.WithInvariants(CheckInvariants,
[](ThrowingVariant* lhs) {
return lhs->valueless_by_exception();
})
.Test());
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
}
#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
{
// is_nothrow_copy_constructible_v<Tj> == false &&
// is_nothrow_move_constructible_v<Tj> == true
// should provide strong guarantee because it is equivalent to
// operator=(variant(rhs)) which creates a temporary then invoke the move
// ctor which shouldn't throw.
const ThrowingVariant rhs(MoveNothrow{});
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithInvariants(CheckInvariants, strong_guarantee)
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
}
}
TEST(VariantExceptionSafetyTest, MoveAssign) {
// variant& operator=(variant&& rhs);
// Let j be rhs.index()
{
// - neither *this nor rhs holds a value
ThrowingVariant rhs = ValuelessByException();
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(ValuelessByException())
.WithInvariants(nothrow_guarantee)
.Test([rhs](ThrowingVariant* lhs) mutable {
*lhs = std::move(rhs);
}));
}
{
// - *this holds a value but rhs does not
ThrowingVariant rhs = ValuelessByException();
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithInvariants(nothrow_guarantee)
.Test([rhs](ThrowingVariant* lhs) mutable {
*lhs = std::move(rhs);
}));
}
{
// - index() == j
// assign get<j>(std::move(rhs)) to the value contained in *this.
// If an exception is thrown during call to Tj's move assignment, the state
// of the contained value is as defined by the exception safety guarantee of
// Tj's move assignment; index() will be j.
ThrowingVariant rhs(ExpectedThrower());
size_t j = rhs.index();
// Since Thrower's move assignment has basic guarantee, so should variant's.
auto tester = MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithOperation([rhs](ThrowingVariant* lhs) mutable {
*lhs = std::move(rhs);
});
EXPECT_TRUE(tester
.WithInvariants(
CheckInvariants,
[j](ThrowingVariant* lhs) { return lhs->index() == j; })
.Test());
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
}
{
// - otherwise (index() != j), equivalent to
// emplace<j>(get<j>(std::move(rhs)))
// - If an exception is thrown during the call to Tj's move construction
// (with j being rhs.index()), the variant will hold no value.
ThrowingVariant rhs(CopyNothrow{});
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithInvariants(CheckInvariants,
[](ThrowingVariant* lhs) {
return lhs->valueless_by_exception();
})
.Test([rhs](ThrowingVariant* lhs) mutable {
*lhs = std::move(rhs);
}));
}
}
TEST(VariantExceptionSafetyTest, ValueAssign) {
// template<class T> variant& operator=(T&& t);
// Let Tj be the type that is selected by overload resolution to be assigned.
{
// If *this holds a Tj, assigns std::forward<T>(t) to the value contained in
// *this. If an exception is thrown during the assignment of
// std::forward<T>(t) to the value contained in *this, the state of the
// contained value and t are as defined by the exception safety guarantee of
// the assignment expression; valueless_by_exception() will be false.
// Since Thrower's copy/move assignment has basic guarantee, so should
// variant's.
Thrower rhs = ExpectedThrower();
// copy assign
auto copy_tester =
MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithOperation([rhs](ThrowingVariant* lhs) { *lhs = rhs; });
EXPECT_TRUE(copy_tester
.WithInvariants(CheckInvariants,
[](ThrowingVariant* lhs) {
return !lhs->valueless_by_exception();
})
.Test());
EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test());
// move assign
auto move_tester = MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithOperation([rhs](ThrowingVariant* lhs) mutable {
*lhs = std::move(rhs);
});
EXPECT_TRUE(move_tester
.WithInvariants(CheckInvariants,
[](ThrowingVariant* lhs) {
return !lhs->valueless_by_exception();
})
.Test());
EXPECT_FALSE(move_tester.WithInvariants(strong_guarantee).Test());
}
// Otherwise (*this holds something else), if is_nothrow_constructible_v<Tj,
// T> || !is_nothrow_move_constructible_v<Tj> is true, equivalent to
// emplace<j>(std::forward<T>(t)).
// We simplify the test by letting T = `const Tj&` or `Tj&&`, so we can reuse
// the CopyNothrow and MoveNothrow types.
// if is_nothrow_constructible_v<Tj, T>
// (i.e. is_nothrow_copy/move_constructible_v<Tj>) is true, emplace() just
// invokes the copy/move constructor and it should not throw.
{
const CopyNothrow rhs;
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithInvariants(nothrow_guarantee)
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
}
{
MoveNothrow rhs;
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithInvariants(nothrow_guarantee)
.Test([rhs](ThrowingVariant* lhs) mutable {
*lhs = std::move(rhs);
}));
}
// if is_nothrow_constructible_v<Tj, T> == false &&
// is_nothrow_move_constructible<Tj> == false
// emplace() invokes the copy/move constructor which may throw so it should
// provide basic guarantee and variant object might not hold a value.
{
Thrower rhs = ExpectedThrower();
// copy
auto copy_tester =
MakeExceptionSafetyTester()
.WithInitialValue(WithCopyNoThrow())
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
EXPECT_TRUE(copy_tester
.WithInvariants(CheckInvariants,
[](ThrowingVariant* lhs) {
return lhs->valueless_by_exception();
})
.Test());
EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test());
// move
auto move_tester = MakeExceptionSafetyTester()
.WithInitialValue(WithCopyNoThrow())
.WithOperation([rhs](ThrowingVariant* lhs) mutable {
*lhs = std::move(rhs);
});
EXPECT_TRUE(move_tester
.WithInvariants(CheckInvariants,
[](ThrowingVariant* lhs) {
return lhs->valueless_by_exception();
})
.Test());
EXPECT_FALSE(move_tester.WithInvariants(strong_guarantee).Test());
}
// Otherwise (if is_nothrow_constructible_v<Tj, T> == false &&
// is_nothrow_move_constructible<Tj> == true),
// equivalent to operator=(variant(std::forward<T>(t)))
// This should have strong guarantee because it creates a temporary variant
// and operator=(variant&&) invokes Tj's move ctor which doesn't throw.
// libstdc++ std::variant has bugs on conversion assignment regarding
// exception safety.
#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
{
MoveNothrow rhs;
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithInvariants(CheckInvariants, strong_guarantee)
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
}
#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
}
TEST(VariantExceptionSafetyTest, Emplace) {
// If an exception during the initialization of the contained value, the
// variant might not hold a value. The standard requires emplace() to provide
// only basic guarantee.
{
Thrower args = ExpectedThrower();
auto tester = MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithOperation([&args](ThrowingVariant* v) {
v->emplace<Thrower>(args);
});
EXPECT_TRUE(tester
.WithInvariants(CheckInvariants,
[](ThrowingVariant* v) {
return v->valueless_by_exception();
})
.Test());
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
}
}
TEST(VariantExceptionSafetyTest, Swap) {
// if both are valueless_by_exception(), no effect
{
ThrowingVariant rhs = ValuelessByException();
EXPECT_TRUE(
MakeExceptionSafetyTester()
.WithInitialValue(ValuelessByException())
.WithInvariants(nothrow_guarantee)
.Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); }));
}
// if index() == rhs.index(), calls swap(get<i>(*this), get<i>(rhs))
// where i is index().
{
ThrowingVariant rhs = ExpectedThrower();
EXPECT_TRUE(
MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithInvariants(CheckInvariants)
.Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); }));
}
// Otherwise, exchanges the value of rhs and *this. The exception safety
// involves variant in moved-from state which is not specified in the
// standard, and since swap is 3-step it's impossible for it to provide a
// overall strong guarantee. So, we are only checking basic guarantee here.
{
ThrowingVariant rhs = ExpectedThrower();
EXPECT_TRUE(
MakeExceptionSafetyTester()
.WithInitialValue(WithCopyNoThrow())
.WithInvariants(CheckInvariants)
.Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); }));
}
{
ThrowingVariant rhs = ExpectedThrower();
EXPECT_TRUE(
MakeExceptionSafetyTester()
.WithInitialValue(WithCopyNoThrow())
.WithInvariants(CheckInvariants)
.Test([rhs](ThrowingVariant* lhs) mutable { rhs.swap(*lhs); }));
}
}
} // namespace
} // namespace absl

View File

@ -119,19 +119,9 @@ struct ConversionException {};
template <class T> template <class T>
struct ExceptionOnConversion { struct ExceptionOnConversion {
// Suppress MSVC 2017 warning "noreturn function has a non-void return type". operator T() const { // NOLINT(runtime/explicit)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4646)
#endif // _MSC_VER
[[noreturn]] operator T() const { // NOLINT(runtime/explicit)
throw ConversionException(); throw ConversionException();
} }
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
}; };
// Forces a variant into the valueless by exception state. // Forces a variant into the valueless by exception state.

View File

@ -22,6 +22,8 @@ list(APPEND UTILITY_PUBLIC_HEADERS
absl_header_library( absl_header_library(
TARGET TARGET
absl_utility absl_utility
PUBLIC_LIBRARIES
absl::base
EXPORT_NAME EXPORT_NAME
utility utility
) )
@ -33,7 +35,12 @@ absl_header_library(
# test utility_test # test utility_test
set(UTILITY_TEST_SRC "utility_test.cc") set(UTILITY_TEST_SRC "utility_test.cc")
set(UTILITY_TEST_PUBLIC_LIBRARIES absl::utility) set(UTILITY_TEST_PUBLIC_LIBRARIES
absl::base
absl::memory
absl::strings
absl::utility
)
absl_test( absl_test(
TARGET TARGET

View File

@ -24,6 +24,7 @@
// * make_index_sequence<N> == std::make_index_sequence<N> // * make_index_sequence<N> == std::make_index_sequence<N>
// * index_sequence_for<Ts...> == std::index_sequence_for<Ts...> // * index_sequence_for<Ts...> == std::index_sequence_for<Ts...>
// * apply<Functor, Tuple> == std::apply<Functor, Tuple> // * apply<Functor, Tuple> == std::apply<Functor, Tuple>
// * exchange<T> == std::exchange<T>
// //
// This header file also provides the tag types `in_place_t`, `in_place_type_t`, // This header file also provides the tag types `in_place_t`, `in_place_type_t`,
// and `in_place_index_t`, as well as the constant `in_place`, and // and `in_place_index_t`, as well as the constant `in_place`, and
@ -264,6 +265,27 @@ auto apply(Functor&& functor, Tuple&& t)
absl::make_index_sequence<std::tuple_size< absl::make_index_sequence<std::tuple_size<
typename std::remove_reference<Tuple>::type>::value>{}); typename std::remove_reference<Tuple>::type>::value>{});
} }
// exchange
//
// Replaces the value of `obj` with `new_value` and returns the old value of
// `obj`. `absl::exchange` is designed to be a drop-in replacement for C++14's
// `std::exchange`.
//
// Example:
//
// Foo& operator=(Foo&& other) {
// ptr1_ = absl::exchange(other.ptr1_, nullptr);
// int1_ = absl::exchange(other.int1_, -1);
// return *this;
// }
template <typename T, typename U = T>
T exchange(T& obj, U&& new_value) {
T old_value = absl::move(obj);
obj = absl::forward<U>(new_value);
return old_value;
}
} // namespace absl } // namespace absl
#endif // ABSL_UTILITY_UTILITY_H_ #endif // ABSL_UTILITY_UTILITY_H_

View File

@ -333,5 +333,13 @@ TEST(ApplyTest, FlipFlop) {
EXPECT_EQ(42, absl::apply(&FlipFlop::member, std::make_tuple(obj))); EXPECT_EQ(42, absl::apply(&FlipFlop::member, std::make_tuple(obj)));
} }
TEST(ExchangeTest, MoveOnly) {
auto a = Factory(1);
EXPECT_EQ(1, *a);
auto b = absl::exchange(a, Factory(2));
EXPECT_EQ(2, *a);
EXPECT_EQ(1, *b);
}
} // namespace } // namespace

View File

@ -22,7 +22,6 @@
'<(DEPTH)/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp:*',
'<(DEPTH)/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp:*',
'<(DEPTH)/chrome/browser/resources/chromeos/sys_internals/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/chromeos/sys_internals/compiled_resources2.gyp:*',
'<(DEPTH)/chrome/browser/resources/ntp4/compiled_resources2.gyp:*',
'<(DEPTH)/ui/webui/resources/cr_components/compiled_resources2.gyp:*', '<(DEPTH)/ui/webui/resources/cr_components/compiled_resources2.gyp:*',
'<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:*', '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:*',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:*', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:*',

View File

@ -1,3 +1,6 @@
michaelpg@chromium.org michaelpg@chromium.org
rdevlin.cronin@chromium.org rdevlin.cronin@chromium.org
stevenjb@chromium.org stevenjb@chromium.org
per-file accessibility_private.js=file://ui/accessibility/OWNERS
per-file automation.js=file://ui/accessibility/OWNERS

View File

@ -285,6 +285,7 @@ chrome.automation.StateType = {
*/ */
chrome.automation.ActionType = { chrome.automation.ActionType = {
BLUR: 'blur', BLUR: 'blur',
CLEAR_ACCESSIBILITY_FOCUS: 'clearAccessibilityFocus',
CUSTOM_ACTION: 'customAction', CUSTOM_ACTION: 'customAction',
DECREMENT: 'decrement', DECREMENT: 'decrement',
DO_DEFAULT: 'doDefault', DO_DEFAULT: 'doDefault',
@ -302,6 +303,7 @@ chrome.automation.ActionType = {
SCROLL_TO_MAKE_VISIBLE: 'scrollToMakeVisible', SCROLL_TO_MAKE_VISIBLE: 'scrollToMakeVisible',
SCROLL_TO_POINT: 'scrollToPoint', SCROLL_TO_POINT: 'scrollToPoint',
SCROLL_UP: 'scrollUp', SCROLL_UP: 'scrollUp',
SET_ACCESSIBILITY_FOCUS: 'setAccessibilityFocus',
SET_SCROLL_OFFSET: 'setScrollOffset', SET_SCROLL_OFFSET: 'setScrollOffset',
SET_SELECTION: 'setSelection', SET_SELECTION: 'setSelection',
SET_SEQUENTIAL_FOCUS_NAVIGATION_STARTING_POINT: 'setSequentialFocusNavigationStartingPoint', SET_SEQUENTIAL_FOCUS_NAVIGATION_STARTING_POINT: 'setSequentialFocusNavigationStartingPoint',

View File

@ -16,3 +16,5 @@ Local changes:
* set 'x' flags: "chmod 750 win32/genversion.bat" * set 'x' flags: "chmod 750 win32/genversion.bat"
* Apply https://git.xiph.org/?p=opus.git;a=commitdiff;h=46560534fcb5710a894a341c2f9526db58fd7087#patch1 * Apply https://git.xiph.org/?p=opus.git;a=commitdiff;h=46560534fcb5710a894a341c2f9526db58fd7087#patch1
* Apply https://github.com/xiph/opus/pull/73 * Apply https://github.com/xiph/opus/pull/73
* Apply https://github.com/xiph/opus/pull/87
* Make sure HB_gain is not NaN in an attempt to fix chromium:826914

View File

@ -80,7 +80,8 @@ opus_int silk_Encode( /* O Returns error co
opus_int nSamplesIn, /* I Number of samples in input vector */ opus_int nSamplesIn, /* I Number of samples in input vector */
ec_enc *psRangeEnc, /* I/O Compressor data structure */ ec_enc *psRangeEnc, /* I/O Compressor data structure */
opus_int32 *nBytesOut, /* I/O Number of bytes in payload (input: Max bytes) */ opus_int32 *nBytesOut, /* I/O Number of bytes in payload (input: Max bytes) */
const opus_int prefillFlag /* I Flag to indicate prefilling buffers no coding */ const opus_int prefillFlag, /* I Flag to indicate prefilling buffers no coding */
int activity /* I Decision of Opus voice activity detector */
); );
/****************************************/ /****************************************/

View File

@ -58,6 +58,11 @@ extern "C"
#define MAX_CONSECUTIVE_DTX 20 /* eq 400 ms */ #define MAX_CONSECUTIVE_DTX 20 /* eq 400 ms */
#define DTX_ACTIVITY_THRESHOLD 0.1f #define DTX_ACTIVITY_THRESHOLD 0.1f
/* VAD decision */
#define VAD_NO_DECISION -1
#define VAD_NO_ACTIVITY 0
#define VAD_ACTIVITY 1
/* Maximum sampling frequency */ /* Maximum sampling frequency */
#define MAX_FS_KHZ 16 #define MAX_FS_KHZ 16
#define MAX_API_FS_KHZ 48 #define MAX_API_FS_KHZ 48

View File

@ -144,7 +144,8 @@ opus_int silk_Encode( /* O Returns error co
opus_int nSamplesIn, /* I Number of samples in input vector */ opus_int nSamplesIn, /* I Number of samples in input vector */
ec_enc *psRangeEnc, /* I/O Compressor data structure */ ec_enc *psRangeEnc, /* I/O Compressor data structure */
opus_int32 *nBytesOut, /* I/O Number of bytes in payload (input: Max bytes) */ opus_int32 *nBytesOut, /* I/O Number of bytes in payload (input: Max bytes) */
const opus_int prefillFlag /* I Flag to indicate prefilling buffers no coding */ const opus_int prefillFlag, /* I Flag to indicate prefilling buffers no coding */
opus_int activity /* I Decision of Opus voice activity detector */
) )
{ {
opus_int n, i, nBits, flags, tmp_payloadSize_ms = 0, tmp_complexity = 0, ret = 0; opus_int n, i, nBits, flags, tmp_payloadSize_ms = 0, tmp_complexity = 0, ret = 0;
@ -425,7 +426,7 @@ opus_int silk_Encode( /* O Returns error co
psEnc->state_Fxx[ 1 ].sCmn.sNSQ.prev_gain_Q16 = 65536; psEnc->state_Fxx[ 1 ].sCmn.sNSQ.prev_gain_Q16 = 65536;
psEnc->state_Fxx[ 1 ].sCmn.first_frame_after_reset = 1; psEnc->state_Fxx[ 1 ].sCmn.first_frame_after_reset = 1;
} }
silk_encode_do_VAD_Fxx( &psEnc->state_Fxx[ 1 ] ); silk_encode_do_VAD_Fxx( &psEnc->state_Fxx[ 1 ], activity );
} else { } else {
psEnc->state_Fxx[ 1 ].sCmn.VAD_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] = 0; psEnc->state_Fxx[ 1 ].sCmn.VAD_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] = 0;
} }
@ -440,7 +441,7 @@ opus_int silk_Encode( /* O Returns error co
silk_memcpy( psEnc->state_Fxx[ 0 ].sCmn.inputBuf, psEnc->sStereo.sMid, 2 * sizeof( opus_int16 ) ); silk_memcpy( psEnc->state_Fxx[ 0 ].sCmn.inputBuf, psEnc->sStereo.sMid, 2 * sizeof( opus_int16 ) );
silk_memcpy( psEnc->sStereo.sMid, &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.frame_length ], 2 * sizeof( opus_int16 ) ); silk_memcpy( psEnc->sStereo.sMid, &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.frame_length ], 2 * sizeof( opus_int16 ) );
} }
silk_encode_do_VAD_Fxx( &psEnc->state_Fxx[ 0 ] ); silk_encode_do_VAD_Fxx( &psEnc->state_Fxx[ 0 ], activity );
/* Encode */ /* Encode */
for( n = 0; n < encControl->nChannelsInternal; n++ ) { for( n = 0; n < encControl->nChannelsInternal; n++ ) {

Some files were not shown because too many files have changed in this diff Show More