Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenlib/BLI_mmap.h59
-rw-r--r--source/blender/blenlib/CMakeLists.txt2
-rw-r--r--source/blender/blenlib/intern/BLI_mmap.c233
-rw-r--r--source/blender/blenloader/intern/readfile.c68
-rw-r--r--source/blender/blenloader/intern/readfile.h4
-rw-r--r--source/blender/imbuf/intern/IMB_allocimbuf.h2
-rw-r--r--source/blender/imbuf/intern/allocimbuf.c2
-rw-r--r--source/blender/imbuf/intern/readimage.c24
-rw-r--r--source/blender/makesdna/intern/CMakeLists.txt6
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt1
10 files changed, 376 insertions, 25 deletions
diff --git a/source/blender/blenlib/BLI_mmap.h b/source/blender/blenlib/BLI_mmap.h
new file mode 100644
index 00000000000..385c56bd10a
--- /dev/null
+++ b/source/blender/blenlib/BLI_mmap.h
@@ -0,0 +1,59 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+#ifndef __BLI_MMAP_H__
+#define __BLI_MMAP_H__
+
+/** \file
+ * \ingroup bli
+ */
+
+#include "BLI_compiler_attrs.h"
+#include "BLI_utildefines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Memory-mapped file IO that implements all the OS-specific details and error handling. */
+
+struct BLI_mmap_file;
+
+typedef struct BLI_mmap_file BLI_mmap_file;
+
+/* Prepares an opened file for memory-mapped IO.
+ * May return NULL if the operation fails.
+ * Note that this seeks to the end of the file to determine its length. */
+BLI_mmap_file *BLI_mmap_open(int fd) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
+
+/* Reads length bytes from file at the given offset into dest.
+ * Returns whether the operation was successful (may fail when reading beyond the file
+ * end or when IO errors occur). */
+bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length)
+ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
+
+void *BLI_mmap_get_pointer(BLI_mmap_file *file) ATTR_WARN_UNUSED_RESULT;
+
+void BLI_mmap_free(BLI_mmap_file *file) ATTR_NONNULL(1);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BLI_MEMPOOL_H__ */
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index aa4c4efe7d4..b7a13596dd0 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -54,6 +54,7 @@ set(SRC
intern/BLI_memblock.c
intern/BLI_memiter.c
intern/BLI_mempool.c
+ intern/BLI_mmap.c
intern/BLI_timer.c
intern/DLRB_tree.c
intern/array_store.c
@@ -243,6 +244,7 @@ set(SRC
BLI_mempool.h
BLI_mesh_boolean.hh
BLI_mesh_intersect.hh
+ BLI_mmap.h
BLI_mpq2.hh
BLI_mpq3.hh
BLI_multi_value_map.hh
diff --git a/source/blender/blenlib/intern/BLI_mmap.c b/source/blender/blenlib/intern/BLI_mmap.c
new file mode 100644
index 00000000000..f0b13183c68
--- /dev/null
+++ b/source/blender/blenlib/intern/BLI_mmap.c
@@ -0,0 +1,233 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include "BLI_mmap.h"
+#include "BLI_fileops.h"
+#include "BLI_listbase.h"
+#include "MEM_guardedalloc.h"
+
+#include <string.h>
+
+#ifndef WIN32
+# include <stdlib.h>
+# include <signal.h>
+# include <sys/mman.h> // for mmap
+# include <unistd.h> // for read close
+#else
+# include "BLI_winstuff.h"
+# include <io.h> // for open close read
+#endif
+
+struct BLI_mmap_file {
+ /* The address to which the file was mapped. */
+ char *memory;
+
+ /* The length of the file (and therefore the mapped region). */
+ size_t length;
+
+ /* Platform-specific handle for the mapping. */
+ void *handle;
+
+ /* Flag to indicate IO errors. Needs to be volatile since it's being set from
+ * within the signal handler, which is not part of the normal execution flow. */
+ volatile bool io_error;
+};
+
+#ifndef WIN32
+/* When using memory-mapped files, any IO errors will result in a SIGBUS signal.
+ * Therefore, we need to catch that signal and stop reading the file in question.
+ * To do so, we keep a list of all current FileDatas that use memory-mapped files,
+ * and if a SIGBUS is caught, we check if the failed address is inside one of the
+ * mapped regions.
+ * If it is, we set a flag to indicate a failed read and remap the memory in
+ * question to a zero-backed region in order to avoid additional signals.
+ * The code that actually reads the memory area has to check whether the flag was
+ * set after it's done reading.
+ * If the error occurred outside of a memory-mapped region, we call the previous
+ * handler if one was configured and abort the process otherwise.
+ */
+
+struct error_handler_data {
+ ListBase open_mmaps;
+ char configured;
+ void (*next_handler)(int, siginfo_t *, void *);
+} error_handler = {0};
+
+static void sigbus_handler(int sig, siginfo_t *siginfo, void *ptr)
+{
+ /* We only handle SIGBUS here for now. */
+ BLI_assert(sig == SIGBUS);
+
+ char *error_addr = (char *)siginfo->si_addr;
+ /* Find the file that this error belongs to. */
+ LISTBASE_FOREACH (LinkData *, link, &error_handler.open_mmaps) {
+ BLI_mmap_file *file = link->data;
+
+ /* Is the address where the error occurred in this file's mapped range? */
+ if (error_addr >= file->memory && error_addr < file->memory + file->length) {
+ file->io_error = true;
+
+ /* Replace the mapped memory with zeroes. */
+ mmap(file->memory, file->length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+
+ return;
+ }
+ }
+
+ /* Fall back to other handler if there was one. */
+ if (error_handler.next_handler) {
+ error_handler.next_handler(sig, siginfo, ptr);
+ }
+ else {
+ fprintf(stderr, "Unhandled SIGBUS caught\n");
+ abort();
+ }
+}
+
+/* Ensures that the error handler is set up and ready. */
+static bool sigbus_handler_setup(void)
+{
+ if (!error_handler.configured) {
+ struct sigaction newact = {0}, oldact = {0};
+
+ newact.sa_sigaction = sigbus_handler;
+ newact.sa_flags = SA_SIGINFO;
+
+ if (sigaction(SIGBUS, &newact, &oldact)) {
+ return false;
+ }
+
+ /* Remember the previously configured handler to fall back to it if the error
+ * does not belong to any of the mapped files. */
+ error_handler.next_handler = oldact.sa_sigaction;
+ error_handler.configured = 1;
+ }
+
+ return true;
+}
+
+/* Adds a file to the list that the error handler checks. */
+static void sigbus_handler_add(BLI_mmap_file *file)
+{
+ BLI_addtail(&error_handler.open_mmaps, BLI_genericNodeN(file));
+}
+
+/* Removes a file from the list that the error handler checks. */
+static void sigbus_handler_remove(BLI_mmap_file *file)
+{
+ LinkData *link = BLI_findptr(&error_handler.open_mmaps, file, offsetof(LinkData, data));
+ BLI_freelinkN(&error_handler.open_mmaps, link);
+}
+#endif
+
+BLI_mmap_file *BLI_mmap_open(int fd)
+{
+ void *memory, *handle = NULL;
+ size_t length = BLI_lseek(fd, 0, SEEK_END);
+
+#ifndef WIN32
+ /* Ensure that the SIGBUS handler is configured. */
+ if (!sigbus_handler_setup()) {
+ return NULL;
+ }
+
+ /* Map the given file to memory. */
+ memory = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (memory == MAP_FAILED) {
+ return NULL;
+ }
+#else
+ /* Convert the POSIX-style file descriptor to a Windows handle. */
+ void *file_handle = (void *)_get_osfhandle(fd);
+ /* Memory mapping on Windows is a two-step process - first we create a mapping,
+ * then we create a view into that mapping.
+ * In our case, one view that spans the entire file is enough. */
+ handle = CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (handle == NULL) {
+ return NULL;
+ }
+ memory = MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0);
+ if (memory == NULL) {
+ CloseHandle(handle);
+ return NULL;
+ }
+#endif
+
+ /* Now that the mapping was successful, allocate memory and set up the BLI_mmap_file. */
+ BLI_mmap_file *file = MEM_callocN(sizeof(BLI_mmap_file), __func__);
+ file->memory = memory;
+ file->handle = handle;
+ file->length = length;
+
+#ifndef WIN32
+ /* Register the file with the error handler. */
+ sigbus_handler_add(file);
+#endif
+
+ return file;
+}
+
+bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length)
+{
+ /* If a previous read has already failed or we try to read past the end,
+ * don't even attempt to read any further. */
+ if (file->io_error || (offset + length > file->length)) {
+ return false;
+ }
+
+#ifndef WIN32
+ /* If an error occurs in this call, sigbus_handler will be called and will set
+ * file->io_error to true. */
+ memcpy(dest, file->memory + offset, length);
+#else
+ /* On Windows, we use exception handling to be notified of errors. */
+ __try {
+ memcpy(dest, file->memory + offset, length);
+ }
+ __except (GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER :
+ EXCEPTION_CONTINUE_SEARCH) {
+ file->io_error = true;
+ return false;
+ }
+#endif
+
+ return !file->io_error;
+}
+
+void *BLI_mmap_get_pointer(BLI_mmap_file *file)
+{
+ return file->memory;
+}
+
+void BLI_mmap_free(BLI_mmap_file *file)
+{
+#ifndef WIN32
+ munmap((void *)file->memory, file->length);
+ sigbus_handler_remove(file);
+#else
+ UnmapViewOfFile(file->memory);
+ CloseHandle(file->handle);
+#endif
+
+ MEM_freeN(file);
+}
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index b61abd4ed06..2192e9a378f 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -68,6 +68,7 @@
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_mempool.h"
+#include "BLI_mmap.h"
#include "BLI_threads.h"
#include "BLT_translation.h"
@@ -1179,6 +1180,53 @@ static ssize_t fd_read_from_memory(FileData *filedata,
return readsize;
}
+/* Memory-mapped file reading.
+ * By using mmap(), we can map a file so that it can be treated like normal memory,
+ * meaning that we can just read from it with memcpy() etc.
+ * This avoids system call overhead and can significantly speed up file loading.
+ */
+
+static ssize_t fd_read_from_mmap(FileData *filedata,
+ void *buffer,
+ size_t size,
+ bool *UNUSED(r_is_memchunck_identical))
+{
+ /* don't read more bytes than there are available in the buffer */
+ size_t readsize = MIN2(size, (size_t)(filedata->buffersize - filedata->file_offset));
+
+ if (!BLI_mmap_read(filedata->mmap_file, buffer, filedata->file_offset, readsize)) {
+ return 0;
+ }
+
+ filedata->file_offset += readsize;
+
+ return readsize;
+}
+
+static off64_t fd_seek_from_mmap(FileData *filedata, off64_t offset, int whence)
+{
+ off64_t new_pos;
+ if (whence == SEEK_CUR) {
+ new_pos = filedata->file_offset + offset;
+ }
+ else if (whence == SEEK_SET) {
+ new_pos = offset;
+ }
+ else if (whence == SEEK_END) {
+ new_pos = filedata->buffersize + offset;
+ }
+ else {
+ return -1;
+ }
+
+ if (new_pos < 0 || new_pos > filedata->buffersize) {
+ return -1;
+ }
+
+ filedata->file_offset = new_pos;
+ return filedata->file_offset;
+}
+
/* MemFile reading. */
static ssize_t fd_read_from_memfile(FileData *filedata,
@@ -1306,6 +1354,8 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath,
{
FileDataReadFn *read_fn = NULL;
FileDataSeekFn *seek_fn = NULL; /* Optional. */
+ size_t buffersize = 0;
+ BLI_mmap_file *mmap_file = NULL;
gzFile gzfile = (gzFile)Z_NULL;
@@ -1322,14 +1372,21 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath,
return NULL;
}
- BLI_lseek(file, 0, SEEK_SET);
-
/* Regular file. */
if (memcmp(header, "BLENDER", sizeof(header)) == 0) {
read_fn = fd_read_data_from_file;
seek_fn = fd_seek_data_from_file;
+
+ mmap_file = BLI_mmap_open(file);
+ if (mmap_file != NULL) {
+ read_fn = fd_read_from_mmap;
+ seek_fn = fd_seek_from_mmap;
+ buffersize = BLI_lseek(file, 0, SEEK_END);
+ }
}
+ BLI_lseek(file, 0, SEEK_SET);
+
/* Gzip file. */
errno = 0;
if ((read_fn == NULL) &&
@@ -1363,6 +1420,8 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath,
fd->read = read_fn;
fd->seek = seek_fn;
+ fd->mmap_file = mmap_file;
+ fd->buffersize = buffersize;
return fd;
}
@@ -1531,6 +1590,11 @@ void blo_filedata_free(FileData *fd)
fd->buffer = NULL;
}
+ if (fd->mmap_file) {
+ BLI_mmap_free(fd->mmap_file);
+ fd->mmap_file = NULL;
+ }
+
/* Free all BHeadN data blocks */
#ifndef NDEBUG
BLI_freelistN(&fd->bhead_list);
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index c724cc32051..86f05eda7b7 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -41,6 +41,7 @@ struct Object;
struct OldNewMap;
struct ReportList;
struct UserDef;
+struct BLI_mmap_file;
typedef struct IDNameLib_Map IDNameLib_Map;
@@ -83,8 +84,9 @@ typedef struct FileData {
/** Regular file reading. */
int filedes;
- /** Variables needed for reading from memory / stream. */
+ /** Variables needed for reading from memory / stream / memory-mapped files. */
const char *buffer;
+ struct BLI_mmap_file *mmap_file;
/** Variables needed for reading from memfile (undo). */
struct MemFile *memfile;
/** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use
diff --git a/source/blender/imbuf/intern/IMB_allocimbuf.h b/source/blender/imbuf/intern/IMB_allocimbuf.h
index 08aa1936a6f..c92d764a104 100644
--- a/source/blender/imbuf/intern/IMB_allocimbuf.h
+++ b/source/blender/imbuf/intern/IMB_allocimbuf.h
@@ -32,7 +32,7 @@ struct ImBuf;
void imb_refcounter_lock_init(void);
void imb_refcounter_lock_exit(void);
-#ifdef WIN32
+#ifndef WIN32
void imb_mmap_lock_init(void);
void imb_mmap_lock_exit(void);
void imb_mmap_lock(void);
diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c
index 8dfb3ada7d6..bfb7bc93ac7 100644
--- a/source/blender/imbuf/intern/allocimbuf.c
+++ b/source/blender/imbuf/intern/allocimbuf.c
@@ -53,7 +53,7 @@ void imb_refcounter_lock_exit(void)
BLI_spin_end(&refcounter_spin);
}
-#ifdef WIN32
+#ifndef WIN32
static SpinLock mmap_spin;
void imb_mmap_lock_init(void)
diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c
index f0daa4543e1..50210650f05 100644
--- a/source/blender/imbuf/intern/readimage.c
+++ b/source/blender/imbuf/intern/readimage.c
@@ -23,13 +23,13 @@
*/
#ifdef _WIN32
-# include "mmap_win.h"
# include <io.h>
# include <stddef.h>
# include <sys/types.h>
#endif
#include "BLI_fileops.h"
+#include "BLI_mmap.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -186,20 +186,19 @@ ImBuf *IMB_loadifffile(
size = BLI_file_descriptor_size(file);
imb_mmap_lock();
- mem = mmap(NULL, size, PROT_READ, MAP_SHARED, file, 0);
+ BLI_mmap_file *mmap_file = BLI_mmap_open(file);
imb_mmap_unlock();
-
- if (mem == (unsigned char *)-1) {
+ if (mmap_file == NULL) {
fprintf(stderr, "%s: couldn't get mapping %s\n", __func__, descr);
return NULL;
}
+ mem = BLI_mmap_get_pointer(mmap_file);
+
ibuf = IMB_ibImageFromMemory(mem, size, flags, colorspace, descr);
imb_mmap_lock();
- if (munmap(mem, size)) {
- fprintf(stderr, "%s: couldn't unmap file %s\n", __func__, descr);
- }
+ BLI_mmap_free(mmap_file);
imb_mmap_unlock();
return ibuf;
@@ -292,14 +291,15 @@ static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int
size = BLI_file_descriptor_size(file);
imb_mmap_lock();
- mem = mmap(NULL, size, PROT_READ, MAP_SHARED, file, 0);
+ BLI_mmap_file *mmap_file = BLI_mmap_open(file);
imb_mmap_unlock();
-
- if (mem == (unsigned char *)-1) {
+ if (mmap_file == NULL) {
fprintf(stderr, "Couldn't get memory mapping for %s\n", ibuf->cachename);
return;
}
+ mem = BLI_mmap_get_pointer(mmap_file);
+
const ImFileType *type = IMB_file_type_from_ibuf(ibuf);
if (type != NULL) {
if (type->load_tile != NULL) {
@@ -308,9 +308,7 @@ static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int
}
imb_mmap_lock();
- if (munmap(mem, size)) {
- fprintf(stderr, "Couldn't unmap memory for %s.\n", ibuf->cachename);
- }
+ BLI_mmap_free(mmap_file);
imb_mmap_unlock();
}
diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt
index a7adaa7e258..5d1ed7b44a1 100644
--- a/source/blender/makesdna/intern/CMakeLists.txt
+++ b/source/blender/makesdna/intern/CMakeLists.txt
@@ -48,12 +48,6 @@ set(SRC
../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c
)
-if(WIN32 AND NOT UNIX)
- list(APPEND SRC
- ../../../../intern/guardedalloc/intern/mmap_win.c
- )
-endif()
-
# SRC_DNA_INC is defined in the parent dir
add_cc_flags_custom_test(makesdna)
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 72cdaecb2c3..94cfd8464ae 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -189,7 +189,6 @@ set(SRC
../../../../intern/guardedalloc/intern/mallocn.c
../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c
../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c
- ../../../../intern/guardedalloc/intern/mmap_win.c
# Needed for defaults.
../../../../release/datafiles/userdef/userdef_default.c