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