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
14namespace {
15
16base::StaticAtomicSequenceNumber constructed_seq_;
17base::StaticAtomicSequenceNumber destructed_seq_;
18
19class ConstructAndDestructLogger {
20 public:
21  ConstructAndDestructLogger() {
22    constructed_seq_.GetNext();
23  }
24  ~ConstructAndDestructLogger() {
25    destructed_seq_.GetNext();
26  }
27};
28
29class 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
44int SlowConstructor::constructed = 0;
45
46class SlowDelegate : public base::DelegateSimpleThread::Delegate {
47 public:
48  explicit SlowDelegate(base::LazyInstance<SlowConstructor>* lazy)
49      : lazy_(lazy) {}
50
51  void Run() override {
52    EXPECT_EQ(12, lazy_->Get().some_int());
53    EXPECT_EQ(12, lazy_->Pointer()->some_int());
54  }
55
56 private:
57  base::LazyInstance<SlowConstructor>* lazy_;
58};
59
60}  // namespace
61
62static base::LazyInstance<ConstructAndDestructLogger> lazy_logger =
63    LAZY_INSTANCE_INITIALIZER;
64
65TEST(LazyInstanceTest, Basic) {
66  {
67    base::ShadowingAtExitManager shadow;
68
69    EXPECT_EQ(0, constructed_seq_.GetNext());
70    EXPECT_EQ(0, destructed_seq_.GetNext());
71
72    lazy_logger.Get();
73    EXPECT_EQ(2, constructed_seq_.GetNext());
74    EXPECT_EQ(1, destructed_seq_.GetNext());
75
76    lazy_logger.Pointer();
77    EXPECT_EQ(3, constructed_seq_.GetNext());
78    EXPECT_EQ(2, destructed_seq_.GetNext());
79  }
80  EXPECT_EQ(4, constructed_seq_.GetNext());
81  EXPECT_EQ(4, destructed_seq_.GetNext());
82}
83
84static base::LazyInstance<SlowConstructor> lazy_slow =
85    LAZY_INSTANCE_INITIALIZER;
86
87TEST(LazyInstanceTest, ConstructorThreadSafety) {
88  {
89    base::ShadowingAtExitManager shadow;
90
91    SlowDelegate delegate(&lazy_slow);
92    EXPECT_EQ(0, SlowConstructor::constructed);
93
94    base::DelegateSimpleThreadPool pool("lazy_instance_cons", 5);
95    pool.AddWork(&delegate, 20);
96    EXPECT_EQ(0, SlowConstructor::constructed);
97
98    pool.Start();
99    pool.JoinAll();
100    EXPECT_EQ(1, SlowConstructor::constructed);
101  }
102}
103
104namespace {
105
106// DeleteLogger is an object which sets a flag when it's destroyed.
107// It accepts a bool* and sets the bool to true when the dtor runs.
108class DeleteLogger {
109 public:
110  DeleteLogger() : deleted_(NULL) {}
111  ~DeleteLogger() { *deleted_ = true; }
112
113  void SetDeletedPtr(bool* deleted) {
114    deleted_ = deleted;
115  }
116
117 private:
118  bool* deleted_;
119};
120
121}  // anonymous namespace
122
123TEST(LazyInstanceTest, LeakyLazyInstance) {
124  // Check that using a plain LazyInstance causes the dtor to run
125  // when the AtExitManager finishes.
126  bool deleted1 = false;
127  {
128    base::ShadowingAtExitManager shadow;
129    static base::LazyInstance<DeleteLogger> test = LAZY_INSTANCE_INITIALIZER;
130    test.Get().SetDeletedPtr(&deleted1);
131  }
132  EXPECT_TRUE(deleted1);
133
134  // Check that using a *leaky* LazyInstance makes the dtor not run
135  // when the AtExitManager finishes.
136  bool deleted2 = false;
137  {
138    base::ShadowingAtExitManager shadow;
139    static base::LazyInstance<DeleteLogger>::Leaky
140        test = LAZY_INSTANCE_INITIALIZER;
141    test.Get().SetDeletedPtr(&deleted2);
142  }
143  EXPECT_FALSE(deleted2);
144}
145
146namespace {
147
148template <size_t alignment>
149class AlignedData {
150 public:
151  AlignedData() {}
152  ~AlignedData() {}
153  base::AlignedMemory<alignment, alignment> data_;
154};
155
156}  // anonymous namespace
157
158#define EXPECT_ALIGNED(ptr, align) \
159    EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
160
161TEST(LazyInstanceTest, Alignment) {
162  using base::LazyInstance;
163
164  // Create some static instances with increasing sizes and alignment
165  // requirements. By ordering this way, the linker will need to do some work to
166  // ensure proper alignment of the static data.
167  static LazyInstance<AlignedData<4> > align4 = LAZY_INSTANCE_INITIALIZER;
168  static LazyInstance<AlignedData<32> > align32 = LAZY_INSTANCE_INITIALIZER;
169  static LazyInstance<AlignedData<4096> > align4096 = LAZY_INSTANCE_INITIALIZER;
170
171  EXPECT_ALIGNED(align4.Pointer(), 4);
172  EXPECT_ALIGNED(align32.Pointer(), 32);
173  EXPECT_ALIGNED(align4096.Pointer(), 4096);
174}
175