mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-11 08:42:35 +03:00
Merge f91fcf34e1
into 7d50816f0a
This commit is contained in:
commit
3ad890a0de
66
setup.py
66
setup.py
|
@ -14,12 +14,17 @@ import shutil
|
||||||
import struct
|
import struct
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import warnings
|
import warnings
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
from typing import Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from setuptools import Extension, setup
|
from setuptools import Extension, setup
|
||||||
from setuptools.command.build_ext import build_ext
|
from setuptools.command.build_ext import build_ext
|
||||||
|
from setuptools.errors import CompileError
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
import distutils.ccompiler
|
||||||
|
|
||||||
|
|
||||||
def get_version() -> str:
|
def get_version() -> str:
|
||||||
|
@ -294,6 +299,47 @@ def _pkg_config(name: str) -> tuple[list[str], list[str]] | None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _try_compile(compiler: distutils.ccompiler.CCompiler, code: str) -> bool:
|
||||||
|
try:
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
fn = os.path.join(d, "test.c")
|
||||||
|
with open(fn, "w") as f:
|
||||||
|
f.write(code)
|
||||||
|
compiler.compile([fn], output_dir=d, extra_preargs=["-Werror"])
|
||||||
|
return True
|
||||||
|
except CompileError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _try_compile_attr(compiler: distutils.ccompiler.CCompiler, attr: str) -> bool:
|
||||||
|
code = f"""
|
||||||
|
#pragma GCC diagnostic error "-Wattributes"
|
||||||
|
#pragma clang diagnostic error "-Wattributes"
|
||||||
|
|
||||||
|
int {attr} foo;
|
||||||
|
int main() {{
|
||||||
|
return 0;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
return _try_compile(compiler, code)
|
||||||
|
|
||||||
|
|
||||||
|
def _try_compile_tls_define_macros(
|
||||||
|
compiler: distutils.ccompiler.CCompiler,
|
||||||
|
) -> list[tuple[str, str | None]]:
|
||||||
|
if _try_compile_attr(compiler, "thread_local"): # C23
|
||||||
|
return [("HAVE_THREAD_LOCAL", None)]
|
||||||
|
elif _try_compile_attr(compiler, "_Thread_local"): # C11/C17
|
||||||
|
return [("HAVE__THREAD_LOCAL", None)]
|
||||||
|
elif _try_compile_attr(compiler, "__thread"): # GCC/clang
|
||||||
|
return [("HAVE___THREAD", None)]
|
||||||
|
elif _try_compile_attr(compiler, "__declspec(thread)"): # MSVC
|
||||||
|
return [("HAVE___DECLSPEC_THREAD_", None)]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class pil_build_ext(build_ext):
|
class pil_build_ext(build_ext):
|
||||||
class ext_feature:
|
class ext_feature:
|
||||||
features = [
|
features = [
|
||||||
|
@ -429,12 +475,13 @@ class pil_build_ext(build_ext):
|
||||||
def _update_extension(
|
def _update_extension(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
libraries: list[str] | list[str | bool | None],
|
libraries: list[str] | list[str | bool | None] | None = None,
|
||||||
define_macros: list[tuple[str, str | None]] | None = None,
|
define_macros: list[tuple[str, str | None]] | None = None,
|
||||||
sources: list[str] | None = None,
|
sources: list[str] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
for extension in self.extensions:
|
for extension in self.extensions:
|
||||||
if extension.name == name:
|
if extension.name == name:
|
||||||
|
if libraries is not None:
|
||||||
extension.libraries += libraries
|
extension.libraries += libraries
|
||||||
if define_macros is not None:
|
if define_macros is not None:
|
||||||
extension.define_macros += define_macros
|
extension.define_macros += define_macros
|
||||||
|
@ -900,7 +947,10 @@ class pil_build_ext(build_ext):
|
||||||
|
|
||||||
defs.append(("PILLOW_VERSION", f'"{PILLOW_VERSION}"'))
|
defs.append(("PILLOW_VERSION", f'"{PILLOW_VERSION}"'))
|
||||||
|
|
||||||
self._update_extension("PIL._imaging", libs, defs)
|
tls_define_macros = _try_compile_tls_define_macros(self.compiler)
|
||||||
|
self._update_extension("PIL._imaging", libs, defs + tls_define_macros)
|
||||||
|
self._update_extension("PIL._imagingmath", define_macros=tls_define_macros)
|
||||||
|
self._update_extension("PIL._imagingmorph", define_macros=tls_define_macros)
|
||||||
|
|
||||||
#
|
#
|
||||||
# additional libraries
|
# additional libraries
|
||||||
|
@ -923,7 +973,9 @@ class pil_build_ext(build_ext):
|
||||||
libs.append(feature.get("fribidi"))
|
libs.append(feature.get("fribidi"))
|
||||||
else: # building FriBiDi shim from src/thirdparty
|
else: # building FriBiDi shim from src/thirdparty
|
||||||
srcs.append("src/thirdparty/fribidi-shim/fribidi.c")
|
srcs.append("src/thirdparty/fribidi-shim/fribidi.c")
|
||||||
self._update_extension("PIL._imagingft", libs, defs, srcs)
|
self._update_extension(
|
||||||
|
"PIL._imagingft", libs, defs + tls_define_macros, srcs
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self._remove_extension("PIL._imagingft")
|
self._remove_extension("PIL._imagingft")
|
||||||
|
@ -932,14 +984,14 @@ class pil_build_ext(build_ext):
|
||||||
libs = [feature.get("lcms")]
|
libs = [feature.get("lcms")]
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
libs.extend(["user32", "gdi32"])
|
libs.extend(["user32", "gdi32"])
|
||||||
self._update_extension("PIL._imagingcms", libs)
|
self._update_extension("PIL._imagingcms", libs, tls_define_macros)
|
||||||
else:
|
else:
|
||||||
self._remove_extension("PIL._imagingcms")
|
self._remove_extension("PIL._imagingcms")
|
||||||
|
|
||||||
webp = feature.get("webp")
|
webp = feature.get("webp")
|
||||||
if isinstance(webp, str):
|
if isinstance(webp, str):
|
||||||
libs = [webp, webp + "mux", webp + "demux"]
|
libs = [webp, webp + "mux", webp + "demux"]
|
||||||
self._update_extension("PIL._webp", libs)
|
self._update_extension("PIL._webp", libs, tls_define_macros)
|
||||||
else:
|
else:
|
||||||
self._remove_extension("PIL._webp")
|
self._remove_extension("PIL._webp")
|
||||||
|
|
||||||
|
@ -952,7 +1004,7 @@ class pil_build_ext(build_ext):
|
||||||
self._remove_extension("PIL._avif")
|
self._remove_extension("PIL._avif")
|
||||||
|
|
||||||
tk_libs = ["psapi"] if sys.platform in ("win32", "cygwin") else []
|
tk_libs = ["psapi"] if sys.platform in ("win32", "cygwin") else []
|
||||||
self._update_extension("PIL._imagingtk", tk_libs)
|
self._update_extension("PIL._imagingtk", tk_libs, tls_define_macros)
|
||||||
|
|
||||||
build_ext.build_extensions(self)
|
build_ext.build_extensions(self)
|
||||||
|
|
||||||
|
|
104
src/_imaging.c
104
src/_imaging.c
|
@ -3822,34 +3822,49 @@ _get_stats(PyObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
long stats_new_count = 0;
|
||||||
ImagingMemoryArena arena = &ImagingDefaultArena;
|
long stats_allocated_blocks = 0;
|
||||||
|
long stats_reused_blocks = 0;
|
||||||
|
long stats_reallocated_blocks = 0;
|
||||||
|
long stats_freed_blocks = 0;
|
||||||
|
long blocks_cached = 0;
|
||||||
|
|
||||||
v = PyLong_FromLong(arena->stats_new_count);
|
ImagingMemoryArena arena;
|
||||||
|
IMAGING_ARENAS_FOREACH(arena) {
|
||||||
|
MUTEX_LOCK(&arena->mutex);
|
||||||
|
stats_new_count += arena->stats_new_count;
|
||||||
|
stats_allocated_blocks += arena->stats_allocated_blocks;
|
||||||
|
stats_reused_blocks += arena->stats_reused_blocks;
|
||||||
|
stats_reallocated_blocks += arena->stats_reallocated_blocks;
|
||||||
|
stats_freed_blocks += arena->stats_freed_blocks;
|
||||||
|
blocks_cached += arena->blocks_cached;
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
v = PyLong_FromLong(stats_new_count);
|
||||||
PyDict_SetItemString(d, "new_count", v ? v : Py_None);
|
PyDict_SetItemString(d, "new_count", v ? v : Py_None);
|
||||||
Py_XDECREF(v);
|
Py_XDECREF(v);
|
||||||
|
|
||||||
v = PyLong_FromLong(arena->stats_allocated_blocks);
|
v = PyLong_FromLong(stats_allocated_blocks);
|
||||||
PyDict_SetItemString(d, "allocated_blocks", v ? v : Py_None);
|
PyDict_SetItemString(d, "allocated_blocks", v ? v : Py_None);
|
||||||
Py_XDECREF(v);
|
Py_XDECREF(v);
|
||||||
|
|
||||||
v = PyLong_FromLong(arena->stats_reused_blocks);
|
v = PyLong_FromLong(stats_reused_blocks);
|
||||||
PyDict_SetItemString(d, "reused_blocks", v ? v : Py_None);
|
PyDict_SetItemString(d, "reused_blocks", v ? v : Py_None);
|
||||||
Py_XDECREF(v);
|
Py_XDECREF(v);
|
||||||
|
|
||||||
v = PyLong_FromLong(arena->stats_reallocated_blocks);
|
v = PyLong_FromLong(stats_reallocated_blocks);
|
||||||
PyDict_SetItemString(d, "reallocated_blocks", v ? v : Py_None);
|
PyDict_SetItemString(d, "reallocated_blocks", v ? v : Py_None);
|
||||||
Py_XDECREF(v);
|
Py_XDECREF(v);
|
||||||
|
|
||||||
v = PyLong_FromLong(arena->stats_freed_blocks);
|
v = PyLong_FromLong(stats_freed_blocks);
|
||||||
PyDict_SetItemString(d, "freed_blocks", v ? v : Py_None);
|
PyDict_SetItemString(d, "freed_blocks", v ? v : Py_None);
|
||||||
Py_XDECREF(v);
|
Py_XDECREF(v);
|
||||||
|
|
||||||
v = PyLong_FromLong(arena->blocks_cached);
|
v = PyLong_FromLong(blocks_cached);
|
||||||
PyDict_SetItemString(d, "blocks_cached", v ? v : Py_None);
|
PyDict_SetItemString(d, "blocks_cached", v ? v : Py_None);
|
||||||
Py_XDECREF(v);
|
Py_XDECREF(v);
|
||||||
|
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3859,14 +3874,16 @@ _reset_stats(PyObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena;
|
||||||
ImagingMemoryArena arena = &ImagingDefaultArena;
|
IMAGING_ARENAS_FOREACH(arena) {
|
||||||
|
MUTEX_LOCK(&arena->mutex);
|
||||||
arena->stats_new_count = 0;
|
arena->stats_new_count = 0;
|
||||||
arena->stats_allocated_blocks = 0;
|
arena->stats_allocated_blocks = 0;
|
||||||
arena->stats_reused_blocks = 0;
|
arena->stats_reused_blocks = 0;
|
||||||
arena->stats_reallocated_blocks = 0;
|
arena->stats_reallocated_blocks = 0;
|
||||||
arena->stats_freed_blocks = 0;
|
arena->stats_freed_blocks = 0;
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
@ -3877,9 +3894,10 @@ _get_alignment(PyObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena = ImagingGetArena();
|
||||||
int alignment = ImagingDefaultArena.alignment;
|
MUTEX_LOCK(&arena->mutex);
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
int alignment = arena->alignment;
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
return PyLong_FromLong(alignment);
|
return PyLong_FromLong(alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3889,9 +3907,10 @@ _get_block_size(PyObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena = ImagingGetArena();
|
||||||
int block_size = ImagingDefaultArena.block_size;
|
MUTEX_LOCK(&arena->mutex);
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
int block_size = arena->block_size;
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
return PyLong_FromLong(block_size);
|
return PyLong_FromLong(block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3901,9 +3920,10 @@ _get_blocks_max(PyObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena = ImagingGetArena();
|
||||||
int blocks_max = ImagingDefaultArena.blocks_max;
|
MUTEX_LOCK(&arena->mutex);
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
int blocks_max = arena->blocks_max;
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
return PyLong_FromLong(blocks_max);
|
return PyLong_FromLong(blocks_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3924,9 +3944,12 @@ _set_alignment(PyObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena;
|
||||||
ImagingDefaultArena.alignment = alignment;
|
IMAGING_ARENAS_FOREACH(arena) {
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
MUTEX_LOCK(&arena->mutex);
|
||||||
|
arena->alignment = alignment;
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
@ -3948,9 +3971,12 @@ _set_block_size(PyObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena;
|
||||||
ImagingDefaultArena.block_size = block_size;
|
IMAGING_ARENAS_FOREACH(arena) {
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
MUTEX_LOCK(&arena->mutex);
|
||||||
|
arena->block_size = block_size;
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
@ -3968,15 +3994,20 @@ _set_blocks_max(PyObject *self, PyObject *args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((unsigned long)blocks_max >
|
if ((unsigned long)blocks_max >
|
||||||
SIZE_MAX / sizeof(ImagingDefaultArena.blocks_pool[0])) {
|
SIZE_MAX / sizeof(ImagingGetArena()->blocks_pool[0])) {
|
||||||
PyErr_SetString(PyExc_ValueError, "blocks_max is too large");
|
PyErr_SetString(PyExc_ValueError, "blocks_max is too large");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
int error = 0;
|
||||||
int status = ImagingMemorySetBlocksMax(&ImagingDefaultArena, blocks_max);
|
ImagingMemoryArena arena;
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
IMAGING_ARENAS_FOREACH(arena) {
|
||||||
if (!status) {
|
MUTEX_LOCK(&arena->mutex);
|
||||||
|
error |= ImagingMemorySetBlocksMax(arena, blocks_max);
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
return ImagingError_MemoryError();
|
return ImagingError_MemoryError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3991,9 +4022,12 @@ _clear_cache(PyObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena;
|
||||||
ImagingMemoryClearCache(&ImagingDefaultArena, i);
|
IMAGING_ARENAS_FOREACH(arena) {
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
MUTEX_LOCK(&arena->mutex);
|
||||||
|
ImagingMemoryClearCache(arena, i);
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,20 @@ extern "C" {
|
||||||
* extensions, see http://www.effbot.org/zone/pil-extending.htm
|
* extensions, see http://www.effbot.org/zone/pil-extending.htm
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#define IMAGING_TLS thread_local
|
||||||
|
#elif defined(HAVE_THREAD_LOCAL)
|
||||||
|
#define IMAGING_TLS thread_local
|
||||||
|
#elif defined(HAVE__THREAD_LOCAL)
|
||||||
|
#define IMAGING_TLS _Thread_local
|
||||||
|
#elif defined(HAVE___THREAD)
|
||||||
|
#define IMAGING_TLS __thread
|
||||||
|
#elif defined(HAVE___DECLSPEC_THREAD_)
|
||||||
|
#define IMAGING_TLS __declspec(thread)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Handles */
|
/* Handles */
|
||||||
|
|
||||||
typedef struct ImagingMemoryInstance *Imaging;
|
typedef struct ImagingMemoryInstance *Imaging;
|
||||||
|
@ -104,6 +118,10 @@ struct ImagingMemoryInstance {
|
||||||
|
|
||||||
/* Virtual methods */
|
/* Virtual methods */
|
||||||
void (*destroy)(Imaging im);
|
void (*destroy)(Imaging im);
|
||||||
|
|
||||||
|
#ifdef IMAGING_TLS
|
||||||
|
int arenaindex; /* Index of the arena this image is associated with. */
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IMAGING_PIXEL_1(im, x, y) ((im)->image8[(y)][(x)])
|
#define IMAGING_PIXEL_1(im, x, y) ((im)->image8[(y)][(x)])
|
||||||
|
@ -161,6 +179,9 @@ typedef struct ImagingMemoryArena {
|
||||||
int stats_reallocated_blocks; /* Number of blocks which were actually reallocated
|
int stats_reallocated_blocks; /* Number of blocks which were actually reallocated
|
||||||
after retrieving */
|
after retrieving */
|
||||||
int stats_freed_blocks; /* Number of freed blocks */
|
int stats_freed_blocks; /* Number of freed blocks */
|
||||||
|
#ifdef IMAGING_TLS
|
||||||
|
int index; /* Index of the arena in the global array. */
|
||||||
|
#endif
|
||||||
#ifdef Py_GIL_DISABLED
|
#ifdef Py_GIL_DISABLED
|
||||||
PyMutex mutex;
|
PyMutex mutex;
|
||||||
#endif
|
#endif
|
||||||
|
@ -169,7 +190,35 @@ typedef struct ImagingMemoryArena {
|
||||||
/* Objects */
|
/* Objects */
|
||||||
/* ------- */
|
/* ------- */
|
||||||
|
|
||||||
|
#ifdef IMAGING_TLS
|
||||||
|
/* In this case we both do not have the GIL and have thread-local storage, so we
|
||||||
|
* will allocate a set of arenas and associated them with threads one at a time.
|
||||||
|
*/
|
||||||
|
#define IMAGING_ARENAS_COUNT 8
|
||||||
|
extern struct ImagingMemoryArena ImagingArenas[IMAGING_ARENAS_COUNT + 1];
|
||||||
|
|
||||||
|
/* Provide a macro that loops through each arena that has been
|
||||||
|
* statically-allocated. This is necessary to properly handle stats.
|
||||||
|
*/
|
||||||
|
#define IMAGING_ARENAS_FOREACH(arena) \
|
||||||
|
for ((arena) = &ImagingArenas[0]; (arena)->index >= 0; ++(arena))
|
||||||
|
#else
|
||||||
|
/* In this case we either have the GIL or do not have thread-local storage, in
|
||||||
|
* which case we will only allocate a single arena.
|
||||||
|
*/
|
||||||
extern struct ImagingMemoryArena ImagingDefaultArena;
|
extern struct ImagingMemoryArena ImagingDefaultArena;
|
||||||
|
|
||||||
|
/* Provide a macro that loops through each arena that has been
|
||||||
|
* statically-allocated. In this case because there is only one, this is
|
||||||
|
* effectively a single block of code.
|
||||||
|
*/
|
||||||
|
#define IMAGING_ARENAS_FOREACH(arena) \
|
||||||
|
for ((arena) = &ImagingDefaultArena; (arena); (arena) = NULL)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ImagingMemoryArena
|
||||||
|
ImagingGetArena(void);
|
||||||
|
|
||||||
extern int
|
extern int
|
||||||
ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max);
|
ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max);
|
||||||
extern void
|
extern void
|
||||||
|
|
|
@ -218,9 +218,10 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena = ImagingGetArena();
|
||||||
ImagingDefaultArena.stats_new_count += 1;
|
MUTEX_LOCK(&arena->mutex);
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
arena->stats_new_count += 1;
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
|
|
||||||
return im;
|
return im;
|
||||||
}
|
}
|
||||||
|
@ -258,10 +259,70 @@ ImagingDelete(Imaging im) {
|
||||||
/* Allocate image as an array of line buffers. */
|
/* Allocate image as an array of line buffers. */
|
||||||
|
|
||||||
#define IMAGING_PAGE_SIZE (4096)
|
#define IMAGING_PAGE_SIZE (4096)
|
||||||
|
#define IMAGING_ARENA_BLOCK_SIZE (16 * 1024 * 1024)
|
||||||
|
|
||||||
|
#ifdef IMAGING_TLS
|
||||||
|
/* This is the overall process-level index that keeps track of the next index
|
||||||
|
* that will be assigned to a thread.
|
||||||
|
*/
|
||||||
|
static uint64_t ImagingArenaIndex = 0;
|
||||||
|
|
||||||
|
/* This is the thread-local index that associated a thread with an arena in the
|
||||||
|
* statically-allocated list.
|
||||||
|
*/
|
||||||
|
static IMAGING_TLS uint64_t ImagingArenaThreadIndex = UINT64_MAX;
|
||||||
|
|
||||||
|
/* These are the statically-allocated arenas. */
|
||||||
|
struct ImagingMemoryArena ImagingArenas[IMAGING_ARENAS_COUNT + 1] = {
|
||||||
|
{1, IMAGING_ARENA_BLOCK_SIZE, 0, 0, NULL, 0, 0, 0, 0, 0, 0, {0}},
|
||||||
|
{1, IMAGING_ARENA_BLOCK_SIZE, 0, 0, NULL, 0, 0, 0, 0, 0, 1, {0}},
|
||||||
|
{1, IMAGING_ARENA_BLOCK_SIZE, 0, 0, NULL, 0, 0, 0, 0, 0, 2, {0}},
|
||||||
|
{1, IMAGING_ARENA_BLOCK_SIZE, 0, 0, NULL, 0, 0, 0, 0, 0, 3, {0}},
|
||||||
|
{1, IMAGING_ARENA_BLOCK_SIZE, 0, 0, NULL, 0, 0, 0, 0, 0, 4, {0}},
|
||||||
|
{1, IMAGING_ARENA_BLOCK_SIZE, 0, 0, NULL, 0, 0, 0, 0, 0, 5, {0}},
|
||||||
|
{1, IMAGING_ARENA_BLOCK_SIZE, 0, 0, NULL, 0, 0, 0, 0, 0, 6, {0}},
|
||||||
|
{1, IMAGING_ARENA_BLOCK_SIZE, 0, 0, NULL, 0, 0, 0, 0, 0, 7, {0}},
|
||||||
|
{1, IMAGING_ARENA_BLOCK_SIZE, 0, 0, NULL, 0, 0, 0, 0, 0, -1, {0}},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Get a pointer to the correct arena for this context. In this case where we
|
||||||
|
* are using a round-robin approach to the statically allocated arenas, we will
|
||||||
|
* return the arena that is assigned to the thread on first use.
|
||||||
|
*/
|
||||||
|
ImagingMemoryArena
|
||||||
|
ImagingGetArena(void) {
|
||||||
|
if (ImagingArenaThreadIndex == UINT64_MAX) {
|
||||||
|
ImagingArenaThreadIndex =
|
||||||
|
_Py_atomic_add_uint64(&ImagingArenaIndex, 1) % IMAGING_ARENAS_COUNT;
|
||||||
|
}
|
||||||
|
return &ImagingArenas[ImagingArenaThreadIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the arena associated with the given image. In this case the index of
|
||||||
|
* the arena is stored on the image itself.
|
||||||
|
*/
|
||||||
|
ImagingMemoryArena
|
||||||
|
ImagingGetArenaFromImaging(Imaging im) {
|
||||||
|
int arenaindex = im->arenaindex;
|
||||||
|
assert(arenaindex >= 0 && arenaindex < IMAGING_ARENAS_COUNT);
|
||||||
|
return &ImagingArenas[arenaindex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the arena index on the given image based on the index of the arena. This
|
||||||
|
* is necessary in order to return the blocks to the correct arena when the
|
||||||
|
* image is destroyed.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ImagingSetArenaOnImaging(Imaging im, ImagingMemoryArena arena) {
|
||||||
|
im->arenaindex = arena->index;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Because we have the GIL (or do not have thread-local storage), we only have a
|
||||||
|
* single arena.
|
||||||
|
*/
|
||||||
struct ImagingMemoryArena ImagingDefaultArena = {
|
struct ImagingMemoryArena ImagingDefaultArena = {
|
||||||
1, // alignment
|
1, // alignment
|
||||||
16 * 1024 * 1024, // block_size
|
IMAGING_ARENA_BLOCK_SIZE, // block_size
|
||||||
0, // blocks_max
|
0, // blocks_max
|
||||||
0, // blocks_cached
|
0, // blocks_cached
|
||||||
NULL, // blocks_pool
|
NULL, // blocks_pool
|
||||||
|
@ -271,10 +332,34 @@ struct ImagingMemoryArena ImagingDefaultArena = {
|
||||||
0,
|
0,
|
||||||
0, // Stats
|
0, // Stats
|
||||||
#ifdef Py_GIL_DISABLED
|
#ifdef Py_GIL_DISABLED
|
||||||
|
/* On the very off-chance that someone is running free-threaded Python on a
|
||||||
|
* platform that does not support thread-local storage, we need a mutex
|
||||||
|
* here.
|
||||||
|
*/
|
||||||
{0},
|
{0},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Get a pointer to the correct arena for this context. In this case where we
|
||||||
|
* either have the GIL or we do not have TLS, we will return only the default
|
||||||
|
* arena.
|
||||||
|
*/
|
||||||
|
ImagingMemoryArena
|
||||||
|
ImagingGetArena(void) {
|
||||||
|
return &ImagingDefaultArena;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the arena associated with the given image. In this case because we
|
||||||
|
* only have one arena, we always return the default arena.
|
||||||
|
*/
|
||||||
|
#define ImagingGetArenaFromImaging(im) &ImagingDefaultArena
|
||||||
|
|
||||||
|
/* Set the arena index on the given image based on the index of the arena. In
|
||||||
|
* this case because we only have one arena, we do not need to do anything.
|
||||||
|
*/
|
||||||
|
#define ImagingSetArenaOnImaging(im, arena)
|
||||||
|
#endif
|
||||||
|
|
||||||
int
|
int
|
||||||
ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max) {
|
ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max) {
|
||||||
void *p;
|
void *p;
|
||||||
|
@ -288,18 +373,18 @@ ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max) {
|
||||||
p = realloc(arena->blocks_pool, sizeof(*arena->blocks_pool) * blocks_max);
|
p = realloc(arena->blocks_pool, sizeof(*arena->blocks_pool) * blocks_max);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
// Leave previous blocks_max value
|
// Leave previous blocks_max value
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
arena->blocks_pool = p;
|
arena->blocks_pool = p;
|
||||||
} else {
|
} else {
|
||||||
arena->blocks_pool = calloc(sizeof(*arena->blocks_pool), blocks_max);
|
arena->blocks_pool = calloc(sizeof(*arena->blocks_pool), blocks_max);
|
||||||
if (!arena->blocks_pool) {
|
if (!arena->blocks_pool) {
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arena->blocks_max = blocks_max;
|
arena->blocks_max = blocks_max;
|
||||||
|
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -369,12 +454,13 @@ ImagingDestroyArray(Imaging im) {
|
||||||
int y = 0;
|
int y = 0;
|
||||||
|
|
||||||
if (im->blocks) {
|
if (im->blocks) {
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena = ImagingGetArenaFromImaging(im);
|
||||||
|
MUTEX_LOCK(&arena->mutex);
|
||||||
while (im->blocks[y].ptr) {
|
while (im->blocks[y].ptr) {
|
||||||
memory_return_block(&ImagingDefaultArena, im->blocks[y]);
|
memory_return_block(arena, im->blocks[y]);
|
||||||
y += 1;
|
y += 1;
|
||||||
}
|
}
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
free(im->blocks);
|
free(im->blocks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -504,11 +590,12 @@ ImagingNewInternal(const char *mode, int xsize, int ysize, int dirty) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
ImagingMemoryArena arena = ImagingGetArena();
|
||||||
Imaging tmp = ImagingAllocateArray(
|
ImagingSetArenaOnImaging(im, arena);
|
||||||
im, &ImagingDefaultArena, dirty, ImagingDefaultArena.block_size
|
|
||||||
);
|
MUTEX_LOCK(&arena->mutex);
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
Imaging tmp = ImagingAllocateArray(im, arena, dirty, arena->block_size);
|
||||||
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
return im;
|
return im;
|
||||||
}
|
}
|
||||||
|
@ -516,9 +603,9 @@ ImagingNewInternal(const char *mode, int xsize, int ysize, int dirty) {
|
||||||
ImagingError_Clear();
|
ImagingError_Clear();
|
||||||
|
|
||||||
// Try to allocate the image once more with smallest possible block size
|
// Try to allocate the image once more with smallest possible block size
|
||||||
MUTEX_LOCK(&ImagingDefaultArena.mutex);
|
MUTEX_LOCK(&arena->mutex);
|
||||||
tmp = ImagingAllocateArray(im, &ImagingDefaultArena, dirty, IMAGING_PAGE_SIZE);
|
tmp = ImagingAllocateArray(im, arena, dirty, IMAGING_PAGE_SIZE);
|
||||||
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
|
MUTEX_UNLOCK(&arena->mutex);
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
return im;
|
return im;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user