af37fc4dc9e4e21a048ee16c738efbb97193ef8e
[folly.git] / folly / test / MemoryMappingTest.cpp
1 /*
2  * Copyright 2017 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 <cstdlib>
18
19 #include <folly/FileUtil.h>
20 #include <folly/MemoryMapping.h>
21 #include <folly/Random.h>
22 #include <folly/portability/GTest.h>
23 #include <folly/portability/SysMman.h>
24
25 static constexpr double kSomeDouble = 3.14;
26
27 namespace folly {
28
29 TEST(MemoryMapping, Basic) {
30   File f = File::temporary();
31   {
32     MemoryMapping m(File(f.fd()), 0, sizeof(double), MemoryMapping::writable());
33     double* d = m.asWritableRange<double>().data();
34     *d = 37 * kSomeDouble;
35   }
36   {
37     MemoryMapping m(File(f.fd()), 0, 3);
38     EXPECT_EQ(0, m.asRange<int>().size()); // not big enough
39   }
40   {
41     MemoryMapping m(File(f.fd()), 0, sizeof(double));
42     const double* d = m.asRange<double>().data();
43     EXPECT_EQ(*d, 37 * kSomeDouble);
44   }
45 }
46
47 TEST(MemoryMapping, Move) {
48   File f = File::temporary();
49   {
50     MemoryMapping m(
51         File(f.fd()), 0, sizeof(double) * 2, MemoryMapping::writable());
52     double* d = m.asWritableRange<double>().data();
53     d[0] = 37 * kSomeDouble;
54     MemoryMapping m2(std::move(m));
55     double* d2 = m2.asWritableRange<double>().data();
56     d2[1] = 39 * kSomeDouble;
57   }
58   {
59     MemoryMapping m(File(f.fd()), 0, sizeof(double));
60     const double* d = m.asRange<double>().data();
61     EXPECT_EQ(d[0], 37 * kSomeDouble);
62     MemoryMapping m2(std::move(m));
63     const double* d2 = m2.asRange<double>().data();
64     EXPECT_EQ(d2[1], 39 * kSomeDouble);
65   }
66 }
67
68 TEST(MemoryMapping, DoublyMapped) {
69   File f = File::temporary();
70   // two mappings of the same memory, different addresses.
71   MemoryMapping mw(File(f.fd()), 0, sizeof(double), MemoryMapping::writable());
72   MemoryMapping mr(File(f.fd()), 0, sizeof(double));
73
74   double* dw = mw.asWritableRange<double>().data();
75   const double* dr = mr.asRange<double>().data();
76
77   // Show that it's truly the same value, even though the pointers differ
78   EXPECT_NE(dw, dr);
79   *dw = 42 * kSomeDouble;
80   EXPECT_EQ(*dr, 42 * kSomeDouble);
81   *dw = 43 * kSomeDouble;
82   EXPECT_EQ(*dr, 43 * kSomeDouble);
83 }
84
85 namespace {
86
87 void writeStringToFileOrDie(const std::string& str, int fd) {
88   const char* b = str.c_str();
89   size_t count = str.size();
90   ssize_t total_bytes = 0;
91   ssize_t r;
92   do {
93     r = write(fd, b, count);
94     if (r == -1) {
95       if (errno == EINTR) {
96         continue;
97       }
98       PCHECK(r) << "write";
99     }
100
101     total_bytes += r;
102     b += r;
103     count -= r;
104   } while (r != 0 && count);
105 }
106
107 }  // anonymous namespace
108
109 TEST(MemoryMapping, Simple) {
110   File f = File::temporary();
111   writeStringToFileOrDie("hello", f.fd());
112
113   {
114     MemoryMapping m(File(f.fd()));
115     EXPECT_EQ("hello", m.data());
116   }
117   {
118     MemoryMapping m(File(f.fd()), 1, 2);
119     EXPECT_EQ("el", m.data());
120   }
121 }
122
123 TEST(MemoryMapping, LargeFile) {
124   std::string fileData;
125   size_t fileSize = sysconf(_SC_PAGESIZE) * 3 + 10;
126   fileData.reserve(fileSize);
127   for (size_t i = 0; i < fileSize; i++) {
128     fileData.push_back(0xff & Random::rand32());
129   }
130
131   File f = File::temporary();
132   writeStringToFileOrDie(fileData, f.fd());
133
134   {
135     MemoryMapping m(File(f.fd()));
136     EXPECT_EQ(fileData, m.data());
137   }
138   {
139     size_t size = sysconf(_SC_PAGESIZE) * 2;
140     StringPiece s(fileData.data() + 9, size - 9);
141     MemoryMapping m(File(f.fd()), 9, size - 9);
142     EXPECT_EQ(s.toString(), m.data());
143   }
144 }
145
146 TEST(MemoryMapping, ZeroLength) {
147   File f = File::temporary();
148   MemoryMapping m(File(f.fd()));
149   EXPECT_TRUE(m.mlock(MemoryMapping::LockMode::MUST_LOCK));
150   EXPECT_TRUE(m.mlocked());
151   EXPECT_EQ(0, m.data().size());
152 }
153
154 TEST(MemoryMapping, Advise) {
155   File f = File::temporary();
156   size_t kPageSize = 4096;
157   size_t size = kPageSize + 10;  // unaligned file size
158   PCHECK(ftruncateNoInt(f.fd(), size) == 0) << size;
159
160   MemoryMapping m(File(f.fd()));
161
162   // NOTE: advise crashes on bad input.
163
164   m.advise(MADV_NORMAL, 0, kPageSize);
165   m.advise(MADV_NORMAL, 1, kPageSize);
166   m.advise(MADV_NORMAL, 0, 2);
167   m.advise(MADV_NORMAL, 1, 2);
168
169   m.advise(MADV_NORMAL, kPageSize, 0);
170   m.advise(MADV_NORMAL, kPageSize, 1);
171   m.advise(MADV_NORMAL, kPageSize, size - kPageSize);
172
173   auto off = kPageSize + 1;
174   m.advise(MADV_NORMAL, off, size - off);
175
176   EXPECT_DEATH(m.advise(MADV_NORMAL, off, size - off + 1), "");
177 }
178
179 } // namespace folly