Home | History | Annotate | Download | only in gtl
      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