remove conflict
[c11concurrency-benchmarks.git] / mabain / src / mmap_file.cpp
1 /**
2  * Copyright (C) 2017 Cisco Inc.
3  *
4  * This program is free software: you can redistribute it and/or  modify
5  * it under the terms of the GNU General Public License, version 2,
6  * as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 // @author Changxue Deng <chadeng@cisco.com>
18
19 #include <cstdlib>
20
21 #include <sys/mman.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <assert.h>
25
26 #include "file_io.h"
27 #include "mmap_file.h"
28 #include "error.h"
29 #include "logger.h"
30 #include "rollable_file.h"
31
32 namespace mabain {
33
34 MmapFileIO::MmapFileIO(const std::string &fpath, int mode, off_t filesize, bool sync)
35        : FileIO(fpath, mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, sync)
36 {
37     mmap_file = false;
38     mmap_size = 0;
39     mmap_start = 0xFFFFFFFFFFFFFFFF;
40     mmap_end = 0;
41     addr = NULL;
42
43     max_offset = 0;
44     curr_offset = 0;
45
46     if(options & MMAP_ANONYMOUS_MODE)
47     {
48         // Do not open file in anonymous mode.
49         return;
50     }
51
52     Logger::Log(LOG_LEVEL_DEBUG, "opening file " + fpath);
53
54     int fd = Open();
55     if(fd < 0)
56     {
57         int level = LOG_LEVEL_DEBUG;
58         if(mode & O_CREAT)
59             level = LOG_LEVEL_ERROR;
60         Logger::Log(level, "failed to open file %s with mode %d, errno %d",
61                 fpath.c_str(), mode, errno);
62         return;
63     }
64
65     if(filesize > 0 && (mode & O_CREAT))
66     {
67         if(TruncateFile(filesize) != 0)
68         {
69             Logger::Log(LOG_LEVEL_ERROR, "failed to truncate file %s with size %d",
70                     fpath.c_str(), static_cast<int>(filesize));
71             Close();
72         }
73     }
74 }
75
76 MmapFileIO::~MmapFileIO()
77 {
78     UnMapFile();
79 }
80
81 uint8_t* MmapFileIO::MapFile(size_t size, off_t offset, bool sliding)
82 {
83     int mode = PROT_READ;
84     if(options & O_RDWR)
85         mode |= PROT_WRITE;
86
87     if(options & MMAP_ANONYMOUS_MODE)
88     {
89         assert(offset == 0 && !sliding);
90         addr = static_cast<unsigned char *>(mmap(NULL, size, mode,
91                                   MAP_SHARED | MAP_ANONYMOUS, -1, 0));
92     }
93     else
94     {
95         addr = static_cast<unsigned char *>(FileIO::MapFile(size, mode,
96                                   MAP_SHARED, offset));
97     }
98
99     if(addr == MAP_FAILED)
100     {
101         Logger::Log(LOG_LEVEL_WARN, "%s mmap (%s) failed errno=%d offset=%llu size=%llu",
102                     (options & MMAP_ANONYMOUS_MODE) ? "anon":"",
103                     path.c_str(), errno, offset, size);
104         return NULL;
105     }
106
107     if(!sliding)
108     {
109         mmap_file = true;
110         mmap_size = size;
111         mmap_start = offset;
112         mmap_end = offset + size;
113     }
114
115     if(mode & PROT_WRITE)
116     {
117         Logger::Log(LOG_LEVEL_DEBUG, "mmap file %s, sliding=%d, size=%d, offset=%d",
118                 path.c_str(), sliding, size, offset);
119     }
120
121     return addr;
122 }
123
124 void MmapFileIO::UnMapFile()
125 {
126     if(mmap_file && addr != NULL)
127     {
128         munmap(addr, mmap_size);
129         addr = NULL;
130     }
131 }
132
133 // SeqWrite:
134 //     find the current offset first
135 //     call RandomWrite using the offset
136 //     reset the offset based on the number of bytes written
137 size_t MmapFileIO::SeqWrite(const void *data, size_t size)
138 {
139     size_t bytes_written = MmapFileIO::RandomWrite(data, size, curr_offset);
140     curr_offset += bytes_written;
141     return bytes_written;
142 }
143
144 size_t MmapFileIO::RandomWrite(const void *data, size_t size, off_t offset)
145 {
146     if(data == NULL)
147         return 0;
148
149     size_t bytes_written;
150
151     if(!mmap_file)
152     {
153         // If the file is not mmaped, we just need to write to the file.
154         bytes_written = FileIO::RandomWrite(data, size, offset);
155         if(bytes_written + offset > max_offset)
156         {
157             max_offset = bytes_written + offset;
158         }
159         return bytes_written;
160     }
161
162     // If the file is mmaped, we need to write to the memory or file.
163     off_t offset_end = static_cast<off_t>(size) + offset;
164     const unsigned char *ptr = static_cast<const unsigned char *>(data);
165     if(offset < mmap_start)
166     {
167         if(offset_end <= mmap_start)
168         {
169             // no overlap with mmap region
170             bytes_written = FileIO::RandomWrite(ptr, size, offset);
171         }
172         else if(offset_end <= mmap_end)
173         {
174             // partial overlap with left region
175             size_t written_left = mmap_start - offset;
176             bytes_written = FileIO::RandomWrite(ptr, written_left, offset);
177             ptr += written_left;
178             // partial overlap with mmap region
179             memcpy(addr+mmap_start, ptr, size-written_left);
180             bytes_written += size - written_left;
181             if(sync_on_write)
182                 RollableFile::ShmSync(addr+mmap_start, size-written_left);
183         }
184         else
185         {
186             // partial overlap with left region
187             size_t written_left = mmap_start - offset;
188             bytes_written = FileIO::RandomWrite(ptr, written_left, offset);
189             ptr += written_left;
190             // full overlap with mmap region
191             memcpy(addr+mmap_start, ptr, mmap_size);
192             bytes_written += mmap_size;
193             written_left += mmap_size;
194             ptr += mmap_size;
195             if(sync_on_write)
196                 RollableFile::ShmSync(addr+mmap_start, mmap_size);
197             // partial overlap with right region
198             bytes_written += FileIO::RandomWrite(ptr, size-written_left, mmap_end);
199         }
200     }
201     else if(offset < mmap_end)
202     {
203         if(offset_end <= mmap_end)
204         {
205             // full data is within the mmap region
206             memcpy(addr+offset, ptr, size);
207             bytes_written = size;
208             if(sync_on_write)
209                 RollableFile::ShmSync(addr+offset, size);
210         }
211         else
212         {
213             // partial overlap with mmap region
214             size_t written_left = mmap_end - offset;
215             memcpy(addr+offset, ptr, written_left);
216             ptr += written_left;
217             bytes_written = written_left;
218             if(sync_on_write)
219                 RollableFile::ShmSync(addr+offset, written_left);
220             // partial overlap with the right region
221             bytes_written += FileIO::RandomWrite(ptr, size-written_left, mmap_end);
222         }
223     }
224     else
225     {
226         // no overlap with mmap region
227         bytes_written = FileIO::RandomWrite(ptr, size, offset);
228     }
229
230     if(bytes_written + offset > max_offset)
231         max_offset = bytes_written + offset;
232
233     return bytes_written;
234 }
235
236 // SeqRead:
237 //     find the current offset first
238 //     call RandomRead using the offset
239 //     reset the offset based on the number of bytes read
240 size_t MmapFileIO::SeqRead(void *buff, size_t size)
241 {
242     size_t bytes_read = MmapFileIO::RandomRead(buff, size, curr_offset);
243     curr_offset += bytes_read;
244     return bytes_read;
245 }
246
247 size_t MmapFileIO::RandomRead(void *buff, size_t size, off_t offset)
248 {
249     if(buff == NULL)
250         return 0;
251
252     if(!mmap_file)
253     {
254         // If file is not mmaped, go directly to file IO.
255         return FileIO::RandomRead(buff, size, offset);
256     }
257
258     size_t bytes_read;
259     off_t offset_end = static_cast<off_t>(size) + offset;
260     unsigned char *ptr = static_cast<unsigned char *>(buff);
261     if(offset < mmap_start)
262     {
263         if(offset_end <= mmap_start)
264         {
265             // no overlap with mmap region
266             bytes_read = FileIO::RandomRead(ptr, size, offset);
267         }
268         else if(offset_end <= mmap_end)
269         {
270             // partial overlap with left region
271             size_t read_left = mmap_start - offset;
272             bytes_read = FileIO::RandomRead(ptr, read_left, offset);
273             ptr += read_left;
274             // partial overlap with mmap region
275             memcpy(ptr, addr+mmap_start, size-read_left);
276             bytes_read += size - read_left;
277         }
278         else
279         {
280             // partial overlap with left region
281             size_t read_left = mmap_start - offset;
282             bytes_read = FileIO::RandomRead(ptr, read_left, offset);
283             ptr += read_left;
284             // full overlap with mmap region
285             memcpy(ptr, addr+mmap_start, mmap_size);
286             bytes_read += mmap_size;
287             read_left += mmap_size;
288             ptr += mmap_size;
289             // partial overlap with right region
290             bytes_read += FileIO::RandomRead(ptr, size-read_left, mmap_end);
291         }
292     }
293     else if(offset < mmap_end)
294     {
295         if(offset_end <= mmap_end)
296         {
297             // full data is within the mmap region
298             memcpy(ptr, addr+offset, size);
299             bytes_read = size;
300         }
301         else
302         {
303             // full overlap with mmap region
304             size_t read_left = mmap_end - offset;
305             memcpy(ptr, addr+offset, read_left);
306             ptr += read_left;
307             bytes_read = read_left;
308             // partial overlap with the right region
309             bytes_read += FileIO::RandomRead(ptr, size-read_left, mmap_end);
310         }
311     }
312     else
313     {
314         // no overlap with mmap region
315         bytes_read = FileIO::RandomRead(ptr, size, offset);
316     }
317
318     return bytes_read;
319 }
320
321 bool MmapFileIO::IsMapped() const
322 {
323     return mmap_file;
324 }
325
326 uint8_t* MmapFileIO::GetMapAddr() const
327 {
328     return addr;
329 }
330
331 void MmapFileIO::Flush()
332 {
333     if(options & MMAP_ANONYMOUS_MODE)
334         return;
335
336     if(addr != NULL)
337         msync(addr, mmap_size, MS_SYNC);
338     FileIO::Flush();
339 }
340
341 }