Blender V4.5
BLI_mmap.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_mmap.h"
10#include "BLI_fileops.h"
11#include "BLI_listbase.h"
12#include "MEM_guardedalloc.h"
13
14#include <cstring>
15
16#ifndef WIN32
17# include <csignal>
18# include <cstdlib>
19# include <sys/mman.h> /* For mmap. */
20# include <unistd.h> /* For read close. */
21#else
22# include "BLI_winstuff.h"
23# include <io.h> /* For open close read. */
24#endif
25
27 /* The address to which the file was mapped. */
28 char *memory;
29
30 /* The length of the file (and therefore the mapped region). */
31 size_t length;
32
33 /* Platform-specific handle for the mapping. */
34 void *handle;
35
36 /* Flag to indicate IO errors. Needs to be volatile since it's being set from
37 * within the signal handler, which is not part of the normal execution flow. */
38 volatile bool io_error;
39};
40
41#ifndef WIN32
42/* When using memory-mapped files, any IO errors will result in a SIGBUS signal.
43 * Therefore, we need to catch that signal and stop reading the file in question.
44 * To do so, we keep a list of all current FileDatas that use memory-mapped files,
45 * and if a SIGBUS is caught, we check if the failed address is inside one of the
46 * mapped regions.
47 * If it is, we set a flag to indicate a failed read and remap the memory in
48 * question to a zero-backed region in order to avoid additional signals.
49 * The code that actually reads the memory area has to check whether the flag was
50 * set after it's done reading.
51 * If the error occurred outside of a memory-mapped region, we call the previous
52 * handler if one was configured and abort the process otherwise.
53 */
54
55static struct error_handler_data {
58 void (*next_handler)(int, siginfo_t *, void *);
59} error_handler = {{nullptr}};
60
61static void sigbus_handler(int sig, siginfo_t *siginfo, void *ptr)
62{
63 /* We only handle SIGBUS here for now. */
64 BLI_assert(sig == SIGBUS);
65
66 const char *error_addr = (const char *)siginfo->si_addr;
67 /* Find the file that this error belongs to. */
68 LISTBASE_FOREACH (LinkData *, link, &error_handler.open_mmaps) {
69 BLI_mmap_file *file = static_cast<BLI_mmap_file *>(link->data);
70
71 /* Is the address where the error occurred in this file's mapped range? */
72 if (error_addr >= file->memory && error_addr < file->memory + file->length) {
73 file->io_error = true;
74
75 /* Replace the mapped memory with zeroes. */
76 const void *mapped_memory = mmap(
77 file->memory, file->length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
78 if (mapped_memory == MAP_FAILED) {
79 fprintf(stderr, "SIGBUS handler: Error replacing mapped file with zeros\n");
80 }
81
82 return;
83 }
84 }
85
86 /* Fall back to other handler if there was one. */
87 if (error_handler.next_handler) {
88 error_handler.next_handler(sig, siginfo, ptr);
89 }
90 else {
91 fprintf(stderr, "Unhandled SIGBUS caught\n");
92 abort();
93 }
94}
95
96/* Ensures that the error handler is set up and ready. */
98{
99 if (!error_handler.configured) {
100 struct sigaction newact = {{nullptr}}, oldact = {{nullptr}};
101
102 newact.sa_sigaction = sigbus_handler;
103 newact.sa_flags = SA_SIGINFO;
104
105 if (sigaction(SIGBUS, &newact, &oldact)) {
106 return false;
107 }
108
109 /* Remember the previously configured handler to fall back to it if the error
110 * does not belong to any of the mapped files. */
111 error_handler.next_handler = oldact.sa_sigaction;
112 error_handler.configured = 1;
113 }
114
115 return true;
116}
117
118/* Adds a file to the list that the error handler checks. */
120{
121 BLI_addtail(&error_handler.open_mmaps, BLI_genericNodeN(file));
122}
123
124/* Removes a file from the list that the error handler checks. */
126{
127 LinkData *link = static_cast<LinkData *>(
128 BLI_findptr(&error_handler.open_mmaps, file, offsetof(LinkData, data)));
129 BLI_freelinkN(&error_handler.open_mmaps, link);
130}
131#endif
132
134{
135 void *memory, *handle = nullptr;
136 const size_t length = BLI_lseek(fd, 0, SEEK_END);
137 if (UNLIKELY(length == size_t(-1))) {
138 return nullptr;
139 }
140
141#ifndef WIN32
142 /* Ensure that the SIGBUS handler is configured. */
143 if (!sigbus_handler_setup()) {
144 return nullptr;
145 }
146
147 /* Map the given file to memory. */
148 memory = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, fd, 0);
149 if (memory == MAP_FAILED) {
150 return nullptr;
151 }
152#else
153 /* Convert the POSIX-style file descriptor to a Windows handle. */
154 void *file_handle = (void *)_get_osfhandle(fd);
155 /* Memory mapping on Windows is a two-step process - first we create a mapping,
156 * then we create a view into that mapping.
157 * In our case, one view that spans the entire file is enough. */
158 handle = CreateFileMapping(file_handle, nullptr, PAGE_READONLY, 0, 0, nullptr);
159 if (handle == nullptr) {
160 return nullptr;
161 }
162 memory = MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0);
163 if (memory == nullptr) {
164 CloseHandle(handle);
165 return nullptr;
166 }
167#endif
168
169 /* Now that the mapping was successful, allocate memory and set up the BLI_mmap_file. */
171 file->memory = static_cast<char *>(memory);
172 file->handle = handle;
173 file->length = length;
174
175#ifndef WIN32
176 /* Register the file with the error handler. */
177 sigbus_handler_add(file);
178#endif
179
180 return file;
181}
182
183bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length)
184{
185 /* If a previous read has already failed or we try to read past the end,
186 * don't even attempt to read any further. */
187 if (file->io_error || (offset + length > file->length)) {
188 return false;
189 }
190
191#ifndef WIN32
192 /* If an error occurs in this call, sigbus_handler will be called and will set
193 * file->io_error to true. */
194 memcpy(dest, file->memory + offset, length);
195#else
196 /* On Windows, we use exception handling to be notified of errors. */
197 __try
198 {
199 memcpy(dest, file->memory + offset, length);
200 }
201 __except (GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER :
202 EXCEPTION_CONTINUE_SEARCH)
203 {
204 file->io_error = true;
205 return false;
206 }
207#endif
208
209 return !file->io_error;
210}
211
213{
214 return file->memory;
215}
216
218{
219 return file->length;
220}
221
223{
224 return file->io_error;
225}
226
228{
229#ifndef WIN32
230 munmap((void *)file->memory, file->length);
232#else
233 UnmapViewOfFile(file->memory);
234 CloseHandle(file->handle);
235#endif
236
237 MEM_freeN(file);
238}
#define BLI_assert(a)
Definition BLI_assert.h:46
File and directory operations.
int64_t BLI_lseek(int fd, int64_t offset, int whence)
Definition storage.cc:221
LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:922
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_mmap_free(BLI_mmap_file *file)
Definition BLI_mmap.cc:227
bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length)
Definition BLI_mmap.cc:183
static void sigbus_handler_remove(BLI_mmap_file *file)
Definition BLI_mmap.cc:125
static void sigbus_handler(int sig, siginfo_t *siginfo, void *ptr)
Definition BLI_mmap.cc:61
BLI_mmap_file * BLI_mmap_open(int fd)
Definition BLI_mmap.cc:133
static bool sigbus_handler_setup()
Definition BLI_mmap.cc:97
static struct error_handler_data error_handler
size_t BLI_mmap_get_length(const BLI_mmap_file *file)
Definition BLI_mmap.cc:217
static void sigbus_handler_add(BLI_mmap_file *file)
Definition BLI_mmap.cc:119
bool BLI_mmap_any_io_error(const BLI_mmap_file *file)
Definition BLI_mmap.cc:222
void * BLI_mmap_get_pointer(BLI_mmap_file *file)
Definition BLI_mmap.cc:212
#define UNLIKELY(x)
Compatibility-like things for windows.
Read Guarded memory(de)allocation.
BMesh const char void * data
#define offsetof(t, d)
float length(VecOp< float, D >) RET
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
char * memory
Definition BLI_mmap.cc:28
void * handle
Definition BLI_mmap.cc:34
size_t length
Definition BLI_mmap.cc:31
volatile bool io_error
Definition BLI_mmap.cc:38
ListBase open_mmaps
Definition BLI_mmap.cc:56
void(* next_handler)(int, siginfo_t *, void *)
Definition BLI_mmap.cc:58
PointerRNA * ptr
Definition wm_files.cc:4226