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> 30 void AssertTypeEq() { 31 static_assert(std::is_same<T1, T2>::value, "unexpected type"); 32 } 33 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 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 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 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 { 75 Appender(string* s, const string& msg) : s_(s), msg_(msg) {} 76 void operator()() const { s_->append(msg_); } 77 string* s_; 78 string msg_; 79 }; 80 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 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 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 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 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 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; 163 F(int* cp, int* i) : cp(cp), i(i) {} 164 F(const F& o) : cp(o.cp), i(o.i) { ++*cp; } 165 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; 173 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; 180 void SetUp() override { g_calls = 0; } 181 static void CleanerFunction() { ++g_calls; } 182 }; 183 int CleanupReferenceTest::g_calls = 0; 184 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 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 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 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 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 267 void Incr(volatile int* ip) { ++*ip; } 268 void Incr() { Incr(&i); } 269 270 void BM_Cleanup(int iters) { 271 while (iters--) { 272 auto fin = gtl::MakeCleanup([] { Incr(); }); 273 } 274 } 275 BENCHMARK(BM_Cleanup); 276 277 void BM_AnyCleanup(int iters) { 278 while (iters--) { 279 AnyCleanup fin = gtl::MakeCleanup([] { Incr(); }); 280 } 281 } 282 BENCHMARK(BM_AnyCleanup); 283 284 void BM_AnyCleanupNoFactory(int iters) { 285 while (iters--) { 286 AnyCleanup fin([] { Incr(); }); 287 } 288 } 289 BENCHMARK(BM_AnyCleanupNoFactory); 290 291 void BM_CleanupBound(int iters) { 292 volatile int* ip = &i; 293 while (iters--) { 294 auto fin = gtl::MakeCleanup([ip] { Incr(ip); }); 295 } 296 } 297 BENCHMARK(BM_CleanupBound); 298 299 void BM_AnyCleanupBound(int iters) { 300 volatile int* ip = &i; 301 while (iters--) { 302 AnyCleanup fin = gtl::MakeCleanup([ip] { Incr(ip); }); 303 } 304 } 305 BENCHMARK(BM_AnyCleanupBound); 306 307 void BM_AnyCleanupNoFactoryBound(int iters) { 308 volatile int* ip = &i; 309 while (iters--) { 310 AnyCleanup fin([ip] { Incr(ip); }); 311 } 312 } 313 BENCHMARK(BM_AnyCleanupNoFactoryBound); 314 315 } // namespace 316 } // namespace tensorflow 317