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