99630d944b6cd5231cfed4fb19e1625838bb1f09
[folly.git] / folly / test / MemoryIdlerTest.cpp
1 /*
2  * Copyright 2014 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 #include <folly/Baton.h>
19 #include <memory>
20 #include <thread>
21 #include <assert.h>
22 #include <semaphore.h>
23 #include <gflags/gflags.h>
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26 #include <folly/Benchmark.h>
27
28 using namespace folly;
29 using namespace folly::detail;
30 using namespace testing;
31
32 TEST(MemoryIdler, releaseStack) {
33   MemoryIdler::unmapUnusedStack();
34 }
35
36 TEST(MemoryIdler, releaseStackMinExtra) {
37   MemoryIdler::unmapUnusedStack(0);
38 }
39
40 TEST(MemoryIdler, releaseStackLargeExtra) {
41   MemoryIdler::unmapUnusedStack(30000000);
42 }
43
44 TEST(MemoryIdler, releaseMallocTLS) {
45   auto p = new int[4];
46   MemoryIdler::flushLocalMallocCaches();
47   delete[] p;
48   MemoryIdler::flushLocalMallocCaches();
49   p = new int[4];
50   MemoryIdler::flushLocalMallocCaches();
51   delete[] p;
52 }
53
54
55 /// MockedAtom gives us a way to select a mocked Futex implementation
56 /// inside Baton, even though the atom itself isn't exercised by the
57 /// mocked futex
58 template <typename T>
59 struct MockAtom : public std::atomic<T> {
60   explicit MockAtom(T init = 0) : std::atomic<T>(init) {}
61 };
62
63
64 /// MockClock is a bit tricky because we are mocking a static function
65 /// (now()), so we need to find the corresponding mock instance without
66 /// extending its scope beyond that of the test.  I generally avoid
67 /// shared_ptr, but a weak_ptr is just the ticket here
68 struct MockClock {
69   typedef std::chrono::steady_clock::duration duration;
70   typedef std::chrono::steady_clock::time_point time_point;
71
72   MOCK_METHOD0(nowImpl, time_point(void));
73
74   /// Hold on to the returned shared_ptr until the end of the test
75   static std::shared_ptr<StrictMock<MockClock>> setup() {
76     auto rv = std::make_shared<StrictMock<MockClock>>();
77     s_mockClockInstance = rv;
78     return rv;
79   }
80
81   static time_point now() {
82     return s_mockClockInstance.lock()->nowImpl();
83   }
84
85   static std::weak_ptr<StrictMock<MockClock>> s_mockClockInstance;
86 };
87
88 std::weak_ptr<StrictMock<MockClock>> MockClock::s_mockClockInstance;
89
90
91
92 namespace folly { namespace detail {
93
94 /// Futex<MockAtom> is our mocked futex implementation.  Note that the
95 /// method signatures differ from the real Futex because we have elided
96 /// unused default params and collapsed templated methods into the
97 /// used type
98 template<>
99 struct Futex<MockAtom> {
100   MOCK_METHOD2(futexWait, bool(uint32_t, uint32_t));
101   MOCK_METHOD3(futexWaitUntil,
102                FutexResult(uint32_t, const MockClock::time_point&, uint32_t));
103 };
104
105 }}
106
107 TEST(MemoryIdler, futexWaitValueChangedEarly) {
108   StrictMock<Futex<MockAtom>> fut;
109   auto clock = MockClock::setup();
110   auto begin = MockClock::time_point(std::chrono::seconds(100));
111   auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
112
113   EXPECT_CALL(*clock, nowImpl())
114       .WillOnce(Return(begin));
115   EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
116                                            Lt(begin + 2 * idleTimeout)), -1))
117       .WillOnce(Return(FutexResult::VALUE_CHANGED));
118   EXPECT_FALSE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
119 }
120
121 TEST(MemoryIdler, futexWaitValueChangedLate) {
122   StrictMock<Futex<MockAtom>> fut;
123   auto clock = MockClock::setup();
124   auto begin = MockClock::time_point(std::chrono::seconds(100));
125   auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
126
127   EXPECT_CALL(*clock, nowImpl())
128       .WillOnce(Return(begin));
129   EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
130                                            Lt(begin + 2 * idleTimeout)), -1))
131       .WillOnce(Return(FutexResult::TIMEDOUT));
132   EXPECT_CALL(fut, futexWait(1, -1))
133       .WillOnce(Return(false));
134   EXPECT_FALSE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
135 }
136
137 TEST(MemoryIdler, futexWaitAwokenEarly) {
138   StrictMock<Futex<MockAtom>> fut;
139   auto clock = MockClock::setup();
140   auto begin = MockClock::time_point(std::chrono::seconds(100));
141   auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
142
143   EXPECT_CALL(*clock, nowImpl())
144       .WillOnce(Return(begin));
145   EXPECT_CALL(fut, futexWaitUntil(1, Ge(begin + idleTimeout), -1))
146       .WillOnce(Return(FutexResult::AWOKEN));
147   EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
148 }
149
150 TEST(MemoryIdler, futexWaitAwokenLate) {
151   StrictMock<Futex<MockAtom>> fut;
152   auto clock = MockClock::setup();
153   auto begin = MockClock::time_point(std::chrono::seconds(100));
154   auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
155
156   EXPECT_CALL(*clock, nowImpl())
157       .WillOnce(Return(begin));
158   EXPECT_CALL(fut, futexWaitUntil(1, begin + idleTimeout, -1))
159       .WillOnce(Return(FutexResult::TIMEDOUT));
160   EXPECT_CALL(fut, futexWait(1, -1))
161       .WillOnce(Return(true));
162   EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
163       fut, 1, -1, idleTimeout, 100, 0.0f)));
164 }
165
166 TEST(MemoryIdler, futexWaitImmediateFlush) {
167   StrictMock<Futex<MockAtom>> fut;
168   auto clock = MockClock::setup();
169
170   EXPECT_CALL(fut, futexWait(2, 0xff))
171       .WillOnce(Return(true));
172   EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
173       fut, 2, 0xff, std::chrono::seconds(0))));
174 }
175
176 TEST(MemoryIdler, futexWaitNeverFlush) {
177   StrictMock<Futex<MockAtom>> fut;
178   auto clock = MockClock::setup();
179
180   EXPECT_CALL(fut, futexWait(1, -1))
181       .WillOnce(Return(true));
182   EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
183       fut, 1, -1, MockClock::duration::max())));
184 }
185
186
187 BENCHMARK(releaseStack, iters) {
188   for (size_t i = 0; i < iters; ++i) {
189     MemoryIdler::unmapUnusedStack();
190   }
191 }
192
193 BENCHMARK(releaseMallocTLS, iters) {
194   for (size_t i = 0; i < iters; ++i) {
195     MemoryIdler::flushLocalMallocCaches();
196   }
197 }
198
199 int main(int argc, char** argv) {
200   testing::InitGoogleTest(&argc, argv);
201   google::ParseCommandLineFlags(&argc, &argv, true);
202
203   auto rv = RUN_ALL_TESTS();
204   if (!rv && FLAGS_benchmark) {
205     folly::runBenchmarks();
206   }
207   return rv;
208 }