Toolbox snapshot
The Reactive C++ Toolbox
Loading...
Searching...
No Matches
Disposer.ut.cpp
Go to the documentation of this file.
1// The Reactive C++ Toolbox.
2// Copyright (C) 2013-2019 Swirly Cloud Limited
3// Copyright (C) 2021 Reactive Markets Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#include "Disposer.hpp"
18
19#include <boost/test/unit_test.hpp>
20
21#include <stdexcept>
22
23using namespace std;
24using namespace toolbox;
25
26namespace {
27struct Disposer : BasicDisposer<Disposer> {
28 using BasicDisposer<Disposer>::is_dispose_deferred;
29 using BasicDisposer<Disposer>::is_locked;
30 using BasicDisposer<Disposer>::lock_this;
31 void dispose_now(CyclTime now) noexcept
32 {
33 // Ensure that dispose_now can safely call dispose.
34 // This could happen if dispose_now invoked a callback.
35 dispose(now);
36 ++disposed;
37 }
38 int disposed{0};
39};
40
41// These helpers are not inlined into the test cases, so the guard is released by the code that
42// runs while an exception unwinds their frame, as when a send throws inside a guard.
43[[gnu::noinline]] void throw_error()
44{
45 throw runtime_error{"error"};
46}
47
48[[gnu::noinline]] void throw_while_locked(Disposer& d, CyclTime now)
49{
50 const auto lock = d.lock_this(now);
52}
53
54[[gnu::noinline]] void dispose_and_throw_while_locked(Disposer& d, CyclTime now)
55{
56 const auto lock = d.lock_this(now);
57 d.dispose(now);
59}
60} // namespace
61
63
65{
66 const auto now = CyclTime::now();
67 Disposer d;
68 BOOST_CHECK(!d.is_locked());
69 BOOST_CHECK(!d.is_dispose_deferred());
70 BOOST_CHECK_EQUAL(d.disposed, 0);
71 {
72 auto lock = d.lock_this(now);
73 BOOST_CHECK(d.is_locked());
74 BOOST_CHECK(!d.is_dispose_deferred());
75 }
76 BOOST_CHECK(!d.is_locked());
77 BOOST_CHECK(!d.is_dispose_deferred());
78 BOOST_CHECK_EQUAL(d.disposed, 0);
79 {
80 auto outer_lock = d.lock_this(now);
81 BOOST_CHECK(d.is_locked());
82 {
83 auto inner_lock = d.lock_this(now);
84 BOOST_CHECK(!d.is_dispose_deferred());
85 d.dispose(now);
86 BOOST_CHECK(d.is_locked());
87 BOOST_CHECK(d.is_dispose_deferred());
88 BOOST_CHECK_EQUAL(d.disposed, 0);
89 }
90 // And again.
91 d.dispose(now);
92 BOOST_CHECK(d.is_locked());
93 BOOST_CHECK(d.is_dispose_deferred());
94 BOOST_CHECK_EQUAL(d.disposed, 0);
95 }
96 BOOST_CHECK_EQUAL(d.disposed, 1);
97}
98
100{
101 // An exception that unwinds through the guard must release it. GCC 14 at -O1 and above
102 // deleted this release when the dispose path wrote the count twice.
103 const auto now = CyclTime::now();
104 Disposer d;
105 BOOST_CHECK_THROW(throw_while_locked(d, now), runtime_error);
106 BOOST_CHECK(!d.is_locked());
107 BOOST_CHECK(!d.is_dispose_deferred());
108 d.dispose(now);
109 BOOST_CHECK_EQUAL(d.disposed, 1);
110}
111
113{
114 // A disposal deferred by the guard runs once when an exception unwinds through it, and the
115 // object then stays locked so that it is not disposed a second time.
116 const auto now = CyclTime::now();
117 Disposer d;
119 BOOST_CHECK_EQUAL(d.disposed, 1);
120 BOOST_CHECK(d.is_locked());
121 d.dispose(now);
122 BOOST_CHECK_EQUAL(d.disposed, 1);
123}
124
BOOST_AUTO_TEST_CASE(DisposerCase)
BOOST_CHECK_EQUAL(v.size(), 10U)
STL namespace.
constexpr auto bind() noexcept
Definition Slot.hpp:97
BOOST_CHECK(isnan(stod(""sv, numeric_limits< double >::quiet_NaN())))