fix a multiline comment warning
[folly.git] / folly / test / MemoryIdlerTest.cpp
1 /*
2  * Copyright 2014-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/detail/MemoryIdler.h>
18
19 #include <folly/portability/GMock.h>
20 #include <folly/portability/GTest.h>
21 #include <folly/synchronization/Baton.h>
22
23 #include <memory>
24 #include <thread>
25
26 using namespace folly;
27 using namespace folly::detail;
28 using namespace testing;
29
30 TEST(MemoryIdler, releaseStack) {
31   MemoryIdler::unmapUnusedStack();
32 }
33
34 TEST(MemoryIdler, releaseStackMinExtra) {
35   MemoryIdler::unmapUnusedStack(0);
36 }
37
38 TEST(MemoryIdler, releaseStackLargeExtra) {
39   MemoryIdler::unmapUnusedStack(30000000);
40 }
41
42 TEST(MemoryIdler, releaseMallocTLS) {
43   auto p = new int[4];
44   MemoryIdler::flushLocalMallocCaches();
45   delete[] p;
46   MemoryIdler::flushLocalMallocCaches();
47   p = new int[4];
48   MemoryIdler::flushLocalMallocCaches();
49   delete[] p;
50 }
51
52
53 /// MockedAtom gives us a way to select a mocked Futex implementation
54 /// inside Baton, even though the atom itself isn't exercised by the
55 /// mocked futex
56 template <typename T>
57 struct MockAtom : public std::atomic<T> {
58   explicit MockAtom(T init = 0) : std::atomic<T>(init) {}
59 };
60
61
62 /// MockClock is a bit tricky because we are mocking a static function
63 /// (now()), so we need to find the corresponding mock instance without
64 /// extending its scope beyond that of the test.  I generally avoid
65 /// shared_ptr, but a weak_ptr is just the ticket here
66 struct MockClock {
67   using duration = std::chrono::steady_clock::duration;
68   using time_point = std::chrono::time_point<MockClock, duration>;
69
70   MOCK_METHOD0(nowImpl, time_point());
71
72   /// Hold on to the returned shared_ptr until the end of the test
73   static std::shared_ptr<StrictMock<MockClock>> setup() {
74     auto rv = std::make_shared<StrictMock<MockClock>>();
75     s_mockClockInstance = rv;
76     return rv;
77   }
78
79   static time_point now() {
80     return s_mockClockInstance.lock()->nowImpl();
81   }
82
83   static std::weak_ptr<StrictMock<MockClock>> s_mockClockInstance;
84 };
85
86 std::weak_ptr<StrictMock<MockClock>> MockClock::s_mockClockInstance;
87
88
89
90 namespace folly { namespace detail {
91
92 /// Futex<MockAtom> is our mocked futex implementation.  Note that the
93 /// method signatures differ from the real Futex because we have elided
94 /// unused default params and collapsed templated methods into the
95 /// used type
96 template <>
97 struct Futex<MockAtom> {
98   MOCK_METHOD2(futexWait, FutexResult(uint32_t, uint32_t));
99   MOCK_METHOD3(futexWaitUntil,
100                FutexResult(uint32_t, const MockClock::time_point&, uint32_t));
101 };
102
103 } // namespace detail
104 } // namespace folly
105
106 static auto const forever = MockClock::time_point::max();
107
108 TEST(MemoryIdler, futexWaitValueChangedEarly) {
109   StrictMock<Futex<MockAtom>> fut;
110   auto clock = MockClock::setup();
111   auto begin = MockClock::time_point(std::chrono::seconds(100));
112   auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
113
114   EXPECT_CALL(*clock, nowImpl())
115       .WillOnce(Return(begin));
116   EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
117                                            Lt(begin + 2 * idleTimeout)), -1))
118       .WillOnce(Return(FutexResult::VALUE_CHANGED));
119   EXPECT_EQ(
120       FutexResult::VALUE_CHANGED, MemoryIdler::futexWaitUntil(fut, 1, forever));
121 }
122
123 TEST(MemoryIdler, futexWaitValueChangedLate) {
124   StrictMock<Futex<MockAtom>> fut;
125   auto clock = MockClock::setup();
126   auto begin = MockClock::time_point(std::chrono::seconds(100));
127   auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
128
129   EXPECT_CALL(*clock, nowImpl())
130       .WillOnce(Return(begin));
131   EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
132                                            Lt(begin + 2 * idleTimeout)), -1))
133       .WillOnce(Return(FutexResult::TIMEDOUT));
134   EXPECT_CALL(fut, futexWaitUntil(1, forever, -1))
135       .WillOnce(Return(FutexResult::VALUE_CHANGED));
136   EXPECT_EQ(
137       FutexResult::VALUE_CHANGED, MemoryIdler::futexWaitUntil(fut, 1, forever));
138 }
139
140 TEST(MemoryIdler, futexWaitAwokenEarly) {
141   StrictMock<Futex<MockAtom>> fut;
142   auto clock = MockClock::setup();
143   auto begin = MockClock::time_point(std::chrono::seconds(100));
144   auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
145
146   EXPECT_CALL(*clock, nowImpl())
147       .WillOnce(Return(begin));
148   EXPECT_CALL(fut, futexWaitUntil(1, Ge(begin + idleTimeout), -1))
149       .WillOnce(Return(FutexResult::AWOKEN));
150   EXPECT_EQ(FutexResult::AWOKEN, MemoryIdler::futexWaitUntil(fut, 1, forever));
151 }
152
153 TEST(MemoryIdler, futexWaitAwokenLate) {
154   StrictMock<Futex<MockAtom>> fut;
155   auto clock = MockClock::setup();
156   auto begin = MockClock::time_point(std::chrono::seconds(100));
157   auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
158
159   EXPECT_CALL(*clock, nowImpl())
160       .WillOnce(Return(begin));
161   EXPECT_CALL(fut, futexWaitUntil(1, begin + idleTimeout, -1))
162       .WillOnce(Return(FutexResult::TIMEDOUT));
163   EXPECT_CALL(fut, futexWaitUntil(1, forever, -1))
164       .WillOnce(Return(FutexResult::AWOKEN));
165   EXPECT_EQ(
166       FutexResult::AWOKEN,
167       MemoryIdler::futexWaitUntil(fut, 1, forever, -1, idleTimeout, 100, 0.0f));
168 }
169
170 TEST(MemoryIdler, futexWaitImmediateFlush) {
171   StrictMock<Futex<MockAtom>> fut;
172   auto clock = MockClock::setup();
173
174   EXPECT_CALL(fut, futexWaitUntil(2, forever, 0xff))
175       .WillOnce(Return(FutexResult::AWOKEN));
176   EXPECT_EQ(
177       FutexResult::AWOKEN,
178       MemoryIdler::futexWaitUntil(
179           fut, 2, forever, 0xff, std::chrono::seconds(0)));
180 }
181
182 TEST(MemoryIdler, futexWaitNeverFlush) {
183   StrictMock<Futex<MockAtom>> fut;
184   auto clock = MockClock::setup();
185
186   EXPECT_CALL(fut, futexWaitUntil(1, forever, -1))
187       .WillOnce(Return(FutexResult::AWOKEN));
188   EXPECT_EQ(
189       FutexResult::AWOKEN,
190       MemoryIdler::futexWaitUntil(
191           fut, 1, forever, -1, std::chrono::seconds(-7)));
192 }