diff options
Diffstat (limited to 'absl/container')
-rw-r--r-- | absl/container/BUILD.bazel | 11 | ||||
-rw-r--r-- | absl/container/CMakeLists.txt | 19 | ||||
-rw-r--r-- | absl/container/fixed_array.h | 52 | ||||
-rw-r--r-- | absl/container/fixed_array_exception_safety_test.cc | 117 |
4 files changed, 170 insertions, 29 deletions
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 119d5c88de2a..07df3675207e 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -63,6 +63,17 @@ cc_test( ) cc_test( + name = "fixed_array_exception_safety_test", + srcs = ["fixed_array_exception_safety_test.cc"], + copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + deps = [ + ":fixed_array", + "//absl/base:exception_safety_testing", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( name = "fixed_array_benchmark", srcs = ["fixed_array_benchmark.cc"], copts = ABSL_TEST_COPTS + ["$(STACK_FRAME_UNLIMITED)"], diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index f56ce92d81b5..d580b48976cb 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -84,6 +84,25 @@ absl_test( ) +# test fixed_array_exception_safety_test +set(FIXED_ARRAY_EXCEPTION_SAFETY_TEST_SRC "fixed_array_exception_safety_test.cc") +set(FIXED_ARRAY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES + absl::container + absl_base_internal_exception_safety_testing +) + +absl_test( + TARGET + fixed_array_exception_safety_test + SOURCES + ${FIXED_ARRAY_EXCEPTION_SAFETY_TEST_SRC} + PUBLIC_LIBRARIES + ${FIXED_ARRAY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES} + PRIVATE_COMPILE_FLAGS + ${ABSL_EXCEPTIONS_FLAG} +) + + # test inlined_vector_test set(INLINED_VECTOR_TEST_SRC "inlined_vector_test.cc") set(INLINED_VECTOR_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib) diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index 06bc80095c98..295f01088d21 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -108,33 +108,46 @@ class FixedArray { ? kInlineBytesDefault / sizeof(value_type) : inlined; - FixedArray(const FixedArray& other) : rep_(other.begin(), other.end()) {} + FixedArray(const FixedArray& other) + : FixedArray(other.begin(), other.end()) {} + FixedArray(FixedArray&& other) noexcept( // clang-format off absl::allocator_is_nothrow<std::allocator<value_type>>::value && // clang-format on std::is_nothrow_move_constructible<value_type>::value) - : rep_(std::make_move_iterator(other.begin()), - std::make_move_iterator(other.end())) {} + : FixedArray(std::make_move_iterator(other.begin()), + std::make_move_iterator(other.end())) {} // Creates an array object that can store `n` elements. // Note that trivially constructible elements will be uninitialized. - explicit FixedArray(size_type n) : rep_(n) {} + explicit FixedArray(size_type n) : rep_(n) { + absl::memory_internal::uninitialized_default_construct_n(rep_.begin(), + size()); + } // Creates an array initialized with `n` copies of `val`. - FixedArray(size_type n, const value_type& val) : rep_(n, val) {} + FixedArray(size_type n, const value_type& val) : rep_(n) { + std::uninitialized_fill_n(data(), size(), val); + } // Creates an array initialized with the elements from the input // range. The array's size will always be `std::distance(first, last)`. // REQUIRES: Iter must be a forward_iterator or better. template <typename Iter, EnableIfForwardIterator<Iter> = 0> - FixedArray(Iter first, Iter last) : rep_(first, last) {} + FixedArray(Iter first, Iter last) : rep_(std::distance(first, last)) { + std::uninitialized_copy(first, last, data()); + } // Creates the array from an initializer_list. FixedArray(std::initializer_list<T> init_list) : FixedArray(init_list.begin(), init_list.end()) {} - ~FixedArray() {} + ~FixedArray() noexcept { + for (Holder* cur = rep_.begin(); cur != rep_.end(); ++cur) { + cur->~Holder(); + } + } // Assignments are deleted because they break the invariant that the size of a // `FixedArray` never changes. @@ -431,32 +444,13 @@ class FixedArray { // Rep // - // A const Rep object holds FixedArray's size and data pointer. + // An instance of Rep manages the inline and out-of-line memory for FixedArray // class Rep : public InlineSpace<inline_elements> { public: - Rep(size_type n, const value_type& val) : n_(n), p_(MakeHolder(n)) { - std::uninitialized_fill_n(p_, n, val); - } - - explicit Rep(size_type n) : n_(n), p_(MakeHolder(n)) { - // Loop optimizes to nothing for trivially constructible T. - for (Holder* p = p_; p != p_ + n; ++p) - // Note: no parens: default init only. - // Also note '::' to avoid Holder class placement new operator. - ::new (static_cast<void*>(p)) Holder; - } - - template <typename Iter> - Rep(Iter first, Iter last) - : n_(std::distance(first, last)), p_(MakeHolder(n_)) { - std::uninitialized_copy(first, last, AsValue(p_)); - } + explicit Rep(size_type n) : n_(n), p_(MakeHolder(n)) {} - ~Rep() { - // Destruction must be in reverse order. - // Loop optimizes to nothing for trivially destructible T. - for (Holder* p = end(); p != begin();) (--p)->~Holder(); + ~Rep() noexcept { if (IsAllocated(size())) { std::allocator<Holder>().deallocate(p_, n_); } else { diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc new file mode 100644 index 000000000000..c123c2a1c0d2 --- /dev/null +++ b/absl/container/fixed_array_exception_safety_test.cc @@ -0,0 +1,117 @@ +// 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 <initializer_list> + +#include "absl/container/fixed_array.h" + +#include "gtest/gtest.h" +#include "absl/base/internal/exception_safety_testing.h" + +namespace absl { + +namespace { + +constexpr size_t kInlined = 25; +constexpr size_t kSmallSize = kInlined / 2; +constexpr size_t kLargeSize = kInlined * 2; + +constexpr int kInitialValue = 5; +constexpr int kUpdatedValue = 10; + +using ::testing::TestThrowingCtor; + +using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>; +using FixedArr = absl::FixedArray<Thrower, kInlined>; + +using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>; +using MoveFixedArr = absl::FixedArray<MoveThrower, kInlined>; + +TEST(FixedArrayExceptionSafety, CopyConstructor) { + auto small = FixedArr(kSmallSize); + TestThrowingCtor<FixedArr>(small); + + auto large = FixedArr(kLargeSize); + TestThrowingCtor<FixedArr>(large); +} + +TEST(FixedArrayExceptionSafety, MoveConstructor) { + TestThrowingCtor<FixedArr>(FixedArr(kSmallSize)); + TestThrowingCtor<FixedArr>(FixedArr(kLargeSize)); + + // TypeSpec::kNoThrowMove + TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kSmallSize)); + TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kLargeSize)); +} + +TEST(FixedArrayExceptionSafety, SizeConstructor) { + TestThrowingCtor<FixedArr>(kSmallSize); + TestThrowingCtor<FixedArr>(kLargeSize); +} + +TEST(FixedArrayExceptionSafety, SizeValueConstructor) { + TestThrowingCtor<FixedArr>(kSmallSize, Thrower()); + TestThrowingCtor<FixedArr>(kLargeSize, Thrower()); +} + +TEST(FixedArrayExceptionSafety, IteratorConstructor) { + auto small = FixedArr(kSmallSize); + TestThrowingCtor<FixedArr>(small.begin(), small.end()); + + auto large = FixedArr(kLargeSize); + TestThrowingCtor<FixedArr>(large.begin(), large.end()); +} + +TEST(FixedArrayExceptionSafety, InitListConstructor) { + constexpr int small_inlined = 3; + using SmallFixedArr = absl::FixedArray<Thrower, small_inlined>; + + TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{}); + // Test inlined allocation + TestThrowingCtor<SmallFixedArr>( + std::initializer_list<Thrower>{Thrower{}, Thrower{}}); + // Test out of line allocation + TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{ + Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}}); +} + +testing::AssertionResult ReadMemory(FixedArr* fixed_arr) { + // Marked volatile to prevent optimization. Used for running asan tests. + volatile int sum = 0; + for (const auto& thrower : *fixed_arr) { + sum += thrower.Get(); + } + return testing::AssertionSuccess() << "Values sum to [" << sum << "]"; +} + +TEST(FixedArrayExceptionSafety, Fill) { + auto test_fill = testing::MakeExceptionSafetyTester() + .WithInvariants(ReadMemory) + .WithOperation([&](FixedArr* fixed_arr_ptr) { + auto thrower = + Thrower(kUpdatedValue, testing::nothrow_ctor); + fixed_arr_ptr->fill(thrower); + }); + + EXPECT_TRUE( + test_fill.WithInitialValue(FixedArr(kSmallSize, Thrower(kInitialValue))) + .Test()); + EXPECT_TRUE( + test_fill.WithInitialValue(FixedArr(kLargeSize, Thrower(kInitialValue))) + .Test()); +} + +} // namespace + +} // namespace absl |