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