[M94 Dev][Tizen] Fix for errors for generating ninja files
[platform/framework/web/chromium-efl.git] / base / no_destructor_unittest.cc
1 // Copyright 2018 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 "base/no_destructor.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/atomicops.h"
13 #include "base/barrier_closure.h"
14 #include "base/bind.h"
15 #include "base/check.h"
16 #include "base/system/sys_info.h"
17 #include "base/threading/platform_thread.h"
18 #include "base/threading/simple_thread.h"
19 #include "build/build_config.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 namespace base {
23
24 namespace {
25
26 struct CheckOnDestroy {
27   ~CheckOnDestroy() { CHECK(false); }
28 };
29
30 TEST(NoDestructorTest, SkipsDestructors) {
31   NoDestructor<CheckOnDestroy> destructor_should_not_run;
32 }
33
34 struct UncopyableUnmovable {
35   UncopyableUnmovable() = default;
36   explicit UncopyableUnmovable(int value) : value(value) {}
37
38   UncopyableUnmovable(const UncopyableUnmovable&) = delete;
39   UncopyableUnmovable& operator=(const UncopyableUnmovable&) = delete;
40
41   int value = 1;
42   std::string something_with_a_nontrivial_destructor;
43 };
44
45 struct CopyOnly {
46   CopyOnly() = default;
47
48   CopyOnly(const CopyOnly&) = default;
49   CopyOnly& operator=(const CopyOnly&) = default;
50
51   CopyOnly(CopyOnly&&) = delete;
52   CopyOnly& operator=(CopyOnly&&) = delete;
53 };
54
55 struct MoveOnly {
56   MoveOnly() = default;
57
58   MoveOnly(const MoveOnly&) = delete;
59   MoveOnly& operator=(const MoveOnly&) = delete;
60
61   MoveOnly(MoveOnly&&) = default;
62   MoveOnly& operator=(MoveOnly&&) = default;
63 };
64
65 struct ForwardingTestStruct {
66   ForwardingTestStruct(const CopyOnly&, MoveOnly&&) {}
67
68   std::string something_with_a_nontrivial_destructor;
69 };
70
71 TEST(NoDestructorTest, UncopyableUnmovable) {
72   static NoDestructor<UncopyableUnmovable> default_constructed;
73   EXPECT_EQ(1, default_constructed->value);
74
75   static NoDestructor<UncopyableUnmovable> constructed_with_arg(-1);
76   EXPECT_EQ(-1, constructed_with_arg->value);
77 }
78
79 TEST(NoDestructorTest, ForwardsArguments) {
80   CopyOnly copy_only;
81   MoveOnly move_only;
82
83   static NoDestructor<ForwardingTestStruct> test_forwarding(
84       copy_only, std::move(move_only));
85 }
86
87 TEST(NoDestructorTest, Accessors) {
88   static NoDestructor<std::string> awesome("awesome");
89
90   EXPECT_EQ("awesome", *awesome);
91   EXPECT_EQ(0, awesome->compare("awesome"));
92   EXPECT_EQ(0, awesome.get()->compare("awesome"));
93 }
94
95 TEST(NoDestructorTest, AllowForTriviallyDestructibleType) {
96   static NoDestructor<bool, AllowForTriviallyDestructibleType>
97       trivially_destructible_type;
98 }
99
100 // Passing initializer list to a NoDestructor like in this test
101 // is ambiguous in GCC.
102 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84849
103 #if !defined(COMPILER_GCC) && !defined(__clang__)
104 TEST(NoDestructorTest, InitializerList) {
105   static NoDestructor<std::vector<std::string>> vector({"a", "b", "c"});
106 }
107 #endif
108 }  // namespace
109
110 namespace {
111
112 // A class whose constructor busy-loops until it is told to complete
113 // construction.
114 class BlockingConstructor {
115  public:
116   BlockingConstructor() {
117     EXPECT_FALSE(WasConstructorCalled());
118     subtle::NoBarrier_Store(&constructor_called_, 1);
119     EXPECT_TRUE(WasConstructorCalled());
120     while (!subtle::NoBarrier_Load(&complete_construction_))
121       PlatformThread::YieldCurrentThread();
122     done_construction_ = true;
123   }
124   BlockingConstructor(const BlockingConstructor&) = delete;
125   BlockingConstructor& operator=(const BlockingConstructor&) = delete;
126   ~BlockingConstructor() = delete;
127
128   // Returns true if BlockingConstructor() was entered.
129   static bool WasConstructorCalled() {
130     return subtle::NoBarrier_Load(&constructor_called_);
131   }
132
133   // Instructs BlockingConstructor() that it may now unblock its construction.
134   static void CompleteConstructionNow() {
135     subtle::NoBarrier_Store(&complete_construction_, 1);
136   }
137
138   bool done_construction() const { return done_construction_; }
139
140  private:
141   // Use Atomic32 instead of AtomicFlag for them to be trivially initialized.
142   static subtle::Atomic32 constructor_called_;
143   static subtle::Atomic32 complete_construction_;
144
145   bool done_construction_ = false;
146 };
147
148 // static
149 subtle::Atomic32 BlockingConstructor::constructor_called_ = 0;
150 // static
151 subtle::Atomic32 BlockingConstructor::complete_construction_ = 0;
152
153 // A SimpleThread running at |thread_priority| which invokes |before_get|
154 // (optional) and then invokes thread-safe
155 // scoped-static-initializationconstruction on its NoDestructor instance.
156 class BlockingConstructorThread : public SimpleThread {
157  public:
158   BlockingConstructorThread(ThreadPriority thread_priority,
159                             OnceClosure before_get)
160       : SimpleThread("BlockingConstructorThread", Options(thread_priority)),
161         before_get_(std::move(before_get)) {}
162   BlockingConstructorThread(const BlockingConstructorThread&) = delete;
163   BlockingConstructorThread& operator=(const BlockingConstructorThread&) =
164       delete;
165
166   void Run() override {
167     if (before_get_)
168       std::move(before_get_).Run();
169
170     static NoDestructor<BlockingConstructor> instance;
171     EXPECT_TRUE(instance->done_construction());
172   }
173
174  private:
175   OnceClosure before_get_;
176 };
177
178 }  // namespace
179
180 // Tests that if the thread assigned to construct the local-static
181 // initialization of the NoDestructor runs at background priority : the
182 // foreground threads will yield to it enough for it to eventually complete
183 // construction. While local-static thread-safe initialization isn't specific to
184 // NoDestructor, it is tested here as NoDestructor is set to replace
185 // LazyInstance and this is an important regression test for it
186 // (https://crbug.com/797129).
187 TEST(NoDestructorTest, PriorityInversionAtStaticInitializationResolves) {
188   TimeTicks test_begin = TimeTicks::Now();
189
190   // Construct BlockingConstructor from a background thread.
191   BlockingConstructorThread background_getter(ThreadPriority::BACKGROUND,
192                                               OnceClosure());
193   background_getter.Start();
194
195   while (!BlockingConstructor::WasConstructorCalled())
196     PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
197
198   // Spin 4 foreground thread per core contending to get the already under
199   // construction NoDestructor. When they are all running and poking at it :
200   // allow the background thread to complete its work.
201   const int kNumForegroundThreads = 4 * SysInfo::NumberOfProcessors();
202   std::vector<std::unique_ptr<SimpleThread>> foreground_threads;
203   RepeatingClosure foreground_thread_ready_callback =
204       BarrierClosure(kNumForegroundThreads,
205                      BindOnce(&BlockingConstructor::CompleteConstructionNow));
206   for (int i = 0; i < kNumForegroundThreads; ++i) {
207     foreground_threads.push_back(std::make_unique<BlockingConstructorThread>(
208         ThreadPriority::NORMAL, foreground_thread_ready_callback));
209     foreground_threads.back()->Start();
210   }
211
212   // This test will hang if the foreground threads become stuck in
213   // NoDestructor's construction per the background thread never being scheduled
214   // to complete construction.
215   for (auto& foreground_thread : foreground_threads)
216     foreground_thread->Join();
217   background_getter.Join();
218
219   // Fail if this test takes more than 5 seconds (it takes 5-10 seconds on a
220   // Z840 without r527445 but is expected to be fast (~30ms) with the fix).
221   EXPECT_LT(TimeTicks::Now() - test_begin, TimeDelta::FromSeconds(5));
222 }
223
224 }  // namespace base