1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/core/lib/gtl/cleanup.h"
17
18 #include <functional>
19 #include <type_traits>
20
21 #include "tensorflow/core/platform/test.h"
22 #include "tensorflow/core/platform/test_benchmark.h"
23
24 namespace tensorflow {
25 namespace {
26
27 using AnyCleanup = gtl::Cleanup<std::function<void()>>;
28
29 template <typename T1, typename T2>
AssertTypeEq()30 void AssertTypeEq() {
31 static_assert(std::is_same<T1, T2>::value, "unexpected type");
32 }
33
TEST(CleanupTest,BasicLambda)34 TEST(CleanupTest, BasicLambda) {
35 string s = "active";
36 {
37 auto s_cleaner = gtl::MakeCleanup([&s] { s.assign("cleaned"); });
38 EXPECT_EQ("active", s);
39 }
40 EXPECT_EQ("cleaned", s);
41 }
42
TEST(FinallyTest,NoCaptureLambda)43 TEST(FinallyTest, NoCaptureLambda) {
44 // Noncapturing lambdas are just structs and use aggregate initializers.
45 // Make sure MakeCleanup is compatible with that kind of initialization.
46 static string& s = *new string;
47 s.assign("active");
48 {
49 auto s_cleaner = gtl::MakeCleanup([] { s.append(" clean"); });
50 EXPECT_EQ("active", s);
51 }
52 EXPECT_EQ("active clean", s);
53 }
54
TEST(CleanupTest,Release)55 TEST(CleanupTest, Release) {
56 string s = "active";
57 {
58 auto s_cleaner = gtl::MakeCleanup([&s] { s.assign("cleaned"); });
59 EXPECT_EQ("active", s);
60 s_cleaner.release();
61 }
62 EXPECT_EQ("active", s); // no cleanup should have occurred.
63 }
64
TEST(FinallyTest,TypeErasedWithoutFactory)65 TEST(FinallyTest, TypeErasedWithoutFactory) {
66 string s = "active";
67 {
68 AnyCleanup s_cleaner([&s] { s.append(" clean"); });
69 EXPECT_EQ("active", s);
70 }
71 EXPECT_EQ("active clean", s);
72 }
73
74 struct Appender {
Appendertensorflow::__anonfbe6ce7a0111::Appender75 Appender(string* s, const string& msg) : s_(s), msg_(msg) {}
operator ()tensorflow::__anonfbe6ce7a0111::Appender76 void operator()() const { s_->append(msg_); }
77 string* s_;
78 string msg_;
79 };
80
TEST(CleanupTest,NonLambda)81 TEST(CleanupTest, NonLambda) {
82 string s = "active";
83 {
84 auto c = gtl::MakeCleanup(Appender(&s, " cleaned"));
85 AssertTypeEq<decltype(c), gtl::Cleanup<Appender>>();
86 EXPECT_EQ("active", s);
87 }
88 EXPECT_EQ("active cleaned", s);
89 }
90
TEST(CleanupTest,Assign)91 TEST(CleanupTest, Assign) {
92 string s = "0";
93 {
94 auto clean1 = gtl::MakeCleanup(Appender(&s, " 1"));
95 auto clean2 = gtl::MakeCleanup(Appender(&s, " 2"));
96 EXPECT_EQ("0", s);
97 clean2 = std::move(clean1);
98 EXPECT_EQ("0 2", s);
99 }
100 EXPECT_EQ("0 2 1", s);
101 }
102
TEST(CleanupTest,AssignAny)103 TEST(CleanupTest, AssignAny) {
104 // Check that implicit conversions can happen in assignment.
105 string s = "0";
106 {
107 auto clean1 = gtl::MakeCleanup(Appender(&s, " 1"));
108 AnyCleanup clean2 = gtl::MakeCleanup(Appender(&s, " 2"));
109 EXPECT_EQ("0", s);
110 clean2 = std::move(clean1);
111 EXPECT_EQ("0 2", s);
112 }
113 EXPECT_EQ("0 2 1", s);
114 }
115
TEST(CleanupTest,AssignFromReleased)116 TEST(CleanupTest, AssignFromReleased) {
117 string s = "0";
118 {
119 auto clean1 = gtl::MakeCleanup(Appender(&s, " 1"));
120 auto clean2 = gtl::MakeCleanup(Appender(&s, " 2"));
121 EXPECT_EQ("0", s);
122 clean1.release();
123 clean2 = std::move(clean1);
124 EXPECT_EQ("0 2", s);
125 }
126 EXPECT_EQ("0 2", s);
127 }
128
TEST(CleanupTest,AssignToReleased)129 TEST(CleanupTest, AssignToReleased) {
130 string s = "0";
131 {
132 auto clean1 = gtl::MakeCleanup(Appender(&s, " 1"));
133 auto clean2 = gtl::MakeCleanup(Appender(&s, " 2"));
134 EXPECT_EQ("0", s);
135 clean2.release();
136 EXPECT_EQ("0", s);
137 clean2 = std::move(clean1);
138 EXPECT_EQ("0", s);
139 }
140 EXPECT_EQ("0 1", s);
141 }
142
TEST(CleanupTest,AssignToDefaultInitialized)143 TEST(CleanupTest, AssignToDefaultInitialized) {
144 string s = "0";
145 {
146 auto clean1 = gtl::MakeCleanup(Appender(&s, " 1"));
147 {
148 AnyCleanup clean2;
149 EXPECT_EQ("0", s);
150 clean2 = std::move(clean1);
151 EXPECT_EQ("0", s);
152 }
153 EXPECT_EQ("0 1", s);
154 }
155 EXPECT_EQ("0 1", s);
156 }
157
158 class CleanupReferenceTest : public ::testing::Test {
159 public:
160 struct F {
161 int* cp;
162 int* i;
Ftensorflow::__anonfbe6ce7a0111::CleanupReferenceTest::F163 F(int* cp, int* i) : cp(cp), i(i) {}
Ftensorflow::__anonfbe6ce7a0111::CleanupReferenceTest::F164 F(const F& o) : cp(o.cp), i(o.i) { ++*cp; }
operator =tensorflow::__anonfbe6ce7a0111::CleanupReferenceTest::F165 F& operator=(const F& o) {
166 cp = o.cp;
167 i = o.i;
168 ++*cp;
169 return *this;
170 }
171 F(F&&) = default;
172 F& operator=(F&&) = default;
operator ()tensorflow::__anonfbe6ce7a0111::CleanupReferenceTest::F173 void operator()() const { ++*i; }
174 };
175 int copies_ = 0;
176 int calls_ = 0;
177 F f_ = F(&copies_, &calls_);
178
179 static int g_calls;
SetUp()180 void SetUp() override { g_calls = 0; }
CleanerFunction()181 static void CleanerFunction() { ++g_calls; }
182 };
183 int CleanupReferenceTest::g_calls = 0;
184
TEST_F(CleanupReferenceTest,FunctionPointer)185 TEST_F(CleanupReferenceTest, FunctionPointer) {
186 {
187 auto c = gtl::MakeCleanup(&CleanerFunction);
188 AssertTypeEq<decltype(c), gtl::Cleanup<void (*)()>>();
189 EXPECT_EQ(0, g_calls);
190 }
191 EXPECT_EQ(1, g_calls);
192 // Test that a function reference decays to a function pointer.
193 {
194 auto c = gtl::MakeCleanup(CleanerFunction);
195 AssertTypeEq<decltype(c), gtl::Cleanup<void (*)()>>();
196 EXPECT_EQ(1, g_calls);
197 }
198 EXPECT_EQ(2, g_calls);
199 }
200
TEST_F(CleanupReferenceTest,AssignLvalue)201 TEST_F(CleanupReferenceTest, AssignLvalue) {
202 string s = "0";
203 Appender app1(&s, "1");
204 Appender app2(&s, "2");
205 {
206 auto c = gtl::MakeCleanup(app1);
207 c.release();
208 c = gtl::MakeCleanup(app2);
209 EXPECT_EQ("0", s);
210 app1();
211 EXPECT_EQ("01", s);
212 }
213 EXPECT_EQ("012", s);
214 }
215
TEST_F(CleanupReferenceTest,FunctorLvalue)216 TEST_F(CleanupReferenceTest, FunctorLvalue) {
217 // Test that MakeCleanup(lvalue) produces Cleanup<F>, not Cleanup<F&>.
218 EXPECT_EQ(0, copies_);
219 EXPECT_EQ(0, calls_);
220 {
221 auto c = gtl::MakeCleanup(f_);
222 AssertTypeEq<decltype(c), gtl::Cleanup<F>>();
223 EXPECT_EQ(1, copies_);
224 EXPECT_EQ(0, calls_);
225 }
226 EXPECT_EQ(1, copies_);
227 EXPECT_EQ(1, calls_);
228 {
229 auto c = gtl::MakeCleanup(f_);
230 EXPECT_EQ(2, copies_);
231 EXPECT_EQ(1, calls_);
232 F f2 = c.release(); // release is a move.
233 EXPECT_EQ(2, copies_);
234 EXPECT_EQ(1, calls_);
235 auto c2 = gtl::MakeCleanup(f2); // copy
236 EXPECT_EQ(3, copies_);
237 EXPECT_EQ(1, calls_);
238 }
239 EXPECT_EQ(3, copies_);
240 EXPECT_EQ(2, calls_);
241 }
242
TEST_F(CleanupReferenceTest,FunctorRvalue)243 TEST_F(CleanupReferenceTest, FunctorRvalue) {
244 {
245 auto c = gtl::MakeCleanup(std::move(f_));
246 AssertTypeEq<decltype(c), gtl::Cleanup<F>>();
247 EXPECT_EQ(0, copies_);
248 EXPECT_EQ(0, calls_);
249 }
250 EXPECT_EQ(0, copies_);
251 EXPECT_EQ(1, calls_);
252 }
253
TEST_F(CleanupReferenceTest,FunctorReferenceWrapper)254 TEST_F(CleanupReferenceTest, FunctorReferenceWrapper) {
255 {
256 auto c = gtl::MakeCleanup(std::cref(f_));
257 AssertTypeEq<decltype(c), gtl::Cleanup<std::reference_wrapper<const F>>>();
258 EXPECT_EQ(0, copies_);
259 EXPECT_EQ(0, calls_);
260 }
261 EXPECT_EQ(0, copies_);
262 EXPECT_EQ(1, calls_);
263 }
264
265 volatile int i;
266
Incr(volatile int * ip)267 void Incr(volatile int* ip) { ++*ip; }
Incr()268 void Incr() { Incr(&i); }
269
BM_Cleanup(::testing::benchmark::State & state)270 void BM_Cleanup(::testing::benchmark::State& state) {
271 for (auto s : state) {
272 auto fin = gtl::MakeCleanup([] { Incr(); });
273 }
274 }
275 BENCHMARK(BM_Cleanup);
276
BM_AnyCleanup(::testing::benchmark::State & state)277 void BM_AnyCleanup(::testing::benchmark::State& state) {
278 for (auto s : state) {
279 AnyCleanup fin = gtl::MakeCleanup([] { Incr(); });
280 }
281 }
282 BENCHMARK(BM_AnyCleanup);
283
BM_AnyCleanupNoFactory(::testing::benchmark::State & state)284 void BM_AnyCleanupNoFactory(::testing::benchmark::State& state) {
285 for (auto s : state) {
286 AnyCleanup fin([] { Incr(); });
287 }
288 }
289 BENCHMARK(BM_AnyCleanupNoFactory);
290
BM_CleanupBound(::testing::benchmark::State & state)291 void BM_CleanupBound(::testing::benchmark::State& state) {
292 volatile int* ip = &i;
293 for (auto s : state) {
294 auto fin = gtl::MakeCleanup([ip] { Incr(ip); });
295 }
296 }
297 BENCHMARK(BM_CleanupBound);
298
BM_AnyCleanupBound(::testing::benchmark::State & state)299 void BM_AnyCleanupBound(::testing::benchmark::State& state) {
300 volatile int* ip = &i;
301 for (auto s : state) {
302 AnyCleanup fin = gtl::MakeCleanup([ip] { Incr(ip); });
303 }
304 }
305 BENCHMARK(BM_AnyCleanupBound);
306
BM_AnyCleanupNoFactoryBound(::testing::benchmark::State & state)307 void BM_AnyCleanupNoFactoryBound(::testing::benchmark::State& state) {
308 volatile int* ip = &i;
309 for (auto s : state) {
310 AnyCleanup fin([ip] { Incr(ip); });
311 }
312 }
313 BENCHMARK(BM_AnyCleanupNoFactoryBound);
314
315 } // namespace
316 } // namespace tensorflow
317