Blender V4.3
BLI_mmap.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_mmap.h"
10#include "BLI_fileops.h"
11#include "BLI_listbase.h"
12#include "MEM_guardedalloc.h"
13
14#include <string.h>
15
16#ifndef WIN32
17# include <signal.h>
18# include <stdlib.h>
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 *);
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. */
69 BLI_mmap_file *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. */
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. */
97static bool sigbus_handler_setup(void)
98{
100 struct sigaction newact = {0}, oldact = {0};
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;
113 }
114
115 return true;
116}
117
118/* Adds a file to the list that the error handler checks. */
123
124/* Removes a file from the list that the error handler checks. */
130#endif
131
133{
134 void *memory, *handle = NULL;
135 const size_t length = BLI_lseek(fd, 0, SEEK_END);
136 if (UNLIKELY(length == (size_t)-1)) {
137 return NULL;
138 }
139
140#ifndef WIN32
141 /* Ensure that the SIGBUS handler is configured. */
142 if (!sigbus_handler_setup()) {
143 return NULL;
144 }
145
146 /* Map the given file to memory. */
147 memory = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
148 if (memory == MAP_FAILED) {
149 return NULL;
150 }
151#else
152 /* Convert the POSIX-style file descriptor to a Windows handle. */
153 void *file_handle = (void *)_get_osfhandle(fd);
154 /* Memory mapping on Windows is a two-step process - first we create a mapping,
155 * then we create a view into that mapping.
156 * In our case, one view that spans the entire file is enough. */
157 handle = CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 0, NULL);
158 if (handle == NULL) {
159 return NULL;
160 }
161 memory = MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0);
162 if (memory == NULL) {
163 CloseHandle(handle);
164 return NULL;
165 }
166#endif
167
168 /* Now that the mapping was successful, allocate memory and set up the BLI_mmap_file. */
169 BLI_mmap_file *file = MEM_callocN(sizeof(BLI_mmap_file), __func__);
170 file->memory = memory;
171 file->handle = handle;
172 file->length = length;
173
174#ifndef WIN32
175 /* Register the file with the error handler. */
176 sigbus_handler_add(file);
177#endif
178
179 return file;
180}
181
182bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length)
183{
184 /* If a previous read has already failed or we try to read past the end,
185 * don't even attempt to read any further. */
186 if (file->io_error || (offset + length > file->length)) {
187 return false;
188 }
189
190#ifndef WIN32
191 /* If an error occurs in this call, sigbus_handler will be called and will set
192 * file->io_error to true. */
193 memcpy(dest, file->memory + offset, length);
194#else
195 /* On Windows, we use exception handling to be notified of errors. */
196 __try
197 {
198 memcpy(dest, file->memory + offset, length);
199 }
200 __except (GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER :
201 EXCEPTION_CONTINUE_SEARCH)
202 {
203 file->io_error = true;
204 return false;
205 }
206#endif
207
208 return !file->io_error;
209}
210
212{
213 return file->memory;
214}
215
217{
218 return file->length;
219}
220
222{
223#ifndef WIN32
224 munmap((void *)file->memory, file->length);
226#else
227 UnmapViewOfFile(file->memory);
228 CloseHandle(file->handle);
229#endif
230
231 MEM_freeN(file);
232}
#define BLI_assert(a)
Definition BLI_assert.h:50
File and directory operations.
int64_t BLI_lseek(int fd, int64_t offset, int whence)
Definition storage.cc:197
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
struct LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:909
static bool sigbus_handler_setup(void)
Definition BLI_mmap.c:97
void BLI_mmap_free(BLI_mmap_file *file)
Definition BLI_mmap.c:221
bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length)
Definition BLI_mmap.c:182
static void sigbus_handler_remove(BLI_mmap_file *file)
Definition BLI_mmap.c:125
static void sigbus_handler(int sig, siginfo_t *siginfo, void *ptr)
Definition BLI_mmap.c:61
BLI_mmap_file * BLI_mmap_open(int fd)
Definition BLI_mmap.c:132
static struct error_handler_data error_handler
size_t BLI_mmap_get_length(const BLI_mmap_file *file)
Definition BLI_mmap.c:216
static void sigbus_handler_add(BLI_mmap_file *file)
Definition BLI_mmap.c:119
void * BLI_mmap_get_pointer(BLI_mmap_file *file)
Definition BLI_mmap.c:211
#define UNLIKELY(x)
Compatibility-like things for windows.
Read Guarded memory(de)allocation.
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
FILE * file
#define NULL
#define offsetof(t, d)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
char * memory
Definition BLI_mmap.c:28
void * handle
Definition BLI_mmap.c:34
size_t length
Definition BLI_mmap.c:31
volatile bool io_error
Definition BLI_mmap.c:38
ListBase open_mmaps
Definition BLI_mmap.c:56
void(* next_handler)(int, siginfo_t *, void *)
Definition BLI_mmap.c:58
PointerRNA * ptr
Definition wm_files.cc:4126