Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <stddef.h>
      6 
      7 #include "base/at_exit.h"
      8 #include "base/atomic_sequence_num.h"
      9 #include "base/lazy_instance.h"
     10 #include "base/memory/aligned_memory.h"
     11 #include "base/threading/simple_thread.h"
     12 #include "testing/gtest/include/gtest/gtest.h"
     13 
     14 namespace {
     15 
     16 base::StaticAtomicSequenceNumber constructed_seq_;
     17 base::StaticAtomicSequenceNumber destructed_seq_;
     18 
     19 class ConstructAndDestructLogger {
     20  public:
     21   ConstructAndDestructLogger() {
     22     constructed_seq_.GetNext();
     23   }
     24   ~ConstructAndDestructLogger() {
     25     destructed_seq_.GetNext();
     26   }
     27 };
     28 
     29 class SlowConstructor {
     30  public:
     31   SlowConstructor() : some_int_(0) {
     32     // Sleep for 1 second to try to cause a race.
     33     base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
     34     ++constructed;
     35     some_int_ = 12;
     36   }
     37   int some_int() const { return some_int_; }
     38 
     39   static int constructed;
     40  private:
     41   int some_int_;
     42 };
     43 
     44 int SlowConstructor::constructed = 0;
     45 
     46 class SlowDelegate : public base::DelegateSimpleThread::Delegate {
     47  public:
     48   explicit SlowDelegate(
     49       base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy)
     50       : lazy_(lazy) {}
     51 
     52   void Run() override {
     53     EXPECT_EQ(12, lazy_->Get().some_int());
     54     EXPECT_EQ(12, lazy_->Pointer()->some_int());
     55   }
     56 
     57  private:
     58   base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy_;
     59 };
     60 
     61 }  // namespace
     62 
     63 static base::LazyInstance<ConstructAndDestructLogger>::DestructorAtExit
     64     lazy_logger = LAZY_INSTANCE_INITIALIZER;
     65 
     66 TEST(LazyInstanceTest, Basic) {
     67   {
     68     base::ShadowingAtExitManager shadow;
     69 
     70     EXPECT_EQ(0, constructed_seq_.GetNext());
     71     EXPECT_EQ(0, destructed_seq_.GetNext());
     72 
     73     lazy_logger.Get();
     74     EXPECT_EQ(2, constructed_seq_.GetNext());
     75     EXPECT_EQ(1, destructed_seq_.GetNext());
     76 
     77     lazy_logger.Pointer();
     78     EXPECT_EQ(3, constructed_seq_.GetNext());
     79     EXPECT_EQ(2, destructed_seq_.GetNext());
     80   }
     81   EXPECT_EQ(4, constructed_seq_.GetNext());
     82   EXPECT_EQ(4, destructed_seq_.GetNext());
     83 }
     84 
     85 static base::LazyInstance<SlowConstructor>::DestructorAtExit lazy_slow =
     86     LAZY_INSTANCE_INITIALIZER;
     87 
     88 TEST(LazyInstanceTest, ConstructorThreadSafety) {
     89   {
     90     base::ShadowingAtExitManager shadow;
     91 
     92     SlowDelegate delegate(&lazy_slow);
     93     EXPECT_EQ(0, SlowConstructor::constructed);
     94 
     95     base::DelegateSimpleThreadPool pool("lazy_instance_cons", 5);
     96     pool.AddWork(&delegate, 20);
     97     EXPECT_EQ(0, SlowConstructor::constructed);
     98 
     99     pool.Start();
    100     pool.JoinAll();
    101     EXPECT_EQ(1, SlowConstructor::constructed);
    102   }
    103 }
    104 
    105 namespace {
    106 
    107 // DeleteLogger is an object which sets a flag when it's destroyed.
    108 // It accepts a bool* and sets the bool to true when the dtor runs.
    109 class DeleteLogger {
    110  public:
    111   DeleteLogger() : deleted_(NULL) {}
    112   ~DeleteLogger() { *deleted_ = true; }
    113 
    114   void SetDeletedPtr(bool* deleted) {
    115     deleted_ = deleted;
    116   }
    117 
    118  private:
    119   bool* deleted_;
    120 };
    121 
    122 }  // anonymous namespace
    123 
    124 TEST(LazyInstanceTest, LeakyLazyInstance) {
    125   // Check that using a plain LazyInstance causes the dtor to run
    126   // when the AtExitManager finishes.
    127   bool deleted1 = false;
    128   {
    129     base::ShadowingAtExitManager shadow;
    130     static base::LazyInstance<DeleteLogger>::DestructorAtExit test =
    131         LAZY_INSTANCE_INITIALIZER;
    132     test.Get().SetDeletedPtr(&deleted1);
    133   }
    134   EXPECT_TRUE(deleted1);
    135 
    136   // Check that using a *leaky* LazyInstance makes the dtor not run
    137   // when the AtExitManager finishes.
    138   bool deleted2 = false;
    139   {
    140     base::ShadowingAtExitManager shadow;
    141     static base::LazyInstance<DeleteLogger>::Leaky
    142         test = LAZY_INSTANCE_INITIALIZER;
    143     test.Get().SetDeletedPtr(&deleted2);
    144   }
    145   EXPECT_FALSE(deleted2);
    146 }
    147 
    148 namespace {
    149 
    150 template <size_t alignment>
    151 class AlignedData {
    152  public:
    153   AlignedData() {}
    154   ~AlignedData() {}
    155   base::AlignedMemory<alignment, alignment> data_;
    156 };
    157 
    158 }  // anonymous namespace
    159 
    160 #define EXPECT_ALIGNED(ptr, align) \
    161     EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
    162 
    163 TEST(LazyInstanceTest, Alignment) {
    164   using base::LazyInstance;
    165 
    166   // Create some static instances with increasing sizes and alignment
    167   // requirements. By ordering this way, the linker will need to do some work to
    168   // ensure proper alignment of the static data.
    169   static LazyInstance<AlignedData<4>>::DestructorAtExit align4 =
    170       LAZY_INSTANCE_INITIALIZER;
    171   static LazyInstance<AlignedData<32>>::DestructorAtExit align32 =
    172       LAZY_INSTANCE_INITIALIZER;
    173   static LazyInstance<AlignedData<4096>>::DestructorAtExit align4096 =
    174       LAZY_INSTANCE_INITIALIZER;
    175 
    176   EXPECT_ALIGNED(align4.Pointer(), 4);
    177   EXPECT_ALIGNED(align32.Pointer(), 32);
    178   EXPECT_ALIGNED(align4096.Pointer(), 4096);
    179 }
    180