//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
// UNSUPPORTED: no-exceptions
#include <__utility/exception_guard.h>
#include <cassert>
#include <type_traits>
#include <utility>
#include "test_macros.h"
TEST_CONSTEXPR_CXX20 bool test() {
// Make sure the transaction is rolled back if it is not marked as complete when
// it goes out of scope.
{
bool rolled_back = false;
{
auto rollback = [&] { rolled_back = true; };
std::__exception_guard<decltype(rollback)> g(rollback);
}
assert(rolled_back);
}
// Make sure the transaction is not rolled back if it is marked as complete when
// it goes out of scope.
{
bool rolled_back = false;
{
auto rollback = [&] { rolled_back = true; };
std::__exception_guard<decltype(rollback)> g(rollback);
g.__complete();
}
assert(!rolled_back);
}
// Make sure that we will perform the right number of rollbacks when a transaction has
// been moved around
{
// When we don't complete it (exactly 1 rollback should happen)
{
int rollbacks = 0;
{
auto rollback = [&] { ++rollbacks; };
std::__exception_guard<decltype(rollback)> g(rollback);
auto other = std::move(g);
}
assert(rollbacks == 1);
}
// When we do complete it (no rollbacks should happen)
{
int rollbacks = 0;
{
auto rollback = [&] { ++rollbacks; };
std::__exception_guard<decltype(rollback)> g(rollback);
auto other = std::move(g);
other.__complete();
}
assert(rollbacks == 0);
}
}
// Basic properties of the type
{
struct Rollback { void operator()() const { } };
using Transaction = std::__exception_guard<Rollback>;
static_assert(!std::is_default_constructible<Transaction>::value, "");
static_assert(!std::is_copy_constructible<Transaction>::value, "");
static_assert( std::is_move_constructible<Transaction>::value, "");
static_assert(!std::is_copy_assignable<Transaction>::value, "");
static_assert(!std::is_move_assignable<Transaction>::value, "");
// Check noexcept-ness of a few operations
{
struct ThrowOnMove {
ThrowOnMove(ThrowOnMove&&) noexcept(false) { }
void operator()() const { }
};
using ThrowOnMoveTransaction = std::__exception_guard<ThrowOnMove>;
ASSERT_NOEXCEPT(std::declval<Transaction>().__complete());
static_assert( std::is_nothrow_move_constructible<Transaction>::value, "");
static_assert(!std::is_nothrow_move_constructible<ThrowOnMoveTransaction>::value, "");
}
}
return true;
}
void test_exceptions() {
#ifndef TEST_HAS_NO_EXCEPTIONS
// Make sure the rollback is performed when an exception is thrown during the
// lifetime of the transaction.
{
bool rolled_back = false;
auto rollback = [&] { rolled_back = true; };
try {
std::__exception_guard<decltype(rollback)> g(rollback);
throw 0;
} catch (...) { }
assert(rolled_back);
}
// Make sure we don't roll back if an exception is thrown but the transaction
// has been marked as complete when that happens.
{
bool rolled_back = false;
auto rollback = [&] { rolled_back = true; };
try {
std::__exception_guard<decltype(rollback)> g(rollback);
g.__complete();
throw 0;
} catch (...) { }
assert(!rolled_back);
}
// Make sure __exception_guard does not rollback if the transaction is marked as
// completed within a destructor.
{
struct S {
explicit S(bool& x) : x_(x) { }
~S() {
auto rollback = [this]{ x_ = true; };
std::__exception_guard<decltype(rollback)> g(rollback);
g.__complete();
}
bool& x_;
};
bool rolled_back = false;
try {
S s(rolled_back);
throw 0;
} catch (...) {
assert(!rolled_back);
}
}
#endif // TEST_HAS_NO_EXCEPTIONS
}
int main(int, char**) {
test();
test_exceptions();
#if TEST_STD_VER > 17
static_assert(test(), "");
#endif
return 0;
}