mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-22 12:43:16 +00:00
Compare commits
185 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0110fa8451 | ||
|
|
b0f56583b7 | ||
|
|
7b8e74f48f | ||
|
|
5d6cb6eb66 | ||
|
|
a4e83e8593 | ||
|
|
9bef119b42 | ||
|
|
8019f4c21c | ||
|
|
23f04ef9b7 | ||
|
|
28b7e42e44 | ||
|
|
77635bf532 | ||
|
|
cb3ad659f0 | ||
|
|
213667fe26 | ||
|
|
32896a902b | ||
|
|
e9012630ac | ||
|
|
dd3ed23aea | ||
|
|
1ceae2f8cb | ||
|
|
62de7680ca | ||
|
|
6c56e194b0 | ||
|
|
eca3bc0131 | ||
|
|
da206df10b | ||
|
|
3f2b8d9cfa | ||
|
|
a99e0fa2d2 | ||
|
|
31db315f17 | ||
|
|
07ee4c5ff4 | ||
|
|
f19bedb04c | ||
|
|
b599b32280 | ||
|
|
4752a54eeb | ||
|
|
1d57c03e33 | ||
|
|
c443b67561 | ||
|
|
35a108c809 | ||
|
|
e46f8f97bc | ||
|
|
35799a5030 | ||
|
|
ed84764a2a | ||
|
|
b99c72f3d2 | ||
|
|
eaecaad8ea | ||
|
|
2c53faf352 | ||
|
|
e7d6779918 | ||
|
|
875ff15e6a | ||
|
|
1eb801bcad | ||
|
|
dc553d52d8 | ||
|
|
5569b4a42c | ||
|
|
962a2979e3 | ||
|
|
e2bcf037d4 | ||
|
|
9737685943 | ||
|
|
a2539fab95 | ||
|
|
d419bb09ef | ||
|
|
a14712b4b8 | ||
|
|
45a5bf6772 | ||
|
|
94e7ffa979 | ||
|
|
b732c395b7 | ||
|
|
5d8db15db9 | ||
|
|
f193fd80cf | ||
|
|
1abb49f09d | ||
|
|
bcd5424b1c | ||
|
|
c19b48fe73 | ||
|
|
af33e9a597 | ||
|
|
79647fe465 | ||
|
|
3bb044c807 | ||
|
|
43d4d7c373 | ||
|
|
92009b19d6 | ||
|
|
15da5f5d9d | ||
|
|
b54c0c2925 | ||
|
|
c128167bca | ||
|
|
4b52518398 | ||
|
|
1d42a99027 | ||
|
|
8701bc7079 | ||
|
|
b770d2da1d | ||
|
|
3573fb93ce | ||
|
|
5acef864f2 | ||
|
|
50a865e15a | ||
|
|
57cddffca6 | ||
|
|
11b5da7533 | ||
|
|
38a96f07ac | ||
|
|
d1207f0d37 | ||
|
|
a6a8e40f7d | ||
|
|
57ed894f8a | ||
|
|
9ebbfca93f | ||
|
|
3de0353352 | ||
|
|
5271b673b2 | ||
|
|
fa09fe798a | ||
|
|
20cd2de5ef | ||
|
|
a7fdcc8b09 | ||
|
|
c7d5298027 | ||
|
|
9de0094e6e | ||
|
|
05a9e4ac65 | ||
|
|
48384dc2d8 | ||
|
|
09d7bdb314 | ||
|
|
f70a254d44 | ||
|
|
09f8585ce8 | ||
|
|
1c35f63797 | ||
|
|
0fc1317fc6 | ||
|
|
73d3d58dc2 | ||
|
|
7790a0ba40 | ||
|
|
3a9ec67626 | ||
|
|
c1e00ef2a6 | ||
|
|
0c12dcabc5 | ||
|
|
919e4a0ea9 | ||
|
|
62c217e613 | ||
|
|
1326010cf4 | ||
|
|
3ef51d7f73 | ||
|
|
c2f970c32b | ||
|
|
dc391adc65 | ||
|
|
04c3c0f9a0 | ||
|
|
c1e9cf47f9 | ||
|
|
7ba6e74233 | ||
|
|
e02b83cc5e | ||
|
|
2e5eb21184 | ||
|
|
417c0c9ef1 | ||
|
|
44d12d435a | ||
|
|
21e33ed317 | ||
|
|
90827a3f3e | ||
|
|
21cda0dc42 | ||
|
|
1eb46ab6e7 | ||
|
|
2687a72087 | ||
|
|
de35328a10 | ||
|
|
c3b008ec39 | ||
|
|
d65db0e402 | ||
|
|
8c83c021b0 | ||
|
|
8d8960f635 | ||
|
|
b40253a93e | ||
|
|
c3ab90483f | ||
|
|
7798c7ac1d | ||
|
|
61111fe239 | ||
|
|
9aa1543e9c | ||
|
|
108c4a11e9 | ||
|
|
174c0c3a9c | ||
|
|
0d970a054e | ||
|
|
7e6749595a | ||
|
|
2fe64d237c | ||
|
|
259f8ebbfc | ||
|
|
71d94828a2 | ||
|
|
19ff2cefba | ||
|
|
66cd953514 | ||
|
|
c9a4bf9170 | ||
|
|
eb68842daf | ||
|
|
b6e07d2389 | ||
|
|
ab0cfe01fa | ||
|
|
a0c632c9d5 | ||
|
|
1423ee9016 | ||
|
|
1b5830178f | ||
|
|
d9394d0ca8 | ||
|
|
2288424325 | ||
|
|
a15841cc7d | ||
|
|
b2c0d6322d | ||
|
|
8413463f3a | ||
|
|
bab58ef401 | ||
|
|
00432331b8 | ||
|
|
bc26d7d99b | ||
|
|
e7642715ac | ||
|
|
667eca2ac2 | ||
|
|
245ae6036c | ||
|
|
96aa67aca8 | ||
|
|
c6a2c39404 | ||
|
|
2ee2f1ec57 | ||
|
|
4a8abbb400 | ||
|
|
a3c4193280 | ||
|
|
b582d2ad94 | ||
|
|
a82070ef5f | ||
|
|
f86bc081d6 | ||
|
|
ce7c0f999b | ||
|
|
0bc716ae27 | ||
|
|
f3060284c5 | ||
|
|
af3184cac0 | ||
|
|
232b13d862 | ||
|
|
ff63dca363 | ||
|
|
61a6dfcd5f | ||
|
|
f6bd2e5a17 | ||
|
|
6a885198c2 | ||
|
|
6c460ad91b | ||
|
|
18903c592f | ||
|
|
22af74e106 | ||
|
|
434ea64b26 | ||
|
|
824b947be0 | ||
|
|
ca18f2834e | ||
|
|
613cdc80f6 | ||
|
|
4a0dbb5ac8 | ||
|
|
f8d80d62a8 | ||
|
|
34dca5671f | ||
|
|
6bafa6678f | ||
|
|
e3998c681d | ||
|
|
022f6891fa | ||
|
|
33184bf698 | ||
|
|
0e1d5c25c6 | ||
|
|
86478b2998 | ||
|
|
994da42326 |
28
.appveyor.yml
Normal file
28
.appveyor.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
version: '{build}'
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- MSYSTEM: MINGW64
|
||||
CPU: x86_64
|
||||
MSVC: amd64
|
||||
- MSYSTEM: MINGW32
|
||||
CPU: i686
|
||||
MSVC: x86
|
||||
- MSYSTEM: MINGW64
|
||||
CPU: x86_64
|
||||
- MSYSTEM: MINGW32
|
||||
CPU: i686
|
||||
|
||||
install:
|
||||
- set PATH=c:\msys64\%MSYSTEM%\bin;c:\msys64\usr\bin;%PATH%
|
||||
- if defined MSVC call "c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %MSVC%
|
||||
- if defined MSVC pacman --noconfirm -Rsc mingw-w64-%CPU%-gcc gcc
|
||||
- pacman --noconfirm -Suy mingw-w64-%CPU%-make
|
||||
|
||||
build_script:
|
||||
- bash -c "autoconf"
|
||||
- bash -c "./configure"
|
||||
- mingw32-make -j3
|
||||
- file lib/jemalloc.dll
|
||||
- mingw32-make -j3 tests
|
||||
- mingw32-make -k check
|
||||
29
.travis.yml
Normal file
29
.travis.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
language: c
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- EXTRA_FLAGS=-m32
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- os: osx
|
||||
compiler: clang
|
||||
- os: osx
|
||||
compiler: clang
|
||||
env:
|
||||
- EXTRA_FLAGS=-m32
|
||||
|
||||
before_script:
|
||||
- autoconf
|
||||
- ./configure${EXTRA_FLAGS:+ CC="$CC $EXTRA_FLAGS"}
|
||||
- make -j3
|
||||
- make -j3 tests
|
||||
|
||||
script:
|
||||
- make check
|
||||
4
COPYING
4
COPYING
@@ -1,10 +1,10 @@
|
||||
Unless otherwise specified, files in the jemalloc source distribution are
|
||||
subject to the following license:
|
||||
--------------------------------------------------------------------------------
|
||||
Copyright (C) 2002-2015 Jason Evans <jasone@canonware.com>.
|
||||
Copyright (C) 2002-2016 Jason Evans <jasone@canonware.com>.
|
||||
All rights reserved.
|
||||
Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
|
||||
Copyright (C) 2009-2015 Facebook, Inc. All rights reserved.
|
||||
Copyright (C) 2009-2016 Facebook, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
112
ChangeLog
112
ChangeLog
@@ -4,6 +4,114 @@ brevity. Much more detail can be found in the git revision history:
|
||||
|
||||
https://github.com/jemalloc/jemalloc
|
||||
|
||||
* 4.3.1 (November 7, 2016)
|
||||
|
||||
Bug fixes:
|
||||
- Fix a severe virtual memory leak. This regression was first released in
|
||||
4.3.0. (@interwq, @jasone)
|
||||
- Refactor atomic and prng APIs to restore support for 32-bit platforms that
|
||||
use pre-C11 toolchains, e.g. FreeBSD's mips. (@jasone)
|
||||
|
||||
* 4.3.0 (November 4, 2016)
|
||||
|
||||
This is the first release that passes the test suite for multiple Windows
|
||||
configurations, thanks in large part to @glandium setting up continuous
|
||||
integration via AppVeyor (and Travis CI for Linux and OS X).
|
||||
|
||||
New features:
|
||||
- Add "J" (JSON) support to malloc_stats_print(). (@jasone)
|
||||
- Add Cray compiler support. (@ronawho)
|
||||
|
||||
Optimizations:
|
||||
- Add/use adaptive spinning for bootstrapping and radix tree node
|
||||
initialization. (@jasone)
|
||||
|
||||
Bug fixes:
|
||||
- Fix large allocation to search starting in the optimal size class heap,
|
||||
which can substantially reduce virtual memory churn and fragmentation. This
|
||||
regression was first released in 4.0.0. (@mjp41, @jasone)
|
||||
- Fix stats.arenas.<i>.nthreads accounting. (@interwq)
|
||||
- Fix and simplify decay-based purging. (@jasone)
|
||||
- Make DSS (sbrk(2)-related) operations lockless, which resolves potential
|
||||
deadlocks during thread exit. (@jasone)
|
||||
- Fix over-sized allocation of radix tree leaf nodes. (@mjp41, @ogaun,
|
||||
@jasone)
|
||||
- Fix over-sized allocation of arena_t (plus associated stats) data
|
||||
structures. (@jasone, @interwq)
|
||||
- Fix EXTRA_CFLAGS to not affect configuration. (@jasone)
|
||||
- Fix a Valgrind integration bug. (@ronawho)
|
||||
- Disallow 0x5a junk filling when running in Valgrind. (@jasone)
|
||||
- Fix a file descriptor leak on Linux. This regression was first released in
|
||||
4.2.0. (@vsarunas, @jasone)
|
||||
- Fix static linking of jemalloc with glibc. (@djwatson)
|
||||
- Use syscall(2) rather than {open,read,close}(2) during boot on Linux. This
|
||||
works around other libraries' system call wrappers performing reentrant
|
||||
allocation. (@kspinka, @Whissi, @jasone)
|
||||
- Fix OS X default zone replacement to work with OS X 10.12. (@glandium,
|
||||
@jasone)
|
||||
- Fix cached memory management to avoid needless commit/decommit operations
|
||||
during purging, which resolves permanent virtual memory map fragmentation
|
||||
issues on Windows. (@mjp41, @jasone)
|
||||
- Fix TSD fetches to avoid (recursive) allocation. This is relevant to
|
||||
non-TLS and Windows configurations. (@jasone)
|
||||
- Fix malloc_conf overriding to work on Windows. (@jasone)
|
||||
- Forcibly disable lazy-lock on Windows (was forcibly *enabled*). (@jasone)
|
||||
|
||||
* 4.2.1 (June 8, 2016)
|
||||
|
||||
Bug fixes:
|
||||
- Fix bootstrapping issues for configurations that require allocation during
|
||||
tsd initialization (e.g. --disable-tls). (@cferris1000, @jasone)
|
||||
- Fix gettimeofday() version of nstime_update(). (@ronawho)
|
||||
- Fix Valgrind regressions in calloc() and chunk_alloc_wrapper(). (@ronawho)
|
||||
- Fix potential VM map fragmentation regression. (@jasone)
|
||||
- Fix opt_zero-triggered in-place huge reallocation zeroing. (@jasone)
|
||||
- Fix heap profiling context leaks in reallocation edge cases. (@jasone)
|
||||
|
||||
* 4.2.0 (May 12, 2016)
|
||||
|
||||
New features:
|
||||
- Add the arena.<i>.reset mallctl, which makes it possible to discard all of
|
||||
an arena's allocations in a single operation. (@jasone)
|
||||
- Add the stats.retained and stats.arenas.<i>.retained statistics. (@jasone)
|
||||
- Add the --with-version configure option. (@jasone)
|
||||
- Support --with-lg-page values larger than actual page size. (@jasone)
|
||||
|
||||
Optimizations:
|
||||
- Use pairing heaps rather than red-black trees for various hot data
|
||||
structures. (@djwatson, @jasone)
|
||||
- Streamline fast paths of rtree operations. (@jasone)
|
||||
- Optimize the fast paths of calloc() and [m,d,sd]allocx(). (@jasone)
|
||||
- Decommit unused virtual memory if the OS does not overcommit. (@jasone)
|
||||
- Specify MAP_NORESERVE on Linux if [heuristic] overcommit is active, in order
|
||||
to avoid unfortunate interactions during fork(2). (@jasone)
|
||||
|
||||
Bug fixes:
|
||||
- Fix chunk accounting related to triggering gdump profiles. (@jasone)
|
||||
- Link against librt for clock_gettime(2) if glibc < 2.17. (@jasone)
|
||||
- Scale leak report summary according to sampling probability. (@jasone)
|
||||
|
||||
* 4.1.1 (May 3, 2016)
|
||||
|
||||
This bugfix release resolves a variety of mostly minor issues, though the
|
||||
bitmap fix is critical for 64-bit Windows.
|
||||
|
||||
Bug fixes:
|
||||
- Fix the linear scan version of bitmap_sfu() to shift by the proper amount
|
||||
even when sizeof(long) is not the same as sizeof(void *), as on 64-bit
|
||||
Windows. (@jasone)
|
||||
- Fix hashing functions to avoid unaligned memory accesses (and resulting
|
||||
crashes). This is relevant at least to some ARM-based platforms.
|
||||
(@rkmisra)
|
||||
- Fix fork()-related lock rank ordering reversals. These reversals were
|
||||
unlikely to cause deadlocks in practice except when heap profiling was
|
||||
enabled and active. (@jasone)
|
||||
- Fix various chunk leaks in OOM code paths. (@jasone)
|
||||
- Fix malloc_stats_print() to print opt.narenas correctly. (@jasone)
|
||||
- Fix MSVC-specific build/test issues. (@rustyx, @yuslepukhin)
|
||||
- Fix a variety of test failures that were due to test fragility rather than
|
||||
core bugs. (@jasone)
|
||||
|
||||
* 4.1.0 (February 28, 2016)
|
||||
|
||||
This release is primarily about optimizations, but it also incorporates a lot
|
||||
@@ -59,14 +167,14 @@ brevity. Much more detail can be found in the git revision history:
|
||||
Bug fixes:
|
||||
- Fix stats.cactive accounting regression. (@rustyx, @jasone)
|
||||
- Handle unaligned keys in hash(). This caused problems for some ARM systems.
|
||||
(@jasone, Christopher Ferris)
|
||||
(@jasone, @cferris1000)
|
||||
- Refactor arenas array. In addition to fixing a fork-related deadlock, this
|
||||
makes arena lookups faster and simpler. (@jasone)
|
||||
- Move retained memory allocation out of the default chunk allocation
|
||||
function, to a location that gets executed even if the application installs
|
||||
a custom chunk allocation function. This resolves a virtual memory leak.
|
||||
(@buchgr)
|
||||
- Fix a potential tsd cleanup leak. (Christopher Ferris, @jasone)
|
||||
- Fix a potential tsd cleanup leak. (@cferris1000, @jasone)
|
||||
- Fix run quantization. In practice this bug had no impact unless
|
||||
applications requested memory with alignment exceeding one page.
|
||||
(@jasone, @djwatson)
|
||||
|
||||
4
INSTALL
4
INSTALL
@@ -35,6 +35,10 @@ any of the following arguments (not a definitive list) to 'configure':
|
||||
will cause files to be installed into /usr/local/include, /usr/local/lib,
|
||||
and /usr/local/man.
|
||||
|
||||
--with-version=<major>.<minor>.<bugfix>-<nrev>-g<gid>
|
||||
Use the specified version string rather than trying to generate one (if in
|
||||
a git repository) or use existing the VERSION file (if present).
|
||||
|
||||
--with-rpath=<colon-separated-rpath>
|
||||
Embed one or more library paths, so that libjemalloc can find the libraries
|
||||
it is linked to. This works only on ELF-based systems.
|
||||
|
||||
75
Makefile.in
75
Makefile.in
@@ -24,11 +24,11 @@ abs_objroot := @abs_objroot@
|
||||
|
||||
# Build parameters.
|
||||
CPPFLAGS := @CPPFLAGS@ -I$(srcroot)include -I$(objroot)include
|
||||
CFLAGS := @CFLAGS@
|
||||
EXTRA_CFLAGS := @EXTRA_CFLAGS@
|
||||
CFLAGS := @CFLAGS@ $(EXTRA_CFLAGS)
|
||||
LDFLAGS := @LDFLAGS@
|
||||
EXTRA_LDFLAGS := @EXTRA_LDFLAGS@
|
||||
LIBS := @LIBS@
|
||||
TESTLIBS := @TESTLIBS@
|
||||
RPATH_EXTRA := @RPATH_EXTRA@
|
||||
SO := @so@
|
||||
IMPORTLIB := @importlib@
|
||||
@@ -53,15 +53,19 @@ enable_prof := @enable_prof@
|
||||
enable_valgrind := @enable_valgrind@
|
||||
enable_zone_allocator := @enable_zone_allocator@
|
||||
MALLOC_CONF := @JEMALLOC_CPREFIX@MALLOC_CONF
|
||||
link_whole_archive := @link_whole_archive@
|
||||
DSO_LDFLAGS = @DSO_LDFLAGS@
|
||||
SOREV = @SOREV@
|
||||
PIC_CFLAGS = @PIC_CFLAGS@
|
||||
CTARGET = @CTARGET@
|
||||
LDTARGET = @LDTARGET@
|
||||
TEST_LD_MODE = @TEST_LD_MODE@
|
||||
MKLIB = @MKLIB@
|
||||
AR = @AR@
|
||||
ARFLAGS = @ARFLAGS@
|
||||
CC_MM = @CC_MM@
|
||||
LM := @LM@
|
||||
INSTALL = @INSTALL@
|
||||
|
||||
ifeq (macho, $(ABI))
|
||||
TEST_LIBRARY_PATH := DYLD_FALLBACK_LIBRARY_PATH="$(objroot)lib"
|
||||
@@ -100,10 +104,12 @@ C_SRCS := $(srcroot)src/jemalloc.c \
|
||||
$(srcroot)src/quarantine.c \
|
||||
$(srcroot)src/rtree.c \
|
||||
$(srcroot)src/stats.c \
|
||||
$(srcroot)src/spin.c \
|
||||
$(srcroot)src/tcache.c \
|
||||
$(srcroot)src/ticker.c \
|
||||
$(srcroot)src/tsd.c \
|
||||
$(srcroot)src/util.c
|
||||
$(srcroot)src/util.c \
|
||||
$(srcroot)src/witness.c
|
||||
ifeq ($(enable_valgrind), 1)
|
||||
C_SRCS += $(srcroot)src/valgrind.c
|
||||
endif
|
||||
@@ -122,6 +128,11 @@ DSOS := $(objroot)lib/$(LIBJEMALLOC).$(SOREV)
|
||||
ifneq ($(SOREV),$(SO))
|
||||
DSOS += $(objroot)lib/$(LIBJEMALLOC).$(SO)
|
||||
endif
|
||||
ifeq (1, $(link_whole_archive))
|
||||
LJEMALLOC := -Wl,--whole-archive -L$(objroot)lib -l$(LIBJEMALLOC) -Wl,--no-whole-archive
|
||||
else
|
||||
LJEMALLOC := $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
|
||||
endif
|
||||
PC := $(objroot)jemalloc.pc
|
||||
MAN3 := $(objroot)doc/jemalloc$(install_suffix).3
|
||||
DOCS_XML := $(objroot)doc/jemalloc$(install_suffix).xml
|
||||
@@ -133,11 +144,19 @@ C_TESTLIB_SRCS := $(srcroot)test/src/btalloc.c $(srcroot)test/src/btalloc_0.c \
|
||||
$(srcroot)test/src/mtx.c $(srcroot)test/src/mq.c \
|
||||
$(srcroot)test/src/SFMT.c $(srcroot)test/src/test.c \
|
||||
$(srcroot)test/src/thd.c $(srcroot)test/src/timer.c
|
||||
ifeq (1, $(link_whole_archive))
|
||||
C_UTIL_INTEGRATION_SRCS :=
|
||||
else
|
||||
C_UTIL_INTEGRATION_SRCS := $(srcroot)src/nstime.c $(srcroot)src/util.c
|
||||
TESTS_UNIT := $(srcroot)test/unit/atomic.c \
|
||||
endif
|
||||
TESTS_UNIT := \
|
||||
$(srcroot)test/unit/a0.c \
|
||||
$(srcroot)test/unit/arena_reset.c \
|
||||
$(srcroot)test/unit/atomic.c \
|
||||
$(srcroot)test/unit/bitmap.c \
|
||||
$(srcroot)test/unit/ckh.c \
|
||||
$(srcroot)test/unit/decay.c \
|
||||
$(srcroot)test/unit/fork.c \
|
||||
$(srcroot)test/unit/hash.c \
|
||||
$(srcroot)test/unit/junk.c \
|
||||
$(srcroot)test/unit/junk_alloc.c \
|
||||
@@ -147,6 +166,7 @@ TESTS_UNIT := $(srcroot)test/unit/atomic.c \
|
||||
$(srcroot)test/unit/math.c \
|
||||
$(srcroot)test/unit/mq.c \
|
||||
$(srcroot)test/unit/mtx.c \
|
||||
$(srcroot)test/unit/ph.c \
|
||||
$(srcroot)test/unit/prng.c \
|
||||
$(srcroot)test/unit/prof_accum.c \
|
||||
$(srcroot)test/unit/prof_active.c \
|
||||
@@ -168,6 +188,7 @@ TESTS_UNIT := $(srcroot)test/unit/atomic.c \
|
||||
$(srcroot)test/unit/nstime.c \
|
||||
$(srcroot)test/unit/tsd.c \
|
||||
$(srcroot)test/unit/util.c \
|
||||
$(srcroot)test/unit/witness.c \
|
||||
$(srcroot)test/unit/zero.c
|
||||
TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
|
||||
$(srcroot)test/integration/allocated.c \
|
||||
@@ -289,69 +310,69 @@ $(STATIC_LIBS):
|
||||
|
||||
$(objroot)test/unit/%$(EXE): $(objroot)test/unit/%.$(O) $(TESTS_UNIT_LINK_OBJS) $(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(TESTLIBS) $(EXTRA_LDFLAGS)
|
||||
$(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)
|
||||
|
||||
$(objroot)test/integration/%$(EXE): $(objroot)test/integration/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(filter -lpthread,$(LIBS))) -lm $(TESTLIBS) $(EXTRA_LDFLAGS)
|
||||
$(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LJEMALLOC) $(LDFLAGS) $(filter-out -lm,$(filter -lrt -lpthread,$(LIBS))) $(LM) $(EXTRA_LDFLAGS)
|
||||
|
||||
$(objroot)test/stress/%$(EXE): $(objroot)test/stress/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_STRESS_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(TESTLIBS) $(EXTRA_LDFLAGS)
|
||||
$(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)
|
||||
|
||||
build_lib_shared: $(DSOS)
|
||||
build_lib_static: $(STATIC_LIBS)
|
||||
build_lib: build_lib_shared build_lib_static
|
||||
|
||||
install_bin:
|
||||
install -d $(BINDIR)
|
||||
$(INSTALL) -d $(BINDIR)
|
||||
@for b in $(BINS); do \
|
||||
echo "install -m 755 $$b $(BINDIR)"; \
|
||||
install -m 755 $$b $(BINDIR); \
|
||||
echo "$(INSTALL) -m 755 $$b $(BINDIR)"; \
|
||||
$(INSTALL) -m 755 $$b $(BINDIR); \
|
||||
done
|
||||
|
||||
install_include:
|
||||
install -d $(INCLUDEDIR)/jemalloc
|
||||
$(INSTALL) -d $(INCLUDEDIR)/jemalloc
|
||||
@for h in $(C_HDRS); do \
|
||||
echo "install -m 644 $$h $(INCLUDEDIR)/jemalloc"; \
|
||||
install -m 644 $$h $(INCLUDEDIR)/jemalloc; \
|
||||
echo "$(INSTALL) -m 644 $$h $(INCLUDEDIR)/jemalloc"; \
|
||||
$(INSTALL) -m 644 $$h $(INCLUDEDIR)/jemalloc; \
|
||||
done
|
||||
|
||||
install_lib_shared: $(DSOS)
|
||||
install -d $(LIBDIR)
|
||||
install -m 755 $(objroot)lib/$(LIBJEMALLOC).$(SOREV) $(LIBDIR)
|
||||
$(INSTALL) -d $(LIBDIR)
|
||||
$(INSTALL) -m 755 $(objroot)lib/$(LIBJEMALLOC).$(SOREV) $(LIBDIR)
|
||||
ifneq ($(SOREV),$(SO))
|
||||
ln -sf $(LIBJEMALLOC).$(SOREV) $(LIBDIR)/$(LIBJEMALLOC).$(SO)
|
||||
endif
|
||||
|
||||
install_lib_static: $(STATIC_LIBS)
|
||||
install -d $(LIBDIR)
|
||||
$(INSTALL) -d $(LIBDIR)
|
||||
@for l in $(STATIC_LIBS); do \
|
||||
echo "install -m 755 $$l $(LIBDIR)"; \
|
||||
install -m 755 $$l $(LIBDIR); \
|
||||
echo "$(INSTALL) -m 755 $$l $(LIBDIR)"; \
|
||||
$(INSTALL) -m 755 $$l $(LIBDIR); \
|
||||
done
|
||||
|
||||
install_lib_pc: $(PC)
|
||||
install -d $(LIBDIR)/pkgconfig
|
||||
$(INSTALL) -d $(LIBDIR)/pkgconfig
|
||||
@for l in $(PC); do \
|
||||
echo "install -m 644 $$l $(LIBDIR)/pkgconfig"; \
|
||||
install -m 644 $$l $(LIBDIR)/pkgconfig; \
|
||||
echo "$(INSTALL) -m 644 $$l $(LIBDIR)/pkgconfig"; \
|
||||
$(INSTALL) -m 644 $$l $(LIBDIR)/pkgconfig; \
|
||||
done
|
||||
|
||||
install_lib: install_lib_shared install_lib_static install_lib_pc
|
||||
|
||||
install_doc_html:
|
||||
install -d $(DATADIR)/doc/jemalloc$(install_suffix)
|
||||
$(INSTALL) -d $(DATADIR)/doc/jemalloc$(install_suffix)
|
||||
@for d in $(DOCS_HTML); do \
|
||||
echo "install -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix)"; \
|
||||
install -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix); \
|
||||
echo "$(INSTALL) -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix)"; \
|
||||
$(INSTALL) -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix); \
|
||||
done
|
||||
|
||||
install_doc_man:
|
||||
install -d $(MANDIR)/man3
|
||||
$(INSTALL) -d $(MANDIR)/man3
|
||||
@for d in $(DOCS_MAN3); do \
|
||||
echo "install -m 644 $$d $(MANDIR)/man3"; \
|
||||
install -m 644 $$d $(MANDIR)/man3; \
|
||||
echo "$(INSTALL) -m 644 $$d $(MANDIR)/man3"; \
|
||||
$(INSTALL) -m 644 $$d $(MANDIR)/man3; \
|
||||
done
|
||||
|
||||
install_doc: install_doc_html install_doc_man
|
||||
|
||||
2
README
2
README
@@ -17,4 +17,4 @@ jemalloc.
|
||||
|
||||
The ChangeLog file contains a brief summary of changes for each release.
|
||||
|
||||
URL: http://www.canonware.com/jemalloc/
|
||||
URL: http://jemalloc.net/
|
||||
|
||||
287
configure.ac
287
configure.ac
@@ -118,6 +118,7 @@ dnl If CFLAGS isn't defined, set CFLAGS to something reasonable. Otherwise,
|
||||
dnl just prevent autoconf from molesting CFLAGS.
|
||||
CFLAGS=$CFLAGS
|
||||
AC_PROG_CC
|
||||
|
||||
if test "x$GCC" != "xyes" ; then
|
||||
AC_CACHE_CHECK([whether compiler is MSVC],
|
||||
[je_cv_msvc],
|
||||
@@ -131,16 +132,59 @@ if test "x$GCC" != "xyes" ; then
|
||||
[je_cv_msvc=no])])
|
||||
fi
|
||||
|
||||
dnl check if a cray prgenv wrapper compiler is being used
|
||||
je_cv_cray_prgenv_wrapper=""
|
||||
if test "x${PE_ENV}" != "x" ; then
|
||||
case "${CC}" in
|
||||
CC|cc)
|
||||
je_cv_cray_prgenv_wrapper="yes"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([whether compiler is cray],
|
||||
[je_cv_cray],
|
||||
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
|
||||
[
|
||||
#ifndef _CRAYC
|
||||
int fail[-1];
|
||||
#endif
|
||||
])],
|
||||
[je_cv_cray=yes],
|
||||
[je_cv_cray=no])])
|
||||
|
||||
if test "x${je_cv_cray}" = "xyes" ; then
|
||||
AC_CACHE_CHECK([whether cray compiler version is 8.4],
|
||||
[je_cv_cray_84],
|
||||
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
|
||||
[
|
||||
#if !(_RELEASE_MAJOR == 8 && _RELEASE_MINOR == 4)
|
||||
int fail[-1];
|
||||
#endif
|
||||
])],
|
||||
[je_cv_cray_84=yes],
|
||||
[je_cv_cray_84=no])])
|
||||
fi
|
||||
|
||||
if test "x$CFLAGS" = "x" ; then
|
||||
no_CFLAGS="yes"
|
||||
if test "x$GCC" = "xyes" ; then
|
||||
JE_CFLAGS_APPEND([-std=gnu99])
|
||||
if test "x$je_cv_cflags_appended" = "x-std=gnu99" ; then
|
||||
dnl JE_CFLAGS_APPEND([-std=gnu99])
|
||||
JE_CFLAGS_APPEND([-std=gnu11])
|
||||
if test "x$je_cv_cflags_appended" = "x-std=gnu11" ; then
|
||||
AC_DEFINE_UNQUOTED([JEMALLOC_HAS_RESTRICT])
|
||||
else
|
||||
JE_CFLAGS_APPEND([-std=gnu99])
|
||||
if test "x$je_cv_cflags_appended" = "x-std=gnu99" ; then
|
||||
AC_DEFINE_UNQUOTED([JEMALLOC_HAS_RESTRICT])
|
||||
fi
|
||||
fi
|
||||
JE_CFLAGS_APPEND([-Wall])
|
||||
JE_CFLAGS_APPEND([-Werror=declaration-after-statement])
|
||||
JE_CFLAGS_APPEND([-Wshorten-64-to-32])
|
||||
JE_CFLAGS_APPEND([-Wsign-compare])
|
||||
JE_CFLAGS_APPEND([-pipe])
|
||||
JE_CFLAGS_APPEND([-g3])
|
||||
elif test "x$je_cv_msvc" = "xyes" ; then
|
||||
@@ -151,11 +195,21 @@ if test "x$CFLAGS" = "x" ; then
|
||||
JE_CFLAGS_APPEND([-FS])
|
||||
CPPFLAGS="$CPPFLAGS -I${srcdir}/include/msvc_compat"
|
||||
fi
|
||||
if test "x$je_cv_cray" = "xyes" ; then
|
||||
dnl cray compiler 8.4 has an inlining bug
|
||||
if test "x$je_cv_cray_84" = "xyes" ; then
|
||||
JE_CFLAGS_APPEND([-hipa2])
|
||||
JE_CFLAGS_APPEND([-hnognu])
|
||||
fi
|
||||
if test "x$enable_cc_silence" != "xno" ; then
|
||||
dnl ignore unreachable code warning
|
||||
JE_CFLAGS_APPEND([-hnomessage=128])
|
||||
dnl ignore redefinition of "malloc", "free", etc warning
|
||||
JE_CFLAGS_APPEND([-hnomessage=1357])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
dnl Append EXTRA_CFLAGS to CFLAGS, if defined.
|
||||
if test "x$EXTRA_CFLAGS" != "x" ; then
|
||||
JE_CFLAGS_APPEND([$EXTRA_CFLAGS])
|
||||
fi
|
||||
AC_SUBST([EXTRA_CFLAGS])
|
||||
AC_PROG_CPP
|
||||
|
||||
AC_C_BIGENDIAN([ac_cv_big_endian=1], [ac_cv_big_endian=0])
|
||||
@@ -262,17 +316,27 @@ o="$ac_objext"
|
||||
a="a"
|
||||
exe="$ac_exeext"
|
||||
libprefix="lib"
|
||||
link_whole_archive="0"
|
||||
DSO_LDFLAGS='-shared -Wl,-soname,$(@F)'
|
||||
RPATH='-Wl,-rpath,$(1)'
|
||||
SOREV="${so}.${rev}"
|
||||
PIC_CFLAGS='-fPIC -DPIC'
|
||||
CTARGET='-o $@'
|
||||
LDTARGET='-o $@'
|
||||
TEST_LD_MODE=
|
||||
EXTRA_LDFLAGS=
|
||||
ARFLAGS='crus'
|
||||
AROUT=' $@'
|
||||
CC_MM=1
|
||||
|
||||
if test "x$je_cv_cray_prgenv_wrapper" = "xyes" ; then
|
||||
TEST_LD_MODE='-dynamic'
|
||||
fi
|
||||
|
||||
if test "x${je_cv_cray}" = "xyes" ; then
|
||||
CC_MM=
|
||||
fi
|
||||
|
||||
AN_MAKEVAR([AR], [AC_PROG_AR])
|
||||
AN_PROGRAM([ar], [AC_PROG_AR])
|
||||
AC_DEFUN([AC_PROG_AR], [AC_CHECK_TOOL(AR, ar, :)])
|
||||
@@ -285,11 +349,11 @@ dnl
|
||||
dnl Define cpp macros in CPPFLAGS, rather than doing AC_DEFINE(macro), since the
|
||||
dnl definitions need to be seen before any headers are included, which is a pain
|
||||
dnl to make happen otherwise.
|
||||
CFLAGS="$CFLAGS"
|
||||
default_munmap="1"
|
||||
maps_coalesce="1"
|
||||
case "${host}" in
|
||||
*-*-darwin* | *-*-ios*)
|
||||
CFLAGS="$CFLAGS"
|
||||
abi="macho"
|
||||
AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
|
||||
RPATH=""
|
||||
@@ -302,32 +366,30 @@ case "${host}" in
|
||||
sbrk_deprecated="1"
|
||||
;;
|
||||
*-*-freebsd*)
|
||||
CFLAGS="$CFLAGS"
|
||||
abi="elf"
|
||||
AC_DEFINE([JEMALLOC_SYSCTL_VM_OVERCOMMIT], [ ])
|
||||
AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
|
||||
force_lazy_lock="1"
|
||||
;;
|
||||
*-*-dragonfly*)
|
||||
CFLAGS="$CFLAGS"
|
||||
abi="elf"
|
||||
AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
|
||||
;;
|
||||
*-*-openbsd*)
|
||||
CFLAGS="$CFLAGS"
|
||||
abi="elf"
|
||||
AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
|
||||
force_tls="0"
|
||||
;;
|
||||
*-*-bitrig*)
|
||||
CFLAGS="$CFLAGS"
|
||||
abi="elf"
|
||||
AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
|
||||
;;
|
||||
*-*-linux*)
|
||||
CFLAGS="$CFLAGS"
|
||||
dnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.
|
||||
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
|
||||
abi="elf"
|
||||
AC_DEFINE([JEMALLOC_HAS_ALLOCA_H])
|
||||
AC_DEFINE([JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY], [ ])
|
||||
AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED], [ ])
|
||||
AC_DEFINE([JEMALLOC_THREADED_INIT], [ ])
|
||||
AC_DEFINE([JEMALLOC_USE_CXX_THROW], [ ])
|
||||
@@ -342,13 +404,12 @@ case "${host}" in
|
||||
#error aout
|
||||
#endif
|
||||
]])],
|
||||
[CFLAGS="$CFLAGS"; abi="elf"],
|
||||
[abi="elf"],
|
||||
[abi="aout"])
|
||||
AC_MSG_RESULT([$abi])
|
||||
AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
|
||||
;;
|
||||
*-*-solaris2*)
|
||||
CFLAGS="$CFLAGS"
|
||||
abi="elf"
|
||||
AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
|
||||
RPATH='-Wl,-R,$(1)'
|
||||
@@ -369,7 +430,6 @@ case "${host}" in
|
||||
*-*-mingw* | *-*-cygwin*)
|
||||
abi="pecoff"
|
||||
force_tls="0"
|
||||
force_lazy_lock="1"
|
||||
maps_coalesce="0"
|
||||
RPATH=""
|
||||
so="dll"
|
||||
@@ -386,6 +446,7 @@ case "${host}" in
|
||||
else
|
||||
importlib="${so}"
|
||||
DSO_LDFLAGS="-shared"
|
||||
link_whole_archive="1"
|
||||
fi
|
||||
a="lib"
|
||||
libprefix=""
|
||||
@@ -423,17 +484,28 @@ AC_SUBST([o])
|
||||
AC_SUBST([a])
|
||||
AC_SUBST([exe])
|
||||
AC_SUBST([libprefix])
|
||||
AC_SUBST([link_whole_archive])
|
||||
AC_SUBST([DSO_LDFLAGS])
|
||||
AC_SUBST([EXTRA_LDFLAGS])
|
||||
AC_SUBST([SOREV])
|
||||
AC_SUBST([PIC_CFLAGS])
|
||||
AC_SUBST([CTARGET])
|
||||
AC_SUBST([LDTARGET])
|
||||
AC_SUBST([TEST_LD_MODE])
|
||||
AC_SUBST([MKLIB])
|
||||
AC_SUBST([ARFLAGS])
|
||||
AC_SUBST([AROUT])
|
||||
AC_SUBST([CC_MM])
|
||||
|
||||
dnl Determine whether libm must be linked to use e.g. log(3).
|
||||
AC_SEARCH_LIBS([log], [m], , [AC_MSG_ERROR([Missing math functions])])
|
||||
if test "x$ac_cv_search_log" != "xnone required" ; then
|
||||
LM="$ac_cv_search_log"
|
||||
else
|
||||
LM=
|
||||
fi
|
||||
AC_SUBST(LM)
|
||||
|
||||
JE_COMPILABLE([__attribute__ syntax],
|
||||
[static __attribute__((unused)) void foo(void){}],
|
||||
[],
|
||||
@@ -447,6 +519,7 @@ fi
|
||||
dnl Check for tls_model attribute support (clang 3.0 still lacks support).
|
||||
SAVED_CFLAGS="${CFLAGS}"
|
||||
JE_CFLAGS_APPEND([-Werror])
|
||||
JE_CFLAGS_APPEND([-herror_on_warning])
|
||||
JE_COMPILABLE([tls_model attribute], [],
|
||||
[static __thread int
|
||||
__attribute__((tls_model("initial-exec"), unused)) foo;
|
||||
@@ -462,6 +535,7 @@ fi
|
||||
dnl Check for alloc_size attribute support.
|
||||
SAVED_CFLAGS="${CFLAGS}"
|
||||
JE_CFLAGS_APPEND([-Werror])
|
||||
JE_CFLAGS_APPEND([-herror_on_warning])
|
||||
JE_COMPILABLE([alloc_size attribute], [#include <stdlib.h>],
|
||||
[void *foo(size_t size) __attribute__((alloc_size(1)));],
|
||||
[je_cv_alloc_size])
|
||||
@@ -472,6 +546,7 @@ fi
|
||||
dnl Check for format(gnu_printf, ...) attribute support.
|
||||
SAVED_CFLAGS="${CFLAGS}"
|
||||
JE_CFLAGS_APPEND([-Werror])
|
||||
JE_CFLAGS_APPEND([-herror_on_warning])
|
||||
JE_COMPILABLE([format(gnu_printf, ...) attribute], [#include <stdlib.h>],
|
||||
[void *foo(const char *format, ...) __attribute__((format(gnu_printf, 1, 2)));],
|
||||
[je_cv_format_gnu_printf])
|
||||
@@ -482,6 +557,7 @@ fi
|
||||
dnl Check for format(printf, ...) attribute support.
|
||||
SAVED_CFLAGS="${CFLAGS}"
|
||||
JE_CFLAGS_APPEND([-Werror])
|
||||
JE_CFLAGS_APPEND([-herror_on_warning])
|
||||
JE_COMPILABLE([format(printf, ...) attribute], [#include <stdlib.h>],
|
||||
[void *foo(const char *format, ...) __attribute__((format(printf, 1, 2)));],
|
||||
[je_cv_format_printf])
|
||||
@@ -876,9 +952,9 @@ fi
|
||||
AC_MSG_CHECKING([configured backtracing method])
|
||||
AC_MSG_RESULT([$backtrace_method])
|
||||
if test "x$enable_prof" = "x1" ; then
|
||||
if test "x$abi" != "xpecoff"; then
|
||||
dnl Heap profiling uses the log(3) function.
|
||||
LIBS="$LIBS -lm"
|
||||
dnl Heap profiling uses the log(3) function.
|
||||
if test "x$LM" != "x" ; then
|
||||
LIBS="$LIBS $LM"
|
||||
fi
|
||||
|
||||
AC_DEFINE([JEMALLOC_PROF], [ ])
|
||||
@@ -1047,6 +1123,23 @@ if test "x$enable_cache_oblivious" = "x1" ; then
|
||||
fi
|
||||
AC_SUBST([enable_cache_oblivious])
|
||||
|
||||
|
||||
|
||||
JE_COMPILABLE([a program using __builtin_unreachable], [
|
||||
void foo (void) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
], [
|
||||
{
|
||||
foo();
|
||||
}
|
||||
], [je_cv_gcc_builtin_unreachable])
|
||||
if test "x${je_cv_gcc_builtin_unreachable}" = "xyes" ; then
|
||||
AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [__builtin_unreachable])
|
||||
else
|
||||
AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [abort])
|
||||
fi
|
||||
|
||||
dnl ============================================================================
|
||||
dnl Check for __builtin_ffsl(), then ffsl(3), and fail if neither are found.
|
||||
dnl One of those two functions should (theoretically) exist on all platforms
|
||||
@@ -1172,27 +1265,36 @@ dnl ============================================================================
|
||||
dnl jemalloc configuration.
|
||||
dnl
|
||||
|
||||
dnl Set VERSION if source directory is inside a git repository.
|
||||
if test "x`test ! \"${srcroot}\" && cd \"${srcroot}\"; git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
|
||||
dnl Pattern globs aren't powerful enough to match both single- and
|
||||
dnl double-digit version numbers, so iterate over patterns to support up to
|
||||
dnl version 99.99.99 without any accidental matches.
|
||||
rm -f "${objroot}VERSION"
|
||||
for pattern in ['[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \
|
||||
'[0-9].[0-9][0-9].[0-9]' '[0-9].[0-9][0-9].[0-9][0-9]' \
|
||||
'[0-9][0-9].[0-9].[0-9]' '[0-9][0-9].[0-9].[0-9][0-9]' \
|
||||
'[0-9][0-9].[0-9][0-9].[0-9]' \
|
||||
'[0-9][0-9].[0-9][0-9].[0-9][0-9]']; do
|
||||
if test ! -e "${objroot}VERSION" ; then
|
||||
(test ! "${srcroot}" && cd "${srcroot}"; git describe --long --abbrev=40 --match="${pattern}") > "${objroot}VERSION.tmp" 2>/dev/null
|
||||
if test $? -eq 0 ; then
|
||||
mv "${objroot}VERSION.tmp" "${objroot}VERSION"
|
||||
break
|
||||
fi
|
||||
AC_ARG_WITH([version],
|
||||
[AS_HELP_STRING([--with-version=<major>.<minor>.<bugfix>-<nrev>-g<gid>],
|
||||
[Version string])],
|
||||
[
|
||||
echo "${with_version}" | grep ['^[0-9]\+\.[0-9]\+\.[0-9]\+-[0-9]\+-g[0-9a-f]\+$'] 2>&1 1>/dev/null
|
||||
if test $? -ne 0 ; then
|
||||
AC_MSG_ERROR([${with_version} does not match <major>.<minor>.<bugfix>-<nrev>-g<gid>])
|
||||
fi
|
||||
done
|
||||
fi
|
||||
rm -f "${objroot}VERSION.tmp"
|
||||
echo "$with_version" > "${objroot}VERSION"
|
||||
], [
|
||||
dnl Set VERSION if source directory is inside a git repository.
|
||||
if test "x`test ! \"${srcroot}\" && cd \"${srcroot}\"; git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
|
||||
dnl Pattern globs aren't powerful enough to match both single- and
|
||||
dnl double-digit version numbers, so iterate over patterns to support up
|
||||
dnl to version 99.99.99 without any accidental matches.
|
||||
for pattern in ['[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \
|
||||
'[0-9].[0-9][0-9].[0-9]' '[0-9].[0-9][0-9].[0-9][0-9]' \
|
||||
'[0-9][0-9].[0-9].[0-9]' '[0-9][0-9].[0-9].[0-9][0-9]' \
|
||||
'[0-9][0-9].[0-9][0-9].[0-9]' \
|
||||
'[0-9][0-9].[0-9][0-9].[0-9][0-9]']; do
|
||||
(test ! "${srcroot}" && cd "${srcroot}"; git describe --long --abbrev=40 --match="${pattern}") > "${objroot}VERSION.tmp" 2>/dev/null
|
||||
if test $? -eq 0 ; then
|
||||
mv "${objroot}VERSION.tmp" "${objroot}VERSION"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
rm -f "${objroot}VERSION.tmp"
|
||||
])
|
||||
|
||||
if test ! -e "${objroot}VERSION" ; then
|
||||
if test ! -e "${srcroot}VERSION" ; then
|
||||
AC_MSG_RESULT(
|
||||
@@ -1229,13 +1331,76 @@ fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -D_REENTRANT"
|
||||
|
||||
dnl Check whether clock_gettime(2) is in libc or librt. This function is only
|
||||
dnl used in test code, so save the result to TESTLIBS to avoid poluting LIBS.
|
||||
SAVED_LIBS="${LIBS}"
|
||||
LIBS=
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt], [TESTLIBS="${LIBS}"])
|
||||
AC_SUBST([TESTLIBS])
|
||||
LIBS="${SAVED_LIBS}"
|
||||
dnl Check whether clock_gettime(2) is in libc or librt.
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt])
|
||||
|
||||
dnl Cray wrapper compiler often adds `-lrt` when using `-static`. Check with
|
||||
dnl `-dynamic` as well in case a user tries to dynamically link in jemalloc
|
||||
if test "x$je_cv_cray_prgenv_wrapper" = "xyes" ; then
|
||||
if test "$ac_cv_search_clock_gettime" != "-lrt"; then
|
||||
SAVED_CFLAGS="${CFLAGS}"
|
||||
|
||||
unset ac_cv_search_clock_gettime
|
||||
JE_CFLAGS_APPEND([-dynamic])
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt])
|
||||
|
||||
CFLAGS="${SAVED_CFLAGS}"
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl check for CLOCK_MONOTONIC_COARSE (Linux-specific).
|
||||
JE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC_COARSE, ...)], [
|
||||
#include <time.h>
|
||||
], [
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
|
||||
], [je_cv_clock_monotonic_coarse])
|
||||
if test "x${je_cv_clock_monotonic_coarse}" = "xyes" ; then
|
||||
AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE])
|
||||
fi
|
||||
|
||||
dnl check for CLOCK_MONOTONIC.
|
||||
JE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC, ...)], [
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
], [
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
#if !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0
|
||||
# error _POSIX_MONOTONIC_CLOCK missing/invalid
|
||||
#endif
|
||||
], [je_cv_clock_monotonic])
|
||||
if test "x${je_cv_clock_monotonic}" = "xyes" ; then
|
||||
AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC])
|
||||
fi
|
||||
|
||||
dnl Check for mach_absolute_time().
|
||||
JE_COMPILABLE([mach_absolute_time()], [
|
||||
#include <mach/mach_time.h>
|
||||
], [
|
||||
mach_absolute_time();
|
||||
], [je_cv_mach_absolute_time])
|
||||
if test "x${je_cv_mach_absolute_time}" = "xyes" ; then
|
||||
AC_DEFINE([JEMALLOC_HAVE_MACH_ABSOLUTE_TIME])
|
||||
fi
|
||||
|
||||
dnl Check if syscall(2) is usable. Treat warnings as errors, so that e.g. OS X
|
||||
dnl 10.12's deprecation warning prevents use.
|
||||
SAVED_CFLAGS="${CFLAGS}"
|
||||
JE_CFLAGS_APPEND([-Werror])
|
||||
JE_COMPILABLE([syscall(2)], [
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
], [
|
||||
syscall(SYS_write, 2, "hello", 5);
|
||||
],
|
||||
[je_cv_syscall])
|
||||
CFLAGS="${SAVED_CFLAGS}"
|
||||
if test "x$je_cv_syscall" = "xyes" ; then
|
||||
AC_DEFINE([JEMALLOC_HAVE_SYSCALL], [ ])
|
||||
fi
|
||||
|
||||
dnl Check if the GNU-specific secure_getenv function exists.
|
||||
AC_CHECK_FUNC([secure_getenv],
|
||||
@@ -1291,9 +1456,17 @@ fi
|
||||
],
|
||||
[enable_lazy_lock=""]
|
||||
)
|
||||
if test "x$enable_lazy_lock" = "x" -a "x${force_lazy_lock}" = "x1" ; then
|
||||
AC_MSG_RESULT([Forcing lazy-lock to avoid allocator/threading bootstrap issues])
|
||||
enable_lazy_lock="1"
|
||||
if test "x${enable_lazy_lock}" = "x" ; then
|
||||
if test "x${force_lazy_lock}" = "x1" ; then
|
||||
AC_MSG_RESULT([Forcing lazy-lock to avoid allocator/threading bootstrap issues])
|
||||
enable_lazy_lock="1"
|
||||
else
|
||||
enable_lazy_lock="0"
|
||||
fi
|
||||
fi
|
||||
if test "x${enable_lazy_lock}" = "x1" -a "x${abi}" = "xpecoff" ; then
|
||||
AC_MSG_RESULT([Forcing no lazy-lock because thread creation monitoring is unimplemented])
|
||||
enable_lazy_lock="0"
|
||||
fi
|
||||
if test "x$enable_lazy_lock" = "x1" ; then
|
||||
if test "x$abi" != "xpecoff" ; then
|
||||
@@ -1304,8 +1477,6 @@ if test "x$enable_lazy_lock" = "x1" ; then
|
||||
])
|
||||
fi
|
||||
AC_DEFINE([JEMALLOC_LAZY_LOCK], [ ])
|
||||
else
|
||||
enable_lazy_lock="0"
|
||||
fi
|
||||
AC_SUBST([enable_lazy_lock])
|
||||
|
||||
@@ -1493,6 +1664,20 @@ if test "x${je_cv_builtin_clz}" = "xyes" ; then
|
||||
AC_DEFINE([JEMALLOC_HAVE_BUILTIN_CLZ], [ ])
|
||||
fi
|
||||
|
||||
dnl ============================================================================
|
||||
dnl Check for os_unfair_lock operations as provided on Darwin.
|
||||
|
||||
JE_COMPILABLE([Darwin os_unfair_lock_*()], [
|
||||
#include <os/lock.h>
|
||||
], [
|
||||
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
|
||||
os_unfair_lock_lock(&lock);
|
||||
os_unfair_lock_unlock(&lock);
|
||||
], [je_cv_os_unfair_lock])
|
||||
if test "x${je_cv_os_unfair_lock}" = "xyes" ; then
|
||||
AC_DEFINE([JEMALLOC_OS_UNFAIR_LOCK], [ ])
|
||||
fi
|
||||
|
||||
dnl ============================================================================
|
||||
dnl Check for spinlock(3) operations as provided on Darwin.
|
||||
|
||||
@@ -1737,11 +1922,11 @@ AC_MSG_RESULT([])
|
||||
AC_MSG_RESULT([CONFIG : ${CONFIG}])
|
||||
AC_MSG_RESULT([CC : ${CC}])
|
||||
AC_MSG_RESULT([CFLAGS : ${CFLAGS}])
|
||||
AC_MSG_RESULT([EXTRA_CFLAGS : ${EXTRA_CFLAGS}])
|
||||
AC_MSG_RESULT([CPPFLAGS : ${CPPFLAGS}])
|
||||
AC_MSG_RESULT([LDFLAGS : ${LDFLAGS}])
|
||||
AC_MSG_RESULT([EXTRA_LDFLAGS : ${EXTRA_LDFLAGS}])
|
||||
AC_MSG_RESULT([LIBS : ${LIBS}])
|
||||
AC_MSG_RESULT([TESTLIBS : ${TESTLIBS}])
|
||||
AC_MSG_RESULT([RPATH_EXTRA : ${RPATH_EXTRA}])
|
||||
AC_MSG_RESULT([])
|
||||
AC_MSG_RESULT([XSLTPROC : ${XSLTPROC}])
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
||||
<xsl:import href="@XSLROOT@/html/docbook.xsl"/>
|
||||
<xsl:import href="@abs_srcroot@doc/stylesheet.xsl"/>
|
||||
<xsl:output method="xml" encoding="utf-8"/>
|
||||
</xsl:stylesheet>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<title>LIBRARY</title>
|
||||
<para>This manual describes jemalloc @jemalloc_version@. More information
|
||||
can be found at the <ulink
|
||||
url="http://www.canonware.com/jemalloc/">jemalloc website</ulink>.</para>
|
||||
url="http://jemalloc.net/">jemalloc website</ulink>.</para>
|
||||
</refsect1>
|
||||
<refsynopsisdiv>
|
||||
<title>SYNOPSIS</title>
|
||||
@@ -180,20 +180,20 @@
|
||||
<refsect2>
|
||||
<title>Standard API</title>
|
||||
|
||||
<para>The <function>malloc<parameter/></function> function allocates
|
||||
<para>The <function>malloc()</function> function allocates
|
||||
<parameter>size</parameter> bytes of uninitialized memory. The allocated
|
||||
space is suitably aligned (after possible pointer coercion) for storage
|
||||
of any type of object.</para>
|
||||
|
||||
<para>The <function>calloc<parameter/></function> function allocates
|
||||
<para>The <function>calloc()</function> function allocates
|
||||
space for <parameter>number</parameter> objects, each
|
||||
<parameter>size</parameter> bytes in length. The result is identical to
|
||||
calling <function>malloc<parameter/></function> with an argument of
|
||||
calling <function>malloc()</function> with an argument of
|
||||
<parameter>number</parameter> * <parameter>size</parameter>, with the
|
||||
exception that the allocated memory is explicitly initialized to zero
|
||||
bytes.</para>
|
||||
|
||||
<para>The <function>posix_memalign<parameter/></function> function
|
||||
<para>The <function>posix_memalign()</function> function
|
||||
allocates <parameter>size</parameter> bytes of memory such that the
|
||||
allocation's base address is a multiple of
|
||||
<parameter>alignment</parameter>, and returns the allocation in the value
|
||||
@@ -201,7 +201,7 @@
|
||||
<parameter>alignment</parameter> must be a power of 2 at least as large as
|
||||
<code language="C">sizeof(<type>void *</type>)</code>.</para>
|
||||
|
||||
<para>The <function>aligned_alloc<parameter/></function> function
|
||||
<para>The <function>aligned_alloc()</function> function
|
||||
allocates <parameter>size</parameter> bytes of memory such that the
|
||||
allocation's base address is a multiple of
|
||||
<parameter>alignment</parameter>. The requested
|
||||
@@ -209,7 +209,7 @@
|
||||
undefined if <parameter>size</parameter> is not an integral multiple of
|
||||
<parameter>alignment</parameter>.</para>
|
||||
|
||||
<para>The <function>realloc<parameter/></function> function changes the
|
||||
<para>The <function>realloc()</function> function changes the
|
||||
size of the previously allocated memory referenced by
|
||||
<parameter>ptr</parameter> to <parameter>size</parameter> bytes. The
|
||||
contents of the memory are unchanged up to the lesser of the new and old
|
||||
@@ -217,26 +217,26 @@
|
||||
portion of the memory are undefined. Upon success, the memory referenced
|
||||
by <parameter>ptr</parameter> is freed and a pointer to the newly
|
||||
allocated memory is returned. Note that
|
||||
<function>realloc<parameter/></function> may move the memory allocation,
|
||||
<function>realloc()</function> may move the memory allocation,
|
||||
resulting in a different return value than <parameter>ptr</parameter>.
|
||||
If <parameter>ptr</parameter> is <constant>NULL</constant>, the
|
||||
<function>realloc<parameter/></function> function behaves identically to
|
||||
<function>malloc<parameter/></function> for the specified size.</para>
|
||||
<function>realloc()</function> function behaves identically to
|
||||
<function>malloc()</function> for the specified size.</para>
|
||||
|
||||
<para>The <function>free<parameter/></function> function causes the
|
||||
<para>The <function>free()</function> function causes the
|
||||
allocated memory referenced by <parameter>ptr</parameter> to be made
|
||||
available for future allocations. If <parameter>ptr</parameter> is
|
||||
<constant>NULL</constant>, no action occurs.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Non-standard API</title>
|
||||
<para>The <function>mallocx<parameter/></function>,
|
||||
<function>rallocx<parameter/></function>,
|
||||
<function>xallocx<parameter/></function>,
|
||||
<function>sallocx<parameter/></function>,
|
||||
<function>dallocx<parameter/></function>,
|
||||
<function>sdallocx<parameter/></function>, and
|
||||
<function>nallocx<parameter/></function> functions all have a
|
||||
<para>The <function>mallocx()</function>,
|
||||
<function>rallocx()</function>,
|
||||
<function>xallocx()</function>,
|
||||
<function>sallocx()</function>,
|
||||
<function>dallocx()</function>,
|
||||
<function>sdallocx()</function>, and
|
||||
<function>nallocx()</function> functions all have a
|
||||
<parameter>flags</parameter> argument that can be used to specify
|
||||
options. The functions only check the options that are contextually
|
||||
relevant. Use bitwise or (<code language="C">|</code>) operations to
|
||||
@@ -307,19 +307,19 @@
|
||||
</variablelist>
|
||||
</para>
|
||||
|
||||
<para>The <function>mallocx<parameter/></function> function allocates at
|
||||
<para>The <function>mallocx()</function> function allocates at
|
||||
least <parameter>size</parameter> bytes of memory, and returns a pointer
|
||||
to the base address of the allocation. Behavior is undefined if
|
||||
<parameter>size</parameter> is <constant>0</constant>.</para>
|
||||
|
||||
<para>The <function>rallocx<parameter/></function> function resizes the
|
||||
<para>The <function>rallocx()</function> function resizes the
|
||||
allocation at <parameter>ptr</parameter> to be at least
|
||||
<parameter>size</parameter> bytes, and returns a pointer to the base
|
||||
address of the resulting allocation, which may or may not have moved from
|
||||
its original location. Behavior is undefined if
|
||||
<parameter>size</parameter> is <constant>0</constant>.</para>
|
||||
|
||||
<para>The <function>xallocx<parameter/></function> function resizes the
|
||||
<para>The <function>xallocx()</function> function resizes the
|
||||
allocation at <parameter>ptr</parameter> in place to be at least
|
||||
<parameter>size</parameter> bytes, and returns the real size of the
|
||||
allocation. If <parameter>extra</parameter> is non-zero, an attempt is
|
||||
@@ -332,32 +332,32 @@
|
||||
language="C">(<parameter>size</parameter> + <parameter>extra</parameter>
|
||||
> <constant>SIZE_T_MAX</constant>)</code>.</para>
|
||||
|
||||
<para>The <function>sallocx<parameter/></function> function returns the
|
||||
<para>The <function>sallocx()</function> function returns the
|
||||
real size of the allocation at <parameter>ptr</parameter>.</para>
|
||||
|
||||
<para>The <function>dallocx<parameter/></function> function causes the
|
||||
<para>The <function>dallocx()</function> function causes the
|
||||
memory referenced by <parameter>ptr</parameter> to be made available for
|
||||
future allocations.</para>
|
||||
|
||||
<para>The <function>sdallocx<parameter/></function> function is an
|
||||
extension of <function>dallocx<parameter/></function> with a
|
||||
<para>The <function>sdallocx()</function> function is an
|
||||
extension of <function>dallocx()</function> with a
|
||||
<parameter>size</parameter> parameter to allow the caller to pass in the
|
||||
allocation size as an optimization. The minimum valid input size is the
|
||||
original requested size of the allocation, and the maximum valid input
|
||||
size is the corresponding value returned by
|
||||
<function>nallocx<parameter/></function> or
|
||||
<function>sallocx<parameter/></function>.</para>
|
||||
<function>nallocx()</function> or
|
||||
<function>sallocx()</function>.</para>
|
||||
|
||||
<para>The <function>nallocx<parameter/></function> function allocates no
|
||||
<para>The <function>nallocx()</function> function allocates no
|
||||
memory, but it performs the same size computation as the
|
||||
<function>mallocx<parameter/></function> function, and returns the real
|
||||
<function>mallocx()</function> function, and returns the real
|
||||
size of the allocation that would result from the equivalent
|
||||
<function>mallocx<parameter/></function> function call, or
|
||||
<function>mallocx()</function> function call, or
|
||||
<constant>0</constant> if the inputs exceed the maximum supported size
|
||||
class and/or alignment. Behavior is undefined if
|
||||
<parameter>size</parameter> is <constant>0</constant>.</para>
|
||||
|
||||
<para>The <function>mallctl<parameter/></function> function provides a
|
||||
<para>The <function>mallctl()</function> function provides a
|
||||
general interface for introspecting the memory allocator, as well as
|
||||
setting modifiable parameters and triggering actions. The
|
||||
period-separated <parameter>name</parameter> argument specifies a
|
||||
@@ -372,12 +372,12 @@
|
||||
<parameter>newlen</parameter>; otherwise pass <constant>NULL</constant>
|
||||
and <constant>0</constant>.</para>
|
||||
|
||||
<para>The <function>mallctlnametomib<parameter/></function> function
|
||||
<para>The <function>mallctlnametomib()</function> function
|
||||
provides a way to avoid repeated name lookups for applications that
|
||||
repeatedly query the same portion of the namespace, by translating a name
|
||||
to a “Management Information Base” (MIB) that can be passed
|
||||
repeatedly to <function>mallctlbymib<parameter/></function>. Upon
|
||||
successful return from <function>mallctlnametomib<parameter/></function>,
|
||||
to a <quote>Management Information Base</quote> (MIB) that can be passed
|
||||
repeatedly to <function>mallctlbymib()</function>. Upon
|
||||
successful return from <function>mallctlnametomib()</function>,
|
||||
<parameter>mibp</parameter> contains an array of
|
||||
<parameter>*miblenp</parameter> integers, where
|
||||
<parameter>*miblenp</parameter> is the lesser of the number of components
|
||||
@@ -410,39 +410,40 @@ for (i = 0; i < nbins; i++) {
|
||||
/* Do something with bin_size... */
|
||||
}]]></programlisting></para>
|
||||
|
||||
<para>The <function>malloc_stats_print<parameter/></function> function
|
||||
writes human-readable summary statistics via the
|
||||
<parameter>write_cb</parameter> callback function pointer and
|
||||
<parameter>cbopaque</parameter> data passed to
|
||||
<parameter>write_cb</parameter>, or
|
||||
<function>malloc_message<parameter/></function> if
|
||||
<parameter>write_cb</parameter> is <constant>NULL</constant>. This
|
||||
function can be called repeatedly. General information that never
|
||||
changes during execution can be omitted by specifying "g" as a character
|
||||
<para>The <function>malloc_stats_print()</function> function writes
|
||||
summary statistics via the <parameter>write_cb</parameter> callback
|
||||
function pointer and <parameter>cbopaque</parameter> data passed to
|
||||
<parameter>write_cb</parameter>, or <function>malloc_message()</function>
|
||||
if <parameter>write_cb</parameter> is <constant>NULL</constant>. The
|
||||
statistics are presented in human-readable form unless <quote>J</quote> is
|
||||
specified as a character within the <parameter>opts</parameter> string, in
|
||||
which case the statistics are presented in <ulink
|
||||
url="http://www.json.org/">JSON format</ulink>. This function can be
|
||||
called repeatedly. General information that never changes during
|
||||
execution can be omitted by specifying <quote>g</quote> as a character
|
||||
within the <parameter>opts</parameter> string. Note that
|
||||
<function>malloc_message<parameter/></function> uses the
|
||||
<function>mallctl*<parameter/></function> functions internally, so
|
||||
inconsistent statistics can be reported if multiple threads use these
|
||||
functions simultaneously. If <option>--enable-stats</option> is
|
||||
specified during configuration, “m” and “a” can
|
||||
be specified to omit merged arena and per arena statistics, respectively;
|
||||
“b”, “l”, and “h” can be specified to
|
||||
omit per size class statistics for bins, large objects, and huge objects,
|
||||
respectively. Unrecognized characters are silently ignored. Note that
|
||||
thread caching may prevent some statistics from being completely up to
|
||||
date, since extra locking would be required to merge counters that track
|
||||
thread cache operations.
|
||||
</para>
|
||||
<function>malloc_message()</function> uses the
|
||||
<function>mallctl*()</function> functions internally, so inconsistent
|
||||
statistics can be reported if multiple threads use these functions
|
||||
simultaneously. If <option>--enable-stats</option> is specified during
|
||||
configuration, <quote>m</quote> and <quote>a</quote> can be specified to
|
||||
omit merged arena and per arena statistics, respectively;
|
||||
<quote>b</quote>, <quote>l</quote>, and <quote>h</quote> can be specified
|
||||
to omit per size class statistics for bins, large objects, and huge
|
||||
objects, respectively. Unrecognized characters are silently ignored.
|
||||
Note that thread caching may prevent some statistics from being completely
|
||||
up to date, since extra locking would be required to merge counters that
|
||||
track thread cache operations.</para>
|
||||
|
||||
<para>The <function>malloc_usable_size<parameter/></function> function
|
||||
<para>The <function>malloc_usable_size()</function> function
|
||||
returns the usable size of the allocation pointed to by
|
||||
<parameter>ptr</parameter>. The return value may be larger than the size
|
||||
that was requested during allocation. The
|
||||
<function>malloc_usable_size<parameter/></function> function is not a
|
||||
mechanism for in-place <function>realloc<parameter/></function>; rather
|
||||
<function>malloc_usable_size()</function> function is not a
|
||||
mechanism for in-place <function>realloc()</function>; rather
|
||||
it is provided solely as a tool for introspection purposes. Any
|
||||
discrepancy between the requested allocation size and the size reported
|
||||
by <function>malloc_usable_size<parameter/></function> should not be
|
||||
by <function>malloc_usable_size()</function> should not be
|
||||
depended on, since such behavior is entirely implementation-dependent.
|
||||
</para>
|
||||
</refsect2>
|
||||
@@ -455,12 +456,12 @@ for (i = 0; i < nbins; i++) {
|
||||
|
||||
<para>The string specified via <option>--with-malloc-conf</option>, the
|
||||
string pointed to by the global variable <varname>malloc_conf</varname>, the
|
||||
“name” of the file referenced by the symbolic link named
|
||||
<quote>name</quote> of the file referenced by the symbolic link named
|
||||
<filename class="symlink">/etc/malloc.conf</filename>, and the value of the
|
||||
environment variable <envar>MALLOC_CONF</envar>, will be interpreted, in
|
||||
that order, from left to right as options. Note that
|
||||
<varname>malloc_conf</varname> may be read before
|
||||
<function>main<parameter/></function> is entered, so the declaration of
|
||||
<function>main()</function> is entered, so the declaration of
|
||||
<varname>malloc_conf</varname> should specify an initializer that contains
|
||||
the final value to be read by jemalloc. <option>--with-malloc-conf</option>
|
||||
and <varname>malloc_conf</varname> are compile-time mechanisms, whereas
|
||||
@@ -540,8 +541,8 @@ for (i = 0; i < nbins; i++) {
|
||||
are smaller than four times the page size, large size classes are smaller
|
||||
than the chunk size (see the <link
|
||||
linkend="opt.lg_chunk"><mallctl>opt.lg_chunk</mallctl></link> option), and
|
||||
huge size classes extend from the chunk size up to one size class less than
|
||||
the full address space size.</para>
|
||||
huge size classes extend from the chunk size up to the largest size class
|
||||
that does not exceed <constant>PTRDIFF_MAX</constant>.</para>
|
||||
|
||||
<para>Allocations are packed tightly together, which can be an issue for
|
||||
multi-threaded applications. If you need to assure that allocations do not
|
||||
@@ -549,14 +550,14 @@ for (i = 0; i < nbins; i++) {
|
||||
nearest multiple of the cacheline size, or specify cacheline alignment when
|
||||
allocating.</para>
|
||||
|
||||
<para>The <function>realloc<parameter/></function>,
|
||||
<function>rallocx<parameter/></function>, and
|
||||
<function>xallocx<parameter/></function> functions may resize allocations
|
||||
<para>The <function>realloc()</function>,
|
||||
<function>rallocx()</function>, and
|
||||
<function>xallocx()</function> functions may resize allocations
|
||||
without moving them under limited circumstances. Unlike the
|
||||
<function>*allocx<parameter/></function> API, the standard API does not
|
||||
<function>*allocx()</function> API, the standard API does not
|
||||
officially round up the usable size of an allocation to the nearest size
|
||||
class, so technically it is necessary to call
|
||||
<function>realloc<parameter/></function> to grow e.g. a 9-byte allocation to
|
||||
<function>realloc()</function> to grow e.g. a 9-byte allocation to
|
||||
16 bytes, or shrink a 16-byte allocation to 9 bytes. Growth and shrinkage
|
||||
trivially succeeds in place as long as the pre-size and post-size both round
|
||||
up to the same size class. No other API guarantees are made regarding
|
||||
@@ -659,7 +660,7 @@ for (i = 0; i < nbins; i++) {
|
||||
<entry>[1280 KiB, 1536 KiB, 1792 KiB]</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry morerows="6">Huge</entry>
|
||||
<entry morerows="8">Huge</entry>
|
||||
<entry>256 KiB</entry>
|
||||
<entry>[2 MiB]</entry>
|
||||
</row>
|
||||
@@ -687,6 +688,14 @@ for (i = 0; i < nbins; i++) {
|
||||
<entry>...</entry>
|
||||
<entry>...</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>512 PiB</entry>
|
||||
<entry>[2560 PiB, 3 EiB, 3584 PiB, 4 EiB]</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>1 EiB</entry>
|
||||
<entry>[5 EiB, 6 EiB, 7 EiB]</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
@@ -694,7 +703,7 @@ for (i = 0; i < nbins; i++) {
|
||||
<refsect1 id="mallctl_namespace">
|
||||
<title>MALLCTL NAMESPACE</title>
|
||||
<para>The following names are defined in the namespace accessible via the
|
||||
<function>mallctl*<parameter/></function> functions. Value types are
|
||||
<function>mallctl*()</function> functions. Value types are
|
||||
specified in parentheses, their readable/writable statuses are encoded as
|
||||
<literal>rw</literal>, <literal>r-</literal>, <literal>-w</literal>, or
|
||||
<literal>--</literal>, and required build configuration flags follow, if
|
||||
@@ -725,7 +734,7 @@ for (i = 0; i < nbins; i++) {
|
||||
<literal>rw</literal>
|
||||
</term>
|
||||
<listitem><para>If a value is passed in, refresh the data from which
|
||||
the <function>mallctl*<parameter/></function> functions report values,
|
||||
the <function>mallctl*()</function> functions report values,
|
||||
and increment the epoch. Return the current epoch. This is useful for
|
||||
detecting whether another thread caused a refresh.</para></listitem>
|
||||
</varlistentry>
|
||||
@@ -909,12 +918,12 @@ for (i = 0; i < nbins; i++) {
|
||||
settings are supported if
|
||||
<citerefentry><refentrytitle>sbrk</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry> is supported by the operating
|
||||
system: “disabled”, “primary”, and
|
||||
“secondary”; otherwise only “disabled” is
|
||||
supported. The default is “secondary” if
|
||||
system: <quote>disabled</quote>, <quote>primary</quote>, and
|
||||
<quote>secondary</quote>; otherwise only <quote>disabled</quote> is
|
||||
supported. The default is <quote>secondary</quote> if
|
||||
<citerefentry><refentrytitle>sbrk</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry> is supported by the operating
|
||||
system; “disabled” otherwise.
|
||||
system; <quote>disabled</quote> otherwise.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -1005,19 +1014,19 @@ for (i = 0; i < nbins; i++) {
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Enable/disable statistics printing at exit. If
|
||||
enabled, the <function>malloc_stats_print<parameter/></function>
|
||||
enabled, the <function>malloc_stats_print()</function>
|
||||
function is called at program exit via an
|
||||
<citerefentry><refentrytitle>atexit</refentrytitle>
|
||||
<manvolnum>3</manvolnum></citerefentry> function. If
|
||||
<option>--enable-stats</option> is specified during configuration, this
|
||||
has the potential to cause deadlock for a multi-threaded process that
|
||||
exits while one or more threads are executing in the memory allocation
|
||||
functions. Furthermore, <function>atexit<parameter/></function> may
|
||||
functions. Furthermore, <function>atexit()</function> may
|
||||
allocate memory during application initialization and then deadlock
|
||||
internally when jemalloc in turn calls
|
||||
<function>atexit<parameter/></function>, so this option is not
|
||||
univerally usable (though the application can register its own
|
||||
<function>atexit<parameter/></function> function with equivalent
|
||||
<function>atexit()</function>, so this option is not
|
||||
universally usable (though the application can register its own
|
||||
<function>atexit()</function> function with equivalent
|
||||
functionality). Therefore, this option should only be used with care;
|
||||
it is primarily intended as a performance tuning aid during application
|
||||
development. This option is disabled by default.</para></listitem>
|
||||
@@ -1030,15 +1039,16 @@ for (i = 0; i < nbins; i++) {
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-fill</option>]
|
||||
</term>
|
||||
<listitem><para>Junk filling. If set to "alloc", each byte of
|
||||
uninitialized allocated memory will be initialized to
|
||||
<literal>0xa5</literal>. If set to "free", all deallocated memory will
|
||||
be initialized to <literal>0x5a</literal>. If set to "true", both
|
||||
allocated and deallocated memory will be initialized, and if set to
|
||||
"false", junk filling be disabled entirely. This is intended for
|
||||
debugging and will impact performance negatively. This option is
|
||||
"false" by default unless <option>--enable-debug</option> is specified
|
||||
during configuration, in which case it is "true" by default unless
|
||||
<listitem><para>Junk filling. If set to <quote>alloc</quote>, each byte
|
||||
of uninitialized allocated memory will be initialized to
|
||||
<literal>0xa5</literal>. If set to <quote>free</quote>, all deallocated
|
||||
memory will be initialized to <literal>0x5a</literal>. If set to
|
||||
<quote>true</quote>, both allocated and deallocated memory will be
|
||||
initialized, and if set to <quote>false</quote>, junk filling be
|
||||
disabled entirely. This is intended for debugging and will impact
|
||||
performance negatively. This option is <quote>false</quote> by default
|
||||
unless <option>--enable-debug</option> is specified during
|
||||
configuration, in which case it is <quote>true</quote> by default unless
|
||||
running inside <ulink
|
||||
url="http://valgrind.org/">Valgrind</ulink>.</para></listitem>
|
||||
</varlistentry>
|
||||
@@ -1093,8 +1103,8 @@ for (i = 0; i < nbins; i++) {
|
||||
<listitem><para>Zero filling enabled/disabled. If enabled, each byte
|
||||
of uninitialized allocated memory will be initialized to 0. Note that
|
||||
this initialization only happens once for each byte, so
|
||||
<function>realloc<parameter/></function> and
|
||||
<function>rallocx<parameter/></function> calls do not zero memory that
|
||||
<function>realloc()</function> and
|
||||
<function>rallocx()</function> calls do not zero memory that
|
||||
was previously allocated. This is intended for debugging and will
|
||||
impact performance negatively. This option is disabled by default.
|
||||
</para></listitem>
|
||||
@@ -1317,11 +1327,11 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
<filename><prefix>.<pid>.<seq>.f.heap</filename>,
|
||||
where <literal><prefix></literal> is controlled by the <link
|
||||
linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
|
||||
option. Note that <function>atexit<parameter/></function> may allocate
|
||||
option. Note that <function>atexit()</function> may allocate
|
||||
memory during application initialization and then deadlock internally
|
||||
when jemalloc in turn calls <function>atexit<parameter/></function>, so
|
||||
this option is not univerally usable (though the application can
|
||||
register its own <function>atexit<parameter/></function> function with
|
||||
when jemalloc in turn calls <function>atexit()</function>, so
|
||||
this option is not universally usable (though the application can
|
||||
register its own <function>atexit()</function> function with
|
||||
equivalent functionality). This option is disabled by
|
||||
default.</para></listitem>
|
||||
</varlistentry>
|
||||
@@ -1380,7 +1390,7 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
<link
|
||||
linkend="thread.allocated"><mallctl>thread.allocated</mallctl></link>
|
||||
mallctl. This is useful for avoiding the overhead of repeated
|
||||
<function>mallctl*<parameter/></function> calls.</para></listitem>
|
||||
<function>mallctl*()</function> calls.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.deallocated">
|
||||
@@ -1407,7 +1417,7 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
<link
|
||||
linkend="thread.deallocated"><mallctl>thread.deallocated</mallctl></link>
|
||||
mallctl. This is useful for avoiding the overhead of repeated
|
||||
<function>mallctl*<parameter/></function> calls.</para></listitem>
|
||||
<function>mallctl*()</function> calls.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.tcache.enabled">
|
||||
@@ -1550,6 +1560,23 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
details.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arena.i.reset">
|
||||
<term>
|
||||
<mallctl>arena.<i>.reset</mallctl>
|
||||
(<type>void</type>)
|
||||
<literal>--</literal>
|
||||
</term>
|
||||
<listitem><para>Discard all of the arena's extant allocations. This
|
||||
interface can only be used with arenas created via <link
|
||||
linkend="arenas.extend"><mallctl>arenas.extend</mallctl></link>. None
|
||||
of the arena's discarded/cached allocations may accessed afterward. As
|
||||
part of this requirement, all thread caches which were used to
|
||||
allocate/deallocate in conjunction with the arena must be flushed
|
||||
beforehand. This interface cannot be used if running inside Valgrind,
|
||||
nor if the <link linkend="opt.quarantine">quarantine</link> size is
|
||||
non-zero.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arena.i.dss">
|
||||
<term>
|
||||
<mallctl>arena.<i>.dss</mallctl>
|
||||
@@ -2062,7 +2089,7 @@ typedef struct {
|
||||
[<option>--enable-prof</option>]
|
||||
</term>
|
||||
<listitem><para>Average number of bytes allocated between
|
||||
inverval-based profile dumps. See the
|
||||
interval-based profile dumps. See the
|
||||
<link
|
||||
linkend="opt.lg_prof_interval"><mallctl>opt.lg_prof_interval</mallctl></link>
|
||||
option for additional information.</para></listitem>
|
||||
@@ -2161,6 +2188,25 @@ typedef struct {
|
||||
linkend="stats.resident"><mallctl>stats.resident</mallctl></link>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.retained">
|
||||
<term>
|
||||
<mallctl>stats.retained</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Total number of bytes in virtual memory mappings that
|
||||
were retained rather than being returned to the operating system via
|
||||
e.g. <citerefentry><refentrytitle>munmap</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry>. Retained virtual memory is
|
||||
typically untouched, decommitted, or purged, so it has no strongly
|
||||
associated physical memory (see <link
|
||||
linkend="arena.i.chunk_hooks">chunk hooks</link> for details). Retained
|
||||
memory is excluded from mapped memory statistics, e.g. <link
|
||||
linkend="stats.mapped"><mallctl>stats.mapped</mallctl></link>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.dss">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.dss</mallctl>
|
||||
@@ -2241,6 +2287,18 @@ typedef struct {
|
||||
<listitem><para>Number of mapped bytes.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.retained">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.retained</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Number of retained bytes. See <link
|
||||
linkend="stats.retained"><mallctl>stats.retained</mallctl></link> for
|
||||
details.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.metadata.mapped">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.metadata.mapped</mallctl>
|
||||
@@ -2678,7 +2736,7 @@ MAPPED_LIBRARIES:
|
||||
of run-time assertions that catch application errors such as double-free,
|
||||
write-after-free, etc.</para>
|
||||
|
||||
<para>Programs often accidentally depend on “uninitialized”
|
||||
<para>Programs often accidentally depend on <quote>uninitialized</quote>
|
||||
memory actually being filled with zero bytes. Junk filling
|
||||
(see the <link linkend="opt.junk"><mallctl>opt.junk</mallctl></link>
|
||||
option) tends to expose such bugs in the form of obviously incorrect
|
||||
@@ -2707,29 +2765,29 @@ MAPPED_LIBRARIES:
|
||||
to override the function which emits the text strings forming the errors
|
||||
and warnings if for some reason the <constant>STDERR_FILENO</constant> file
|
||||
descriptor is not suitable for this.
|
||||
<function>malloc_message<parameter/></function> takes the
|
||||
<function>malloc_message()</function> takes the
|
||||
<parameter>cbopaque</parameter> pointer argument that is
|
||||
<constant>NULL</constant> unless overridden by the arguments in a call to
|
||||
<function>malloc_stats_print<parameter/></function>, followed by a string
|
||||
<function>malloc_stats_print()</function>, followed by a string
|
||||
pointer. Please note that doing anything which tries to allocate memory in
|
||||
this function is likely to result in a crash or deadlock.</para>
|
||||
|
||||
<para>All messages are prefixed by
|
||||
“<computeroutput><jemalloc>: </computeroutput>”.</para>
|
||||
<quote><computeroutput><jemalloc>: </computeroutput></quote>.</para>
|
||||
</refsect1>
|
||||
<refsect1 id="return_values">
|
||||
<title>RETURN VALUES</title>
|
||||
<refsect2>
|
||||
<title>Standard API</title>
|
||||
<para>The <function>malloc<parameter/></function> and
|
||||
<function>calloc<parameter/></function> functions return a pointer to the
|
||||
<para>The <function>malloc()</function> and
|
||||
<function>calloc()</function> functions return a pointer to the
|
||||
allocated memory if successful; otherwise a <constant>NULL</constant>
|
||||
pointer is returned and <varname>errno</varname> is set to
|
||||
<errorname>ENOMEM</errorname>.</para>
|
||||
|
||||
<para>The <function>posix_memalign<parameter/></function> function
|
||||
<para>The <function>posix_memalign()</function> function
|
||||
returns the value 0 if successful; otherwise it returns an error value.
|
||||
The <function>posix_memalign<parameter/></function> function will fail
|
||||
The <function>posix_memalign()</function> function will fail
|
||||
if:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
@@ -2748,11 +2806,11 @@ MAPPED_LIBRARIES:
|
||||
</variablelist>
|
||||
</para>
|
||||
|
||||
<para>The <function>aligned_alloc<parameter/></function> function returns
|
||||
<para>The <function>aligned_alloc()</function> function returns
|
||||
a pointer to the allocated memory if successful; otherwise a
|
||||
<constant>NULL</constant> pointer is returned and
|
||||
<varname>errno</varname> is set. The
|
||||
<function>aligned_alloc<parameter/></function> function will fail if:
|
||||
<function>aligned_alloc()</function> function will fail if:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><errorname>EINVAL</errorname></term>
|
||||
@@ -2769,44 +2827,44 @@ MAPPED_LIBRARIES:
|
||||
</variablelist>
|
||||
</para>
|
||||
|
||||
<para>The <function>realloc<parameter/></function> function returns a
|
||||
<para>The <function>realloc()</function> function returns a
|
||||
pointer, possibly identical to <parameter>ptr</parameter>, to the
|
||||
allocated memory if successful; otherwise a <constant>NULL</constant>
|
||||
pointer is returned, and <varname>errno</varname> is set to
|
||||
<errorname>ENOMEM</errorname> if the error was the result of an
|
||||
allocation failure. The <function>realloc<parameter/></function>
|
||||
allocation failure. The <function>realloc()</function>
|
||||
function always leaves the original buffer intact when an error occurs.
|
||||
</para>
|
||||
|
||||
<para>The <function>free<parameter/></function> function returns no
|
||||
<para>The <function>free()</function> function returns no
|
||||
value.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Non-standard API</title>
|
||||
<para>The <function>mallocx<parameter/></function> and
|
||||
<function>rallocx<parameter/></function> functions return a pointer to
|
||||
<para>The <function>mallocx()</function> and
|
||||
<function>rallocx()</function> functions return a pointer to
|
||||
the allocated memory if successful; otherwise a <constant>NULL</constant>
|
||||
pointer is returned to indicate insufficient contiguous memory was
|
||||
available to service the allocation request. </para>
|
||||
|
||||
<para>The <function>xallocx<parameter/></function> function returns the
|
||||
<para>The <function>xallocx()</function> function returns the
|
||||
real size of the resulting resized allocation pointed to by
|
||||
<parameter>ptr</parameter>, which is a value less than
|
||||
<parameter>size</parameter> if the allocation could not be adequately
|
||||
grown in place. </para>
|
||||
|
||||
<para>The <function>sallocx<parameter/></function> function returns the
|
||||
<para>The <function>sallocx()</function> function returns the
|
||||
real size of the allocation pointed to by <parameter>ptr</parameter>.
|
||||
</para>
|
||||
|
||||
<para>The <function>nallocx<parameter/></function> returns the real size
|
||||
<para>The <function>nallocx()</function> returns the real size
|
||||
that would result from a successful equivalent
|
||||
<function>mallocx<parameter/></function> function call, or zero if
|
||||
<function>mallocx()</function> function call, or zero if
|
||||
insufficient memory is available to perform the size computation. </para>
|
||||
|
||||
<para>The <function>mallctl<parameter/></function>,
|
||||
<function>mallctlnametomib<parameter/></function>, and
|
||||
<function>mallctlbymib<parameter/></function> functions return 0 on
|
||||
<para>The <function>mallctl()</function>,
|
||||
<function>mallctlnametomib()</function>, and
|
||||
<function>mallctlbymib()</function> functions return 0 on
|
||||
success; otherwise they return an error value. The functions will fail
|
||||
if:
|
||||
<variablelist>
|
||||
@@ -2842,13 +2900,13 @@ MAPPED_LIBRARIES:
|
||||
<term><errorname>EFAULT</errorname></term>
|
||||
|
||||
<listitem><para>An interface with side effects failed in some way
|
||||
not directly related to <function>mallctl*<parameter/></function>
|
||||
not directly related to <function>mallctl*()</function>
|
||||
read/write processing.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
|
||||
<para>The <function>malloc_usable_size<parameter/></function> function
|
||||
<para>The <function>malloc_usable_size()</function> function
|
||||
returns the usable size of the allocation pointed to by
|
||||
<parameter>ptr</parameter>. </para>
|
||||
</refsect2>
|
||||
@@ -2896,13 +2954,13 @@ malloc_conf = "lg_chunk:24";]]></programlisting></para>
|
||||
</refsect1>
|
||||
<refsect1 id="standards">
|
||||
<title>STANDARDS</title>
|
||||
<para>The <function>malloc<parameter/></function>,
|
||||
<function>calloc<parameter/></function>,
|
||||
<function>realloc<parameter/></function>, and
|
||||
<function>free<parameter/></function> functions conform to ISO/IEC
|
||||
9899:1990 (“ISO C90”).</para>
|
||||
<para>The <function>malloc()</function>,
|
||||
<function>calloc()</function>,
|
||||
<function>realloc()</function>, and
|
||||
<function>free()</function> functions conform to ISO/IEC
|
||||
9899:1990 (<quote>ISO C90</quote>).</para>
|
||||
|
||||
<para>The <function>posix_memalign<parameter/></function> function conforms
|
||||
to IEEE Std 1003.1-2001 (“POSIX.1”).</para>
|
||||
<para>The <function>posix_memalign()</function> function conforms
|
||||
to IEEE Std 1003.1-2001 (<quote>POSIX.1</quote>).</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
||||
<xsl:param name="funcsynopsis.style">ansi</xsl:param>
|
||||
<xsl:param name="function.parens" select="1"/>
|
||||
<xsl:param name="function.parens" select="0"/>
|
||||
<xsl:template match="function">
|
||||
<xsl:call-template name="inline.monoseq"/>
|
||||
</xsl:template>
|
||||
<xsl:template match="mallctl">
|
||||
"<xsl:call-template name="inline.monoseq"/>"
|
||||
<quote><xsl:call-template name="inline.monoseq"/></quote>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
|
||||
@@ -36,11 +36,13 @@ typedef enum {
|
||||
#define DECAY_NTICKS_PER_UPDATE 1000
|
||||
|
||||
typedef struct arena_runs_dirty_link_s arena_runs_dirty_link_t;
|
||||
typedef struct arena_avail_links_s arena_avail_links_t;
|
||||
typedef struct arena_run_s arena_run_t;
|
||||
typedef struct arena_chunk_map_bits_s arena_chunk_map_bits_t;
|
||||
typedef struct arena_chunk_map_misc_s arena_chunk_map_misc_t;
|
||||
typedef struct arena_chunk_s arena_chunk_t;
|
||||
typedef struct arena_bin_info_s arena_bin_info_t;
|
||||
typedef struct arena_decay_s arena_decay_t;
|
||||
typedef struct arena_bin_s arena_bin_t;
|
||||
typedef struct arena_s arena_t;
|
||||
typedef struct arena_tdata_s arena_tdata_t;
|
||||
@@ -153,13 +155,13 @@ struct arena_runs_dirty_link_s {
|
||||
*/
|
||||
struct arena_chunk_map_misc_s {
|
||||
/*
|
||||
* Linkage for run trees. There are two disjoint uses:
|
||||
* Linkage for run heaps. There are two disjoint uses:
|
||||
*
|
||||
* 1) arena_t's runs_avail tree.
|
||||
* 1) arena_t's runs_avail heaps.
|
||||
* 2) arena_run_t conceptually uses this linkage for in-use non-full
|
||||
* runs, rather than directly embedding linkage.
|
||||
*/
|
||||
rb_node(arena_chunk_map_misc_t) rb_link;
|
||||
phn(arena_chunk_map_misc_t) ph_link;
|
||||
|
||||
union {
|
||||
/* Linkage for list of dirty runs. */
|
||||
@@ -175,7 +177,7 @@ struct arena_chunk_map_misc_s {
|
||||
arena_run_t run;
|
||||
};
|
||||
};
|
||||
typedef rb_tree(arena_chunk_map_misc_t) arena_run_tree_t;
|
||||
typedef ph(arena_chunk_map_misc_t) arena_run_heap_t;
|
||||
#endif /* JEMALLOC_ARENA_STRUCTS_A */
|
||||
|
||||
#ifdef JEMALLOC_ARENA_STRUCTS_B
|
||||
@@ -256,6 +258,49 @@ struct arena_bin_info_s {
|
||||
uint32_t reg0_offset;
|
||||
};
|
||||
|
||||
struct arena_decay_s {
|
||||
/*
|
||||
* Approximate time in seconds from the creation of a set of unused
|
||||
* dirty pages until an equivalent set of unused dirty pages is purged
|
||||
* and/or reused.
|
||||
*/
|
||||
ssize_t time;
|
||||
/* time / SMOOTHSTEP_NSTEPS. */
|
||||
nstime_t interval;
|
||||
/*
|
||||
* Time at which the current decay interval logically started. We do
|
||||
* not actually advance to a new epoch until sometime after it starts
|
||||
* because of scheduling and computation delays, and it is even possible
|
||||
* to completely skip epochs. In all cases, during epoch advancement we
|
||||
* merge all relevant activity into the most recently recorded epoch.
|
||||
*/
|
||||
nstime_t epoch;
|
||||
/* Deadline randomness generator. */
|
||||
uint64_t jitter_state;
|
||||
/*
|
||||
* Deadline for current epoch. This is the sum of interval and per
|
||||
* epoch jitter which is a uniform random variable in [0..interval).
|
||||
* Epochs always advance by precise multiples of interval, but we
|
||||
* randomize the deadline to reduce the likelihood of arenas purging in
|
||||
* lockstep.
|
||||
*/
|
||||
nstime_t deadline;
|
||||
/*
|
||||
* Number of dirty pages at beginning of current epoch. During epoch
|
||||
* advancement we use the delta between arena->decay.ndirty and
|
||||
* arena->ndirty to determine how many dirty pages, if any, were
|
||||
* generated.
|
||||
*/
|
||||
size_t ndirty;
|
||||
/*
|
||||
* Trailing log of how many unused dirty pages were generated during
|
||||
* each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last
|
||||
* element is the most recent epoch. Corresponding epoch times are
|
||||
* relative to epoch.
|
||||
*/
|
||||
size_t backlog[SMOOTHSTEP_NSTEPS];
|
||||
};
|
||||
|
||||
struct arena_bin_s {
|
||||
/*
|
||||
* All operations on runcur, runs, and stats require that lock be
|
||||
@@ -272,13 +317,13 @@ struct arena_bin_s {
|
||||
arena_run_t *runcur;
|
||||
|
||||
/*
|
||||
* Tree of non-full runs. This tree is used when looking for an
|
||||
* Heap of non-full runs. This heap is used when looking for an
|
||||
* existing run when runcur is no longer usable. We choose the
|
||||
* non-full run that is lowest in memory; this policy tends to keep
|
||||
* objects packed well, and it can also help reduce the number of
|
||||
* almost-empty chunks.
|
||||
*/
|
||||
arena_run_tree_t runs;
|
||||
arena_run_heap_t runs;
|
||||
|
||||
/* Bin statistics. */
|
||||
malloc_bin_stats_t stats;
|
||||
@@ -289,10 +334,18 @@ struct arena_s {
|
||||
unsigned ind;
|
||||
|
||||
/*
|
||||
* Number of threads currently assigned to this arena. This field is
|
||||
* synchronized via atomic operations.
|
||||
* Number of threads currently assigned to this arena, synchronized via
|
||||
* atomic operations. Each thread has two distinct assignments, one for
|
||||
* application-serving allocation, and the other for internal metadata
|
||||
* allocation. Internal metadata must not be allocated from arenas
|
||||
* created via the arenas.extend mallctl, because the arena.<i>.reset
|
||||
* mallctl indiscriminately discards all allocations for the affected
|
||||
* arena.
|
||||
*
|
||||
* 0: Application allocation.
|
||||
* 1: Internal metadata allocation.
|
||||
*/
|
||||
unsigned nthreads;
|
||||
unsigned nthreads[2];
|
||||
|
||||
/*
|
||||
* There are three classes of arena operations from a locking
|
||||
@@ -317,10 +370,14 @@ struct arena_s {
|
||||
* PRNG state for cache index randomization of large allocation base
|
||||
* pointers.
|
||||
*/
|
||||
uint64_t offset_state;
|
||||
size_t offset_state;
|
||||
|
||||
dss_prec_t dss_prec;
|
||||
|
||||
|
||||
/* Extant arena chunks. */
|
||||
ql_head(extent_node_t) achunks;
|
||||
|
||||
/*
|
||||
* In order to avoid rapid chunk allocation/deallocation when an arena
|
||||
* oscillates right on the cusp of needing a new chunk, cache the most
|
||||
@@ -381,52 +438,8 @@ struct arena_s {
|
||||
arena_runs_dirty_link_t runs_dirty;
|
||||
extent_node_t chunks_cache;
|
||||
|
||||
/*
|
||||
* Approximate time in seconds from the creation of a set of unused
|
||||
* dirty pages until an equivalent set of unused dirty pages is purged
|
||||
* and/or reused.
|
||||
*/
|
||||
ssize_t decay_time;
|
||||
/* decay_time / SMOOTHSTEP_NSTEPS. */
|
||||
nstime_t decay_interval;
|
||||
/*
|
||||
* Time at which the current decay interval logically started. We do
|
||||
* not actually advance to a new epoch until sometime after it starts
|
||||
* because of scheduling and computation delays, and it is even possible
|
||||
* to completely skip epochs. In all cases, during epoch advancement we
|
||||
* merge all relevant activity into the most recently recorded epoch.
|
||||
*/
|
||||
nstime_t decay_epoch;
|
||||
/* decay_deadline randomness generator. */
|
||||
uint64_t decay_jitter_state;
|
||||
/*
|
||||
* Deadline for current epoch. This is the sum of decay_interval and
|
||||
* per epoch jitter which is a uniform random variable in
|
||||
* [0..decay_interval). Epochs always advance by precise multiples of
|
||||
* decay_interval, but we randomize the deadline to reduce the
|
||||
* likelihood of arenas purging in lockstep.
|
||||
*/
|
||||
nstime_t decay_deadline;
|
||||
/*
|
||||
* Number of dirty pages at beginning of current epoch. During epoch
|
||||
* advancement we use the delta between decay_ndirty and ndirty to
|
||||
* determine how many dirty pages, if any, were generated, and record
|
||||
* the result in decay_backlog.
|
||||
*/
|
||||
size_t decay_ndirty;
|
||||
/*
|
||||
* Memoized result of arena_decay_backlog_npages_limit() corresponding
|
||||
* to the current contents of decay_backlog, i.e. the limit on how many
|
||||
* pages are allowed to exist for the decay epochs.
|
||||
*/
|
||||
size_t decay_backlog_npages_limit;
|
||||
/*
|
||||
* Trailing log of how many unused dirty pages were generated during
|
||||
* each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last
|
||||
* element is the most recent epoch. Corresponding epoch times are
|
||||
* relative to decay_epoch.
|
||||
*/
|
||||
size_t decay_backlog[SMOOTHSTEP_NSTEPS];
|
||||
/* Decay-based purging state. */
|
||||
arena_decay_t decay;
|
||||
|
||||
/* Extant huge allocations. */
|
||||
ql_head(extent_node_t) huge;
|
||||
@@ -457,10 +470,12 @@ struct arena_s {
|
||||
arena_bin_t bins[NBINS];
|
||||
|
||||
/*
|
||||
* Quantized address-ordered trees of this arena's available runs. The
|
||||
* trees are used for first-best-fit run allocation.
|
||||
* Size-segregated address-ordered heaps of this arena's available runs,
|
||||
* used for first-best-fit run allocation. Runs are quantized, i.e.
|
||||
* they reside in the last heap which corresponds to a size class less
|
||||
* than or equal to the run size.
|
||||
*/
|
||||
arena_run_tree_t runs_avail[1]; /* Dynamically sized. */
|
||||
arena_run_heap_t runs_avail[NPSIZES];
|
||||
};
|
||||
|
||||
/* Used in conjunction with tsd for fast arena-related context lookup. */
|
||||
@@ -492,7 +507,6 @@ extern size_t map_bias; /* Number of arena chunk header pages. */
|
||||
extern size_t map_misc_offset;
|
||||
extern size_t arena_maxrun; /* Max run size for arenas. */
|
||||
extern size_t large_maxclass; /* Max large size class. */
|
||||
extern size_t run_quantize_max; /* Max run_quantize_*() input. */
|
||||
extern unsigned nlclasses; /* Number of large size classes. */
|
||||
extern unsigned nhclasses; /* Number of huge size classes. */
|
||||
|
||||
@@ -505,25 +519,28 @@ void arena_chunk_cache_maybe_insert(arena_t *arena, extent_node_t *node,
|
||||
bool cache);
|
||||
void arena_chunk_cache_maybe_remove(arena_t *arena, extent_node_t *node,
|
||||
bool cache);
|
||||
extent_node_t *arena_node_alloc(arena_t *arena);
|
||||
void arena_node_dalloc(arena_t *arena, extent_node_t *node);
|
||||
void *arena_chunk_alloc_huge(arena_t *arena, size_t usize, size_t alignment,
|
||||
bool *zero);
|
||||
void arena_chunk_dalloc_huge(arena_t *arena, void *chunk, size_t usize);
|
||||
void arena_chunk_ralloc_huge_similar(arena_t *arena, void *chunk,
|
||||
size_t oldsize, size_t usize);
|
||||
void arena_chunk_ralloc_huge_shrink(arena_t *arena, void *chunk,
|
||||
size_t oldsize, size_t usize);
|
||||
bool arena_chunk_ralloc_huge_expand(arena_t *arena, void *chunk,
|
||||
size_t oldsize, size_t usize, bool *zero);
|
||||
ssize_t arena_lg_dirty_mult_get(arena_t *arena);
|
||||
bool arena_lg_dirty_mult_set(arena_t *arena, ssize_t lg_dirty_mult);
|
||||
ssize_t arena_decay_time_get(arena_t *arena);
|
||||
bool arena_decay_time_set(arena_t *arena, ssize_t decay_time);
|
||||
void arena_maybe_purge(arena_t *arena);
|
||||
void arena_purge(arena_t *arena, bool all);
|
||||
void arena_tcache_fill_small(tsd_t *tsd, arena_t *arena, tcache_bin_t *tbin,
|
||||
szind_t binind, uint64_t prof_accumbytes);
|
||||
extent_node_t *arena_node_alloc(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_node_dalloc(tsdn_t *tsdn, arena_t *arena, extent_node_t *node);
|
||||
void *arena_chunk_alloc_huge(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
size_t alignment, bool *zero);
|
||||
void arena_chunk_dalloc_huge(tsdn_t *tsdn, arena_t *arena, void *chunk,
|
||||
size_t usize);
|
||||
void arena_chunk_ralloc_huge_similar(tsdn_t *tsdn, arena_t *arena,
|
||||
void *chunk, size_t oldsize, size_t usize);
|
||||
void arena_chunk_ralloc_huge_shrink(tsdn_t *tsdn, arena_t *arena,
|
||||
void *chunk, size_t oldsize, size_t usize);
|
||||
bool arena_chunk_ralloc_huge_expand(tsdn_t *tsdn, arena_t *arena,
|
||||
void *chunk, size_t oldsize, size_t usize, bool *zero);
|
||||
ssize_t arena_lg_dirty_mult_get(tsdn_t *tsdn, arena_t *arena);
|
||||
bool arena_lg_dirty_mult_set(tsdn_t *tsdn, arena_t *arena,
|
||||
ssize_t lg_dirty_mult);
|
||||
ssize_t arena_decay_time_get(tsdn_t *tsdn, arena_t *arena);
|
||||
bool arena_decay_time_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_time);
|
||||
void arena_purge(tsdn_t *tsdn, arena_t *arena, bool all);
|
||||
void arena_maybe_purge(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_reset(tsd_t *tsd, arena_t *arena);
|
||||
void arena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena,
|
||||
tcache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes);
|
||||
void arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info,
|
||||
bool zero);
|
||||
#ifdef JEMALLOC_JET
|
||||
@@ -536,17 +553,18 @@ extern arena_dalloc_junk_small_t *arena_dalloc_junk_small;
|
||||
void arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info);
|
||||
#endif
|
||||
void arena_quarantine_junk_small(void *ptr, size_t usize);
|
||||
void *arena_malloc_large(tsd_t *tsd, arena_t *arena, szind_t ind, bool zero);
|
||||
void *arena_malloc_hard(tsd_t *tsd, arena_t *arena, size_t size, szind_t ind,
|
||||
bool zero, tcache_t *tcache);
|
||||
void *arena_palloc(tsd_t *tsd, arena_t *arena, size_t usize,
|
||||
void *arena_malloc_large(tsdn_t *tsdn, arena_t *arena, szind_t ind,
|
||||
bool zero);
|
||||
void *arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size,
|
||||
szind_t ind, bool zero);
|
||||
void *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
size_t alignment, bool zero, tcache_t *tcache);
|
||||
void arena_prof_promoted(const void *ptr, size_t size);
|
||||
void arena_dalloc_bin_junked_locked(arena_t *arena, arena_chunk_t *chunk,
|
||||
void *ptr, arena_chunk_map_bits_t *bitselm);
|
||||
void arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,
|
||||
size_t pageind, arena_chunk_map_bits_t *bitselm);
|
||||
void arena_dalloc_small(tsd_t *tsd, arena_t *arena, arena_chunk_t *chunk,
|
||||
void arena_prof_promoted(tsdn_t *tsdn, const void *ptr, size_t size);
|
||||
void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena,
|
||||
arena_chunk_t *chunk, void *ptr, arena_chunk_map_bits_t *bitselm);
|
||||
void arena_dalloc_bin(tsdn_t *tsdn, arena_t *arena, arena_chunk_t *chunk,
|
||||
void *ptr, size_t pageind, arena_chunk_map_bits_t *bitselm);
|
||||
void arena_dalloc_small(tsdn_t *tsdn, arena_t *arena, arena_chunk_t *chunk,
|
||||
void *ptr, size_t pageind);
|
||||
#ifdef JEMALLOC_JET
|
||||
typedef void (arena_dalloc_junk_large_t)(void *, size_t);
|
||||
@@ -554,67 +572,80 @@ extern arena_dalloc_junk_large_t *arena_dalloc_junk_large;
|
||||
#else
|
||||
void arena_dalloc_junk_large(void *ptr, size_t usize);
|
||||
#endif
|
||||
void arena_dalloc_large_junked_locked(arena_t *arena, arena_chunk_t *chunk,
|
||||
void *ptr);
|
||||
void arena_dalloc_large(tsd_t *tsd, arena_t *arena, arena_chunk_t *chunk,
|
||||
void arena_dalloc_large_junked_locked(tsdn_t *tsdn, arena_t *arena,
|
||||
arena_chunk_t *chunk, void *ptr);
|
||||
void arena_dalloc_large(tsdn_t *tsdn, arena_t *arena, arena_chunk_t *chunk,
|
||||
void *ptr);
|
||||
#ifdef JEMALLOC_JET
|
||||
typedef void (arena_ralloc_junk_large_t)(void *, size_t, size_t);
|
||||
extern arena_ralloc_junk_large_t *arena_ralloc_junk_large;
|
||||
#endif
|
||||
bool arena_ralloc_no_move(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, bool zero);
|
||||
bool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize,
|
||||
size_t size, size_t extra, bool zero);
|
||||
void *arena_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize,
|
||||
size_t size, size_t alignment, bool zero, tcache_t *tcache);
|
||||
dss_prec_t arena_dss_prec_get(arena_t *arena);
|
||||
bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
|
||||
dss_prec_t arena_dss_prec_get(tsdn_t *tsdn, arena_t *arena);
|
||||
bool arena_dss_prec_set(tsdn_t *tsdn, arena_t *arena, dss_prec_t dss_prec);
|
||||
ssize_t arena_lg_dirty_mult_default_get(void);
|
||||
bool arena_lg_dirty_mult_default_set(ssize_t lg_dirty_mult);
|
||||
ssize_t arena_decay_time_default_get(void);
|
||||
bool arena_decay_time_default_set(ssize_t decay_time);
|
||||
void arena_basic_stats_merge(arena_t *arena, unsigned *nthreads,
|
||||
void arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena,
|
||||
unsigned *nthreads, const char **dss, ssize_t *lg_dirty_mult,
|
||||
ssize_t *decay_time, size_t *nactive, size_t *ndirty);
|
||||
void arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
const char **dss, ssize_t *lg_dirty_mult, ssize_t *decay_time,
|
||||
size_t *nactive, size_t *ndirty);
|
||||
void arena_stats_merge(arena_t *arena, unsigned *nthreads, const char **dss,
|
||||
ssize_t *lg_dirty_mult, ssize_t *decay_time, size_t *nactive,
|
||||
size_t *ndirty, arena_stats_t *astats, malloc_bin_stats_t *bstats,
|
||||
malloc_large_stats_t *lstats, malloc_huge_stats_t *hstats);
|
||||
unsigned arena_nthreads_get(arena_t *arena);
|
||||
void arena_nthreads_inc(arena_t *arena);
|
||||
void arena_nthreads_dec(arena_t *arena);
|
||||
arena_t *arena_new(unsigned ind);
|
||||
bool arena_boot(void);
|
||||
void arena_prefork(arena_t *arena);
|
||||
void arena_postfork_parent(arena_t *arena);
|
||||
void arena_postfork_child(arena_t *arena);
|
||||
size_t *nactive, size_t *ndirty, arena_stats_t *astats,
|
||||
malloc_bin_stats_t *bstats, malloc_large_stats_t *lstats,
|
||||
malloc_huge_stats_t *hstats);
|
||||
unsigned arena_nthreads_get(arena_t *arena, bool internal);
|
||||
void arena_nthreads_inc(arena_t *arena, bool internal);
|
||||
void arena_nthreads_dec(arena_t *arena, bool internal);
|
||||
arena_t *arena_new(tsdn_t *tsdn, unsigned ind);
|
||||
void arena_boot(void);
|
||||
void arena_prefork0(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork1(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork2(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork3(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_postfork_parent(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_postfork_child(tsdn_t *tsdn, arena_t *arena);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_INLINES
|
||||
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
arena_chunk_map_bits_t *arena_bitselm_get(arena_chunk_t *chunk,
|
||||
arena_chunk_map_bits_t *arena_bitselm_get_mutable(arena_chunk_t *chunk,
|
||||
size_t pageind);
|
||||
arena_chunk_map_misc_t *arena_miscelm_get(arena_chunk_t *chunk,
|
||||
const arena_chunk_map_bits_t *arena_bitselm_get_const(
|
||||
const arena_chunk_t *chunk, size_t pageind);
|
||||
arena_chunk_map_misc_t *arena_miscelm_get_mutable(arena_chunk_t *chunk,
|
||||
size_t pageind);
|
||||
const arena_chunk_map_misc_t *arena_miscelm_get_const(
|
||||
const arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_miscelm_to_pageind(const arena_chunk_map_misc_t *miscelm);
|
||||
void *arena_miscelm_to_rpages(arena_chunk_map_misc_t *miscelm);
|
||||
void *arena_miscelm_to_rpages(const arena_chunk_map_misc_t *miscelm);
|
||||
arena_chunk_map_misc_t *arena_rd_to_miscelm(arena_runs_dirty_link_t *rd);
|
||||
arena_chunk_map_misc_t *arena_run_to_miscelm(arena_run_t *run);
|
||||
size_t *arena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbitsp_read(size_t *mapbitsp);
|
||||
size_t arena_mapbits_get(arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_size_decode(size_t mapbits);
|
||||
size_t arena_mapbits_unallocated_size_get(arena_chunk_t *chunk,
|
||||
size_t *arena_mapbitsp_get_mutable(arena_chunk_t *chunk, size_t pageind);
|
||||
const size_t *arena_mapbitsp_get_const(const arena_chunk_t *chunk,
|
||||
size_t pageind);
|
||||
size_t arena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind);
|
||||
szind_t arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_decommitted_get(arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbitsp_read(const size_t *mapbitsp);
|
||||
size_t arena_mapbits_get(const arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_size_decode(size_t mapbits);
|
||||
size_t arena_mapbits_unallocated_size_get(const arena_chunk_t *chunk,
|
||||
size_t pageind);
|
||||
size_t arena_mapbits_large_size_get(const arena_chunk_t *chunk,
|
||||
size_t pageind);
|
||||
size_t arena_mapbits_small_runind_get(const arena_chunk_t *chunk,
|
||||
size_t pageind);
|
||||
szind_t arena_mapbits_binind_get(const arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_dirty_get(const arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_unzeroed_get(const arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_decommitted_get(const arena_chunk_t *chunk,
|
||||
size_t pageind);
|
||||
size_t arena_mapbits_large_get(const arena_chunk_t *chunk, size_t pageind);
|
||||
size_t arena_mapbits_allocated_get(const arena_chunk_t *chunk, size_t pageind);
|
||||
void arena_mapbitsp_write(size_t *mapbitsp, size_t mapbits);
|
||||
size_t arena_mapbits_size_encode(size_t size);
|
||||
void arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind,
|
||||
@@ -634,29 +665,31 @@ void arena_metadata_allocated_sub(arena_t *arena, size_t size);
|
||||
size_t arena_metadata_allocated_get(arena_t *arena);
|
||||
bool arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes);
|
||||
bool arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes);
|
||||
bool arena_prof_accum(arena_t *arena, uint64_t accumbytes);
|
||||
bool arena_prof_accum(tsdn_t *tsdn, arena_t *arena, uint64_t accumbytes);
|
||||
szind_t arena_ptr_small_binind_get(const void *ptr, size_t mapbits);
|
||||
szind_t arena_bin_index(arena_t *arena, arena_bin_t *bin);
|
||||
size_t arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info,
|
||||
const void *ptr);
|
||||
prof_tctx_t *arena_prof_tctx_get(const void *ptr);
|
||||
void arena_prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx);
|
||||
void arena_prof_tctx_reset(const void *ptr, size_t usize,
|
||||
prof_tctx_t *arena_prof_tctx_get(tsdn_t *tsdn, const void *ptr);
|
||||
void arena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx);
|
||||
void arena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
const void *old_ptr, prof_tctx_t *old_tctx);
|
||||
void arena_decay_ticks(tsd_t *tsd, arena_t *arena, unsigned nticks);
|
||||
void arena_decay_tick(tsd_t *tsd, arena_t *arena);
|
||||
void *arena_malloc(tsd_t *tsd, arena_t *arena, size_t size, szind_t ind,
|
||||
void arena_decay_ticks(tsdn_t *tsdn, arena_t *arena, unsigned nticks);
|
||||
void arena_decay_tick(tsdn_t *tsdn, arena_t *arena);
|
||||
void *arena_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind,
|
||||
bool zero, tcache_t *tcache, bool slow_path);
|
||||
arena_t *arena_aalloc(const void *ptr);
|
||||
size_t arena_salloc(const void *ptr, bool demote);
|
||||
void arena_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path);
|
||||
void arena_sdalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache);
|
||||
size_t arena_salloc(tsdn_t *tsdn, const void *ptr, bool demote);
|
||||
void arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache, bool slow_path);
|
||||
void arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
bool slow_path);
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ARENA_C_))
|
||||
# ifdef JEMALLOC_ARENA_INLINE_A
|
||||
JEMALLOC_ALWAYS_INLINE arena_chunk_map_bits_t *
|
||||
arena_bitselm_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_bitselm_get_mutable(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
assert(pageind >= map_bias);
|
||||
@@ -665,8 +698,15 @@ arena_bitselm_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (&chunk->map_bits[pageind-map_bias]);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE const arena_chunk_map_bits_t *
|
||||
arena_bitselm_get_const(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
return (arena_bitselm_get_mutable((arena_chunk_t *)chunk, pageind));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE arena_chunk_map_misc_t *
|
||||
arena_miscelm_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_miscelm_get_mutable(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
assert(pageind >= map_bias);
|
||||
@@ -676,6 +716,13 @@ arena_miscelm_get(arena_chunk_t *chunk, size_t pageind)
|
||||
(uintptr_t)map_misc_offset) + pageind-map_bias);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE const arena_chunk_map_misc_t *
|
||||
arena_miscelm_get_const(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
return (arena_miscelm_get_mutable((arena_chunk_t *)chunk, pageind));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_miscelm_to_pageind(const arena_chunk_map_misc_t *miscelm)
|
||||
{
|
||||
@@ -690,7 +737,7 @@ arena_miscelm_to_pageind(const arena_chunk_map_misc_t *miscelm)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
arena_miscelm_to_rpages(arena_chunk_map_misc_t *miscelm)
|
||||
arena_miscelm_to_rpages(const arena_chunk_map_misc_t *miscelm)
|
||||
{
|
||||
arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(miscelm);
|
||||
size_t pageind = arena_miscelm_to_pageind(miscelm);
|
||||
@@ -723,24 +770,31 @@ arena_run_to_miscelm(arena_run_t *run)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t *
|
||||
arena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbitsp_get_mutable(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
return (&arena_bitselm_get(chunk, pageind)->bits);
|
||||
return (&arena_bitselm_get_mutable(chunk, pageind)->bits);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE const size_t *
|
||||
arena_mapbitsp_get_const(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
return (arena_mapbitsp_get_mutable((arena_chunk_t *)chunk, pageind));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbitsp_read(size_t *mapbitsp)
|
||||
arena_mapbitsp_read(const size_t *mapbitsp)
|
||||
{
|
||||
|
||||
return (*mapbitsp);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
return (arena_mapbitsp_read(arena_mapbitsp_get(chunk, pageind)));
|
||||
return (arena_mapbitsp_read(arena_mapbitsp_get_const(chunk, pageind)));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
@@ -760,7 +814,7 @@ arena_mapbits_size_decode(size_t mapbits)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_unallocated_size_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
|
||||
@@ -770,7 +824,7 @@ arena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_large_size_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
|
||||
@@ -781,7 +835,7 @@ arena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_small_runind_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
|
||||
@@ -792,7 +846,7 @@ arena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE szind_t
|
||||
arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_binind_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
szind_t binind;
|
||||
@@ -804,7 +858,7 @@ arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_dirty_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
|
||||
@@ -815,7 +869,7 @@ arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_unzeroed_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
|
||||
@@ -826,7 +880,7 @@ arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_decommitted_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_decommitted_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
|
||||
@@ -837,7 +891,7 @@ arena_mapbits_decommitted_get(arena_chunk_t *chunk, size_t pageind)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_large_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
|
||||
@@ -846,7 +900,7 @@ arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind)
|
||||
arena_mapbits_allocated_get(const arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
|
||||
@@ -882,7 +936,7 @@ JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size,
|
||||
size_t flags)
|
||||
{
|
||||
size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
|
||||
size_t *mapbitsp = arena_mapbitsp_get_mutable(chunk, pageind);
|
||||
|
||||
assert((size & PAGE_MASK) == 0);
|
||||
assert((flags & CHUNK_MAP_FLAGS_MASK) == flags);
|
||||
@@ -896,7 +950,7 @@ JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind,
|
||||
size_t size)
|
||||
{
|
||||
size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
|
||||
size_t *mapbitsp = arena_mapbitsp_get_mutable(chunk, pageind);
|
||||
size_t mapbits = arena_mapbitsp_read(mapbitsp);
|
||||
|
||||
assert((size & PAGE_MASK) == 0);
|
||||
@@ -908,7 +962,7 @@ arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind,
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_internal_set(arena_chunk_t *chunk, size_t pageind, size_t flags)
|
||||
{
|
||||
size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
|
||||
size_t *mapbitsp = arena_mapbitsp_get_mutable(chunk, pageind);
|
||||
|
||||
assert((flags & CHUNK_MAP_UNZEROED) == flags);
|
||||
arena_mapbitsp_write(mapbitsp, flags);
|
||||
@@ -918,7 +972,7 @@ JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind, size_t size,
|
||||
size_t flags)
|
||||
{
|
||||
size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
|
||||
size_t *mapbitsp = arena_mapbitsp_get_mutable(chunk, pageind);
|
||||
|
||||
assert((size & PAGE_MASK) == 0);
|
||||
assert((flags & CHUNK_MAP_FLAGS_MASK) == flags);
|
||||
@@ -933,7 +987,7 @@ JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind,
|
||||
szind_t binind)
|
||||
{
|
||||
size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
|
||||
size_t *mapbitsp = arena_mapbitsp_get_mutable(chunk, pageind);
|
||||
size_t mapbits = arena_mapbitsp_read(mapbitsp);
|
||||
|
||||
assert(binind <= BININD_INVALID);
|
||||
@@ -947,7 +1001,7 @@ JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind, size_t runind,
|
||||
szind_t binind, size_t flags)
|
||||
{
|
||||
size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
|
||||
size_t *mapbitsp = arena_mapbitsp_get_mutable(chunk, pageind);
|
||||
|
||||
assert(binind < BININD_INVALID);
|
||||
assert(pageind - runind >= map_bias);
|
||||
@@ -1004,7 +1058,7 @@ arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes)
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE bool
|
||||
arena_prof_accum(arena_t *arena, uint64_t accumbytes)
|
||||
arena_prof_accum(tsdn_t *tsdn, arena_t *arena, uint64_t accumbytes)
|
||||
{
|
||||
|
||||
cassert(config_prof);
|
||||
@@ -1015,9 +1069,9 @@ arena_prof_accum(arena_t *arena, uint64_t accumbytes)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
malloc_mutex_lock(&arena->lock);
|
||||
malloc_mutex_lock(tsdn, &arena->lock);
|
||||
ret = arena_prof_accum_impl(arena, accumbytes);
|
||||
malloc_mutex_unlock(&arena->lock);
|
||||
malloc_mutex_unlock(tsdn, &arena->lock);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
@@ -1035,12 +1089,12 @@ arena_ptr_small_binind_get(const void *ptr, size_t mapbits)
|
||||
size_t pageind;
|
||||
size_t actual_mapbits;
|
||||
size_t rpages_ind;
|
||||
arena_run_t *run;
|
||||
const arena_run_t *run;
|
||||
arena_bin_t *bin;
|
||||
szind_t run_binind, actual_binind;
|
||||
arena_bin_info_t *bin_info;
|
||||
arena_chunk_map_misc_t *miscelm;
|
||||
void *rpages;
|
||||
const arena_chunk_map_misc_t *miscelm;
|
||||
const void *rpages;
|
||||
|
||||
assert(binind != BININD_INVALID);
|
||||
assert(binind < NBINS);
|
||||
@@ -1053,7 +1107,7 @@ arena_ptr_small_binind_get(const void *ptr, size_t mapbits)
|
||||
assert(arena_mapbits_allocated_get(chunk, pageind) != 0);
|
||||
rpages_ind = pageind - arena_mapbits_small_runind_get(chunk,
|
||||
pageind);
|
||||
miscelm = arena_miscelm_get(chunk, rpages_ind);
|
||||
miscelm = arena_miscelm_get_const(chunk, rpages_ind);
|
||||
run = &miscelm->run;
|
||||
run_binind = run->binind;
|
||||
bin = &arena->bins[run_binind];
|
||||
@@ -1153,7 +1207,7 @@ arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info, const void *ptr)
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE prof_tctx_t *
|
||||
arena_prof_tctx_get(const void *ptr)
|
||||
arena_prof_tctx_get(tsdn_t *tsdn, const void *ptr)
|
||||
{
|
||||
prof_tctx_t *ret;
|
||||
arena_chunk_t *chunk;
|
||||
@@ -1169,18 +1223,19 @@ arena_prof_tctx_get(const void *ptr)
|
||||
if (likely((mapbits & CHUNK_MAP_LARGE) == 0))
|
||||
ret = (prof_tctx_t *)(uintptr_t)1U;
|
||||
else {
|
||||
arena_chunk_map_misc_t *elm = arena_miscelm_get(chunk,
|
||||
pageind);
|
||||
arena_chunk_map_misc_t *elm =
|
||||
arena_miscelm_get_mutable(chunk, pageind);
|
||||
ret = atomic_read_p(&elm->prof_tctx_pun);
|
||||
}
|
||||
} else
|
||||
ret = huge_prof_tctx_get(ptr);
|
||||
ret = huge_prof_tctx_get(tsdn, ptr);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
arena_prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx)
|
||||
arena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx)
|
||||
{
|
||||
arena_chunk_t *chunk;
|
||||
|
||||
@@ -1199,7 +1254,7 @@ arena_prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx)
|
||||
|
||||
assert(arena_mapbits_large_get(chunk, pageind) != 0);
|
||||
|
||||
elm = arena_miscelm_get(chunk, pageind);
|
||||
elm = arena_miscelm_get_mutable(chunk, pageind);
|
||||
atomic_write_p(&elm->prof_tctx_pun, tctx);
|
||||
} else {
|
||||
/*
|
||||
@@ -1211,12 +1266,12 @@ arena_prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx)
|
||||
assert(arena_mapbits_large_get(chunk, pageind) == 0);
|
||||
}
|
||||
} else
|
||||
huge_prof_tctx_set(ptr, tctx);
|
||||
huge_prof_tctx_set(tsdn, ptr, tctx);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
arena_prof_tctx_reset(const void *ptr, size_t usize, const void *old_ptr,
|
||||
prof_tctx_t *old_tctx)
|
||||
arena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
const void *old_ptr, prof_tctx_t *old_tctx)
|
||||
{
|
||||
|
||||
cassert(config_prof);
|
||||
@@ -1235,56 +1290,59 @@ arena_prof_tctx_reset(const void *ptr, size_t usize, const void *old_ptr,
|
||||
0);
|
||||
assert(arena_mapbits_large_get(chunk, pageind) != 0);
|
||||
|
||||
elm = arena_miscelm_get(chunk, pageind);
|
||||
elm = arena_miscelm_get_mutable(chunk, pageind);
|
||||
atomic_write_p(&elm->prof_tctx_pun,
|
||||
(prof_tctx_t *)(uintptr_t)1U);
|
||||
} else
|
||||
huge_prof_tctx_reset(ptr);
|
||||
huge_prof_tctx_reset(tsdn, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_decay_ticks(tsd_t *tsd, arena_t *arena, unsigned nticks)
|
||||
arena_decay_ticks(tsdn_t *tsdn, arena_t *arena, unsigned nticks)
|
||||
{
|
||||
tsd_t *tsd;
|
||||
ticker_t *decay_ticker;
|
||||
|
||||
if (unlikely(tsd == NULL))
|
||||
if (unlikely(tsdn_null(tsdn)))
|
||||
return;
|
||||
tsd = tsdn_tsd(tsdn);
|
||||
decay_ticker = decay_ticker_get(tsd, arena->ind);
|
||||
if (unlikely(decay_ticker == NULL))
|
||||
return;
|
||||
if (unlikely(ticker_ticks(decay_ticker, nticks)))
|
||||
arena_purge(arena, false);
|
||||
arena_purge(tsdn, arena, false);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_decay_tick(tsd_t *tsd, arena_t *arena)
|
||||
arena_decay_tick(tsdn_t *tsdn, arena_t *arena)
|
||||
{
|
||||
|
||||
arena_decay_ticks(tsd, arena, 1);
|
||||
arena_decay_ticks(tsdn, arena, 1);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
arena_malloc(tsd_t *tsd, arena_t *arena, size_t size, szind_t ind, bool zero,
|
||||
arena_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, bool zero,
|
||||
tcache_t *tcache, bool slow_path)
|
||||
{
|
||||
|
||||
assert(!tsdn_null(tsdn) || tcache == NULL);
|
||||
assert(size != 0);
|
||||
|
||||
if (likely(tcache != NULL)) {
|
||||
if (likely(size <= SMALL_MAXCLASS)) {
|
||||
return (tcache_alloc_small(tsd, arena, tcache, size,
|
||||
ind, zero, slow_path));
|
||||
return (tcache_alloc_small(tsdn_tsd(tsdn), arena,
|
||||
tcache, size, ind, zero, slow_path));
|
||||
}
|
||||
if (likely(size <= tcache_maxclass)) {
|
||||
return (tcache_alloc_large(tsd, arena, tcache, size,
|
||||
ind, zero, slow_path));
|
||||
return (tcache_alloc_large(tsdn_tsd(tsdn), arena,
|
||||
tcache, size, ind, zero, slow_path));
|
||||
}
|
||||
/* (size > tcache_maxclass) case falls through. */
|
||||
assert(size > tcache_maxclass);
|
||||
}
|
||||
|
||||
return (arena_malloc_hard(tsd, arena, size, ind, zero, tcache));
|
||||
return (arena_malloc_hard(tsdn, arena, size, ind, zero));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE arena_t *
|
||||
@@ -1301,7 +1359,7 @@ arena_aalloc(const void *ptr)
|
||||
|
||||
/* Return the size of the allocation pointed to by ptr. */
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_salloc(const void *ptr, bool demote)
|
||||
arena_salloc(tsdn_t *tsdn, const void *ptr, bool demote)
|
||||
{
|
||||
size_t ret;
|
||||
arena_chunk_t *chunk;
|
||||
@@ -1344,17 +1402,18 @@ arena_salloc(const void *ptr, bool demote)
|
||||
ret = index2size(binind);
|
||||
}
|
||||
} else
|
||||
ret = huge_salloc(ptr);
|
||||
ret = huge_salloc(tsdn, ptr);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path)
|
||||
arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache, bool slow_path)
|
||||
{
|
||||
arena_chunk_t *chunk;
|
||||
size_t pageind, mapbits;
|
||||
|
||||
assert(!tsdn_null(tsdn) || tcache == NULL);
|
||||
assert(ptr != NULL);
|
||||
|
||||
chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
|
||||
@@ -1367,11 +1426,12 @@ arena_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path)
|
||||
if (likely(tcache != NULL)) {
|
||||
szind_t binind = arena_ptr_small_binind_get(ptr,
|
||||
mapbits);
|
||||
tcache_dalloc_small(tsd, tcache, ptr, binind,
|
||||
slow_path);
|
||||
tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr,
|
||||
binind, slow_path);
|
||||
} else {
|
||||
arena_dalloc_small(tsd, extent_node_arena_get(
|
||||
&chunk->node), chunk, ptr, pageind);
|
||||
arena_dalloc_small(tsdn,
|
||||
extent_node_arena_get(&chunk->node), chunk,
|
||||
ptr, pageind);
|
||||
}
|
||||
} else {
|
||||
size_t size = arena_mapbits_large_size_get(chunk,
|
||||
@@ -1382,22 +1442,26 @@ arena_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path)
|
||||
|
||||
if (likely(tcache != NULL) && size - large_pad <=
|
||||
tcache_maxclass) {
|
||||
tcache_dalloc_large(tsd, tcache, ptr, size -
|
||||
large_pad, slow_path);
|
||||
tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,
|
||||
size - large_pad, slow_path);
|
||||
} else {
|
||||
arena_dalloc_large(tsd, extent_node_arena_get(
|
||||
&chunk->node), chunk, ptr);
|
||||
arena_dalloc_large(tsdn,
|
||||
extent_node_arena_get(&chunk->node), chunk,
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
} else
|
||||
huge_dalloc(tsd, ptr, tcache);
|
||||
huge_dalloc(tsdn, ptr);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_sdalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache)
|
||||
arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
bool slow_path)
|
||||
{
|
||||
arena_chunk_t *chunk;
|
||||
|
||||
assert(!tsdn_null(tsdn) || tcache == NULL);
|
||||
|
||||
chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
|
||||
if (likely(chunk != ptr)) {
|
||||
if (config_prof && opt_prof) {
|
||||
@@ -1414,34 +1478,36 @@ arena_sdalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache)
|
||||
pageind) - large_pad;
|
||||
}
|
||||
}
|
||||
assert(s2u(size) == s2u(arena_salloc(ptr, false)));
|
||||
assert(s2u(size) == s2u(arena_salloc(tsdn, ptr, false)));
|
||||
|
||||
if (likely(size <= SMALL_MAXCLASS)) {
|
||||
/* Small allocation. */
|
||||
if (likely(tcache != NULL)) {
|
||||
szind_t binind = size2index(size);
|
||||
tcache_dalloc_small(tsd, tcache, ptr, binind,
|
||||
true);
|
||||
tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr,
|
||||
binind, slow_path);
|
||||
} else {
|
||||
size_t pageind = ((uintptr_t)ptr -
|
||||
(uintptr_t)chunk) >> LG_PAGE;
|
||||
arena_dalloc_small(tsd, extent_node_arena_get(
|
||||
&chunk->node), chunk, ptr, pageind);
|
||||
arena_dalloc_small(tsdn,
|
||||
extent_node_arena_get(&chunk->node), chunk,
|
||||
ptr, pageind);
|
||||
}
|
||||
} else {
|
||||
assert(config_cache_oblivious || ((uintptr_t)ptr &
|
||||
PAGE_MASK) == 0);
|
||||
|
||||
if (likely(tcache != NULL) && size <= tcache_maxclass) {
|
||||
tcache_dalloc_large(tsd, tcache, ptr, size,
|
||||
true);
|
||||
tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,
|
||||
size, slow_path);
|
||||
} else {
|
||||
arena_dalloc_large(tsd, extent_node_arena_get(
|
||||
&chunk->node), chunk, ptr);
|
||||
arena_dalloc_large(tsdn,
|
||||
extent_node_arena_get(&chunk->node), chunk,
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
} else
|
||||
huge_dalloc(tsd, ptr, tcache);
|
||||
huge_dalloc(tsdn, ptr);
|
||||
}
|
||||
# endif /* JEMALLOC_ARENA_INLINE_B */
|
||||
#endif
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
void *base_alloc(size_t size);
|
||||
void base_stats_get(size_t *allocated, size_t *resident, size_t *mapped);
|
||||
void *base_alloc(tsdn_t *tsdn, size_t size);
|
||||
void base_stats_get(tsdn_t *tsdn, size_t *allocated, size_t *resident,
|
||||
size_t *mapped);
|
||||
bool base_boot(void);
|
||||
void base_prefork(void);
|
||||
void base_postfork_parent(void);
|
||||
void base_postfork_child(void);
|
||||
void base_prefork(tsdn_t *tsdn);
|
||||
void base_postfork_parent(tsdn_t *tsdn);
|
||||
void base_postfork_child(tsdn_t *tsdn);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -17,8 +17,8 @@ typedef unsigned long bitmap_t;
|
||||
|
||||
/*
|
||||
* Do some analysis on how big the bitmap is before we use a tree. For a brute
|
||||
* force linear search, if we would have to call ffsl more than 2^3 times, use a
|
||||
* tree instead.
|
||||
* force linear search, if we would have to call ffs_lu() more than 2^3 times,
|
||||
* use a tree instead.
|
||||
*/
|
||||
#if LG_BITMAP_MAXBITS - LG_BITMAP_GROUP_NBITS > 3
|
||||
# define USE_TREE
|
||||
@@ -223,7 +223,7 @@ bitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo)
|
||||
i++;
|
||||
g = bitmap[i];
|
||||
}
|
||||
bit = (bit - 1) + (i << 6);
|
||||
bit = (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);
|
||||
#endif
|
||||
bitmap_set(bitmap, binfo, bit);
|
||||
return (bit);
|
||||
|
||||
@@ -48,32 +48,29 @@ extern size_t chunk_npages;
|
||||
|
||||
extern const chunk_hooks_t chunk_hooks_default;
|
||||
|
||||
chunk_hooks_t chunk_hooks_get(arena_t *arena);
|
||||
chunk_hooks_t chunk_hooks_set(arena_t *arena,
|
||||
chunk_hooks_t chunk_hooks_get(tsdn_t *tsdn, arena_t *arena);
|
||||
chunk_hooks_t chunk_hooks_set(tsdn_t *tsdn, arena_t *arena,
|
||||
const chunk_hooks_t *chunk_hooks);
|
||||
|
||||
bool chunk_register(const void *chunk, const extent_node_t *node);
|
||||
bool chunk_register(tsdn_t *tsdn, const void *chunk,
|
||||
const extent_node_t *node);
|
||||
void chunk_deregister(const void *chunk, const extent_node_t *node);
|
||||
void *chunk_alloc_base(size_t size);
|
||||
void *chunk_alloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *new_addr, size_t size, size_t alignment, bool *zero,
|
||||
bool dalloc_node);
|
||||
void *chunk_alloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit);
|
||||
void chunk_dalloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *chunk, size_t size, bool committed);
|
||||
void chunk_dalloc_arena(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *chunk, size_t size, bool zeroed, bool committed);
|
||||
void chunk_dalloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *chunk, size_t size, bool committed);
|
||||
bool chunk_purge_arena(arena_t *arena, void *chunk, size_t offset,
|
||||
void *chunk_alloc_cache(tsdn_t *tsdn, arena_t *arena,
|
||||
chunk_hooks_t *chunk_hooks, void *new_addr, size_t size, size_t alignment,
|
||||
bool *zero, bool *commit, bool dalloc_node);
|
||||
void *chunk_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
chunk_hooks_t *chunk_hooks, void *new_addr, size_t size, size_t alignment,
|
||||
bool *zero, bool *commit);
|
||||
void chunk_dalloc_cache(tsdn_t *tsdn, arena_t *arena,
|
||||
chunk_hooks_t *chunk_hooks, void *chunk, size_t size, bool committed);
|
||||
void chunk_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
chunk_hooks_t *chunk_hooks, void *chunk, size_t size, bool zeroed,
|
||||
bool committed);
|
||||
bool chunk_purge_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
chunk_hooks_t *chunk_hooks, void *chunk, size_t size, size_t offset,
|
||||
size_t length);
|
||||
bool chunk_purge_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *chunk, size_t size, size_t offset, size_t length);
|
||||
bool chunk_boot(void);
|
||||
void chunk_prefork(void);
|
||||
void chunk_postfork_parent(void);
|
||||
void chunk_postfork_child(void);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -23,13 +23,11 @@ extern const char *dss_prec_names[];
|
||||
|
||||
dss_prec_t chunk_dss_prec_get(void);
|
||||
bool chunk_dss_prec_set(dss_prec_t dss_prec);
|
||||
void *chunk_alloc_dss(arena_t *arena, void *new_addr, size_t size,
|
||||
size_t alignment, bool *zero, bool *commit);
|
||||
void *chunk_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr,
|
||||
size_t size, size_t alignment, bool *zero, bool *commit);
|
||||
bool chunk_in_dss(void *chunk);
|
||||
bool chunk_dss_boot(void);
|
||||
void chunk_dss_prefork(void);
|
||||
void chunk_dss_postfork_parent(void);
|
||||
void chunk_dss_postfork_child(void);
|
||||
bool chunk_dss_mergeable(void *chunk_a, void *chunk_b);
|
||||
void chunk_dss_boot(void);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -21,13 +21,14 @@ struct ctl_named_node_s {
|
||||
/* If (nchildren == 0), this is a terminal node. */
|
||||
unsigned nchildren;
|
||||
const ctl_node_t *children;
|
||||
int (*ctl)(const size_t *, size_t, void *, size_t *,
|
||||
void *, size_t);
|
||||
int (*ctl)(tsd_t *, const size_t *, size_t, void *,
|
||||
size_t *, void *, size_t);
|
||||
};
|
||||
|
||||
struct ctl_indexed_node_s {
|
||||
struct ctl_node_s node;
|
||||
const ctl_named_node_t *(*index)(const size_t *, size_t, size_t);
|
||||
const ctl_named_node_t *(*index)(tsdn_t *, const size_t *, size_t,
|
||||
size_t);
|
||||
};
|
||||
|
||||
struct ctl_arena_stats_s {
|
||||
@@ -60,6 +61,7 @@ struct ctl_stats_s {
|
||||
size_t metadata;
|
||||
size_t resident;
|
||||
size_t mapped;
|
||||
size_t retained;
|
||||
unsigned narenas;
|
||||
ctl_arena_stats_t *arenas; /* (narenas + 1) elements. */
|
||||
};
|
||||
@@ -68,16 +70,17 @@ struct ctl_stats_s {
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
int ctl_byname(const char *name, void *oldp, size_t *oldlenp, void *newp,
|
||||
size_t newlen);
|
||||
int ctl_nametomib(const char *name, size_t *mibp, size_t *miblenp);
|
||||
|
||||
int ctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
||||
int ctl_byname(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,
|
||||
void *newp, size_t newlen);
|
||||
int ctl_nametomib(tsdn_t *tsdn, const char *name, size_t *mibp,
|
||||
size_t *miblenp);
|
||||
|
||||
int ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
size_t *oldlenp, void *newp, size_t newlen);
|
||||
bool ctl_boot(void);
|
||||
void ctl_prefork(void);
|
||||
void ctl_postfork_parent(void);
|
||||
void ctl_postfork_child(void);
|
||||
void ctl_prefork(tsdn_t *tsdn);
|
||||
void ctl_postfork_parent(tsdn_t *tsdn);
|
||||
void ctl_postfork_child(tsdn_t *tsdn);
|
||||
|
||||
#define xmallctl(name, oldp, oldlenp, newp, newlen) do { \
|
||||
if (je_mallctl(name, oldp, oldlenp, newp, newlen) \
|
||||
|
||||
@@ -48,7 +48,7 @@ struct extent_node_s {
|
||||
/* Linkage for the size/address-ordered tree. */
|
||||
rb_node(extent_node_t) szad_link;
|
||||
|
||||
/* Linkage for arena's huge and node_cache lists. */
|
||||
/* Linkage for arena's achunks, huge, and node_cache lists. */
|
||||
ql_elm(extent_node_t) ql_link;
|
||||
};
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ hash_get_block_32(const uint32_t *p, int i)
|
||||
if (unlikely((uintptr_t)p & (sizeof(uint32_t)-1)) != 0) {
|
||||
uint32_t ret;
|
||||
|
||||
memcpy(&ret, &p[i], sizeof(uint32_t));
|
||||
memcpy(&ret, (uint8_t *)(p + i), sizeof(uint32_t));
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ hash_get_block_64(const uint64_t *p, int i)
|
||||
if (unlikely((uintptr_t)p & (sizeof(uint64_t)-1)) != 0) {
|
||||
uint64_t ret;
|
||||
|
||||
memcpy(&ret, &p[i], sizeof(uint64_t));
|
||||
memcpy(&ret, (uint8_t *)(p + i), sizeof(uint64_t));
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,10 @@
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
void *huge_malloc(tsd_t *tsd, arena_t *arena, size_t usize, bool zero,
|
||||
tcache_t *tcache);
|
||||
void *huge_palloc(tsd_t *tsd, arena_t *arena, size_t usize, size_t alignment,
|
||||
bool zero, tcache_t *tcache);
|
||||
bool huge_ralloc_no_move(tsd_t *tsd, void *ptr, size_t oldsize,
|
||||
void *huge_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero);
|
||||
void *huge_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
size_t alignment, bool zero);
|
||||
bool huge_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize,
|
||||
size_t usize_min, size_t usize_max, bool zero);
|
||||
void *huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize,
|
||||
size_t usize, size_t alignment, bool zero, tcache_t *tcache);
|
||||
@@ -21,12 +20,12 @@ void *huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize,
|
||||
typedef void (huge_dalloc_junk_t)(void *, size_t);
|
||||
extern huge_dalloc_junk_t *huge_dalloc_junk;
|
||||
#endif
|
||||
void huge_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache);
|
||||
void huge_dalloc(tsdn_t *tsdn, void *ptr);
|
||||
arena_t *huge_aalloc(const void *ptr);
|
||||
size_t huge_salloc(const void *ptr);
|
||||
prof_tctx_t *huge_prof_tctx_get(const void *ptr);
|
||||
void huge_prof_tctx_set(const void *ptr, prof_tctx_t *tctx);
|
||||
void huge_prof_tctx_reset(const void *ptr);
|
||||
size_t huge_salloc(tsdn_t *tsdn, const void *ptr);
|
||||
prof_tctx_t *huge_prof_tctx_get(tsdn_t *tsdn, const void *ptr);
|
||||
void huge_prof_tctx_set(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx);
|
||||
void huge_prof_tctx_reset(tsdn_t *tsdn, const void *ptr);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -161,7 +161,10 @@ static const bool config_cache_oblivious =
|
||||
#include <malloc/malloc.h>
|
||||
#endif
|
||||
|
||||
#include "jemalloc/internal/ph.h"
|
||||
#ifndef __PGI
|
||||
#define RB_COMPACT
|
||||
#endif
|
||||
#include "jemalloc/internal/rb.h"
|
||||
#include "jemalloc/internal/qr.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
@@ -184,6 +187,9 @@ static const bool config_cache_oblivious =
|
||||
|
||||
#include "jemalloc/internal/jemalloc_internal_macros.h"
|
||||
|
||||
/* Page size index type. */
|
||||
typedef unsigned pszind_t;
|
||||
|
||||
/* Size class index type. */
|
||||
typedef unsigned szind_t;
|
||||
|
||||
@@ -233,7 +239,7 @@ typedef unsigned szind_t;
|
||||
# ifdef __alpha__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined(__sparc64__) || defined(__sparcv9))
|
||||
# if (defined(__sparc64__) || defined(__sparcv9) || defined(__sparc_v9__))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined(__amd64__) || defined(__x86_64__) || defined(_M_X64))
|
||||
@@ -257,6 +263,9 @@ typedef unsigned szind_t;
|
||||
# ifdef __powerpc__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __riscv__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __s390__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
@@ -360,6 +369,7 @@ typedef unsigned szind_t;
|
||||
#include "jemalloc/internal/valgrind.h"
|
||||
#include "jemalloc/internal/util.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/spin.h"
|
||||
#include "jemalloc/internal/prng.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
#include "jemalloc/internal/ckh.h"
|
||||
@@ -367,6 +377,7 @@ typedef unsigned szind_t;
|
||||
#include "jemalloc/internal/smoothstep.h"
|
||||
#include "jemalloc/internal/stats.h"
|
||||
#include "jemalloc/internal/ctl.h"
|
||||
#include "jemalloc/internal/witness.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/tsd.h"
|
||||
#include "jemalloc/internal/mb.h"
|
||||
@@ -391,6 +402,7 @@ typedef unsigned szind_t;
|
||||
#include "jemalloc/internal/valgrind.h"
|
||||
#include "jemalloc/internal/util.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/spin.h"
|
||||
#include "jemalloc/internal/prng.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
#include "jemalloc/internal/ckh.h"
|
||||
@@ -398,6 +410,7 @@ typedef unsigned szind_t;
|
||||
#include "jemalloc/internal/smoothstep.h"
|
||||
#include "jemalloc/internal/stats.h"
|
||||
#include "jemalloc/internal/ctl.h"
|
||||
#include "jemalloc/internal/witness.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/mb.h"
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
@@ -440,17 +453,25 @@ extern bool in_valgrind;
|
||||
/* Number of CPUs. */
|
||||
extern unsigned ncpus;
|
||||
|
||||
/* Number of arenas used for automatic multiplexing of threads and arenas. */
|
||||
extern unsigned narenas_auto;
|
||||
|
||||
/*
|
||||
* Arenas that are used to service external requests. Not all elements of the
|
||||
* arenas array are necessarily used; arenas are created lazily as needed.
|
||||
*/
|
||||
extern arena_t **arenas;
|
||||
|
||||
/*
|
||||
* pind2sz_tab encodes the same information as could be computed by
|
||||
* pind2sz_compute().
|
||||
*/
|
||||
extern size_t const pind2sz_tab[NPSIZES];
|
||||
/*
|
||||
* index2size_tab encodes the same information as could be computed (at
|
||||
* unacceptable cost in some code paths) by index2size_compute().
|
||||
*/
|
||||
extern size_t const index2size_tab[NSIZES+1];
|
||||
extern size_t const index2size_tab[NSIZES];
|
||||
/*
|
||||
* size2index_tab is a compact lookup table that rounds request sizes up to
|
||||
* size classes. In order to reduce cache footprint, the table is compressed,
|
||||
@@ -458,19 +479,20 @@ extern size_t const index2size_tab[NSIZES+1];
|
||||
*/
|
||||
extern uint8_t const size2index_tab[];
|
||||
|
||||
arena_t *a0get(void);
|
||||
void *a0malloc(size_t size);
|
||||
void a0dalloc(void *ptr);
|
||||
void *bootstrap_malloc(size_t size);
|
||||
void *bootstrap_calloc(size_t num, size_t size);
|
||||
void bootstrap_free(void *ptr);
|
||||
arena_t *arenas_extend(unsigned ind);
|
||||
unsigned narenas_total_get(void);
|
||||
arena_t *arena_init(unsigned ind);
|
||||
arena_t *arena_init(tsdn_t *tsdn, unsigned ind);
|
||||
arena_tdata_t *arena_tdata_get_hard(tsd_t *tsd, unsigned ind);
|
||||
arena_t *arena_choose_hard(tsd_t *tsd);
|
||||
arena_t *arena_choose_hard(tsd_t *tsd, bool internal);
|
||||
void arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind);
|
||||
void thread_allocated_cleanup(tsd_t *tsd);
|
||||
void thread_deallocated_cleanup(tsd_t *tsd);
|
||||
void iarena_cleanup(tsd_t *tsd);
|
||||
void arena_cleanup(tsd_t *tsd);
|
||||
void arenas_tdata_cleanup(tsd_t *tsd);
|
||||
void narenas_tdata_cleanup(tsd_t *tsd);
|
||||
@@ -483,6 +505,7 @@ void jemalloc_postfork_child(void);
|
||||
#include "jemalloc/internal/valgrind.h"
|
||||
#include "jemalloc/internal/util.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/spin.h"
|
||||
#include "jemalloc/internal/prng.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
#include "jemalloc/internal/ckh.h"
|
||||
@@ -490,6 +513,7 @@ void jemalloc_postfork_child(void);
|
||||
#include "jemalloc/internal/smoothstep.h"
|
||||
#include "jemalloc/internal/stats.h"
|
||||
#include "jemalloc/internal/ctl.h"
|
||||
#include "jemalloc/internal/witness.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/mb.h"
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
@@ -514,6 +538,7 @@ void jemalloc_postfork_child(void);
|
||||
#include "jemalloc/internal/valgrind.h"
|
||||
#include "jemalloc/internal/util.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/spin.h"
|
||||
#include "jemalloc/internal/prng.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
#include "jemalloc/internal/ckh.h"
|
||||
@@ -521,8 +546,9 @@ void jemalloc_postfork_child(void);
|
||||
#include "jemalloc/internal/smoothstep.h"
|
||||
#include "jemalloc/internal/stats.h"
|
||||
#include "jemalloc/internal/ctl.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/tsd.h"
|
||||
#include "jemalloc/internal/witness.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/mb.h"
|
||||
#include "jemalloc/internal/extent.h"
|
||||
#include "jemalloc/internal/base.h"
|
||||
@@ -532,6 +558,11 @@ void jemalloc_postfork_child(void);
|
||||
#include "jemalloc/internal/huge.h"
|
||||
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
pszind_t psz2ind(size_t psz);
|
||||
size_t pind2sz_compute(pszind_t pind);
|
||||
size_t pind2sz_lookup(pszind_t pind);
|
||||
size_t pind2sz(pszind_t pind);
|
||||
size_t psz2u(size_t psz);
|
||||
szind_t size2index_compute(size_t size);
|
||||
szind_t size2index_lookup(size_t size);
|
||||
szind_t size2index(size_t size);
|
||||
@@ -542,18 +573,100 @@ size_t s2u_compute(size_t size);
|
||||
size_t s2u_lookup(size_t size);
|
||||
size_t s2u(size_t size);
|
||||
size_t sa2u(size_t size, size_t alignment);
|
||||
arena_t *arena_choose_impl(tsd_t *tsd, arena_t *arena, bool internal);
|
||||
arena_t *arena_choose(tsd_t *tsd, arena_t *arena);
|
||||
arena_t *arena_ichoose(tsd_t *tsd, arena_t *arena);
|
||||
arena_tdata_t *arena_tdata_get(tsd_t *tsd, unsigned ind,
|
||||
bool refresh_if_missing);
|
||||
arena_t *arena_get(unsigned ind, bool init_if_missing);
|
||||
arena_t *arena_get(tsdn_t *tsdn, unsigned ind, bool init_if_missing);
|
||||
ticker_t *decay_ticker_get(tsd_t *tsd, unsigned ind);
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))
|
||||
JEMALLOC_INLINE pszind_t
|
||||
psz2ind(size_t psz)
|
||||
{
|
||||
|
||||
if (unlikely(psz > HUGE_MAXCLASS))
|
||||
return (NPSIZES);
|
||||
{
|
||||
pszind_t x = lg_floor((psz<<1)-1);
|
||||
pszind_t shift = (x < LG_SIZE_CLASS_GROUP + LG_PAGE) ? 0 : x -
|
||||
(LG_SIZE_CLASS_GROUP + LG_PAGE);
|
||||
pszind_t grp = shift << LG_SIZE_CLASS_GROUP;
|
||||
|
||||
pszind_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_PAGE + 1) ?
|
||||
LG_PAGE : x - LG_SIZE_CLASS_GROUP - 1;
|
||||
|
||||
size_t delta_inverse_mask = ZI(-1) << lg_delta;
|
||||
pszind_t mod = ((((psz-1) & delta_inverse_mask) >> lg_delta)) &
|
||||
((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);
|
||||
|
||||
pszind_t ind = grp + mod;
|
||||
return (ind);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
pind2sz_compute(pszind_t pind)
|
||||
{
|
||||
|
||||
{
|
||||
size_t grp = pind >> LG_SIZE_CLASS_GROUP;
|
||||
size_t mod = pind & ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);
|
||||
|
||||
size_t grp_size_mask = ~((!!grp)-1);
|
||||
size_t grp_size = ((ZU(1) << (LG_PAGE +
|
||||
(LG_SIZE_CLASS_GROUP-1))) << grp) & grp_size_mask;
|
||||
|
||||
size_t shift = (grp == 0) ? 1 : grp;
|
||||
size_t lg_delta = shift + (LG_PAGE-1);
|
||||
size_t mod_size = (mod+1) << lg_delta;
|
||||
|
||||
size_t sz = grp_size + mod_size;
|
||||
return (sz);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
pind2sz_lookup(pszind_t pind)
|
||||
{
|
||||
size_t ret = (size_t)pind2sz_tab[pind];
|
||||
assert(ret == pind2sz_compute(pind));
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
pind2sz(pszind_t pind)
|
||||
{
|
||||
|
||||
assert(pind < NPSIZES);
|
||||
return (pind2sz_lookup(pind));
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
psz2u(size_t psz)
|
||||
{
|
||||
|
||||
if (unlikely(psz > HUGE_MAXCLASS))
|
||||
return (0);
|
||||
{
|
||||
size_t x = lg_floor((psz<<1)-1);
|
||||
size_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_PAGE + 1) ?
|
||||
LG_PAGE : x - LG_SIZE_CLASS_GROUP - 1;
|
||||
size_t delta = ZU(1) << lg_delta;
|
||||
size_t delta_mask = delta - 1;
|
||||
size_t usize = (psz + delta_mask) & ~delta_mask;
|
||||
return (usize);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE szind_t
|
||||
size2index_compute(size_t size)
|
||||
{
|
||||
|
||||
if (unlikely(size > HUGE_MAXCLASS))
|
||||
return (NSIZES);
|
||||
#if (NTBINS != 0)
|
||||
if (size <= (ZU(1) << LG_TINY_MAXCLASS)) {
|
||||
szind_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;
|
||||
@@ -562,9 +675,7 @@ size2index_compute(size_t size)
|
||||
}
|
||||
#endif
|
||||
{
|
||||
szind_t x = unlikely(ZI(size) < 0) ? ((size<<1) ?
|
||||
(ZU(1)<<(LG_SIZEOF_PTR+3)) : ((ZU(1)<<(LG_SIZEOF_PTR+3))-1))
|
||||
: lg_floor((size<<1)-1);
|
||||
szind_t x = lg_floor((size<<1)-1);
|
||||
szind_t shift = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM) ? 0 :
|
||||
x - (LG_SIZE_CLASS_GROUP + LG_QUANTUM);
|
||||
szind_t grp = shift << LG_SIZE_CLASS_GROUP;
|
||||
@@ -650,6 +761,8 @@ JEMALLOC_ALWAYS_INLINE size_t
|
||||
s2u_compute(size_t size)
|
||||
{
|
||||
|
||||
if (unlikely(size > HUGE_MAXCLASS))
|
||||
return (0);
|
||||
#if (NTBINS > 0)
|
||||
if (size <= (ZU(1) << LG_TINY_MAXCLASS)) {
|
||||
size_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;
|
||||
@@ -659,9 +772,7 @@ s2u_compute(size_t size)
|
||||
}
|
||||
#endif
|
||||
{
|
||||
size_t x = unlikely(ZI(size) < 0) ? ((size<<1) ?
|
||||
(ZU(1)<<(LG_SIZEOF_PTR+3)) : ((ZU(1)<<(LG_SIZEOF_PTR+3))-1))
|
||||
: lg_floor((size<<1)-1);
|
||||
size_t x = lg_floor((size<<1)-1);
|
||||
size_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)
|
||||
? LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;
|
||||
size_t delta = ZU(1) << lg_delta;
|
||||
@@ -780,19 +891,34 @@ sa2u(size_t size, size_t alignment)
|
||||
|
||||
/* Choose an arena based on a per-thread value. */
|
||||
JEMALLOC_INLINE arena_t *
|
||||
arena_choose(tsd_t *tsd, arena_t *arena)
|
||||
arena_choose_impl(tsd_t *tsd, arena_t *arena, bool internal)
|
||||
{
|
||||
arena_t *ret;
|
||||
|
||||
if (arena != NULL)
|
||||
return (arena);
|
||||
|
||||
if (unlikely((ret = tsd_arena_get(tsd)) == NULL))
|
||||
ret = arena_choose_hard(tsd);
|
||||
ret = internal ? tsd_iarena_get(tsd) : tsd_arena_get(tsd);
|
||||
if (unlikely(ret == NULL))
|
||||
ret = arena_choose_hard(tsd, internal);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE arena_t *
|
||||
arena_choose(tsd_t *tsd, arena_t *arena)
|
||||
{
|
||||
|
||||
return (arena_choose_impl(tsd, arena, false));
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE arena_t *
|
||||
arena_ichoose(tsd_t *tsd, arena_t *arena)
|
||||
{
|
||||
|
||||
return (arena_choose_impl(tsd, arena, true));
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE arena_tdata_t *
|
||||
arena_tdata_get(tsd_t *tsd, unsigned ind, bool refresh_if_missing)
|
||||
{
|
||||
@@ -819,7 +945,7 @@ arena_tdata_get(tsd_t *tsd, unsigned ind, bool refresh_if_missing)
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE arena_t *
|
||||
arena_get(unsigned ind, bool init_if_missing)
|
||||
arena_get(tsdn_t *tsdn, unsigned ind, bool init_if_missing)
|
||||
{
|
||||
arena_t *ret;
|
||||
|
||||
@@ -829,7 +955,7 @@ arena_get(unsigned ind, bool init_if_missing)
|
||||
if (unlikely(ret == NULL)) {
|
||||
ret = atomic_read_p((void *)&arenas[ind]);
|
||||
if (init_if_missing && unlikely(ret == NULL))
|
||||
ret = arena_init(ind);
|
||||
ret = arena_init(tsdn, ind);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
@@ -863,30 +989,27 @@ decay_ticker_get(tsd_t *tsd, unsigned ind)
|
||||
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
arena_t *iaalloc(const void *ptr);
|
||||
size_t isalloc(const void *ptr, bool demote);
|
||||
void *iallocztm(tsd_t *tsd, size_t size, szind_t ind, bool zero,
|
||||
size_t isalloc(tsdn_t *tsdn, const void *ptr, bool demote);
|
||||
void *iallocztm(tsdn_t *tsdn, size_t size, szind_t ind, bool zero,
|
||||
tcache_t *tcache, bool is_metadata, arena_t *arena, bool slow_path);
|
||||
void *imalloct(tsd_t *tsd, size_t size, szind_t ind, tcache_t *tcache,
|
||||
arena_t *arena);
|
||||
void *imalloc(tsd_t *tsd, size_t size, szind_t ind, bool slow_path);
|
||||
void *icalloct(tsd_t *tsd, size_t size, szind_t ind, tcache_t *tcache,
|
||||
arena_t *arena);
|
||||
void *icalloc(tsd_t *tsd, size_t size, szind_t ind);
|
||||
void *ipallocztm(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
|
||||
void *ialloc(tsd_t *tsd, size_t size, szind_t ind, bool zero,
|
||||
bool slow_path);
|
||||
void *ipallocztm(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
|
||||
tcache_t *tcache, bool is_metadata, arena_t *arena);
|
||||
void *ipalloct(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
|
||||
void *ipalloct(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
|
||||
tcache_t *tcache, arena_t *arena);
|
||||
void *ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero);
|
||||
size_t ivsalloc(const void *ptr, bool demote);
|
||||
size_t ivsalloc(tsdn_t *tsdn, const void *ptr, bool demote);
|
||||
size_t u2rz(size_t usize);
|
||||
size_t p2rz(const void *ptr);
|
||||
void idalloctm(tsd_t *tsd, void *ptr, tcache_t *tcache, bool is_metadata,
|
||||
size_t p2rz(tsdn_t *tsdn, const void *ptr);
|
||||
void idalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache, bool is_metadata,
|
||||
bool slow_path);
|
||||
void idalloct(tsd_t *tsd, void *ptr, tcache_t *tcache);
|
||||
void idalloc(tsd_t *tsd, void *ptr);
|
||||
void iqalloc(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path);
|
||||
void isdalloct(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache);
|
||||
void isqalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache);
|
||||
void isdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
bool slow_path);
|
||||
void isqalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache,
|
||||
bool slow_path);
|
||||
void *iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, size_t alignment, bool zero, tcache_t *tcache,
|
||||
arena_t *arena);
|
||||
@@ -894,7 +1017,7 @@ void *iralloct(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
|
||||
size_t alignment, bool zero, tcache_t *tcache, arena_t *arena);
|
||||
void *iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
|
||||
size_t alignment, bool zero);
|
||||
bool ixalloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
|
||||
bool ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, size_t alignment, bool zero);
|
||||
#endif
|
||||
|
||||
@@ -910,102 +1033,85 @@ iaalloc(const void *ptr)
|
||||
|
||||
/*
|
||||
* Typical usage:
|
||||
* tsdn_t *tsdn = [...]
|
||||
* void *ptr = [...]
|
||||
* size_t sz = isalloc(ptr, config_prof);
|
||||
* size_t sz = isalloc(tsdn, ptr, config_prof);
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
isalloc(const void *ptr, bool demote)
|
||||
isalloc(tsdn_t *tsdn, const void *ptr, bool demote)
|
||||
{
|
||||
|
||||
assert(ptr != NULL);
|
||||
/* Demotion only makes sense if config_prof is true. */
|
||||
assert(config_prof || !demote);
|
||||
|
||||
return (arena_salloc(ptr, demote));
|
||||
return (arena_salloc(tsdn, ptr, demote));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iallocztm(tsd_t *tsd, size_t size, szind_t ind, bool zero, tcache_t *tcache,
|
||||
iallocztm(tsdn_t *tsdn, size_t size, szind_t ind, bool zero, tcache_t *tcache,
|
||||
bool is_metadata, arena_t *arena, bool slow_path)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
assert(size != 0);
|
||||
assert(!is_metadata || tcache == NULL);
|
||||
assert(!is_metadata || arena == NULL || arena->ind < narenas_auto);
|
||||
|
||||
ret = arena_malloc(tsd, arena, size, ind, zero, tcache, slow_path);
|
||||
ret = arena_malloc(tsdn, arena, size, ind, zero, tcache, slow_path);
|
||||
if (config_stats && is_metadata && likely(ret != NULL)) {
|
||||
arena_metadata_allocated_add(iaalloc(ret), isalloc(ret,
|
||||
config_prof));
|
||||
arena_metadata_allocated_add(iaalloc(ret),
|
||||
isalloc(tsdn, ret, config_prof));
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
imalloct(tsd_t *tsd, size_t size, szind_t ind, tcache_t *tcache, arena_t *arena)
|
||||
ialloc(tsd_t *tsd, size_t size, szind_t ind, bool zero, bool slow_path)
|
||||
{
|
||||
|
||||
return (iallocztm(tsd, size, ind, false, tcache, false, arena, true));
|
||||
return (iallocztm(tsd_tsdn(tsd), size, ind, zero, tcache_get(tsd, true),
|
||||
false, NULL, slow_path));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
imalloc(tsd_t *tsd, size_t size, szind_t ind, bool slow_path)
|
||||
{
|
||||
|
||||
return (iallocztm(tsd, size, ind, false, tcache_get(tsd, true), false,
|
||||
NULL, slow_path));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
icalloct(tsd_t *tsd, size_t size, szind_t ind, tcache_t *tcache, arena_t *arena)
|
||||
{
|
||||
|
||||
return (iallocztm(tsd, size, ind, true, tcache, false, arena, true));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
icalloc(tsd_t *tsd, size_t size, szind_t ind)
|
||||
{
|
||||
|
||||
return (iallocztm(tsd, size, ind, true, tcache_get(tsd, true), false,
|
||||
NULL, true));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipallocztm(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
|
||||
ipallocztm(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
|
||||
tcache_t *tcache, bool is_metadata, arena_t *arena)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
assert(usize != 0);
|
||||
assert(usize == sa2u(usize, alignment));
|
||||
assert(!is_metadata || tcache == NULL);
|
||||
assert(!is_metadata || arena == NULL || arena->ind < narenas_auto);
|
||||
|
||||
ret = arena_palloc(tsd, arena, usize, alignment, zero, tcache);
|
||||
ret = arena_palloc(tsdn, arena, usize, alignment, zero, tcache);
|
||||
assert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret);
|
||||
if (config_stats && is_metadata && likely(ret != NULL)) {
|
||||
arena_metadata_allocated_add(iaalloc(ret), isalloc(ret,
|
||||
arena_metadata_allocated_add(iaalloc(ret), isalloc(tsdn, ret,
|
||||
config_prof));
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipalloct(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
|
||||
ipalloct(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
|
||||
tcache_t *tcache, arena_t *arena)
|
||||
{
|
||||
|
||||
return (ipallocztm(tsd, usize, alignment, zero, tcache, false, arena));
|
||||
return (ipallocztm(tsdn, usize, alignment, zero, tcache, false, arena));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero)
|
||||
{
|
||||
|
||||
return (ipallocztm(tsd, usize, alignment, zero, tcache_get(tsd, true),
|
||||
false, NULL));
|
||||
return (ipallocztm(tsd_tsdn(tsd), usize, alignment, zero,
|
||||
tcache_get(tsd, true), false, NULL));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
ivsalloc(const void *ptr, bool demote)
|
||||
ivsalloc(tsdn_t *tsdn, const void *ptr, bool demote)
|
||||
{
|
||||
extent_node_t *node;
|
||||
|
||||
@@ -1017,7 +1123,7 @@ ivsalloc(const void *ptr, bool demote)
|
||||
assert(extent_node_addr_get(node) == ptr ||
|
||||
extent_node_achunk_get(node));
|
||||
|
||||
return (isalloc(ptr, demote));
|
||||
return (isalloc(tsdn, ptr, demote));
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
@@ -1035,39 +1141,34 @@ u2rz(size_t usize)
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
p2rz(const void *ptr)
|
||||
p2rz(tsdn_t *tsdn, const void *ptr)
|
||||
{
|
||||
size_t usize = isalloc(ptr, false);
|
||||
size_t usize = isalloc(tsdn, ptr, false);
|
||||
|
||||
return (u2rz(usize));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
idalloctm(tsd_t *tsd, void *ptr, tcache_t *tcache, bool is_metadata,
|
||||
idalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache, bool is_metadata,
|
||||
bool slow_path)
|
||||
{
|
||||
|
||||
assert(ptr != NULL);
|
||||
assert(!is_metadata || tcache == NULL);
|
||||
assert(!is_metadata || iaalloc(ptr)->ind < narenas_auto);
|
||||
if (config_stats && is_metadata) {
|
||||
arena_metadata_allocated_sub(iaalloc(ptr), isalloc(ptr,
|
||||
arena_metadata_allocated_sub(iaalloc(ptr), isalloc(tsdn, ptr,
|
||||
config_prof));
|
||||
}
|
||||
|
||||
arena_dalloc(tsd, ptr, tcache, slow_path);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
idalloct(tsd_t *tsd, void *ptr, tcache_t *tcache)
|
||||
{
|
||||
|
||||
idalloctm(tsd, ptr, tcache, false, true);
|
||||
arena_dalloc(tsdn, ptr, tcache, slow_path);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
idalloc(tsd_t *tsd, void *ptr)
|
||||
{
|
||||
|
||||
idalloctm(tsd, ptr, tcache_get(tsd, false), false, true);
|
||||
idalloctm(tsd_tsdn(tsd), ptr, tcache_get(tsd, false), false, true);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
@@ -1077,24 +1178,25 @@ iqalloc(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path)
|
||||
if (slow_path && config_fill && unlikely(opt_quarantine))
|
||||
quarantine(tsd, ptr);
|
||||
else
|
||||
idalloctm(tsd, ptr, tcache, false, slow_path);
|
||||
idalloctm(tsd_tsdn(tsd), ptr, tcache, false, slow_path);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
isdalloct(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache)
|
||||
isdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
bool slow_path)
|
||||
{
|
||||
|
||||
arena_sdalloc(tsd, ptr, size, tcache);
|
||||
arena_sdalloc(tsdn, ptr, size, tcache, slow_path);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
isqalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache)
|
||||
isqalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache, bool slow_path)
|
||||
{
|
||||
|
||||
if (config_fill && unlikely(opt_quarantine))
|
||||
if (slow_path && config_fill && unlikely(opt_quarantine))
|
||||
quarantine(tsd, ptr);
|
||||
else
|
||||
isdalloct(tsd, ptr, size, tcache);
|
||||
isdalloct(tsd_tsdn(tsd), ptr, size, tcache, slow_path);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
@@ -1107,7 +1209,7 @@ iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
|
||||
usize = sa2u(size + extra, alignment);
|
||||
if (unlikely(usize == 0 || usize > HUGE_MAXCLASS))
|
||||
return (NULL);
|
||||
p = ipalloct(tsd, usize, alignment, zero, tcache, arena);
|
||||
p = ipalloct(tsd_tsdn(tsd), usize, alignment, zero, tcache, arena);
|
||||
if (p == NULL) {
|
||||
if (extra == 0)
|
||||
return (NULL);
|
||||
@@ -1115,7 +1217,8 @@ iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
|
||||
usize = sa2u(size, alignment);
|
||||
if (unlikely(usize == 0 || usize > HUGE_MAXCLASS))
|
||||
return (NULL);
|
||||
p = ipalloct(tsd, usize, alignment, zero, tcache, arena);
|
||||
p = ipalloct(tsd_tsdn(tsd), usize, alignment, zero, tcache,
|
||||
arena);
|
||||
if (p == NULL)
|
||||
return (NULL);
|
||||
}
|
||||
@@ -1125,7 +1228,7 @@ iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
|
||||
*/
|
||||
copysize = (size < oldsize) ? size : oldsize;
|
||||
memcpy(p, ptr, copysize);
|
||||
isqalloc(tsd, ptr, oldsize, tcache);
|
||||
isqalloc(tsd, ptr, oldsize, tcache, true);
|
||||
return (p);
|
||||
}
|
||||
|
||||
@@ -1161,7 +1264,7 @@ iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
ixalloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
size_t alignment, bool zero)
|
||||
{
|
||||
|
||||
@@ -1174,7 +1277,7 @@ ixalloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (arena_ralloc_no_move(tsd, ptr, oldsize, size, extra, zero));
|
||||
return (arena_ralloc_no_move(tsdn, ptr, oldsize, size, extra, zero));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -17,8 +17,18 @@
|
||||
# include <sys/uio.h>
|
||||
# endif
|
||||
# include <pthread.h>
|
||||
# ifdef JEMALLOC_OS_UNFAIR_LOCK
|
||||
# include <os/lock.h>
|
||||
# endif
|
||||
# ifdef JEMALLOC_GLIBC_MALLOC_HOOK
|
||||
# include <sched.h>
|
||||
# endif
|
||||
# include <errno.h>
|
||||
# include <sys/time.h>
|
||||
# include <time.h>
|
||||
# ifdef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME
|
||||
# include <mach/mach_time.h>
|
||||
# endif
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
@@ -60,12 +60,20 @@
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_MADVISE
|
||||
|
||||
/*
|
||||
* Defined if os_unfair_lock_*() functions are available, as provided by Darwin.
|
||||
*/
|
||||
#undef JEMALLOC_OS_UNFAIR_LOCK
|
||||
|
||||
/*
|
||||
* Defined if OSSpin*() functions are available, as provided by Darwin, and
|
||||
* documented in the spinlock(3) manual page.
|
||||
*/
|
||||
#undef JEMALLOC_OSSPIN
|
||||
|
||||
/* Defined if syscall(2) is available. */
|
||||
#undef JEMALLOC_HAVE_SYSCALL
|
||||
|
||||
/*
|
||||
* Defined if secure_getenv(3) is available.
|
||||
*/
|
||||
@@ -76,6 +84,21 @@
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_ISSETUGID
|
||||
|
||||
/*
|
||||
* Defined if clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is available.
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE
|
||||
|
||||
/*
|
||||
* Defined if clock_gettime(CLOCK_MONOTONIC, ...) is available.
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_CLOCK_MONOTONIC
|
||||
|
||||
/*
|
||||
* Defined if mach_absolute_time() is available.
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME
|
||||
|
||||
/*
|
||||
* Defined if _malloc_thread_cleanup() exists. At least in the case of
|
||||
* FreeBSD, pthread_key_create() allocates, which if used during malloc
|
||||
@@ -188,6 +211,12 @@
|
||||
/* TLS is used to map arenas and magazine caches to threads. */
|
||||
#undef JEMALLOC_TLS
|
||||
|
||||
/*
|
||||
* Used to mark unreachable code to quiet "end of non-void" compiler warnings.
|
||||
* Don't use this directly; instead use unreachable() from util.h
|
||||
*/
|
||||
#undef JEMALLOC_INTERNAL_UNREACHABLE
|
||||
|
||||
/*
|
||||
* ffs*() functions to use for bitmapping. Don't use these directly; instead,
|
||||
* use ffs_*() from util.h.
|
||||
@@ -214,6 +243,15 @@
|
||||
#undef JEMALLOC_ZONE
|
||||
#undef JEMALLOC_ZONE_VERSION
|
||||
|
||||
/*
|
||||
* Methods for determining whether the OS overcommits.
|
||||
* JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY: Linux's
|
||||
* /proc/sys/vm.overcommit_memory file.
|
||||
* JEMALLOC_SYSCTL_VM_OVERCOMMIT: FreeBSD's vm.overcommit sysctl.
|
||||
*/
|
||||
#undef JEMALLOC_SYSCTL_VM_OVERCOMMIT
|
||||
#undef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY
|
||||
|
||||
/*
|
||||
* Methods for purging unused pages differ between operating systems.
|
||||
*
|
||||
|
||||
@@ -42,7 +42,7 @@ mb_write(void)
|
||||
: /* Inputs. */
|
||||
: "memory" /* Clobbers. */
|
||||
);
|
||||
#else
|
||||
# else
|
||||
/*
|
||||
* This is hopefully enough to keep the compiler from reordering
|
||||
* instructions around this one.
|
||||
@@ -52,7 +52,7 @@ mb_write(void)
|
||||
: /* Inputs. */
|
||||
: "memory" /* Clobbers. */
|
||||
);
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
#elif (defined(__amd64__) || defined(__x86_64__))
|
||||
JEMALLOC_INLINE void
|
||||
@@ -104,9 +104,9 @@ mb_write(void)
|
||||
{
|
||||
malloc_mutex_t mtx;
|
||||
|
||||
malloc_mutex_init(&mtx);
|
||||
malloc_mutex_lock(&mtx);
|
||||
malloc_mutex_unlock(&mtx);
|
||||
malloc_mutex_init(&mtx, "mb", WITNESS_RANK_OMIT);
|
||||
malloc_mutex_lock(TSDN_NULL, &mtx);
|
||||
malloc_mutex_unlock(TSDN_NULL, &mtx);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -5,18 +5,25 @@ typedef struct malloc_mutex_s malloc_mutex_t;
|
||||
|
||||
#ifdef _WIN32
|
||||
# define MALLOC_MUTEX_INITIALIZER
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{OS_UNFAIR_LOCK_INIT, WITNESS_INITIALIZER(WITNESS_RANK_OMIT)}
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
# define MALLOC_MUTEX_INITIALIZER {0}
|
||||
# define MALLOC_MUTEX_INITIALIZER {0, WITNESS_INITIALIZER(WITNESS_RANK_OMIT)}
|
||||
#elif (defined(JEMALLOC_MUTEX_INIT_CB))
|
||||
# define MALLOC_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER, NULL}
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{PTHREAD_MUTEX_INITIALIZER, NULL, WITNESS_INITIALIZER(WITNESS_RANK_OMIT)}
|
||||
#else
|
||||
# if (defined(JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP) && \
|
||||
defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP))
|
||||
# define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_ADAPTIVE_NP
|
||||
# define MALLOC_MUTEX_INITIALIZER {PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP}
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP, \
|
||||
WITNESS_INITIALIZER(WITNESS_RANK_OMIT)}
|
||||
# else
|
||||
# define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT
|
||||
# define MALLOC_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER}
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{PTHREAD_MUTEX_INITIALIZER, WITNESS_INITIALIZER(WITNESS_RANK_OMIT)}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -31,6 +38,8 @@ struct malloc_mutex_s {
|
||||
# else
|
||||
CRITICAL_SECTION lock;
|
||||
# endif
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
os_unfair_lock lock;
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
OSSpinLock lock;
|
||||
#elif (defined(JEMALLOC_MUTEX_INIT_CB))
|
||||
@@ -39,6 +48,7 @@ struct malloc_mutex_s {
|
||||
#else
|
||||
pthread_mutex_t lock;
|
||||
#endif
|
||||
witness_t witness;
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_H_STRUCTS */
|
||||
@@ -52,52 +62,62 @@ extern bool isthreaded;
|
||||
# define isthreaded true
|
||||
#endif
|
||||
|
||||
bool malloc_mutex_init(malloc_mutex_t *mutex);
|
||||
void malloc_mutex_prefork(malloc_mutex_t *mutex);
|
||||
void malloc_mutex_postfork_parent(malloc_mutex_t *mutex);
|
||||
void malloc_mutex_postfork_child(malloc_mutex_t *mutex);
|
||||
bool mutex_boot(void);
|
||||
bool malloc_mutex_init(malloc_mutex_t *mutex, const char *name,
|
||||
witness_rank_t rank);
|
||||
void malloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex);
|
||||
void malloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex);
|
||||
void malloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex);
|
||||
bool malloc_mutex_boot(void);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_INLINES
|
||||
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
void malloc_mutex_lock(malloc_mutex_t *mutex);
|
||||
void malloc_mutex_unlock(malloc_mutex_t *mutex);
|
||||
void malloc_mutex_lock(tsdn_t *tsdn, malloc_mutex_t *mutex);
|
||||
void malloc_mutex_unlock(tsdn_t *tsdn, malloc_mutex_t *mutex);
|
||||
void malloc_mutex_assert_owner(tsdn_t *tsdn, malloc_mutex_t *mutex);
|
||||
void malloc_mutex_assert_not_owner(tsdn_t *tsdn, malloc_mutex_t *mutex);
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_MUTEX_C_))
|
||||
JEMALLOC_INLINE void
|
||||
malloc_mutex_lock(malloc_mutex_t *mutex)
|
||||
malloc_mutex_lock(tsdn_t *tsdn, malloc_mutex_t *mutex)
|
||||
{
|
||||
|
||||
if (isthreaded) {
|
||||
witness_assert_not_owner(tsdn, &mutex->witness);
|
||||
#ifdef _WIN32
|
||||
# if _WIN32_WINNT >= 0x0600
|
||||
AcquireSRWLockExclusive(&mutex->lock);
|
||||
# else
|
||||
EnterCriticalSection(&mutex->lock);
|
||||
# endif
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
os_unfair_lock_lock(&mutex->lock);
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
OSSpinLockLock(&mutex->lock);
|
||||
#else
|
||||
pthread_mutex_lock(&mutex->lock);
|
||||
#endif
|
||||
witness_lock(tsdn, &mutex->witness);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
malloc_mutex_unlock(malloc_mutex_t *mutex)
|
||||
malloc_mutex_unlock(tsdn_t *tsdn, malloc_mutex_t *mutex)
|
||||
{
|
||||
|
||||
if (isthreaded) {
|
||||
witness_unlock(tsdn, &mutex->witness);
|
||||
#ifdef _WIN32
|
||||
# if _WIN32_WINNT >= 0x0600
|
||||
ReleaseSRWLockExclusive(&mutex->lock);
|
||||
# else
|
||||
LeaveCriticalSection(&mutex->lock);
|
||||
# endif
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
os_unfair_lock_unlock(&mutex->lock);
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
OSSpinLockUnlock(&mutex->lock);
|
||||
#else
|
||||
@@ -105,6 +125,22 @@ malloc_mutex_unlock(malloc_mutex_t *mutex)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
malloc_mutex_assert_owner(tsdn_t *tsdn, malloc_mutex_t *mutex)
|
||||
{
|
||||
|
||||
if (isthreaded)
|
||||
witness_assert_owner(tsdn, &mutex->witness);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
malloc_mutex_assert_not_owner(tsdn_t *tsdn, malloc_mutex_t *mutex)
|
||||
{
|
||||
|
||||
if (isthreaded)
|
||||
witness_assert_not_owner(tsdn, &mutex->witness);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* JEMALLOC_H_INLINES */
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_TYPES
|
||||
|
||||
#define JEMALLOC_CLOCK_GETTIME defined(_POSIX_MONOTONIC_CLOCK) \
|
||||
&& _POSIX_MONOTONIC_CLOCK >= 0
|
||||
|
||||
typedef struct nstime_s nstime_t;
|
||||
|
||||
/* Maximum supported number of seconds (~584 years). */
|
||||
#define NSTIME_SEC_MAX 18446744072
|
||||
#define NSTIME_SEC_MAX KQU(18446744072)
|
||||
|
||||
#endif /* JEMALLOC_H_TYPES */
|
||||
/******************************************************************************/
|
||||
@@ -34,9 +31,12 @@ void nstime_imultiply(nstime_t *time, uint64_t multiplier);
|
||||
void nstime_idivide(nstime_t *time, uint64_t divisor);
|
||||
uint64_t nstime_divide(const nstime_t *time, const nstime_t *divisor);
|
||||
#ifdef JEMALLOC_JET
|
||||
typedef bool (nstime_monotonic_t)(void);
|
||||
extern nstime_monotonic_t *nstime_monotonic;
|
||||
typedef bool (nstime_update_t)(nstime_t *);
|
||||
extern nstime_update_t *nstime_update;
|
||||
#else
|
||||
bool nstime_monotonic(void);
|
||||
bool nstime_update(nstime_t *time);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -9,13 +9,14 @@
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
void *pages_map(void *addr, size_t size);
|
||||
void *pages_map(void *addr, size_t size, bool *commit);
|
||||
void pages_unmap(void *addr, size_t size);
|
||||
void *pages_trim(void *addr, size_t alloc_size, size_t leadsize,
|
||||
size_t size);
|
||||
size_t size, bool *commit);
|
||||
bool pages_commit(void *addr, size_t size);
|
||||
bool pages_decommit(void *addr, size_t size);
|
||||
bool pages_purge(void *addr, size_t size);
|
||||
void pages_boot(void);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
|
||||
345
include/jemalloc/internal/ph.h
Normal file
345
include/jemalloc/internal/ph.h
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* A Pairing Heap implementation.
|
||||
*
|
||||
* "The Pairing Heap: A New Form of Self-Adjusting Heap"
|
||||
* https://www.cs.cmu.edu/~sleator/papers/pairing-heaps.pdf
|
||||
*
|
||||
* With auxiliary twopass list, described in a follow on paper.
|
||||
*
|
||||
* "Pairing Heaps: Experiments and Analysis"
|
||||
* http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.106.2988&rep=rep1&type=pdf
|
||||
*
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef PH_H_
|
||||
#define PH_H_
|
||||
|
||||
/* Node structure. */
|
||||
#define phn(a_type) \
|
||||
struct { \
|
||||
a_type *phn_prev; \
|
||||
a_type *phn_next; \
|
||||
a_type *phn_lchild; \
|
||||
}
|
||||
|
||||
/* Root structure. */
|
||||
#define ph(a_type) \
|
||||
struct { \
|
||||
a_type *ph_root; \
|
||||
}
|
||||
|
||||
/* Internal utility macros. */
|
||||
#define phn_lchild_get(a_type, a_field, a_phn) \
|
||||
(a_phn->a_field.phn_lchild)
|
||||
#define phn_lchild_set(a_type, a_field, a_phn, a_lchild) do { \
|
||||
a_phn->a_field.phn_lchild = a_lchild; \
|
||||
} while (0)
|
||||
|
||||
#define phn_next_get(a_type, a_field, a_phn) \
|
||||
(a_phn->a_field.phn_next)
|
||||
#define phn_prev_set(a_type, a_field, a_phn, a_prev) do { \
|
||||
a_phn->a_field.phn_prev = a_prev; \
|
||||
} while (0)
|
||||
|
||||
#define phn_prev_get(a_type, a_field, a_phn) \
|
||||
(a_phn->a_field.phn_prev)
|
||||
#define phn_next_set(a_type, a_field, a_phn, a_next) do { \
|
||||
a_phn->a_field.phn_next = a_next; \
|
||||
} while (0)
|
||||
|
||||
#define phn_merge_ordered(a_type, a_field, a_phn0, a_phn1, a_cmp) do { \
|
||||
a_type *phn0child; \
|
||||
\
|
||||
assert(a_phn0 != NULL); \
|
||||
assert(a_phn1 != NULL); \
|
||||
assert(a_cmp(a_phn0, a_phn1) <= 0); \
|
||||
\
|
||||
phn_prev_set(a_type, a_field, a_phn1, a_phn0); \
|
||||
phn0child = phn_lchild_get(a_type, a_field, a_phn0); \
|
||||
phn_next_set(a_type, a_field, a_phn1, phn0child); \
|
||||
if (phn0child != NULL) \
|
||||
phn_prev_set(a_type, a_field, phn0child, a_phn1); \
|
||||
phn_lchild_set(a_type, a_field, a_phn0, a_phn1); \
|
||||
} while (0)
|
||||
|
||||
#define phn_merge(a_type, a_field, a_phn0, a_phn1, a_cmp, r_phn) do { \
|
||||
if (a_phn0 == NULL) \
|
||||
r_phn = a_phn1; \
|
||||
else if (a_phn1 == NULL) \
|
||||
r_phn = a_phn0; \
|
||||
else if (a_cmp(a_phn0, a_phn1) < 0) { \
|
||||
phn_merge_ordered(a_type, a_field, a_phn0, a_phn1, \
|
||||
a_cmp); \
|
||||
r_phn = a_phn0; \
|
||||
} else { \
|
||||
phn_merge_ordered(a_type, a_field, a_phn1, a_phn0, \
|
||||
a_cmp); \
|
||||
r_phn = a_phn1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ph_merge_siblings(a_type, a_field, a_phn, a_cmp, r_phn) do { \
|
||||
a_type *head = NULL; \
|
||||
a_type *tail = NULL; \
|
||||
a_type *phn0 = a_phn; \
|
||||
a_type *phn1 = phn_next_get(a_type, a_field, phn0); \
|
||||
\
|
||||
/* \
|
||||
* Multipass merge, wherein the first two elements of a FIFO \
|
||||
* are repeatedly merged, and each result is appended to the \
|
||||
* singly linked FIFO, until the FIFO contains only a single \
|
||||
* element. We start with a sibling list but no reference to \
|
||||
* its tail, so we do a single pass over the sibling list to \
|
||||
* populate the FIFO. \
|
||||
*/ \
|
||||
if (phn1 != NULL) { \
|
||||
a_type *phnrest = phn_next_get(a_type, a_field, phn1); \
|
||||
if (phnrest != NULL) \
|
||||
phn_prev_set(a_type, a_field, phnrest, NULL); \
|
||||
phn_prev_set(a_type, a_field, phn0, NULL); \
|
||||
phn_next_set(a_type, a_field, phn0, NULL); \
|
||||
phn_prev_set(a_type, a_field, phn1, NULL); \
|
||||
phn_next_set(a_type, a_field, phn1, NULL); \
|
||||
phn_merge(a_type, a_field, phn0, phn1, a_cmp, phn0); \
|
||||
head = tail = phn0; \
|
||||
phn0 = phnrest; \
|
||||
while (phn0 != NULL) { \
|
||||
phn1 = phn_next_get(a_type, a_field, phn0); \
|
||||
if (phn1 != NULL) { \
|
||||
phnrest = phn_next_get(a_type, a_field, \
|
||||
phn1); \
|
||||
if (phnrest != NULL) { \
|
||||
phn_prev_set(a_type, a_field, \
|
||||
phnrest, NULL); \
|
||||
} \
|
||||
phn_prev_set(a_type, a_field, phn0, \
|
||||
NULL); \
|
||||
phn_next_set(a_type, a_field, phn0, \
|
||||
NULL); \
|
||||
phn_prev_set(a_type, a_field, phn1, \
|
||||
NULL); \
|
||||
phn_next_set(a_type, a_field, phn1, \
|
||||
NULL); \
|
||||
phn_merge(a_type, a_field, phn0, phn1, \
|
||||
a_cmp, phn0); \
|
||||
phn_next_set(a_type, a_field, tail, \
|
||||
phn0); \
|
||||
tail = phn0; \
|
||||
phn0 = phnrest; \
|
||||
} else { \
|
||||
phn_next_set(a_type, a_field, tail, \
|
||||
phn0); \
|
||||
tail = phn0; \
|
||||
phn0 = NULL; \
|
||||
} \
|
||||
} \
|
||||
phn0 = head; \
|
||||
phn1 = phn_next_get(a_type, a_field, phn0); \
|
||||
if (phn1 != NULL) { \
|
||||
while (true) { \
|
||||
head = phn_next_get(a_type, a_field, \
|
||||
phn1); \
|
||||
assert(phn_prev_get(a_type, a_field, \
|
||||
phn0) == NULL); \
|
||||
phn_next_set(a_type, a_field, phn0, \
|
||||
NULL); \
|
||||
assert(phn_prev_get(a_type, a_field, \
|
||||
phn1) == NULL); \
|
||||
phn_next_set(a_type, a_field, phn1, \
|
||||
NULL); \
|
||||
phn_merge(a_type, a_field, phn0, phn1, \
|
||||
a_cmp, phn0); \
|
||||
if (head == NULL) \
|
||||
break; \
|
||||
phn_next_set(a_type, a_field, tail, \
|
||||
phn0); \
|
||||
tail = phn0; \
|
||||
phn0 = head; \
|
||||
phn1 = phn_next_get(a_type, a_field, \
|
||||
phn0); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
r_phn = phn0; \
|
||||
} while (0)
|
||||
|
||||
#define ph_merge_aux(a_type, a_field, a_ph, a_cmp) do { \
|
||||
a_type *phn = phn_next_get(a_type, a_field, a_ph->ph_root); \
|
||||
if (phn != NULL) { \
|
||||
phn_prev_set(a_type, a_field, a_ph->ph_root, NULL); \
|
||||
phn_next_set(a_type, a_field, a_ph->ph_root, NULL); \
|
||||
phn_prev_set(a_type, a_field, phn, NULL); \
|
||||
ph_merge_siblings(a_type, a_field, phn, a_cmp, phn); \
|
||||
assert(phn_next_get(a_type, a_field, phn) == NULL); \
|
||||
phn_merge(a_type, a_field, a_ph->ph_root, phn, a_cmp, \
|
||||
a_ph->ph_root); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ph_merge_children(a_type, a_field, a_phn, a_cmp, r_phn) do { \
|
||||
a_type *lchild = phn_lchild_get(a_type, a_field, a_phn); \
|
||||
if (lchild == NULL) \
|
||||
r_phn = NULL; \
|
||||
else { \
|
||||
ph_merge_siblings(a_type, a_field, lchild, a_cmp, \
|
||||
r_phn); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* The ph_proto() macro generates function prototypes that correspond to the
|
||||
* functions generated by an equivalently parameterized call to ph_gen().
|
||||
*/
|
||||
#define ph_proto(a_attr, a_prefix, a_ph_type, a_type) \
|
||||
a_attr void a_prefix##new(a_ph_type *ph); \
|
||||
a_attr bool a_prefix##empty(a_ph_type *ph); \
|
||||
a_attr a_type *a_prefix##first(a_ph_type *ph); \
|
||||
a_attr void a_prefix##insert(a_ph_type *ph, a_type *phn); \
|
||||
a_attr a_type *a_prefix##remove_first(a_ph_type *ph); \
|
||||
a_attr void a_prefix##remove(a_ph_type *ph, a_type *phn);
|
||||
|
||||
/*
|
||||
* The ph_gen() macro generates a type-specific pairing heap implementation,
|
||||
* based on the above cpp macros.
|
||||
*/
|
||||
#define ph_gen(a_attr, a_prefix, a_ph_type, a_type, a_field, a_cmp) \
|
||||
a_attr void \
|
||||
a_prefix##new(a_ph_type *ph) \
|
||||
{ \
|
||||
\
|
||||
memset(ph, 0, sizeof(ph(a_type))); \
|
||||
} \
|
||||
a_attr bool \
|
||||
a_prefix##empty(a_ph_type *ph) \
|
||||
{ \
|
||||
\
|
||||
return (ph->ph_root == NULL); \
|
||||
} \
|
||||
a_attr a_type * \
|
||||
a_prefix##first(a_ph_type *ph) \
|
||||
{ \
|
||||
\
|
||||
if (ph->ph_root == NULL) \
|
||||
return (NULL); \
|
||||
ph_merge_aux(a_type, a_field, ph, a_cmp); \
|
||||
return (ph->ph_root); \
|
||||
} \
|
||||
a_attr void \
|
||||
a_prefix##insert(a_ph_type *ph, a_type *phn) \
|
||||
{ \
|
||||
\
|
||||
memset(&phn->a_field, 0, sizeof(phn(a_type))); \
|
||||
\
|
||||
/* \
|
||||
* Treat the root as an aux list during insertion, and lazily \
|
||||
* merge during a_prefix##remove_first(). For elements that \
|
||||
* are inserted, then removed via a_prefix##remove() before the \
|
||||
* aux list is ever processed, this makes insert/remove \
|
||||
* constant-time, whereas eager merging would make insert \
|
||||
* O(log n). \
|
||||
*/ \
|
||||
if (ph->ph_root == NULL) \
|
||||
ph->ph_root = phn; \
|
||||
else { \
|
||||
phn_next_set(a_type, a_field, phn, phn_next_get(a_type, \
|
||||
a_field, ph->ph_root)); \
|
||||
if (phn_next_get(a_type, a_field, ph->ph_root) != \
|
||||
NULL) { \
|
||||
phn_prev_set(a_type, a_field, \
|
||||
phn_next_get(a_type, a_field, ph->ph_root), \
|
||||
phn); \
|
||||
} \
|
||||
phn_prev_set(a_type, a_field, phn, ph->ph_root); \
|
||||
phn_next_set(a_type, a_field, ph->ph_root, phn); \
|
||||
} \
|
||||
} \
|
||||
a_attr a_type * \
|
||||
a_prefix##remove_first(a_ph_type *ph) \
|
||||
{ \
|
||||
a_type *ret; \
|
||||
\
|
||||
if (ph->ph_root == NULL) \
|
||||
return (NULL); \
|
||||
ph_merge_aux(a_type, a_field, ph, a_cmp); \
|
||||
\
|
||||
ret = ph->ph_root; \
|
||||
\
|
||||
ph_merge_children(a_type, a_field, ph->ph_root, a_cmp, \
|
||||
ph->ph_root); \
|
||||
\
|
||||
return (ret); \
|
||||
} \
|
||||
a_attr void \
|
||||
a_prefix##remove(a_ph_type *ph, a_type *phn) \
|
||||
{ \
|
||||
a_type *replace, *parent; \
|
||||
\
|
||||
/* \
|
||||
* We can delete from aux list without merging it, but we need \
|
||||
* to merge if we are dealing with the root node. \
|
||||
*/ \
|
||||
if (ph->ph_root == phn) { \
|
||||
ph_merge_aux(a_type, a_field, ph, a_cmp); \
|
||||
if (ph->ph_root == phn) { \
|
||||
ph_merge_children(a_type, a_field, ph->ph_root, \
|
||||
a_cmp, ph->ph_root); \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/* Get parent (if phn is leftmost child) before mutating. */ \
|
||||
if ((parent = phn_prev_get(a_type, a_field, phn)) != NULL) { \
|
||||
if (phn_lchild_get(a_type, a_field, parent) != phn) \
|
||||
parent = NULL; \
|
||||
} \
|
||||
/* Find a possible replacement node, and link to parent. */ \
|
||||
ph_merge_children(a_type, a_field, phn, a_cmp, replace); \
|
||||
/* Set next/prev for sibling linked list. */ \
|
||||
if (replace != NULL) { \
|
||||
if (parent != NULL) { \
|
||||
phn_prev_set(a_type, a_field, replace, parent); \
|
||||
phn_lchild_set(a_type, a_field, parent, \
|
||||
replace); \
|
||||
} else { \
|
||||
phn_prev_set(a_type, a_field, replace, \
|
||||
phn_prev_get(a_type, a_field, phn)); \
|
||||
if (phn_prev_get(a_type, a_field, phn) != \
|
||||
NULL) { \
|
||||
phn_next_set(a_type, a_field, \
|
||||
phn_prev_get(a_type, a_field, phn), \
|
||||
replace); \
|
||||
} \
|
||||
} \
|
||||
phn_next_set(a_type, a_field, replace, \
|
||||
phn_next_get(a_type, a_field, phn)); \
|
||||
if (phn_next_get(a_type, a_field, phn) != NULL) { \
|
||||
phn_prev_set(a_type, a_field, \
|
||||
phn_next_get(a_type, a_field, phn), \
|
||||
replace); \
|
||||
} \
|
||||
} else { \
|
||||
if (parent != NULL) { \
|
||||
a_type *next = phn_next_get(a_type, a_field, \
|
||||
phn); \
|
||||
phn_lchild_set(a_type, a_field, parent, next); \
|
||||
if (next != NULL) { \
|
||||
phn_prev_set(a_type, a_field, next, \
|
||||
parent); \
|
||||
} \
|
||||
} else { \
|
||||
assert(phn_prev_get(a_type, a_field, phn) != \
|
||||
NULL); \
|
||||
phn_next_set(a_type, a_field, \
|
||||
phn_prev_get(a_type, a_field, phn), \
|
||||
phn_next_get(a_type, a_field, phn)); \
|
||||
} \
|
||||
if (phn_next_get(a_type, a_field, phn) != NULL) { \
|
||||
phn_prev_set(a_type, a_field, \
|
||||
phn_next_get(a_type, a_field, phn), \
|
||||
phn_prev_get(a_type, a_field, phn)); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif /* PH_H_ */
|
||||
@@ -1,14 +1,17 @@
|
||||
a0dalloc
|
||||
a0get
|
||||
a0malloc
|
||||
arena_aalloc
|
||||
arena_alloc_junk_small
|
||||
arena_basic_stats_merge
|
||||
arena_bin_index
|
||||
arena_bin_info
|
||||
arena_bitselm_get
|
||||
arena_bitselm_get_const
|
||||
arena_bitselm_get_mutable
|
||||
arena_boot
|
||||
arena_choose
|
||||
arena_choose_hard
|
||||
arena_choose_impl
|
||||
arena_chunk_alloc_huge
|
||||
arena_chunk_cache_maybe_insert
|
||||
arena_chunk_cache_maybe_remove
|
||||
@@ -34,6 +37,7 @@ arena_decay_time_set
|
||||
arena_dss_prec_get
|
||||
arena_dss_prec_set
|
||||
arena_get
|
||||
arena_ichoose
|
||||
arena_init
|
||||
arena_lg_dirty_mult_default_get
|
||||
arena_lg_dirty_mult_default_set
|
||||
@@ -60,7 +64,8 @@ arena_mapbits_unallocated_set
|
||||
arena_mapbits_unallocated_size_get
|
||||
arena_mapbits_unallocated_size_set
|
||||
arena_mapbits_unzeroed_get
|
||||
arena_mapbitsp_get
|
||||
arena_mapbitsp_get_const
|
||||
arena_mapbitsp_get_mutable
|
||||
arena_mapbitsp_read
|
||||
arena_mapbitsp_write
|
||||
arena_maxrun
|
||||
@@ -69,7 +74,8 @@ arena_metadata_allocated_add
|
||||
arena_metadata_allocated_get
|
||||
arena_metadata_allocated_sub
|
||||
arena_migrate
|
||||
arena_miscelm_get
|
||||
arena_miscelm_get_const
|
||||
arena_miscelm_get_mutable
|
||||
arena_miscelm_to_pageind
|
||||
arena_miscelm_to_rpages
|
||||
arena_new
|
||||
@@ -81,7 +87,10 @@ arena_nthreads_inc
|
||||
arena_palloc
|
||||
arena_postfork_child
|
||||
arena_postfork_parent
|
||||
arena_prefork
|
||||
arena_prefork0
|
||||
arena_prefork1
|
||||
arena_prefork2
|
||||
arena_prefork3
|
||||
arena_prof_accum
|
||||
arena_prof_accum_impl
|
||||
arena_prof_accum_locked
|
||||
@@ -97,6 +106,7 @@ arena_ralloc_junk_large
|
||||
arena_ralloc_no_move
|
||||
arena_rd_to_miscelm
|
||||
arena_redzone_corruption
|
||||
arena_reset
|
||||
arena_run_regind
|
||||
arena_run_to_miscelm
|
||||
arena_salloc
|
||||
@@ -123,6 +133,11 @@ atomic_sub_u
|
||||
atomic_sub_uint32
|
||||
atomic_sub_uint64
|
||||
atomic_sub_z
|
||||
atomic_write_p
|
||||
atomic_write_u
|
||||
atomic_write_uint32
|
||||
atomic_write_uint64
|
||||
atomic_write_z
|
||||
base_alloc
|
||||
base_boot
|
||||
base_postfork_child
|
||||
@@ -148,27 +163,20 @@ chunk_alloc_dss
|
||||
chunk_alloc_mmap
|
||||
chunk_alloc_wrapper
|
||||
chunk_boot
|
||||
chunk_dalloc_arena
|
||||
chunk_dalloc_cache
|
||||
chunk_dalloc_mmap
|
||||
chunk_dalloc_wrapper
|
||||
chunk_deregister
|
||||
chunk_dss_boot
|
||||
chunk_dss_postfork_child
|
||||
chunk_dss_postfork_parent
|
||||
chunk_dss_mergeable
|
||||
chunk_dss_prec_get
|
||||
chunk_dss_prec_set
|
||||
chunk_dss_prefork
|
||||
chunk_hooks_default
|
||||
chunk_hooks_get
|
||||
chunk_hooks_set
|
||||
chunk_in_dss
|
||||
chunk_lookup
|
||||
chunk_npages
|
||||
chunk_postfork_child
|
||||
chunk_postfork_parent
|
||||
chunk_prefork
|
||||
chunk_purge_arena
|
||||
chunk_purge_wrapper
|
||||
chunk_register
|
||||
chunks_rtree
|
||||
@@ -200,6 +208,8 @@ extent_node_addr_get
|
||||
extent_node_addr_set
|
||||
extent_node_arena_get
|
||||
extent_node_arena_set
|
||||
extent_node_committed_get
|
||||
extent_node_committed_set
|
||||
extent_node_dirty_insert
|
||||
extent_node_dirty_linkage_init
|
||||
extent_node_dirty_remove
|
||||
@@ -210,6 +220,8 @@ extent_node_size_get
|
||||
extent_node_size_set
|
||||
extent_node_zeroed_get
|
||||
extent_node_zeroed_set
|
||||
extent_tree_ad_destroy
|
||||
extent_tree_ad_destroy_recurse
|
||||
extent_tree_ad_empty
|
||||
extent_tree_ad_first
|
||||
extent_tree_ad_insert
|
||||
@@ -227,6 +239,8 @@ extent_tree_ad_reverse_iter
|
||||
extent_tree_ad_reverse_iter_recurse
|
||||
extent_tree_ad_reverse_iter_start
|
||||
extent_tree_ad_search
|
||||
extent_tree_szad_destroy
|
||||
extent_tree_szad_destroy_recurse
|
||||
extent_tree_szad_empty
|
||||
extent_tree_szad_first
|
||||
extent_tree_szad_insert
|
||||
@@ -273,14 +287,11 @@ huge_ralloc
|
||||
huge_ralloc_no_move
|
||||
huge_salloc
|
||||
iaalloc
|
||||
ialloc
|
||||
iallocztm
|
||||
icalloc
|
||||
icalloct
|
||||
iarena_cleanup
|
||||
idalloc
|
||||
idalloct
|
||||
idalloctm
|
||||
imalloc
|
||||
imalloct
|
||||
in_valgrind
|
||||
index2size
|
||||
index2size_compute
|
||||
@@ -304,7 +315,11 @@ jemalloc_postfork_parent
|
||||
jemalloc_prefork
|
||||
large_maxclass
|
||||
lg_floor
|
||||
lg_prof_sample
|
||||
malloc_cprintf
|
||||
malloc_mutex_assert_not_owner
|
||||
malloc_mutex_assert_owner
|
||||
malloc_mutex_boot
|
||||
malloc_mutex_init
|
||||
malloc_mutex_lock
|
||||
malloc_mutex_postfork_child
|
||||
@@ -326,11 +341,13 @@ malloc_write
|
||||
map_bias
|
||||
map_misc_offset
|
||||
mb_write
|
||||
mutex_boot
|
||||
narenas_auto
|
||||
narenas_tdata_cleanup
|
||||
narenas_total_get
|
||||
ncpus
|
||||
nhbins
|
||||
nhclasses
|
||||
nlclasses
|
||||
nstime_add
|
||||
nstime_compare
|
||||
nstime_copy
|
||||
@@ -339,6 +356,7 @@ nstime_idivide
|
||||
nstime_imultiply
|
||||
nstime_init
|
||||
nstime_init2
|
||||
nstime_monotonic
|
||||
nstime_ns
|
||||
nstime_nsec
|
||||
nstime_sec
|
||||
@@ -373,17 +391,30 @@ opt_utrace
|
||||
opt_xmalloc
|
||||
opt_zero
|
||||
p2rz
|
||||
pages_boot
|
||||
pages_commit
|
||||
pages_decommit
|
||||
pages_map
|
||||
pages_purge
|
||||
pages_trim
|
||||
pages_unmap
|
||||
pind2sz
|
||||
pind2sz_compute
|
||||
pind2sz_lookup
|
||||
pind2sz_tab
|
||||
pow2_ceil_u32
|
||||
pow2_ceil_u64
|
||||
pow2_ceil_zu
|
||||
prng_lg_range
|
||||
prng_range
|
||||
prng_lg_range_u32
|
||||
prng_lg_range_u64
|
||||
prng_lg_range_zu
|
||||
prng_range_u32
|
||||
prng_range_u64
|
||||
prng_range_zu
|
||||
prng_state_next_u32
|
||||
prng_state_next_u64
|
||||
prng_state_next_zu
|
||||
prof_active
|
||||
prof_active_get
|
||||
prof_active_get_unlocked
|
||||
prof_active_set
|
||||
@@ -393,6 +424,7 @@ prof_backtrace
|
||||
prof_boot0
|
||||
prof_boot1
|
||||
prof_boot2
|
||||
prof_bt_count
|
||||
prof_dump_header
|
||||
prof_dump_open
|
||||
prof_free
|
||||
@@ -410,7 +442,8 @@ prof_malloc_sample_object
|
||||
prof_mdump
|
||||
prof_postfork_child
|
||||
prof_postfork_parent
|
||||
prof_prefork
|
||||
prof_prefork0
|
||||
prof_prefork1
|
||||
prof_realloc
|
||||
prof_reset
|
||||
prof_sample_accum_update
|
||||
@@ -419,6 +452,7 @@ prof_tctx_get
|
||||
prof_tctx_reset
|
||||
prof_tctx_set
|
||||
prof_tdata_cleanup
|
||||
prof_tdata_count
|
||||
prof_tdata_get
|
||||
prof_tdata_init
|
||||
prof_tdata_reinit
|
||||
@@ -428,12 +462,13 @@ prof_thread_active_init_set
|
||||
prof_thread_active_set
|
||||
prof_thread_name_get
|
||||
prof_thread_name_set
|
||||
psz2ind
|
||||
psz2u
|
||||
purge_mode_names
|
||||
quarantine
|
||||
quarantine_alloc_hook
|
||||
quarantine_alloc_hook_work
|
||||
quarantine_cleanup
|
||||
register_zone
|
||||
rtree_child_read
|
||||
rtree_child_read_hard
|
||||
rtree_child_tryread
|
||||
@@ -451,7 +486,6 @@ rtree_val_read
|
||||
rtree_val_write
|
||||
run_quantize_ceil
|
||||
run_quantize_floor
|
||||
run_quantize_max
|
||||
s2u
|
||||
s2u_compute
|
||||
s2u_lookup
|
||||
@@ -461,6 +495,8 @@ size2index
|
||||
size2index_compute
|
||||
size2index_lookup
|
||||
size2index_tab
|
||||
spin_adaptive
|
||||
spin_init
|
||||
stats_cactive
|
||||
stats_cactive_add
|
||||
stats_cactive_get
|
||||
@@ -470,8 +506,6 @@ tcache_alloc_easy
|
||||
tcache_alloc_large
|
||||
tcache_alloc_small
|
||||
tcache_alloc_small_hard
|
||||
tcache_arena_associate
|
||||
tcache_arena_dissociate
|
||||
tcache_arena_reassociate
|
||||
tcache_bin_flush_large
|
||||
tcache_bin_flush_small
|
||||
@@ -506,38 +540,87 @@ ticker_tick
|
||||
ticker_ticks
|
||||
tsd_arena_get
|
||||
tsd_arena_set
|
||||
tsd_arenap_get
|
||||
tsd_arenas_tdata_bypass_get
|
||||
tsd_arenas_tdata_bypass_set
|
||||
tsd_arenas_tdata_bypassp_get
|
||||
tsd_arenas_tdata_get
|
||||
tsd_arenas_tdata_set
|
||||
tsd_arenas_tdatap_get
|
||||
tsd_boot
|
||||
tsd_boot0
|
||||
tsd_boot1
|
||||
tsd_booted
|
||||
tsd_booted_get
|
||||
tsd_cleanup
|
||||
tsd_cleanup_wrapper
|
||||
tsd_fetch
|
||||
tsd_fetch_impl
|
||||
tsd_get
|
||||
tsd_wrapper_get
|
||||
tsd_wrapper_set
|
||||
tsd_get_allocates
|
||||
tsd_iarena_get
|
||||
tsd_iarena_set
|
||||
tsd_iarenap_get
|
||||
tsd_initialized
|
||||
tsd_init_check_recursion
|
||||
tsd_init_finish
|
||||
tsd_init_head
|
||||
tsd_narenas_tdata_get
|
||||
tsd_narenas_tdata_set
|
||||
tsd_narenas_tdatap_get
|
||||
tsd_wrapper_get
|
||||
tsd_wrapper_set
|
||||
tsd_nominal
|
||||
tsd_prof_tdata_get
|
||||
tsd_prof_tdata_set
|
||||
tsd_prof_tdatap_get
|
||||
tsd_quarantine_get
|
||||
tsd_quarantine_set
|
||||
tsd_quarantinep_get
|
||||
tsd_set
|
||||
tsd_tcache_enabled_get
|
||||
tsd_tcache_enabled_set
|
||||
tsd_tcache_enabledp_get
|
||||
tsd_tcache_get
|
||||
tsd_tcache_set
|
||||
tsd_tcachep_get
|
||||
tsd_thread_allocated_get
|
||||
tsd_thread_allocated_set
|
||||
tsd_thread_allocatedp_get
|
||||
tsd_thread_deallocated_get
|
||||
tsd_thread_deallocated_set
|
||||
tsd_thread_deallocatedp_get
|
||||
tsd_tls
|
||||
tsd_tsd
|
||||
tsd_tsdn
|
||||
tsd_witness_fork_get
|
||||
tsd_witness_fork_set
|
||||
tsd_witness_forkp_get
|
||||
tsd_witnesses_get
|
||||
tsd_witnesses_set
|
||||
tsd_witnessesp_get
|
||||
tsdn_fetch
|
||||
tsdn_null
|
||||
tsdn_tsd
|
||||
u2rz
|
||||
valgrind_freelike_block
|
||||
valgrind_make_mem_defined
|
||||
valgrind_make_mem_noaccess
|
||||
valgrind_make_mem_undefined
|
||||
witness_assert_lockless
|
||||
witness_assert_not_owner
|
||||
witness_assert_owner
|
||||
witness_fork_cleanup
|
||||
witness_init
|
||||
witness_lock
|
||||
witness_lock_error
|
||||
witness_lockless_error
|
||||
witness_not_owner_error
|
||||
witness_owner
|
||||
witness_owner_error
|
||||
witness_postfork_child
|
||||
witness_postfork_parent
|
||||
witness_prefork
|
||||
witness_unlock
|
||||
witnesses_cleanup
|
||||
zone_register
|
||||
|
||||
@@ -19,8 +19,12 @@
|
||||
* the next has a cycle of 4, etc. For this reason, we prefer to use the upper
|
||||
* bits.
|
||||
*/
|
||||
#define PRNG_A UINT64_C(6364136223846793005)
|
||||
#define PRNG_C UINT64_C(1442695040888963407)
|
||||
|
||||
#define PRNG_A_32 UINT32_C(1103515241)
|
||||
#define PRNG_C_32 UINT32_C(12347)
|
||||
|
||||
#define PRNG_A_64 UINT64_C(6364136223846793005)
|
||||
#define PRNG_C_64 UINT64_C(1442695040888963407)
|
||||
|
||||
#endif /* JEMALLOC_H_TYPES */
|
||||
/******************************************************************************/
|
||||
@@ -35,28 +39,133 @@
|
||||
#ifdef JEMALLOC_H_INLINES
|
||||
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
uint64_t prng_lg_range(uint64_t *state, unsigned lg_range);
|
||||
uint64_t prng_range(uint64_t *state, uint64_t range);
|
||||
uint32_t prng_state_next_u32(uint32_t state);
|
||||
uint64_t prng_state_next_u64(uint64_t state);
|
||||
size_t prng_state_next_zu(size_t state);
|
||||
|
||||
uint32_t prng_lg_range_u32(uint32_t *state, unsigned lg_range,
|
||||
bool atomic);
|
||||
uint64_t prng_lg_range_u64(uint64_t *state, unsigned lg_range);
|
||||
size_t prng_lg_range_zu(size_t *state, unsigned lg_range, bool atomic);
|
||||
|
||||
uint32_t prng_range_u32(uint32_t *state, uint32_t range, bool atomic);
|
||||
uint64_t prng_range_u64(uint64_t *state, uint64_t range);
|
||||
size_t prng_range_zu(size_t *state, size_t range, bool atomic);
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_PRNG_C_))
|
||||
JEMALLOC_ALWAYS_INLINE uint64_t
|
||||
prng_lg_range(uint64_t *state, unsigned lg_range)
|
||||
JEMALLOC_ALWAYS_INLINE uint32_t
|
||||
prng_state_next_u32(uint32_t state)
|
||||
{
|
||||
uint64_t ret;
|
||||
|
||||
return ((state * PRNG_A_32) + PRNG_C_32);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE uint64_t
|
||||
prng_state_next_u64(uint64_t state)
|
||||
{
|
||||
|
||||
return ((state * PRNG_A_64) + PRNG_C_64);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
prng_state_next_zu(size_t state)
|
||||
{
|
||||
|
||||
#if LG_SIZEOF_PTR == 2
|
||||
return ((state * PRNG_A_32) + PRNG_C_32);
|
||||
#elif LG_SIZEOF_PTR == 3
|
||||
return ((state * PRNG_A_64) + PRNG_C_64);
|
||||
#else
|
||||
#error Unsupported pointer size
|
||||
#endif
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE uint32_t
|
||||
prng_lg_range_u32(uint32_t *state, unsigned lg_range, bool atomic)
|
||||
{
|
||||
uint32_t ret, state1;
|
||||
|
||||
assert(lg_range > 0);
|
||||
assert(lg_range <= 32);
|
||||
|
||||
if (atomic) {
|
||||
uint32_t state0;
|
||||
|
||||
do {
|
||||
state0 = atomic_read_uint32(state);
|
||||
state1 = prng_state_next_u32(state0);
|
||||
} while (atomic_cas_uint32(state, state0, state1));
|
||||
} else {
|
||||
state1 = prng_state_next_u32(*state);
|
||||
*state = state1;
|
||||
}
|
||||
ret = state1 >> (32 - lg_range);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* 64-bit atomic operations cannot be supported on all relevant platforms. */
|
||||
JEMALLOC_ALWAYS_INLINE uint64_t
|
||||
prng_lg_range_u64(uint64_t *state, unsigned lg_range)
|
||||
{
|
||||
uint64_t ret, state1;
|
||||
|
||||
assert(lg_range > 0);
|
||||
assert(lg_range <= 64);
|
||||
|
||||
ret = (*state * PRNG_A) + PRNG_C;
|
||||
*state = ret;
|
||||
ret >>= (64 - lg_range);
|
||||
state1 = prng_state_next_u64(*state);
|
||||
*state = state1;
|
||||
ret = state1 >> (64 - lg_range);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
prng_lg_range_zu(size_t *state, unsigned lg_range, bool atomic)
|
||||
{
|
||||
size_t ret, state1;
|
||||
|
||||
assert(lg_range > 0);
|
||||
assert(lg_range <= ZU(1) << (3 + LG_SIZEOF_PTR));
|
||||
|
||||
if (atomic) {
|
||||
size_t state0;
|
||||
|
||||
do {
|
||||
state0 = atomic_read_z(state);
|
||||
state1 = prng_state_next_zu(state0);
|
||||
} while (atomic_cas_z(state, state0, state1));
|
||||
} else {
|
||||
state1 = prng_state_next_zu(*state);
|
||||
*state = state1;
|
||||
}
|
||||
ret = state1 >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) - lg_range);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE uint32_t
|
||||
prng_range_u32(uint32_t *state, uint32_t range, bool atomic)
|
||||
{
|
||||
uint32_t ret;
|
||||
unsigned lg_range;
|
||||
|
||||
assert(range > 1);
|
||||
|
||||
/* Compute the ceiling of lg(range). */
|
||||
lg_range = ffs_u32(pow2_ceil_u32(range)) - 1;
|
||||
|
||||
/* Generate a result in [0..range) via repeated trial. */
|
||||
do {
|
||||
ret = prng_lg_range_u32(state, lg_range, atomic);
|
||||
} while (ret >= range);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE uint64_t
|
||||
prng_range(uint64_t *state, uint64_t range)
|
||||
prng_range_u64(uint64_t *state, uint64_t range)
|
||||
{
|
||||
uint64_t ret;
|
||||
unsigned lg_range;
|
||||
@@ -68,7 +177,26 @@ prng_range(uint64_t *state, uint64_t range)
|
||||
|
||||
/* Generate a result in [0..range) via repeated trial. */
|
||||
do {
|
||||
ret = prng_lg_range(state, lg_range);
|
||||
ret = prng_lg_range_u64(state, lg_range);
|
||||
} while (ret >= range);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
prng_range_zu(size_t *state, size_t range, bool atomic)
|
||||
{
|
||||
size_t ret;
|
||||
unsigned lg_range;
|
||||
|
||||
assert(range > 1);
|
||||
|
||||
/* Compute the ceiling of lg(range). */
|
||||
lg_range = ffs_u64(pow2_ceil_u64(range)) - 1;
|
||||
|
||||
/* Generate a result in [0..range) via repeated trial. */
|
||||
do {
|
||||
ret = prng_lg_range_zu(state, lg_range, atomic);
|
||||
} while (ret >= range);
|
||||
|
||||
return (ret);
|
||||
|
||||
@@ -281,7 +281,7 @@ extern uint64_t prof_interval;
|
||||
extern size_t lg_prof_sample;
|
||||
|
||||
void prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated);
|
||||
void prof_malloc_sample_object(const void *ptr, size_t usize,
|
||||
void prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx);
|
||||
void prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_tctx_t *tctx);
|
||||
void bt_init(prof_bt_t *bt, void **vec);
|
||||
@@ -293,32 +293,33 @@ size_t prof_bt_count(void);
|
||||
const prof_cnt_t *prof_cnt_all(void);
|
||||
typedef int (prof_dump_open_t)(bool, const char *);
|
||||
extern prof_dump_open_t *prof_dump_open;
|
||||
typedef bool (prof_dump_header_t)(bool, const prof_cnt_t *);
|
||||
typedef bool (prof_dump_header_t)(tsdn_t *, bool, const prof_cnt_t *);
|
||||
extern prof_dump_header_t *prof_dump_header;
|
||||
#endif
|
||||
void prof_idump(void);
|
||||
bool prof_mdump(const char *filename);
|
||||
void prof_gdump(void);
|
||||
void prof_idump(tsdn_t *tsdn);
|
||||
bool prof_mdump(tsd_t *tsd, const char *filename);
|
||||
void prof_gdump(tsdn_t *tsdn);
|
||||
prof_tdata_t *prof_tdata_init(tsd_t *tsd);
|
||||
prof_tdata_t *prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata);
|
||||
void prof_reset(tsd_t *tsd, size_t lg_sample);
|
||||
void prof_tdata_cleanup(tsd_t *tsd);
|
||||
const char *prof_thread_name_get(void);
|
||||
bool prof_active_get(void);
|
||||
bool prof_active_set(bool active);
|
||||
bool prof_active_get(tsdn_t *tsdn);
|
||||
bool prof_active_set(tsdn_t *tsdn, bool active);
|
||||
const char *prof_thread_name_get(tsd_t *tsd);
|
||||
int prof_thread_name_set(tsd_t *tsd, const char *thread_name);
|
||||
bool prof_thread_active_get(void);
|
||||
bool prof_thread_active_set(bool active);
|
||||
bool prof_thread_active_init_get(void);
|
||||
bool prof_thread_active_init_set(bool active_init);
|
||||
bool prof_gdump_get(void);
|
||||
bool prof_gdump_set(bool active);
|
||||
bool prof_thread_active_get(tsd_t *tsd);
|
||||
bool prof_thread_active_set(tsd_t *tsd, bool active);
|
||||
bool prof_thread_active_init_get(tsdn_t *tsdn);
|
||||
bool prof_thread_active_init_set(tsdn_t *tsdn, bool active_init);
|
||||
bool prof_gdump_get(tsdn_t *tsdn);
|
||||
bool prof_gdump_set(tsdn_t *tsdn, bool active);
|
||||
void prof_boot0(void);
|
||||
void prof_boot1(void);
|
||||
bool prof_boot2(void);
|
||||
void prof_prefork(void);
|
||||
void prof_postfork_parent(void);
|
||||
void prof_postfork_child(void);
|
||||
bool prof_boot2(tsd_t *tsd);
|
||||
void prof_prefork0(tsdn_t *tsdn);
|
||||
void prof_prefork1(tsdn_t *tsdn);
|
||||
void prof_postfork_parent(tsdn_t *tsdn);
|
||||
void prof_postfork_child(tsdn_t *tsdn);
|
||||
void prof_sample_threshold_update(prof_tdata_t *tdata);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
@@ -329,17 +330,17 @@ void prof_sample_threshold_update(prof_tdata_t *tdata);
|
||||
bool prof_active_get_unlocked(void);
|
||||
bool prof_gdump_get_unlocked(void);
|
||||
prof_tdata_t *prof_tdata_get(tsd_t *tsd, bool create);
|
||||
prof_tctx_t *prof_tctx_get(tsdn_t *tsdn, const void *ptr);
|
||||
void prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx);
|
||||
void prof_tctx_reset(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
const void *old_ptr, prof_tctx_t *tctx);
|
||||
bool prof_sample_accum_update(tsd_t *tsd, size_t usize, bool commit,
|
||||
prof_tdata_t **tdata_out);
|
||||
prof_tctx_t *prof_alloc_prep(tsd_t *tsd, size_t usize, bool prof_active,
|
||||
bool update);
|
||||
prof_tctx_t *prof_tctx_get(const void *ptr);
|
||||
void prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx);
|
||||
void prof_tctx_reset(const void *ptr, size_t usize, const void *old_ptr,
|
||||
void prof_malloc(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx);
|
||||
void prof_malloc_sample_object(const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx);
|
||||
void prof_malloc(const void *ptr, size_t usize, prof_tctx_t *tctx);
|
||||
void prof_realloc(tsd_t *tsd, const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx, bool prof_active, bool updated, const void *old_ptr,
|
||||
size_t old_usize, prof_tctx_t *old_tctx);
|
||||
@@ -397,34 +398,34 @@ prof_tdata_get(tsd_t *tsd, bool create)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE prof_tctx_t *
|
||||
prof_tctx_get(const void *ptr)
|
||||
prof_tctx_get(tsdn_t *tsdn, const void *ptr)
|
||||
{
|
||||
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
return (arena_prof_tctx_get(ptr));
|
||||
return (arena_prof_tctx_get(tsdn, ptr));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx)
|
||||
prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize, prof_tctx_t *tctx)
|
||||
{
|
||||
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
arena_prof_tctx_set(ptr, usize, tctx);
|
||||
arena_prof_tctx_set(tsdn, ptr, usize, tctx);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
prof_tctx_reset(const void *ptr, size_t usize, const void *old_ptr,
|
||||
prof_tctx_reset(tsdn_t *tsdn, const void *ptr, size_t usize, const void *old_ptr,
|
||||
prof_tctx_t *old_tctx)
|
||||
{
|
||||
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
arena_prof_tctx_reset(ptr, usize, old_ptr, old_tctx);
|
||||
arena_prof_tctx_reset(tsdn, ptr, usize, old_ptr, old_tctx);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
@@ -479,17 +480,17 @@ prof_alloc_prep(tsd_t *tsd, size_t usize, bool prof_active, bool update)
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
prof_malloc(const void *ptr, size_t usize, prof_tctx_t *tctx)
|
||||
prof_malloc(tsdn_t *tsdn, const void *ptr, size_t usize, prof_tctx_t *tctx)
|
||||
{
|
||||
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
assert(usize == isalloc(ptr, true));
|
||||
assert(usize == isalloc(tsdn, ptr, true));
|
||||
|
||||
if (unlikely((uintptr_t)tctx > (uintptr_t)1U))
|
||||
prof_malloc_sample_object(ptr, usize, tctx);
|
||||
prof_malloc_sample_object(tsdn, ptr, usize, tctx);
|
||||
else
|
||||
prof_tctx_set(ptr, usize, (prof_tctx_t *)(uintptr_t)1U);
|
||||
prof_tctx_set(tsdn, ptr, usize, (prof_tctx_t *)(uintptr_t)1U);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
@@ -503,7 +504,7 @@ prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,
|
||||
assert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U);
|
||||
|
||||
if (prof_active && !updated && ptr != NULL) {
|
||||
assert(usize == isalloc(ptr, true));
|
||||
assert(usize == isalloc(tsd_tsdn(tsd), ptr, true));
|
||||
if (prof_sample_accum_update(tsd, usize, true, NULL)) {
|
||||
/*
|
||||
* Don't sample. The usize passed to prof_alloc_prep()
|
||||
@@ -512,6 +513,7 @@ prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,
|
||||
* though its actual usize was insufficient to cross the
|
||||
* sample threshold.
|
||||
*/
|
||||
prof_alloc_rollback(tsd, tctx, true);
|
||||
tctx = (prof_tctx_t *)(uintptr_t)1U;
|
||||
}
|
||||
}
|
||||
@@ -520,9 +522,9 @@ prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,
|
||||
old_sampled = ((uintptr_t)old_tctx > (uintptr_t)1U);
|
||||
|
||||
if (unlikely(sampled))
|
||||
prof_malloc_sample_object(ptr, usize, tctx);
|
||||
prof_malloc_sample_object(tsd_tsdn(tsd), ptr, usize, tctx);
|
||||
else
|
||||
prof_tctx_reset(ptr, usize, old_ptr, old_tctx);
|
||||
prof_tctx_reset(tsd_tsdn(tsd), ptr, usize, old_ptr, old_tctx);
|
||||
|
||||
if (unlikely(old_sampled))
|
||||
prof_free_sampled_object(tsd, old_usize, old_tctx);
|
||||
@@ -531,10 +533,10 @@ prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
prof_free(tsd_t *tsd, const void *ptr, size_t usize)
|
||||
{
|
||||
prof_tctx_t *tctx = prof_tctx_get(ptr);
|
||||
prof_tctx_t *tctx = prof_tctx_get(tsd_tsdn(tsd), ptr);
|
||||
|
||||
cassert(config_prof);
|
||||
assert(usize == isalloc(ptr, true));
|
||||
assert(usize == isalloc(tsd_tsdn(tsd), ptr, true));
|
||||
|
||||
if (unlikely((uintptr_t)tctx > (uintptr_t)1U))
|
||||
prof_free_sampled_object(tsd, usize, tctx);
|
||||
|
||||
@@ -15,9 +15,10 @@ typedef struct rtree_s rtree_t;
|
||||
* machine address width.
|
||||
*/
|
||||
#define LG_RTREE_BITS_PER_LEVEL 4
|
||||
#define RTREE_BITS_PER_LEVEL (ZU(1) << LG_RTREE_BITS_PER_LEVEL)
|
||||
#define RTREE_BITS_PER_LEVEL (1U << LG_RTREE_BITS_PER_LEVEL)
|
||||
/* Maximum rtree height. */
|
||||
#define RTREE_HEIGHT_MAX \
|
||||
((ZU(1) << (LG_SIZEOF_PTR+3)) / RTREE_BITS_PER_LEVEL)
|
||||
((1U << (LG_SIZEOF_PTR+3)) / RTREE_BITS_PER_LEVEL)
|
||||
|
||||
/* Used for two-stage lock-free node initialization. */
|
||||
#define RTREE_NODE_INITIALIZING ((rtree_node_elm_t *)0x1)
|
||||
@@ -111,22 +112,25 @@ unsigned rtree_start_level(rtree_t *rtree, uintptr_t key);
|
||||
uintptr_t rtree_subkey(rtree_t *rtree, uintptr_t key, unsigned level);
|
||||
|
||||
bool rtree_node_valid(rtree_node_elm_t *node);
|
||||
rtree_node_elm_t *rtree_child_tryread(rtree_node_elm_t *elm);
|
||||
rtree_node_elm_t *rtree_child_tryread(rtree_node_elm_t *elm,
|
||||
bool dependent);
|
||||
rtree_node_elm_t *rtree_child_read(rtree_t *rtree, rtree_node_elm_t *elm,
|
||||
unsigned level);
|
||||
unsigned level, bool dependent);
|
||||
extent_node_t *rtree_val_read(rtree_t *rtree, rtree_node_elm_t *elm,
|
||||
bool dependent);
|
||||
void rtree_val_write(rtree_t *rtree, rtree_node_elm_t *elm,
|
||||
const extent_node_t *val);
|
||||
rtree_node_elm_t *rtree_subtree_tryread(rtree_t *rtree, unsigned level);
|
||||
rtree_node_elm_t *rtree_subtree_read(rtree_t *rtree, unsigned level);
|
||||
rtree_node_elm_t *rtree_subtree_tryread(rtree_t *rtree, unsigned level,
|
||||
bool dependent);
|
||||
rtree_node_elm_t *rtree_subtree_read(rtree_t *rtree, unsigned level,
|
||||
bool dependent);
|
||||
|
||||
extent_node_t *rtree_get(rtree_t *rtree, uintptr_t key, bool dependent);
|
||||
bool rtree_set(rtree_t *rtree, uintptr_t key, const extent_node_t *val);
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_RTREE_C_))
|
||||
JEMALLOC_INLINE unsigned
|
||||
JEMALLOC_ALWAYS_INLINE unsigned
|
||||
rtree_start_level(rtree_t *rtree, uintptr_t key)
|
||||
{
|
||||
unsigned start_level;
|
||||
@@ -140,7 +144,7 @@ rtree_start_level(rtree_t *rtree, uintptr_t key)
|
||||
return (start_level);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE uintptr_t
|
||||
JEMALLOC_ALWAYS_INLINE uintptr_t
|
||||
rtree_subkey(rtree_t *rtree, uintptr_t key, unsigned level)
|
||||
{
|
||||
|
||||
@@ -149,37 +153,40 @@ rtree_subkey(rtree_t *rtree, uintptr_t key, unsigned level)
|
||||
rtree->levels[level].bits) - 1));
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE bool
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
rtree_node_valid(rtree_node_elm_t *node)
|
||||
{
|
||||
|
||||
return ((uintptr_t)node > (uintptr_t)RTREE_NODE_INITIALIZING);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE rtree_node_elm_t *
|
||||
rtree_child_tryread(rtree_node_elm_t *elm)
|
||||
JEMALLOC_ALWAYS_INLINE rtree_node_elm_t *
|
||||
rtree_child_tryread(rtree_node_elm_t *elm, bool dependent)
|
||||
{
|
||||
rtree_node_elm_t *child;
|
||||
|
||||
/* Double-checked read (first read may be stale. */
|
||||
child = elm->child;
|
||||
if (!rtree_node_valid(child))
|
||||
if (!dependent && !rtree_node_valid(child))
|
||||
child = atomic_read_p(&elm->pun);
|
||||
assert(!dependent || child != NULL);
|
||||
return (child);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE rtree_node_elm_t *
|
||||
rtree_child_read(rtree_t *rtree, rtree_node_elm_t *elm, unsigned level)
|
||||
JEMALLOC_ALWAYS_INLINE rtree_node_elm_t *
|
||||
rtree_child_read(rtree_t *rtree, rtree_node_elm_t *elm, unsigned level,
|
||||
bool dependent)
|
||||
{
|
||||
rtree_node_elm_t *child;
|
||||
|
||||
child = rtree_child_tryread(elm);
|
||||
if (unlikely(!rtree_node_valid(child)))
|
||||
child = rtree_child_tryread(elm, dependent);
|
||||
if (!dependent && unlikely(!rtree_node_valid(child)))
|
||||
child = rtree_child_read_hard(rtree, elm, level);
|
||||
assert(!dependent || child != NULL);
|
||||
return (child);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE extent_node_t *
|
||||
JEMALLOC_ALWAYS_INLINE extent_node_t *
|
||||
rtree_val_read(rtree_t *rtree, rtree_node_elm_t *elm, bool dependent)
|
||||
{
|
||||
|
||||
@@ -208,54 +215,119 @@ rtree_val_write(rtree_t *rtree, rtree_node_elm_t *elm, const extent_node_t *val)
|
||||
atomic_write_p(&elm->pun, val);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE rtree_node_elm_t *
|
||||
rtree_subtree_tryread(rtree_t *rtree, unsigned level)
|
||||
JEMALLOC_ALWAYS_INLINE rtree_node_elm_t *
|
||||
rtree_subtree_tryread(rtree_t *rtree, unsigned level, bool dependent)
|
||||
{
|
||||
rtree_node_elm_t *subtree;
|
||||
|
||||
/* Double-checked read (first read may be stale. */
|
||||
subtree = rtree->levels[level].subtree;
|
||||
if (!rtree_node_valid(subtree))
|
||||
if (!dependent && unlikely(!rtree_node_valid(subtree)))
|
||||
subtree = atomic_read_p(&rtree->levels[level].subtree_pun);
|
||||
assert(!dependent || subtree != NULL);
|
||||
return (subtree);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE rtree_node_elm_t *
|
||||
rtree_subtree_read(rtree_t *rtree, unsigned level)
|
||||
JEMALLOC_ALWAYS_INLINE rtree_node_elm_t *
|
||||
rtree_subtree_read(rtree_t *rtree, unsigned level, bool dependent)
|
||||
{
|
||||
rtree_node_elm_t *subtree;
|
||||
|
||||
subtree = rtree_subtree_tryread(rtree, level);
|
||||
if (unlikely(!rtree_node_valid(subtree)))
|
||||
subtree = rtree_subtree_tryread(rtree, level, dependent);
|
||||
if (!dependent && unlikely(!rtree_node_valid(subtree)))
|
||||
subtree = rtree_subtree_read_hard(rtree, level);
|
||||
assert(!dependent || subtree != NULL);
|
||||
return (subtree);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE extent_node_t *
|
||||
JEMALLOC_ALWAYS_INLINE extent_node_t *
|
||||
rtree_get(rtree_t *rtree, uintptr_t key, bool dependent)
|
||||
{
|
||||
uintptr_t subkey;
|
||||
unsigned i, start_level;
|
||||
rtree_node_elm_t *node, *child;
|
||||
unsigned start_level;
|
||||
rtree_node_elm_t *node;
|
||||
|
||||
start_level = rtree_start_level(rtree, key);
|
||||
|
||||
for (i = start_level, node = rtree_subtree_tryread(rtree, start_level);
|
||||
/**/; i++, node = child) {
|
||||
if (!dependent && unlikely(!rtree_node_valid(node)))
|
||||
return (NULL);
|
||||
subkey = rtree_subkey(rtree, key, i);
|
||||
if (i == rtree->height - 1) {
|
||||
/*
|
||||
* node is a leaf, so it contains values rather than
|
||||
* child pointers.
|
||||
*/
|
||||
return (rtree_val_read(rtree, &node[subkey],
|
||||
dependent));
|
||||
}
|
||||
assert(i < rtree->height - 1);
|
||||
child = rtree_child_tryread(&node[subkey]);
|
||||
node = rtree_subtree_tryread(rtree, start_level, dependent);
|
||||
#define RTREE_GET_BIAS (RTREE_HEIGHT_MAX - rtree->height)
|
||||
switch (start_level + RTREE_GET_BIAS) {
|
||||
#define RTREE_GET_SUBTREE(level) \
|
||||
case level: \
|
||||
assert(level < (RTREE_HEIGHT_MAX-1)); \
|
||||
if (!dependent && unlikely(!rtree_node_valid(node))) \
|
||||
return (NULL); \
|
||||
subkey = rtree_subkey(rtree, key, level - \
|
||||
RTREE_GET_BIAS); \
|
||||
node = rtree_child_tryread(&node[subkey], dependent); \
|
||||
/* Fall through. */
|
||||
#define RTREE_GET_LEAF(level) \
|
||||
case level: \
|
||||
assert(level == (RTREE_HEIGHT_MAX-1)); \
|
||||
if (!dependent && unlikely(!rtree_node_valid(node))) \
|
||||
return (NULL); \
|
||||
subkey = rtree_subkey(rtree, key, level - \
|
||||
RTREE_GET_BIAS); \
|
||||
/* \
|
||||
* node is a leaf, so it contains values rather than \
|
||||
* child pointers. \
|
||||
*/ \
|
||||
return (rtree_val_read(rtree, &node[subkey], \
|
||||
dependent));
|
||||
#if RTREE_HEIGHT_MAX > 1
|
||||
RTREE_GET_SUBTREE(0)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 2
|
||||
RTREE_GET_SUBTREE(1)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 3
|
||||
RTREE_GET_SUBTREE(2)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 4
|
||||
RTREE_GET_SUBTREE(3)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 5
|
||||
RTREE_GET_SUBTREE(4)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 6
|
||||
RTREE_GET_SUBTREE(5)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 7
|
||||
RTREE_GET_SUBTREE(6)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 8
|
||||
RTREE_GET_SUBTREE(7)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 9
|
||||
RTREE_GET_SUBTREE(8)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 10
|
||||
RTREE_GET_SUBTREE(9)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 11
|
||||
RTREE_GET_SUBTREE(10)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 12
|
||||
RTREE_GET_SUBTREE(11)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 13
|
||||
RTREE_GET_SUBTREE(12)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 14
|
||||
RTREE_GET_SUBTREE(13)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 15
|
||||
RTREE_GET_SUBTREE(14)
|
||||
#endif
|
||||
#if RTREE_HEIGHT_MAX > 16
|
||||
# error Unsupported RTREE_HEIGHT_MAX
|
||||
#endif
|
||||
RTREE_GET_LEAF(RTREE_HEIGHT_MAX-1)
|
||||
#undef RTREE_GET_SUBTREE
|
||||
#undef RTREE_GET_LEAF
|
||||
default: not_reached();
|
||||
}
|
||||
#undef RTREE_GET_BIAS
|
||||
not_reached();
|
||||
}
|
||||
|
||||
@@ -268,7 +340,7 @@ rtree_set(rtree_t *rtree, uintptr_t key, const extent_node_t *val)
|
||||
|
||||
start_level = rtree_start_level(rtree, key);
|
||||
|
||||
node = rtree_subtree_read(rtree, start_level);
|
||||
node = rtree_subtree_read(rtree, start_level, false);
|
||||
if (node == NULL)
|
||||
return (true);
|
||||
for (i = start_level; /**/; i++, node = child) {
|
||||
@@ -282,7 +354,7 @@ rtree_set(rtree_t *rtree, uintptr_t key, const extent_node_t *val)
|
||||
return (false);
|
||||
}
|
||||
assert(i + 1 < rtree->height);
|
||||
child = rtree_child_read(rtree, &node[subkey], i);
|
||||
child = rtree_child_read(rtree, &node[subkey], i, false);
|
||||
if (child == NULL)
|
||||
return (true);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,21 @@ size_class() {
|
||||
lg_p=$5
|
||||
lg_kmax=$6
|
||||
|
||||
if [ ${lg_delta} -ge ${lg_p} ] ; then
|
||||
psz="yes"
|
||||
else
|
||||
pow2 ${lg_p}; p=${pow2_result}
|
||||
pow2 ${lg_grp}; grp=${pow2_result}
|
||||
pow2 ${lg_delta}; delta=${pow2_result}
|
||||
sz=$((${grp} + ${delta} * ${ndelta}))
|
||||
npgs=$((${sz} / ${p}))
|
||||
if [ ${sz} -eq $((${npgs} * ${p})) ] ; then
|
||||
psz="yes"
|
||||
else
|
||||
psz="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
lg ${ndelta}; lg_ndelta=${lg_result}; pow2 ${lg_ndelta}
|
||||
if [ ${pow2_result} -lt ${ndelta} ] ; then
|
||||
rem="yes"
|
||||
@@ -74,14 +89,15 @@ size_class() {
|
||||
else
|
||||
lg_delta_lookup="no"
|
||||
fi
|
||||
printf ' SC(%3d, %6d, %8d, %6d, %3s, %2s) \\\n' ${index} ${lg_grp} ${lg_delta} ${ndelta} ${bin} ${lg_delta_lookup}
|
||||
printf ' SC(%3d, %6d, %8d, %6d, %3s, %3s, %2s) \\\n' ${index} ${lg_grp} ${lg_delta} ${ndelta} ${psz} ${bin} ${lg_delta_lookup}
|
||||
# Defined upon return:
|
||||
# - lg_delta_lookup (${lg_delta} or "no")
|
||||
# - psz ("yes" or "no")
|
||||
# - bin ("yes" or "no")
|
||||
# - lg_delta_lookup (${lg_delta} or "no")
|
||||
}
|
||||
|
||||
sep_line() {
|
||||
echo " \\"
|
||||
echo " \\"
|
||||
}
|
||||
|
||||
size_classes() {
|
||||
@@ -95,12 +111,13 @@ size_classes() {
|
||||
pow2 ${lg_g}; g=${pow2_result}
|
||||
|
||||
echo "#define SIZE_CLASSES \\"
|
||||
echo " /* index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup */ \\"
|
||||
echo " /* index, lg_grp, lg_delta, ndelta, psz, bin, lg_delta_lookup */ \\"
|
||||
|
||||
ntbins=0
|
||||
nlbins=0
|
||||
lg_tiny_maxclass='"NA"'
|
||||
nbins=0
|
||||
npsizes=0
|
||||
|
||||
# Tiny size classes.
|
||||
ndelta=0
|
||||
@@ -112,6 +129,9 @@ size_classes() {
|
||||
if [ ${lg_delta_lookup} != "no" ] ; then
|
||||
nlbins=$((${index} + 1))
|
||||
fi
|
||||
if [ ${psz} = "yes" ] ; then
|
||||
npsizes=$((${npsizes} + 1))
|
||||
fi
|
||||
if [ ${bin} != "no" ] ; then
|
||||
nbins=$((${index} + 1))
|
||||
fi
|
||||
@@ -133,11 +153,17 @@ size_classes() {
|
||||
index=$((${index} + 1))
|
||||
lg_grp=$((${lg_grp} + 1))
|
||||
lg_delta=$((${lg_delta} + 1))
|
||||
if [ ${psz} = "yes" ] ; then
|
||||
npsizes=$((${npsizes} + 1))
|
||||
fi
|
||||
fi
|
||||
while [ ${ndelta} -lt ${g} ] ; do
|
||||
size_class ${index} ${lg_grp} ${lg_delta} ${ndelta} ${lg_p} ${lg_kmax}
|
||||
index=$((${index} + 1))
|
||||
ndelta=$((${ndelta} + 1))
|
||||
if [ ${psz} = "yes" ] ; then
|
||||
npsizes=$((${npsizes} + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# All remaining groups.
|
||||
@@ -157,6 +183,9 @@ size_classes() {
|
||||
# Final written value is correct:
|
||||
lookup_maxclass="((((size_t)1) << ${lg_grp}) + (((size_t)${ndelta}) << ${lg_delta}))"
|
||||
fi
|
||||
if [ ${psz} = "yes" ] ; then
|
||||
npsizes=$((${npsizes} + 1))
|
||||
fi
|
||||
if [ ${bin} != "no" ] ; then
|
||||
nbins=$((${index} + 1))
|
||||
# Final written value is correct:
|
||||
@@ -183,6 +212,7 @@ size_classes() {
|
||||
# - nlbins
|
||||
# - nbins
|
||||
# - nsizes
|
||||
# - npsizes
|
||||
# - lg_tiny_maxclass
|
||||
# - lookup_maxclass
|
||||
# - small_maxclass
|
||||
@@ -200,13 +230,13 @@ cat <<EOF
|
||||
* be defined prior to inclusion, and it in turn defines:
|
||||
*
|
||||
* LG_SIZE_CLASS_GROUP: Lg of size class count for each size doubling.
|
||||
* SIZE_CLASSES: Complete table of
|
||||
* SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup)
|
||||
* tuples.
|
||||
* SIZE_CLASSES: Complete table of SC(index, lg_grp, lg_delta, ndelta, psz,
|
||||
* bin, lg_delta_lookup) tuples.
|
||||
* index: Size class index.
|
||||
* lg_grp: Lg group base size (no deltas added).
|
||||
* lg_delta: Lg delta to previous size class.
|
||||
* ndelta: Delta multiplier. size == 1<<lg_grp + ndelta<<lg_delta
|
||||
* psz: 'yes' if a multiple of the page size, 'no' otherwise.
|
||||
* bin: 'yes' if a small bin size class, 'no' otherwise.
|
||||
* lg_delta_lookup: Same as lg_delta if a lookup table size class, 'no'
|
||||
* otherwise.
|
||||
@@ -214,6 +244,7 @@ cat <<EOF
|
||||
* NLBINS: Number of bins supported by the lookup table.
|
||||
* NBINS: Number of small size class bins.
|
||||
* NSIZES: Number of size classes.
|
||||
* NPSIZES: Number of size classes that are a multiple of (1U << LG_PAGE).
|
||||
* LG_TINY_MAXCLASS: Lg of maximum tiny size class.
|
||||
* LOOKUP_MAXCLASS: Maximum size class included in lookup table.
|
||||
* SMALL_MAXCLASS: Maximum small size class.
|
||||
@@ -238,6 +269,7 @@ for lg_z in ${lg_zarr} ; do
|
||||
echo "#define NLBINS ${nlbins}"
|
||||
echo "#define NBINS ${nbins}"
|
||||
echo "#define NSIZES ${nsizes}"
|
||||
echo "#define NPSIZES ${npsizes}"
|
||||
echo "#define LG_TINY_MAXCLASS ${lg_tiny_maxclass}"
|
||||
echo "#define LOOKUP_MAXCLASS ${lookup_maxclass}"
|
||||
echo "#define SMALL_MAXCLASS ${small_maxclass}"
|
||||
|
||||
51
include/jemalloc/internal/spin.h
Normal file
51
include/jemalloc/internal/spin.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_TYPES
|
||||
|
||||
typedef struct spin_s spin_t;
|
||||
|
||||
#endif /* JEMALLOC_H_TYPES */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_STRUCTS
|
||||
|
||||
struct spin_s {
|
||||
unsigned iteration;
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_H_STRUCTS */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_INLINES
|
||||
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
void spin_init(spin_t *spin);
|
||||
void spin_adaptive(spin_t *spin);
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_SPIN_C_))
|
||||
JEMALLOC_INLINE void
|
||||
spin_init(spin_t *spin)
|
||||
{
|
||||
|
||||
spin->iteration = 0;
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
spin_adaptive(spin_t *spin)
|
||||
{
|
||||
volatile uint64_t i;
|
||||
|
||||
for (i = 0; i < (KQU(1) << spin->iteration); i++)
|
||||
CPU_SPINWAIT;
|
||||
|
||||
if (spin->iteration < 63)
|
||||
spin->iteration++;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* JEMALLOC_H_INLINES */
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -102,6 +102,14 @@ struct arena_stats_s {
|
||||
/* Number of bytes currently mapped. */
|
||||
size_t mapped;
|
||||
|
||||
/*
|
||||
* Number of bytes currently retained as a side effect of munmap() being
|
||||
* disabled/bypassed. Retained bytes are technically mapped (though
|
||||
* always decommitted or purged), but they are excluded from the mapped
|
||||
* statistic (above).
|
||||
*/
|
||||
size_t retained;
|
||||
|
||||
/*
|
||||
* Total number of purge sweeps, total number of madvise calls made,
|
||||
* and total pages purged in order to keep dirty unused memory under
|
||||
|
||||
@@ -130,27 +130,25 @@ extern size_t tcache_maxclass;
|
||||
*/
|
||||
extern tcaches_t *tcaches;
|
||||
|
||||
size_t tcache_salloc(const void *ptr);
|
||||
size_t tcache_salloc(tsdn_t *tsdn, const void *ptr);
|
||||
void tcache_event_hard(tsd_t *tsd, tcache_t *tcache);
|
||||
void *tcache_alloc_small_hard(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
|
||||
void *tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
|
||||
tcache_bin_t *tbin, szind_t binind, bool *tcache_success);
|
||||
void tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin,
|
||||
szind_t binind, unsigned rem);
|
||||
void tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, szind_t binind,
|
||||
unsigned rem, tcache_t *tcache);
|
||||
void tcache_arena_associate(tcache_t *tcache, arena_t *arena);
|
||||
void tcache_arena_reassociate(tcache_t *tcache, arena_t *oldarena,
|
||||
arena_t *newarena);
|
||||
void tcache_arena_dissociate(tcache_t *tcache, arena_t *arena);
|
||||
void tcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache,
|
||||
arena_t *oldarena, arena_t *newarena);
|
||||
tcache_t *tcache_get_hard(tsd_t *tsd);
|
||||
tcache_t *tcache_create(tsd_t *tsd, arena_t *arena);
|
||||
tcache_t *tcache_create(tsdn_t *tsdn, arena_t *arena);
|
||||
void tcache_cleanup(tsd_t *tsd);
|
||||
void tcache_enabled_cleanup(tsd_t *tsd);
|
||||
void tcache_stats_merge(tcache_t *tcache, arena_t *arena);
|
||||
void tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);
|
||||
bool tcaches_create(tsd_t *tsd, unsigned *r_ind);
|
||||
void tcaches_flush(tsd_t *tsd, unsigned ind);
|
||||
void tcaches_destroy(tsd_t *tsd, unsigned ind);
|
||||
bool tcache_boot(void);
|
||||
bool tcache_boot(tsdn_t *tsdn);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
@@ -297,8 +295,8 @@ tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
|
||||
if (unlikely(arena == NULL))
|
||||
return (NULL);
|
||||
|
||||
ret = tcache_alloc_small_hard(tsd, arena, tcache, tbin, binind,
|
||||
&tcache_hard_success);
|
||||
ret = tcache_alloc_small_hard(tsd_tsdn(tsd), arena, tcache,
|
||||
tbin, binind, &tcache_hard_success);
|
||||
if (tcache_hard_success == false)
|
||||
return (NULL);
|
||||
}
|
||||
@@ -310,7 +308,7 @@ tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
|
||||
*/
|
||||
if (config_prof || (slow_path && config_fill) || unlikely(zero)) {
|
||||
usize = index2size(binind);
|
||||
assert(tcache_salloc(ret) == usize);
|
||||
assert(tcache_salloc(tsd_tsdn(tsd), ret) == usize);
|
||||
}
|
||||
|
||||
if (likely(!zero)) {
|
||||
@@ -358,7 +356,7 @@ tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
|
||||
if (unlikely(arena == NULL))
|
||||
return (NULL);
|
||||
|
||||
ret = arena_malloc_large(tsd, arena, binind, zero);
|
||||
ret = arena_malloc_large(tsd_tsdn(tsd), arena, binind, zero);
|
||||
if (ret == NULL)
|
||||
return (NULL);
|
||||
} else {
|
||||
@@ -381,9 +379,10 @@ tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
|
||||
}
|
||||
if (likely(!zero)) {
|
||||
if (slow_path && config_fill) {
|
||||
if (unlikely(opt_junk_alloc))
|
||||
memset(ret, 0xa5, usize);
|
||||
else if (unlikely(opt_zero))
|
||||
if (unlikely(opt_junk_alloc)) {
|
||||
memset(ret, JEMALLOC_ALLOC_JUNK,
|
||||
usize);
|
||||
} else if (unlikely(opt_zero))
|
||||
memset(ret, 0, usize);
|
||||
}
|
||||
} else
|
||||
@@ -406,7 +405,7 @@ tcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,
|
||||
tcache_bin_t *tbin;
|
||||
tcache_bin_info_t *tbin_info;
|
||||
|
||||
assert(tcache_salloc(ptr) <= SMALL_MAXCLASS);
|
||||
assert(tcache_salloc(tsd_tsdn(tsd), ptr) <= SMALL_MAXCLASS);
|
||||
|
||||
if (slow_path && config_fill && unlikely(opt_junk_free))
|
||||
arena_dalloc_junk_small(ptr, &arena_bin_info[binind]);
|
||||
@@ -433,8 +432,8 @@ tcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr, size_t size,
|
||||
tcache_bin_info_t *tbin_info;
|
||||
|
||||
assert((size & PAGE_MASK) == 0);
|
||||
assert(tcache_salloc(ptr) > SMALL_MAXCLASS);
|
||||
assert(tcache_salloc(ptr) <= tcache_maxclass);
|
||||
assert(tcache_salloc(tsd_tsdn(tsd), ptr) > SMALL_MAXCLASS);
|
||||
assert(tcache_salloc(tsd_tsdn(tsd), ptr) <= tcache_maxclass);
|
||||
|
||||
binind = size2index(size);
|
||||
|
||||
@@ -458,8 +457,10 @@ JEMALLOC_ALWAYS_INLINE tcache_t *
|
||||
tcaches_get(tsd_t *tsd, unsigned ind)
|
||||
{
|
||||
tcaches_t *elm = &tcaches[ind];
|
||||
if (unlikely(elm->tcache == NULL))
|
||||
elm->tcache = tcache_create(tsd, arena_choose(tsd, NULL));
|
||||
if (unlikely(elm->tcache == NULL)) {
|
||||
elm->tcache = tcache_create(tsd_tsdn(tsd), arena_choose(tsd,
|
||||
NULL));
|
||||
}
|
||||
return (elm->tcache);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,9 @@ typedef struct tsd_init_head_s tsd_init_head_t;
|
||||
#endif
|
||||
|
||||
typedef struct tsd_s tsd_t;
|
||||
typedef struct tsdn_s tsdn_t;
|
||||
|
||||
#define TSDN_NULL ((tsdn_t *)0)
|
||||
|
||||
typedef enum {
|
||||
tsd_state_uninitialized,
|
||||
@@ -44,7 +47,8 @@ typedef enum {
|
||||
* The result is a set of generated functions, e.g.:
|
||||
*
|
||||
* bool example_tsd_boot(void) {...}
|
||||
* example_t *example_tsd_get() {...}
|
||||
* bool example_tsd_booted_get(void) {...}
|
||||
* example_t *example_tsd_get(bool init) {...}
|
||||
* void example_tsd_set(example_t *val) {...}
|
||||
*
|
||||
* Note that all of the functions deal in terms of (a_type *) rather than
|
||||
@@ -98,8 +102,10 @@ a_attr void \
|
||||
a_name##tsd_boot1(void); \
|
||||
a_attr bool \
|
||||
a_name##tsd_boot(void); \
|
||||
a_attr bool \
|
||||
a_name##tsd_booted_get(void); \
|
||||
a_attr a_type * \
|
||||
a_name##tsd_get(void); \
|
||||
a_name##tsd_get(bool init); \
|
||||
a_attr void \
|
||||
a_name##tsd_set(a_type *val);
|
||||
|
||||
@@ -201,9 +207,21 @@ a_name##tsd_boot(void) \
|
||||
\
|
||||
return (a_name##tsd_boot0()); \
|
||||
} \
|
||||
a_attr bool \
|
||||
a_name##tsd_booted_get(void) \
|
||||
{ \
|
||||
\
|
||||
return (a_name##tsd_booted); \
|
||||
} \
|
||||
a_attr bool \
|
||||
a_name##tsd_get_allocates(void) \
|
||||
{ \
|
||||
\
|
||||
return (false); \
|
||||
} \
|
||||
/* Get/set. */ \
|
||||
a_attr a_type * \
|
||||
a_name##tsd_get(void) \
|
||||
a_name##tsd_get(bool init) \
|
||||
{ \
|
||||
\
|
||||
assert(a_name##tsd_booted); \
|
||||
@@ -246,9 +264,21 @@ a_name##tsd_boot(void) \
|
||||
\
|
||||
return (a_name##tsd_boot0()); \
|
||||
} \
|
||||
a_attr bool \
|
||||
a_name##tsd_booted_get(void) \
|
||||
{ \
|
||||
\
|
||||
return (a_name##tsd_booted); \
|
||||
} \
|
||||
a_attr bool \
|
||||
a_name##tsd_get_allocates(void) \
|
||||
{ \
|
||||
\
|
||||
return (false); \
|
||||
} \
|
||||
/* Get/set. */ \
|
||||
a_attr a_type * \
|
||||
a_name##tsd_get(void) \
|
||||
a_name##tsd_get(bool init) \
|
||||
{ \
|
||||
\
|
||||
assert(a_name##tsd_booted); \
|
||||
@@ -307,14 +337,14 @@ a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper) \
|
||||
} \
|
||||
} \
|
||||
a_attr a_name##tsd_wrapper_t * \
|
||||
a_name##tsd_wrapper_get(void) \
|
||||
a_name##tsd_wrapper_get(bool init) \
|
||||
{ \
|
||||
DWORD error = GetLastError(); \
|
||||
a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *) \
|
||||
TlsGetValue(a_name##tsd_tsd); \
|
||||
SetLastError(error); \
|
||||
\
|
||||
if (unlikely(wrapper == NULL)) { \
|
||||
if (init && unlikely(wrapper == NULL)) { \
|
||||
wrapper = (a_name##tsd_wrapper_t *) \
|
||||
malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t)); \
|
||||
if (wrapper == NULL) { \
|
||||
@@ -368,14 +398,28 @@ a_name##tsd_boot(void) \
|
||||
a_name##tsd_boot1(); \
|
||||
return (false); \
|
||||
} \
|
||||
a_attr bool \
|
||||
a_name##tsd_booted_get(void) \
|
||||
{ \
|
||||
\
|
||||
return (a_name##tsd_booted); \
|
||||
} \
|
||||
a_attr bool \
|
||||
a_name##tsd_get_allocates(void) \
|
||||
{ \
|
||||
\
|
||||
return (true); \
|
||||
} \
|
||||
/* Get/set. */ \
|
||||
a_attr a_type * \
|
||||
a_name##tsd_get(void) \
|
||||
a_name##tsd_get(bool init) \
|
||||
{ \
|
||||
a_name##tsd_wrapper_t *wrapper; \
|
||||
\
|
||||
assert(a_name##tsd_booted); \
|
||||
wrapper = a_name##tsd_wrapper_get(); \
|
||||
wrapper = a_name##tsd_wrapper_get(init); \
|
||||
if (a_name##tsd_get_allocates() && !init && wrapper == NULL) \
|
||||
return (NULL); \
|
||||
return (&wrapper->val); \
|
||||
} \
|
||||
a_attr void \
|
||||
@@ -384,7 +428,7 @@ a_name##tsd_set(a_type *val) \
|
||||
a_name##tsd_wrapper_t *wrapper; \
|
||||
\
|
||||
assert(a_name##tsd_booted); \
|
||||
wrapper = a_name##tsd_wrapper_get(); \
|
||||
wrapper = a_name##tsd_wrapper_get(true); \
|
||||
wrapper->val = *(val); \
|
||||
if (a_cleanup != malloc_tsd_no_cleanup) \
|
||||
wrapper->initialized = true; \
|
||||
@@ -428,12 +472,12 @@ a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper) \
|
||||
} \
|
||||
} \
|
||||
a_attr a_name##tsd_wrapper_t * \
|
||||
a_name##tsd_wrapper_get(void) \
|
||||
a_name##tsd_wrapper_get(bool init) \
|
||||
{ \
|
||||
a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *) \
|
||||
pthread_getspecific(a_name##tsd_tsd); \
|
||||
\
|
||||
if (unlikely(wrapper == NULL)) { \
|
||||
if (init && unlikely(wrapper == NULL)) { \
|
||||
tsd_init_block_t block; \
|
||||
wrapper = tsd_init_check_recursion( \
|
||||
&a_name##tsd_init_head, &block); \
|
||||
@@ -490,14 +534,28 @@ a_name##tsd_boot(void) \
|
||||
a_name##tsd_boot1(); \
|
||||
return (false); \
|
||||
} \
|
||||
a_attr bool \
|
||||
a_name##tsd_booted_get(void) \
|
||||
{ \
|
||||
\
|
||||
return (a_name##tsd_booted); \
|
||||
} \
|
||||
a_attr bool \
|
||||
a_name##tsd_get_allocates(void) \
|
||||
{ \
|
||||
\
|
||||
return (true); \
|
||||
} \
|
||||
/* Get/set. */ \
|
||||
a_attr a_type * \
|
||||
a_name##tsd_get(void) \
|
||||
a_name##tsd_get(bool init) \
|
||||
{ \
|
||||
a_name##tsd_wrapper_t *wrapper; \
|
||||
\
|
||||
assert(a_name##tsd_booted); \
|
||||
wrapper = a_name##tsd_wrapper_get(); \
|
||||
wrapper = a_name##tsd_wrapper_get(init); \
|
||||
if (a_name##tsd_get_allocates() && !init && wrapper == NULL) \
|
||||
return (NULL); \
|
||||
return (&wrapper->val); \
|
||||
} \
|
||||
a_attr void \
|
||||
@@ -506,7 +564,7 @@ a_name##tsd_set(a_type *val) \
|
||||
a_name##tsd_wrapper_t *wrapper; \
|
||||
\
|
||||
assert(a_name##tsd_booted); \
|
||||
wrapper = a_name##tsd_wrapper_get(); \
|
||||
wrapper = a_name##tsd_wrapper_get(true); \
|
||||
wrapper->val = *(val); \
|
||||
if (a_cleanup != malloc_tsd_no_cleanup) \
|
||||
wrapper->initialized = true; \
|
||||
@@ -536,12 +594,15 @@ struct tsd_init_head_s {
|
||||
O(thread_allocated, uint64_t) \
|
||||
O(thread_deallocated, uint64_t) \
|
||||
O(prof_tdata, prof_tdata_t *) \
|
||||
O(iarena, arena_t *) \
|
||||
O(arena, arena_t *) \
|
||||
O(arenas_tdata, arena_tdata_t *) \
|
||||
O(narenas_tdata, unsigned) \
|
||||
O(arenas_tdata_bypass, bool) \
|
||||
O(tcache_enabled, tcache_enabled_t) \
|
||||
O(quarantine, quarantine_t *) \
|
||||
O(witnesses, witness_list_t) \
|
||||
O(witness_fork, bool) \
|
||||
|
||||
#define TSD_INITIALIZER { \
|
||||
tsd_state_uninitialized, \
|
||||
@@ -551,10 +612,13 @@ struct tsd_init_head_s {
|
||||
NULL, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
0, \
|
||||
false, \
|
||||
tcache_enabled_default, \
|
||||
NULL \
|
||||
NULL, \
|
||||
ql_head_initializer(witnesses), \
|
||||
false \
|
||||
}
|
||||
|
||||
struct tsd_s {
|
||||
@@ -565,6 +629,15 @@ MALLOC_TSD
|
||||
#undef O
|
||||
};
|
||||
|
||||
/*
|
||||
* Wrapper around tsd_t that makes it possible to avoid implicit conversion
|
||||
* between tsd_t and tsdn_t, where tsdn_t is "nullable" and has to be
|
||||
* explicitly converted to tsd_t, which is non-nullable.
|
||||
*/
|
||||
struct tsdn_s {
|
||||
tsd_t tsd;
|
||||
};
|
||||
|
||||
static const tsd_t tsd_initializer = TSD_INITIALIZER;
|
||||
|
||||
malloc_tsd_types(, tsd_t)
|
||||
@@ -577,7 +650,7 @@ void *malloc_tsd_malloc(size_t size);
|
||||
void malloc_tsd_dalloc(void *wrapper);
|
||||
void malloc_tsd_no_cleanup(void *arg);
|
||||
void malloc_tsd_cleanup_register(bool (*f)(void));
|
||||
bool malloc_tsd_boot0(void);
|
||||
tsd_t *malloc_tsd_boot0(void);
|
||||
void malloc_tsd_boot1(void);
|
||||
#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
|
||||
!defined(_WIN32))
|
||||
@@ -594,7 +667,9 @@ void tsd_cleanup(void *arg);
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
malloc_tsd_protos(JEMALLOC_ATTR(unused), , tsd_t)
|
||||
|
||||
tsd_t *tsd_fetch_impl(bool init);
|
||||
tsd_t *tsd_fetch(void);
|
||||
tsdn_t *tsd_tsdn(tsd_t *tsd);
|
||||
bool tsd_nominal(tsd_t *tsd);
|
||||
#define O(n, t) \
|
||||
t *tsd_##n##p_get(tsd_t *tsd); \
|
||||
@@ -602,6 +677,9 @@ t tsd_##n##_get(tsd_t *tsd); \
|
||||
void tsd_##n##_set(tsd_t *tsd, t n);
|
||||
MALLOC_TSD
|
||||
#undef O
|
||||
tsdn_t *tsdn_fetch(void);
|
||||
bool tsdn_null(const tsdn_t *tsdn);
|
||||
tsd_t *tsdn_tsd(tsdn_t *tsdn);
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TSD_C_))
|
||||
@@ -609,9 +687,13 @@ malloc_tsd_externs(, tsd_t)
|
||||
malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, , tsd_t, tsd_initializer, tsd_cleanup)
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE tsd_t *
|
||||
tsd_fetch(void)
|
||||
tsd_fetch_impl(bool init)
|
||||
{
|
||||
tsd_t *tsd = tsd_get();
|
||||
tsd_t *tsd = tsd_get(init);
|
||||
|
||||
if (!init && tsd_get_allocates() && tsd == NULL)
|
||||
return (NULL);
|
||||
assert(tsd != NULL);
|
||||
|
||||
if (unlikely(tsd->state != tsd_state_nominal)) {
|
||||
if (tsd->state == tsd_state_uninitialized) {
|
||||
@@ -628,6 +710,20 @@ tsd_fetch(void)
|
||||
return (tsd);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE tsd_t *
|
||||
tsd_fetch(void)
|
||||
{
|
||||
|
||||
return (tsd_fetch_impl(true));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE tsdn_t *
|
||||
tsd_tsdn(tsd_t *tsd)
|
||||
{
|
||||
|
||||
return ((tsdn_t *)tsd);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE bool
|
||||
tsd_nominal(tsd_t *tsd)
|
||||
{
|
||||
@@ -659,6 +755,32 @@ tsd_##n##_set(tsd_t *tsd, t n) \
|
||||
}
|
||||
MALLOC_TSD
|
||||
#undef O
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE tsdn_t *
|
||||
tsdn_fetch(void)
|
||||
{
|
||||
|
||||
if (!tsd_booted_get())
|
||||
return (NULL);
|
||||
|
||||
return (tsd_tsdn(tsd_fetch_impl(false)));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
tsdn_null(const tsdn_t *tsdn)
|
||||
{
|
||||
|
||||
return (tsdn == NULL);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE tsd_t *
|
||||
tsdn_tsd(tsdn_t *tsdn)
|
||||
{
|
||||
|
||||
assert(!tsdn_null(tsdn));
|
||||
|
||||
return (&tsdn->tsd);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* JEMALLOC_H_INLINES */
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
*/
|
||||
#define MALLOC_PRINTF_BUFSIZE 4096
|
||||
|
||||
/* Junk fill patterns. */
|
||||
#define JEMALLOC_ALLOC_JUNK ((uint8_t)0xa5)
|
||||
#define JEMALLOC_FREE_JUNK ((uint8_t)0x5a)
|
||||
|
||||
/*
|
||||
* Wrap a cpp argument that contains commas such that it isn't broken up into
|
||||
* multiple arguments.
|
||||
@@ -57,30 +61,20 @@
|
||||
# define JEMALLOC_CC_SILENCE_INIT(v)
|
||||
#endif
|
||||
|
||||
#define JEMALLOC_GNUC_PREREQ(major, minor) \
|
||||
(!defined(__clang__) && \
|
||||
(__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))))
|
||||
#ifndef __has_builtin
|
||||
# define __has_builtin(builtin) (0)
|
||||
#endif
|
||||
#define JEMALLOC_CLANG_HAS_BUILTIN(builtin) \
|
||||
(defined(__clang__) && __has_builtin(builtin))
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define likely(x) __builtin_expect(!!(x), 1)
|
||||
# define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
# if JEMALLOC_GNUC_PREREQ(4, 6) || \
|
||||
JEMALLOC_CLANG_HAS_BUILTIN(__builtin_unreachable)
|
||||
# define unreachable() __builtin_unreachable()
|
||||
# else
|
||||
# define unreachable()
|
||||
# endif
|
||||
#else
|
||||
# define likely(x) !!(x)
|
||||
# define unlikely(x) !!(x)
|
||||
# define unreachable()
|
||||
#endif
|
||||
|
||||
#if !defined(JEMALLOC_INTERNAL_UNREACHABLE)
|
||||
# error JEMALLOC_INTERNAL_UNREACHABLE should have been defined by configure
|
||||
#endif
|
||||
|
||||
#define unreachable() JEMALLOC_INTERNAL_UNREACHABLE()
|
||||
|
||||
#include "jemalloc/internal/assert.h"
|
||||
|
||||
/* Use to assert a particular configuration, e.g., cassert(config_debug). */
|
||||
@@ -106,9 +100,9 @@ void malloc_write(const char *s);
|
||||
* malloc_vsnprintf() supports a subset of snprintf(3) that avoids floating
|
||||
* point math.
|
||||
*/
|
||||
int malloc_vsnprintf(char *str, size_t size, const char *format,
|
||||
size_t malloc_vsnprintf(char *str, size_t size, const char *format,
|
||||
va_list ap);
|
||||
int malloc_snprintf(char *str, size_t size, const char *format, ...)
|
||||
size_t malloc_snprintf(char *str, size_t size, const char *format, ...)
|
||||
JEMALLOC_FORMAT_PRINTF(3, 4);
|
||||
void malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
const char *format, va_list ap);
|
||||
|
||||
@@ -30,15 +30,17 @@
|
||||
* calls must be embedded in macros rather than in functions so that when
|
||||
* Valgrind reports errors, there are no extra stack frames in the backtraces.
|
||||
*/
|
||||
#define JEMALLOC_VALGRIND_MALLOC(cond, ptr, usize, zero) do { \
|
||||
if (unlikely(in_valgrind && cond)) \
|
||||
VALGRIND_MALLOCLIKE_BLOCK(ptr, usize, p2rz(ptr), zero); \
|
||||
#define JEMALLOC_VALGRIND_MALLOC(cond, tsdn, ptr, usize, zero) do { \
|
||||
if (unlikely(in_valgrind && cond)) { \
|
||||
VALGRIND_MALLOCLIKE_BLOCK(ptr, usize, p2rz(tsdn, ptr), \
|
||||
zero); \
|
||||
} \
|
||||
} while (0)
|
||||
#define JEMALLOC_VALGRIND_REALLOC(maybe_moved, ptr, usize, \
|
||||
#define JEMALLOC_VALGRIND_REALLOC(maybe_moved, tsdn, ptr, usize, \
|
||||
ptr_maybe_null, old_ptr, old_usize, old_rzsize, old_ptr_maybe_null, \
|
||||
zero) do { \
|
||||
if (unlikely(in_valgrind)) { \
|
||||
size_t rzsize = p2rz(ptr); \
|
||||
size_t rzsize = p2rz(tsdn, ptr); \
|
||||
\
|
||||
if (!maybe_moved || ptr == old_ptr) { \
|
||||
VALGRIND_RESIZEINPLACE_BLOCK(ptr, old_usize, \
|
||||
@@ -81,8 +83,8 @@
|
||||
#define JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(ptr, usize) do {} while (0)
|
||||
#define JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ptr, usize) do {} while (0)
|
||||
#define JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ptr, usize) do {} while (0)
|
||||
#define JEMALLOC_VALGRIND_MALLOC(cond, ptr, usize, zero) do {} while (0)
|
||||
#define JEMALLOC_VALGRIND_REALLOC(maybe_moved, ptr, usize, \
|
||||
#define JEMALLOC_VALGRIND_MALLOC(cond, tsdn, ptr, usize, zero) do {} while (0)
|
||||
#define JEMALLOC_VALGRIND_REALLOC(maybe_moved, tsdn, ptr, usize, \
|
||||
ptr_maybe_null, old_ptr, old_usize, old_rzsize, old_ptr_maybe_null, \
|
||||
zero) do {} while (0)
|
||||
#define JEMALLOC_VALGRIND_FREE(ptr, rzsize) do {} while (0)
|
||||
|
||||
266
include/jemalloc/internal/witness.h
Normal file
266
include/jemalloc/internal/witness.h
Normal file
@@ -0,0 +1,266 @@
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_TYPES
|
||||
|
||||
typedef struct witness_s witness_t;
|
||||
typedef unsigned witness_rank_t;
|
||||
typedef ql_head(witness_t) witness_list_t;
|
||||
typedef int witness_comp_t (const witness_t *, const witness_t *);
|
||||
|
||||
/*
|
||||
* Lock ranks. Witnesses with rank WITNESS_RANK_OMIT are completely ignored by
|
||||
* the witness machinery.
|
||||
*/
|
||||
#define WITNESS_RANK_OMIT 0U
|
||||
|
||||
#define WITNESS_RANK_INIT 1U
|
||||
#define WITNESS_RANK_CTL 1U
|
||||
#define WITNESS_RANK_ARENAS 2U
|
||||
|
||||
#define WITNESS_RANK_PROF_DUMP 3U
|
||||
#define WITNESS_RANK_PROF_BT2GCTX 4U
|
||||
#define WITNESS_RANK_PROF_TDATAS 5U
|
||||
#define WITNESS_RANK_PROF_TDATA 6U
|
||||
#define WITNESS_RANK_PROF_GCTX 7U
|
||||
|
||||
#define WITNESS_RANK_ARENA 8U
|
||||
#define WITNESS_RANK_ARENA_CHUNKS 9U
|
||||
#define WITNESS_RANK_ARENA_NODE_CACHE 10
|
||||
|
||||
#define WITNESS_RANK_BASE 11U
|
||||
|
||||
#define WITNESS_RANK_LEAF 0xffffffffU
|
||||
#define WITNESS_RANK_ARENA_BIN WITNESS_RANK_LEAF
|
||||
#define WITNESS_RANK_ARENA_HUGE WITNESS_RANK_LEAF
|
||||
#define WITNESS_RANK_DSS WITNESS_RANK_LEAF
|
||||
#define WITNESS_RANK_PROF_ACTIVE WITNESS_RANK_LEAF
|
||||
#define WITNESS_RANK_PROF_DUMP_SEQ WITNESS_RANK_LEAF
|
||||
#define WITNESS_RANK_PROF_GDUMP WITNESS_RANK_LEAF
|
||||
#define WITNESS_RANK_PROF_NEXT_THR_UID WITNESS_RANK_LEAF
|
||||
#define WITNESS_RANK_PROF_THREAD_ACTIVE_INIT WITNESS_RANK_LEAF
|
||||
|
||||
#define WITNESS_INITIALIZER(rank) {"initializer", rank, NULL, {NULL, NULL}}
|
||||
|
||||
#endif /* JEMALLOC_H_TYPES */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_STRUCTS
|
||||
|
||||
struct witness_s {
|
||||
/* Name, used for printing lock order reversal messages. */
|
||||
const char *name;
|
||||
|
||||
/*
|
||||
* Witness rank, where 0 is lowest and UINT_MAX is highest. Witnesses
|
||||
* must be acquired in order of increasing rank.
|
||||
*/
|
||||
witness_rank_t rank;
|
||||
|
||||
/*
|
||||
* If two witnesses are of equal rank and they have the samp comp
|
||||
* function pointer, it is called as a last attempt to differentiate
|
||||
* between witnesses of equal rank.
|
||||
*/
|
||||
witness_comp_t *comp;
|
||||
|
||||
/* Linkage for thread's currently owned locks. */
|
||||
ql_elm(witness_t) link;
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_H_STRUCTS */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
void witness_init(witness_t *witness, const char *name, witness_rank_t rank,
|
||||
witness_comp_t *comp);
|
||||
#ifdef JEMALLOC_JET
|
||||
typedef void (witness_lock_error_t)(const witness_list_t *, const witness_t *);
|
||||
extern witness_lock_error_t *witness_lock_error;
|
||||
#else
|
||||
void witness_lock_error(const witness_list_t *witnesses,
|
||||
const witness_t *witness);
|
||||
#endif
|
||||
#ifdef JEMALLOC_JET
|
||||
typedef void (witness_owner_error_t)(const witness_t *);
|
||||
extern witness_owner_error_t *witness_owner_error;
|
||||
#else
|
||||
void witness_owner_error(const witness_t *witness);
|
||||
#endif
|
||||
#ifdef JEMALLOC_JET
|
||||
typedef void (witness_not_owner_error_t)(const witness_t *);
|
||||
extern witness_not_owner_error_t *witness_not_owner_error;
|
||||
#else
|
||||
void witness_not_owner_error(const witness_t *witness);
|
||||
#endif
|
||||
#ifdef JEMALLOC_JET
|
||||
typedef void (witness_lockless_error_t)(const witness_list_t *);
|
||||
extern witness_lockless_error_t *witness_lockless_error;
|
||||
#else
|
||||
void witness_lockless_error(const witness_list_t *witnesses);
|
||||
#endif
|
||||
|
||||
void witnesses_cleanup(tsd_t *tsd);
|
||||
void witness_fork_cleanup(tsd_t *tsd);
|
||||
void witness_prefork(tsd_t *tsd);
|
||||
void witness_postfork_parent(tsd_t *tsd);
|
||||
void witness_postfork_child(tsd_t *tsd);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_INLINES
|
||||
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
bool witness_owner(tsd_t *tsd, const witness_t *witness);
|
||||
void witness_assert_owner(tsdn_t *tsdn, const witness_t *witness);
|
||||
void witness_assert_not_owner(tsdn_t *tsdn, const witness_t *witness);
|
||||
void witness_assert_lockless(tsdn_t *tsdn);
|
||||
void witness_lock(tsdn_t *tsdn, witness_t *witness);
|
||||
void witness_unlock(tsdn_t *tsdn, witness_t *witness);
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_MUTEX_C_))
|
||||
JEMALLOC_INLINE bool
|
||||
witness_owner(tsd_t *tsd, const witness_t *witness)
|
||||
{
|
||||
witness_list_t *witnesses;
|
||||
witness_t *w;
|
||||
|
||||
witnesses = tsd_witnessesp_get(tsd);
|
||||
ql_foreach(w, witnesses, link) {
|
||||
if (w == witness)
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
witness_assert_owner(tsdn_t *tsdn, const witness_t *witness)
|
||||
{
|
||||
tsd_t *tsd;
|
||||
|
||||
if (!config_debug)
|
||||
return;
|
||||
|
||||
if (tsdn_null(tsdn))
|
||||
return;
|
||||
tsd = tsdn_tsd(tsdn);
|
||||
if (witness->rank == WITNESS_RANK_OMIT)
|
||||
return;
|
||||
|
||||
if (witness_owner(tsd, witness))
|
||||
return;
|
||||
witness_owner_error(witness);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
witness_assert_not_owner(tsdn_t *tsdn, const witness_t *witness)
|
||||
{
|
||||
tsd_t *tsd;
|
||||
witness_list_t *witnesses;
|
||||
witness_t *w;
|
||||
|
||||
if (!config_debug)
|
||||
return;
|
||||
|
||||
if (tsdn_null(tsdn))
|
||||
return;
|
||||
tsd = tsdn_tsd(tsdn);
|
||||
if (witness->rank == WITNESS_RANK_OMIT)
|
||||
return;
|
||||
|
||||
witnesses = tsd_witnessesp_get(tsd);
|
||||
ql_foreach(w, witnesses, link) {
|
||||
if (w == witness)
|
||||
witness_not_owner_error(witness);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
witness_assert_lockless(tsdn_t *tsdn)
|
||||
{
|
||||
tsd_t *tsd;
|
||||
witness_list_t *witnesses;
|
||||
witness_t *w;
|
||||
|
||||
if (!config_debug)
|
||||
return;
|
||||
|
||||
if (tsdn_null(tsdn))
|
||||
return;
|
||||
tsd = tsdn_tsd(tsdn);
|
||||
|
||||
witnesses = tsd_witnessesp_get(tsd);
|
||||
w = ql_last(witnesses, link);
|
||||
if (w != NULL)
|
||||
witness_lockless_error(witnesses);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
witness_lock(tsdn_t *tsdn, witness_t *witness)
|
||||
{
|
||||
tsd_t *tsd;
|
||||
witness_list_t *witnesses;
|
||||
witness_t *w;
|
||||
|
||||
if (!config_debug)
|
||||
return;
|
||||
|
||||
if (tsdn_null(tsdn))
|
||||
return;
|
||||
tsd = tsdn_tsd(tsdn);
|
||||
if (witness->rank == WITNESS_RANK_OMIT)
|
||||
return;
|
||||
|
||||
witness_assert_not_owner(tsdn, witness);
|
||||
|
||||
witnesses = tsd_witnessesp_get(tsd);
|
||||
w = ql_last(witnesses, link);
|
||||
if (w == NULL) {
|
||||
/* No other locks; do nothing. */
|
||||
} else if (tsd_witness_fork_get(tsd) && w->rank <= witness->rank) {
|
||||
/* Forking, and relaxed ranking satisfied. */
|
||||
} else if (w->rank > witness->rank) {
|
||||
/* Not forking, rank order reversal. */
|
||||
witness_lock_error(witnesses, witness);
|
||||
} else if (w->rank == witness->rank && (w->comp == NULL || w->comp !=
|
||||
witness->comp || w->comp(w, witness) > 0)) {
|
||||
/*
|
||||
* Missing/incompatible comparison function, or comparison
|
||||
* function indicates rank order reversal.
|
||||
*/
|
||||
witness_lock_error(witnesses, witness);
|
||||
}
|
||||
|
||||
ql_elm_new(witness, link);
|
||||
ql_tail_insert(witnesses, witness, link);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
witness_unlock(tsdn_t *tsdn, witness_t *witness)
|
||||
{
|
||||
tsd_t *tsd;
|
||||
witness_list_t *witnesses;
|
||||
|
||||
if (!config_debug)
|
||||
return;
|
||||
|
||||
if (tsdn_null(tsdn))
|
||||
return;
|
||||
tsd = tsdn_tsd(tsdn);
|
||||
if (witness->rank == WITNESS_RANK_OMIT)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check whether owner before removal, rather than relying on
|
||||
* witness_assert_owner() to abort, so that unit tests can test this
|
||||
* function's failure mode without causing undefined behavior.
|
||||
*/
|
||||
if (witness_owner(tsd, witness)) {
|
||||
witnesses = tsd_witnessesp_get(tsd);
|
||||
ql_remove(witnesses, witness, link);
|
||||
} else
|
||||
witness_assert_owner(tsdn, witness);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* JEMALLOC_H_INLINES */
|
||||
/******************************************************************************/
|
||||
@@ -13,11 +13,11 @@
|
||||
|
||||
# define MALLOCX_LG_ALIGN(la) ((int)(la))
|
||||
# if LG_SIZEOF_PTR == 2
|
||||
# define MALLOCX_ALIGN(a) ((int)(ffs(a)-1))
|
||||
# define MALLOCX_ALIGN(a) ((int)(ffs((int)(a))-1))
|
||||
# else
|
||||
# define MALLOCX_ALIGN(a) \
|
||||
((int)(((a) < (size_t)INT_MAX) ? ffs((int)(a))-1 : \
|
||||
ffs((int)((a)>>32))+31))
|
||||
((int)(((size_t)(a) < (size_t)INT_MAX) ? ffs((int)(a))-1 : \
|
||||
ffs((int)(((size_t)(a))>>32))+31))
|
||||
# endif
|
||||
# define MALLOCX_ZERO ((int)0x40)
|
||||
/*
|
||||
@@ -29,7 +29,7 @@
|
||||
/*
|
||||
* Bias arena index bits so that 0 encodes "use an automatically chosen arena".
|
||||
*/
|
||||
# define MALLOCX_ARENA(a) ((int)(((a)+1) << 20))
|
||||
# define MALLOCX_ARENA(a) ((((int)(a))+1) << 20)
|
||||
|
||||
#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW)
|
||||
# define JEMALLOC_CXX_THROW throw()
|
||||
|
||||
@@ -1,26 +1,6 @@
|
||||
#ifndef MSVC_COMPAT_WINDOWS_EXTRA_H
|
||||
#define MSVC_COMPAT_WINDOWS_EXTRA_H
|
||||
|
||||
#ifndef ENOENT
|
||||
# define ENOENT ERROR_PATH_NOT_FOUND
|
||||
#endif
|
||||
#ifndef EINVAL
|
||||
# define EINVAL ERROR_BAD_ARGUMENTS
|
||||
#endif
|
||||
#ifndef EAGAIN
|
||||
# define EAGAIN ERROR_OUTOFMEMORY
|
||||
#endif
|
||||
#ifndef EPERM
|
||||
# define EPERM ERROR_WRITE_FAULT
|
||||
#endif
|
||||
#ifndef EFAULT
|
||||
# define EFAULT ERROR_INVALID_ADDRESS
|
||||
#endif
|
||||
#ifndef ENOMEM
|
||||
# define ENOMEM ERROR_NOT_ENOUGH_MEMORY
|
||||
#endif
|
||||
#ifndef ERANGE
|
||||
# define ERANGE ERROR_INVALID_DATA
|
||||
#endif
|
||||
#include <errno.h>
|
||||
|
||||
#endif /* MSVC_COMPAT_WINDOWS_EXTRA_H */
|
||||
|
||||
@@ -6,7 +6,7 @@ install_suffix=@install_suffix@
|
||||
|
||||
Name: jemalloc
|
||||
Description: A general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support.
|
||||
URL: http://www.canonware.com/jemalloc
|
||||
URL: http://jemalloc.net/
|
||||
Version: @jemalloc_version@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -ljemalloc${install_suffix}
|
||||
|
||||
@@ -17,7 +17,7 @@ How to build jemalloc for Windows
|
||||
(note: x86/x64 doesn't matter at this point)
|
||||
|
||||
5. Generate header files:
|
||||
sh -c "./autogen.sh CC=cl --enable-lazy-lock=no"
|
||||
sh -c "CC=cl ./autogen.sh"
|
||||
|
||||
6. Now the project can be opened and built in Visual Studio:
|
||||
msvc\jemalloc_vc2015.sln
|
||||
|
||||
@@ -54,7 +54,9 @@
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\jemalloc_internal_macros.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\mb.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\mutex.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\nstime.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\pages.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\ph.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\private_namespace.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\private_unnamespace.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\prng.h" />
|
||||
@@ -67,11 +69,14 @@
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\rb.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\rtree.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\size_classes.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\smoothstep.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\spin.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\stats.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\tcache.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\ticker.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\tsd.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\util.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\valgrind.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\witness.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\jemalloc.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\jemalloc_defs.h" />
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\jemalloc_macros.h" />
|
||||
@@ -103,13 +108,17 @@
|
||||
<ClCompile Include="..\..\..\..\src\mutex.c" />
|
||||
<ClCompile Include="..\..\..\..\src\nstime.c" />
|
||||
<ClCompile Include="..\..\..\..\src\pages.c" />
|
||||
<ClCompile Include="..\..\..\..\src\prng.c" />
|
||||
<ClCompile Include="..\..\..\..\src\prof.c" />
|
||||
<ClCompile Include="..\..\..\..\src\quarantine.c" />
|
||||
<ClCompile Include="..\..\..\..\src\rtree.c" />
|
||||
<ClCompile Include="..\..\..\..\src\spin.c" />
|
||||
<ClCompile Include="..\..\..\..\src\stats.c" />
|
||||
<ClCompile Include="..\..\..\..\src\tcache.c" />
|
||||
<ClCompile Include="..\..\..\..\src\ticker.c" />
|
||||
<ClCompile Include="..\..\..\..\src\tsd.c" />
|
||||
<ClCompile Include="..\..\..\..\src\util.c" />
|
||||
<ClCompile Include="..\..\..\..\src\witness.c" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{8D6BB292-9E1C-413D-9F98-4864BDC1514A}</ProjectGuid>
|
||||
@@ -227,7 +236,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-static|x64'">
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>
|
||||
<TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
@@ -236,7 +245,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-static|x64'">
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>
|
||||
<TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -246,7 +255,7 @@
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;..\..\..\..\include\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4090;4146;4244;4267;4334</DisableSpecificWarnings>
|
||||
<DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -263,7 +272,7 @@
|
||||
<PreprocessorDefinitions>JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;..\..\..\..\include\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<DisableSpecificWarnings>4090;4146;4244;4267;4334</DisableSpecificWarnings>
|
||||
<DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -279,7 +288,7 @@
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;..\..\..\..\include\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4090;4146;4244;4267;4334</DisableSpecificWarnings>
|
||||
<DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -296,8 +305,9 @@
|
||||
<PreprocessorDefinitions>JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;..\..\..\..\include\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<DisableSpecificWarnings>4090;4146;4244;4267;4334</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>
|
||||
<DebugInformationFormat>OldStyle</DebugInformationFormat>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@@ -314,7 +324,7 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;..\..\..\..\include\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4090;4146;4244;4267;4334</DisableSpecificWarnings>
|
||||
<DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -335,7 +345,7 @@
|
||||
<PreprocessorDefinitions>_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;..\..\..\..\include\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<DisableSpecificWarnings>4090;4146;4244;4267;4334</DisableSpecificWarnings>
|
||||
<DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -355,7 +365,7 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;..\..\..\..\include\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4090;4146;4244;4267;4334</DisableSpecificWarnings>
|
||||
<DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -376,8 +386,8 @@
|
||||
<PreprocessorDefinitions>_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;..\..\..\..\include\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<DisableSpecificWarnings>4090;4146;4244;4267;4334</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>
|
||||
<DebugInformationFormat>OldStyle</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
||||
@@ -101,9 +101,15 @@
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\mutex.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\nstime.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\pages.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\ph.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\private_namespace.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
@@ -140,19 +146,28 @@
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\size_classes.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\smoothstep.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\spin.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\stats.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\tcache.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\ticker.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\tsd.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\util.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\valgrind.h">
|
||||
<ClInclude Include="..\..\..\..\include\jemalloc\internal\witness.h">
|
||||
<Filter>Header Files\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\msvc_compat\strings.h">
|
||||
@@ -214,9 +229,15 @@
|
||||
<ClCompile Include="..\..\..\..\src\mutex.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\nstime.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\pages.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\prng.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\prof.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -226,20 +247,26 @@
|
||||
<ClCompile Include="..\..\..\..\src\rtree.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\spin.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\stats.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\tcache.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\ticker.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\tsd.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\util.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\src\nstime.c">
|
||||
<ClCompile Include="..\..\..\..\src\witness.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(Configuration)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
@@ -306,7 +306,7 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(Configuration)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
1570
src/arena.c
1570
src/arena.c
File diff suppressed because it is too large
Load Diff
49
src/base.c
49
src/base.c
@@ -13,12 +13,13 @@ static size_t base_mapped;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* base_mtx must be held. */
|
||||
static extent_node_t *
|
||||
base_node_try_alloc(void)
|
||||
base_node_try_alloc(tsdn_t *tsdn)
|
||||
{
|
||||
extent_node_t *node;
|
||||
|
||||
malloc_mutex_assert_owner(tsdn, &base_mtx);
|
||||
|
||||
if (base_nodes == NULL)
|
||||
return (NULL);
|
||||
node = base_nodes;
|
||||
@@ -27,33 +28,34 @@ base_node_try_alloc(void)
|
||||
return (node);
|
||||
}
|
||||
|
||||
/* base_mtx must be held. */
|
||||
static void
|
||||
base_node_dalloc(extent_node_t *node)
|
||||
base_node_dalloc(tsdn_t *tsdn, extent_node_t *node)
|
||||
{
|
||||
|
||||
malloc_mutex_assert_owner(tsdn, &base_mtx);
|
||||
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t));
|
||||
*(extent_node_t **)node = base_nodes;
|
||||
base_nodes = node;
|
||||
}
|
||||
|
||||
/* base_mtx must be held. */
|
||||
static extent_node_t *
|
||||
base_chunk_alloc(size_t minsize)
|
||||
base_chunk_alloc(tsdn_t *tsdn, size_t minsize)
|
||||
{
|
||||
extent_node_t *node;
|
||||
size_t csize, nsize;
|
||||
void *addr;
|
||||
|
||||
malloc_mutex_assert_owner(tsdn, &base_mtx);
|
||||
assert(minsize != 0);
|
||||
node = base_node_try_alloc();
|
||||
node = base_node_try_alloc(tsdn);
|
||||
/* Allocate enough space to also carve a node out if necessary. */
|
||||
nsize = (node == NULL) ? CACHELINE_CEILING(sizeof(extent_node_t)) : 0;
|
||||
csize = CHUNK_CEILING(minsize + nsize);
|
||||
addr = chunk_alloc_base(csize);
|
||||
if (addr == NULL) {
|
||||
if (node != NULL)
|
||||
base_node_dalloc(node);
|
||||
base_node_dalloc(tsdn, node);
|
||||
return (NULL);
|
||||
}
|
||||
base_mapped += csize;
|
||||
@@ -76,7 +78,7 @@ base_chunk_alloc(size_t minsize)
|
||||
* physical memory usage.
|
||||
*/
|
||||
void *
|
||||
base_alloc(size_t size)
|
||||
base_alloc(tsdn_t *tsdn, size_t size)
|
||||
{
|
||||
void *ret;
|
||||
size_t csize, usize;
|
||||
@@ -91,14 +93,14 @@ base_alloc(size_t size)
|
||||
|
||||
usize = s2u(csize);
|
||||
extent_node_init(&key, NULL, NULL, usize, false, false);
|
||||
malloc_mutex_lock(&base_mtx);
|
||||
malloc_mutex_lock(tsdn, &base_mtx);
|
||||
node = extent_tree_szad_nsearch(&base_avail_szad, &key);
|
||||
if (node != NULL) {
|
||||
/* Use existing space. */
|
||||
extent_tree_szad_remove(&base_avail_szad, node);
|
||||
} else {
|
||||
/* Try to allocate more space. */
|
||||
node = base_chunk_alloc(csize);
|
||||
node = base_chunk_alloc(tsdn, csize);
|
||||
}
|
||||
if (node == NULL) {
|
||||
ret = NULL;
|
||||
@@ -111,7 +113,7 @@ base_alloc(size_t size)
|
||||
extent_node_size_set(node, extent_node_size_get(node) - csize);
|
||||
extent_tree_szad_insert(&base_avail_szad, node);
|
||||
} else
|
||||
base_node_dalloc(node);
|
||||
base_node_dalloc(tsdn, node);
|
||||
if (config_stats) {
|
||||
base_allocated += csize;
|
||||
/*
|
||||
@@ -123,28 +125,29 @@ base_alloc(size_t size)
|
||||
}
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, csize);
|
||||
label_return:
|
||||
malloc_mutex_unlock(&base_mtx);
|
||||
malloc_mutex_unlock(tsdn, &base_mtx);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
base_stats_get(size_t *allocated, size_t *resident, size_t *mapped)
|
||||
base_stats_get(tsdn_t *tsdn, size_t *allocated, size_t *resident,
|
||||
size_t *mapped)
|
||||
{
|
||||
|
||||
malloc_mutex_lock(&base_mtx);
|
||||
malloc_mutex_lock(tsdn, &base_mtx);
|
||||
assert(base_allocated <= base_resident);
|
||||
assert(base_resident <= base_mapped);
|
||||
*allocated = base_allocated;
|
||||
*resident = base_resident;
|
||||
*mapped = base_mapped;
|
||||
malloc_mutex_unlock(&base_mtx);
|
||||
malloc_mutex_unlock(tsdn, &base_mtx);
|
||||
}
|
||||
|
||||
bool
|
||||
base_boot(void)
|
||||
{
|
||||
|
||||
if (malloc_mutex_init(&base_mtx))
|
||||
if (malloc_mutex_init(&base_mtx, "base", WITNESS_RANK_BASE))
|
||||
return (true);
|
||||
extent_tree_szad_new(&base_avail_szad);
|
||||
base_nodes = NULL;
|
||||
@@ -153,22 +156,22 @@ base_boot(void)
|
||||
}
|
||||
|
||||
void
|
||||
base_prefork(void)
|
||||
base_prefork(tsdn_t *tsdn)
|
||||
{
|
||||
|
||||
malloc_mutex_prefork(&base_mtx);
|
||||
malloc_mutex_prefork(tsdn, &base_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
base_postfork_parent(void)
|
||||
base_postfork_parent(tsdn_t *tsdn)
|
||||
{
|
||||
|
||||
malloc_mutex_postfork_parent(&base_mtx);
|
||||
malloc_mutex_postfork_parent(tsdn, &base_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
base_postfork_child(void)
|
||||
base_postfork_child(tsdn_t *tsdn)
|
||||
{
|
||||
|
||||
malloc_mutex_postfork_child(&base_mtx);
|
||||
malloc_mutex_postfork_child(tsdn, &base_mtx);
|
||||
}
|
||||
|
||||
11
src/bitmap.c
11
src/bitmap.c
@@ -74,15 +74,11 @@ bitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo)
|
||||
void
|
||||
bitmap_info_init(bitmap_info_t *binfo, size_t nbits)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(nbits > 0);
|
||||
assert(nbits <= (ZU(1) << LG_BITMAP_MAXBITS));
|
||||
|
||||
i = nbits >> LG_BITMAP_GROUP_NBITS;
|
||||
if (nbits % BITMAP_GROUP_NBITS != 0)
|
||||
i++;
|
||||
binfo->ngroups = i;
|
||||
binfo->ngroups = BITMAP_BITS2GROUPS(nbits);
|
||||
binfo->nbits = nbits;
|
||||
}
|
||||
|
||||
@@ -99,9 +95,10 @@ bitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo)
|
||||
size_t extra;
|
||||
|
||||
memset(bitmap, 0xffU, bitmap_size(binfo));
|
||||
extra = (binfo->nbits % (binfo->ngroups * BITMAP_GROUP_NBITS));
|
||||
extra = (BITMAP_GROUP_NBITS - (binfo->nbits & BITMAP_GROUP_NBITS_MASK))
|
||||
& BITMAP_GROUP_NBITS_MASK;
|
||||
if (extra != 0)
|
||||
bitmap[binfo->ngroups - 1] >>= (BITMAP_GROUP_NBITS - extra);
|
||||
bitmap[binfo->ngroups - 1] >>= extra;
|
||||
}
|
||||
|
||||
#endif /* USE_TREE */
|
||||
|
||||
313
src/chunk.c
313
src/chunk.c
@@ -49,9 +49,10 @@ const chunk_hooks_t chunk_hooks_default = {
|
||||
* definition.
|
||||
*/
|
||||
|
||||
static void chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache,
|
||||
void *chunk, size_t size, bool zeroed, bool committed);
|
||||
static void chunk_record(tsdn_t *tsdn, arena_t *arena,
|
||||
chunk_hooks_t *chunk_hooks, extent_tree_t *chunks_szad,
|
||||
extent_tree_t *chunks_ad, bool cache, void *chunk, size_t size, bool zeroed,
|
||||
bool committed);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -63,23 +64,23 @@ chunk_hooks_get_locked(arena_t *arena)
|
||||
}
|
||||
|
||||
chunk_hooks_t
|
||||
chunk_hooks_get(arena_t *arena)
|
||||
chunk_hooks_get(tsdn_t *tsdn, arena_t *arena)
|
||||
{
|
||||
chunk_hooks_t chunk_hooks;
|
||||
|
||||
malloc_mutex_lock(&arena->chunks_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->chunks_mtx);
|
||||
chunk_hooks = chunk_hooks_get_locked(arena);
|
||||
malloc_mutex_unlock(&arena->chunks_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->chunks_mtx);
|
||||
|
||||
return (chunk_hooks);
|
||||
}
|
||||
|
||||
chunk_hooks_t
|
||||
chunk_hooks_set(arena_t *arena, const chunk_hooks_t *chunk_hooks)
|
||||
chunk_hooks_set(tsdn_t *tsdn, arena_t *arena, const chunk_hooks_t *chunk_hooks)
|
||||
{
|
||||
chunk_hooks_t old_chunk_hooks;
|
||||
|
||||
malloc_mutex_lock(&arena->chunks_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->chunks_mtx);
|
||||
old_chunk_hooks = arena->chunk_hooks;
|
||||
/*
|
||||
* Copy each field atomically so that it is impossible for readers to
|
||||
@@ -104,14 +105,14 @@ chunk_hooks_set(arena_t *arena, const chunk_hooks_t *chunk_hooks)
|
||||
ATOMIC_COPY_HOOK(split);
|
||||
ATOMIC_COPY_HOOK(merge);
|
||||
#undef ATOMIC_COPY_HOOK
|
||||
malloc_mutex_unlock(&arena->chunks_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->chunks_mtx);
|
||||
|
||||
return (old_chunk_hooks);
|
||||
}
|
||||
|
||||
static void
|
||||
chunk_hooks_assure_initialized_impl(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
bool locked)
|
||||
chunk_hooks_assure_initialized_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
chunk_hooks_t *chunk_hooks, bool locked)
|
||||
{
|
||||
static const chunk_hooks_t uninitialized_hooks =
|
||||
CHUNK_HOOKS_INITIALIZER;
|
||||
@@ -119,27 +120,28 @@ chunk_hooks_assure_initialized_impl(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
if (memcmp(chunk_hooks, &uninitialized_hooks, sizeof(chunk_hooks_t)) ==
|
||||
0) {
|
||||
*chunk_hooks = locked ? chunk_hooks_get_locked(arena) :
|
||||
chunk_hooks_get(arena);
|
||||
chunk_hooks_get(tsdn, arena);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chunk_hooks_assure_initialized_locked(arena_t *arena,
|
||||
chunk_hooks_assure_initialized_locked(tsdn_t *tsdn, arena_t *arena,
|
||||
chunk_hooks_t *chunk_hooks)
|
||||
{
|
||||
|
||||
chunk_hooks_assure_initialized_impl(arena, chunk_hooks, true);
|
||||
chunk_hooks_assure_initialized_impl(tsdn, arena, chunk_hooks, true);
|
||||
}
|
||||
|
||||
static void
|
||||
chunk_hooks_assure_initialized(arena_t *arena, chunk_hooks_t *chunk_hooks)
|
||||
chunk_hooks_assure_initialized(tsdn_t *tsdn, arena_t *arena,
|
||||
chunk_hooks_t *chunk_hooks)
|
||||
{
|
||||
|
||||
chunk_hooks_assure_initialized_impl(arena, chunk_hooks, false);
|
||||
chunk_hooks_assure_initialized_impl(tsdn, arena, chunk_hooks, false);
|
||||
}
|
||||
|
||||
bool
|
||||
chunk_register(const void *chunk, const extent_node_t *node)
|
||||
chunk_register(tsdn_t *tsdn, const void *chunk, const extent_node_t *node)
|
||||
{
|
||||
|
||||
assert(extent_node_addr_get(node) == chunk);
|
||||
@@ -159,7 +161,7 @@ chunk_register(const void *chunk, const extent_node_t *node)
|
||||
high = atomic_read_z(&highchunks);
|
||||
}
|
||||
if (cur > high && prof_gdump_get_unlocked())
|
||||
prof_gdump();
|
||||
prof_gdump(tsdn);
|
||||
}
|
||||
|
||||
return (false);
|
||||
@@ -197,7 +199,7 @@ chunk_first_best_fit(arena_t *arena, extent_tree_t *chunks_szad,
|
||||
}
|
||||
|
||||
static void *
|
||||
chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
chunk_recycle(tsdn_t *tsdn, arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache,
|
||||
void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit,
|
||||
bool dalloc_node)
|
||||
@@ -219,8 +221,8 @@ chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
/* Beware size_t wrap-around. */
|
||||
if (alloc_size < size)
|
||||
return (NULL);
|
||||
malloc_mutex_lock(&arena->chunks_mtx);
|
||||
chunk_hooks_assure_initialized_locked(arena, chunk_hooks);
|
||||
malloc_mutex_lock(tsdn, &arena->chunks_mtx);
|
||||
chunk_hooks_assure_initialized_locked(tsdn, arena, chunk_hooks);
|
||||
if (new_addr != NULL) {
|
||||
extent_node_t key;
|
||||
extent_node_init(&key, arena, new_addr, alloc_size, false,
|
||||
@@ -232,7 +234,7 @@ chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
}
|
||||
if (node == NULL || (new_addr != NULL && extent_node_size_get(node) <
|
||||
size)) {
|
||||
malloc_mutex_unlock(&arena->chunks_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->chunks_mtx);
|
||||
return (NULL);
|
||||
}
|
||||
leadsize = ALIGNMENT_CEILING((uintptr_t)extent_node_addr_get(node),
|
||||
@@ -251,7 +253,7 @@ chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
if (leadsize != 0 &&
|
||||
chunk_hooks->split(extent_node_addr_get(node),
|
||||
extent_node_size_get(node), leadsize, size, false, arena->ind)) {
|
||||
malloc_mutex_unlock(&arena->chunks_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->chunks_mtx);
|
||||
return (NULL);
|
||||
}
|
||||
/* Remove node from the tree. */
|
||||
@@ -271,20 +273,21 @@ chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
if (chunk_hooks->split(ret, size + trailsize, size,
|
||||
trailsize, false, arena->ind)) {
|
||||
if (dalloc_node && node != NULL)
|
||||
arena_node_dalloc(arena, node);
|
||||
malloc_mutex_unlock(&arena->chunks_mtx);
|
||||
chunk_record(arena, chunk_hooks, chunks_szad, chunks_ad,
|
||||
cache, ret, size + trailsize, zeroed, committed);
|
||||
arena_node_dalloc(tsdn, arena, node);
|
||||
malloc_mutex_unlock(tsdn, &arena->chunks_mtx);
|
||||
chunk_record(tsdn, arena, chunk_hooks, chunks_szad,
|
||||
chunks_ad, cache, ret, size + trailsize, zeroed,
|
||||
committed);
|
||||
return (NULL);
|
||||
}
|
||||
/* Insert the trailing space as a smaller chunk. */
|
||||
if (node == NULL) {
|
||||
node = arena_node_alloc(arena);
|
||||
node = arena_node_alloc(tsdn, arena);
|
||||
if (node == NULL) {
|
||||
malloc_mutex_unlock(&arena->chunks_mtx);
|
||||
chunk_record(arena, chunk_hooks, chunks_szad,
|
||||
chunks_ad, cache, ret, size + trailsize,
|
||||
zeroed, committed);
|
||||
malloc_mutex_unlock(tsdn, &arena->chunks_mtx);
|
||||
chunk_record(tsdn, arena, chunk_hooks,
|
||||
chunks_szad, chunks_ad, cache, ret, size +
|
||||
trailsize, zeroed, committed);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
@@ -296,16 +299,16 @@ chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
node = NULL;
|
||||
}
|
||||
if (!committed && chunk_hooks->commit(ret, size, 0, size, arena->ind)) {
|
||||
malloc_mutex_unlock(&arena->chunks_mtx);
|
||||
chunk_record(arena, chunk_hooks, chunks_szad, chunks_ad, cache,
|
||||
ret, size, zeroed, committed);
|
||||
malloc_mutex_unlock(tsdn, &arena->chunks_mtx);
|
||||
chunk_record(tsdn, arena, chunk_hooks, chunks_szad, chunks_ad,
|
||||
cache, ret, size, zeroed, committed);
|
||||
return (NULL);
|
||||
}
|
||||
malloc_mutex_unlock(&arena->chunks_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->chunks_mtx);
|
||||
|
||||
assert(dalloc_node || node != NULL);
|
||||
if (dalloc_node && node != NULL)
|
||||
arena_node_dalloc(arena, node);
|
||||
arena_node_dalloc(tsdn, arena, node);
|
||||
if (*zero) {
|
||||
if (!zeroed)
|
||||
memset(ret, 0, size);
|
||||
@@ -313,10 +316,11 @@ chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
size_t i;
|
||||
size_t *p = (size_t *)(uintptr_t)ret;
|
||||
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, size);
|
||||
for (i = 0; i < size / sizeof(size_t); i++)
|
||||
assert(p[i] == 0);
|
||||
}
|
||||
if (config_valgrind)
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, size);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
@@ -328,8 +332,8 @@ chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
* them if they are returned.
|
||||
*/
|
||||
static void *
|
||||
chunk_alloc_core(arena_t *arena, void *new_addr, size_t size, size_t alignment,
|
||||
bool *zero, bool *commit, dss_prec_t dss_prec)
|
||||
chunk_alloc_core(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
|
||||
size_t alignment, bool *zero, bool *commit, dss_prec_t dss_prec)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
@@ -340,8 +344,8 @@ chunk_alloc_core(arena_t *arena, void *new_addr, size_t size, size_t alignment,
|
||||
|
||||
/* "primary" dss. */
|
||||
if (have_dss && dss_prec == dss_prec_primary && (ret =
|
||||
chunk_alloc_dss(arena, new_addr, size, alignment, zero, commit)) !=
|
||||
NULL)
|
||||
chunk_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,
|
||||
commit)) != NULL)
|
||||
return (ret);
|
||||
/* mmap. */
|
||||
if ((ret = chunk_alloc_mmap(new_addr, size, alignment, zero, commit)) !=
|
||||
@@ -349,8 +353,8 @@ chunk_alloc_core(arena_t *arena, void *new_addr, size_t size, size_t alignment,
|
||||
return (ret);
|
||||
/* "secondary" dss. */
|
||||
if (have_dss && dss_prec == dss_prec_secondary && (ret =
|
||||
chunk_alloc_dss(arena, new_addr, size, alignment, zero, commit)) !=
|
||||
NULL)
|
||||
chunk_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,
|
||||
commit)) != NULL)
|
||||
return (ret);
|
||||
|
||||
/* All strategies for allocation failed. */
|
||||
@@ -380,35 +384,33 @@ chunk_alloc_base(size_t size)
|
||||
}
|
||||
|
||||
void *
|
||||
chunk_alloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr,
|
||||
size_t size, size_t alignment, bool *zero, bool dalloc_node)
|
||||
chunk_alloc_cache(tsdn_t *tsdn, arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit,
|
||||
bool dalloc_node)
|
||||
{
|
||||
void *ret;
|
||||
bool commit;
|
||||
|
||||
assert(size != 0);
|
||||
assert((size & chunksize_mask) == 0);
|
||||
assert(alignment != 0);
|
||||
assert((alignment & chunksize_mask) == 0);
|
||||
|
||||
commit = true;
|
||||
ret = chunk_recycle(arena, chunk_hooks, &arena->chunks_szad_cached,
|
||||
&arena->chunks_ad_cached, true, new_addr, size, alignment, zero,
|
||||
&commit, dalloc_node);
|
||||
ret = chunk_recycle(tsdn, arena, chunk_hooks,
|
||||
&arena->chunks_szad_cached, &arena->chunks_ad_cached, true,
|
||||
new_addr, size, alignment, zero, commit, dalloc_node);
|
||||
if (ret == NULL)
|
||||
return (NULL);
|
||||
assert(commit);
|
||||
if (config_valgrind)
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static arena_t *
|
||||
chunk_arena_get(unsigned arena_ind)
|
||||
chunk_arena_get(tsdn_t *tsdn, unsigned arena_ind)
|
||||
{
|
||||
arena_t *arena;
|
||||
|
||||
arena = arena_get(arena_ind, false);
|
||||
arena = arena_get(tsdn, arena_ind, false);
|
||||
/*
|
||||
* The arena we're allocating on behalf of must have been initialized
|
||||
* already.
|
||||
@@ -418,14 +420,12 @@ chunk_arena_get(unsigned arena_ind)
|
||||
}
|
||||
|
||||
static void *
|
||||
chunk_alloc_default(void *new_addr, size_t size, size_t alignment, bool *zero,
|
||||
bool *commit, unsigned arena_ind)
|
||||
chunk_alloc_default_impl(tsdn_t *tsdn, arena_t *arena, void *new_addr,
|
||||
size_t size, size_t alignment, bool *zero, bool *commit)
|
||||
{
|
||||
void *ret;
|
||||
arena_t *arena;
|
||||
|
||||
arena = chunk_arena_get(arena_ind);
|
||||
ret = chunk_alloc_core(arena, new_addr, size, alignment, zero,
|
||||
ret = chunk_alloc_core(tsdn, arena, new_addr, size, alignment, zero,
|
||||
commit, arena->dss_prec);
|
||||
if (ret == NULL)
|
||||
return (NULL);
|
||||
@@ -436,44 +436,73 @@ chunk_alloc_default(void *new_addr, size_t size, size_t alignment, bool *zero,
|
||||
}
|
||||
|
||||
static void *
|
||||
chunk_alloc_retained(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr,
|
||||
size_t size, size_t alignment, bool *zero, bool *commit)
|
||||
chunk_alloc_default(void *new_addr, size_t size, size_t alignment, bool *zero,
|
||||
bool *commit, unsigned arena_ind)
|
||||
{
|
||||
tsdn_t *tsdn;
|
||||
arena_t *arena;
|
||||
|
||||
tsdn = tsdn_fetch();
|
||||
arena = chunk_arena_get(tsdn, arena_ind);
|
||||
|
||||
return (chunk_alloc_default_impl(tsdn, arena, new_addr, size, alignment,
|
||||
zero, commit));
|
||||
}
|
||||
|
||||
static void *
|
||||
chunk_alloc_retained(tsdn_t *tsdn, arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
assert(size != 0);
|
||||
assert((size & chunksize_mask) == 0);
|
||||
assert(alignment != 0);
|
||||
assert((alignment & chunksize_mask) == 0);
|
||||
|
||||
return (chunk_recycle(arena, chunk_hooks, &arena->chunks_szad_retained,
|
||||
&arena->chunks_ad_retained, false, new_addr, size, alignment, zero,
|
||||
commit, true));
|
||||
ret = chunk_recycle(tsdn, arena, chunk_hooks,
|
||||
&arena->chunks_szad_retained, &arena->chunks_ad_retained, false,
|
||||
new_addr, size, alignment, zero, commit, true);
|
||||
|
||||
if (config_stats && ret != NULL)
|
||||
arena->stats.retained -= size;
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void *
|
||||
chunk_alloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr,
|
||||
size_t size, size_t alignment, bool *zero, bool *commit)
|
||||
chunk_alloc_wrapper(tsdn_t *tsdn, arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
chunk_hooks_assure_initialized(arena, chunk_hooks);
|
||||
chunk_hooks_assure_initialized(tsdn, arena, chunk_hooks);
|
||||
|
||||
ret = chunk_alloc_retained(arena, chunk_hooks, new_addr, size,
|
||||
ret = chunk_alloc_retained(tsdn, arena, chunk_hooks, new_addr, size,
|
||||
alignment, zero, commit);
|
||||
if (ret == NULL) {
|
||||
ret = chunk_hooks->alloc(new_addr, size, alignment, zero,
|
||||
commit, arena->ind);
|
||||
if (chunk_hooks->alloc == chunk_alloc_default) {
|
||||
/* Call directly to propagate tsdn. */
|
||||
ret = chunk_alloc_default_impl(tsdn, arena, new_addr,
|
||||
size, alignment, zero, commit);
|
||||
} else {
|
||||
ret = chunk_hooks->alloc(new_addr, size, alignment,
|
||||
zero, commit, arena->ind);
|
||||
}
|
||||
|
||||
if (ret == NULL)
|
||||
return (NULL);
|
||||
|
||||
if (config_valgrind && chunk_hooks->alloc !=
|
||||
chunk_alloc_default)
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, chunksize);
|
||||
}
|
||||
|
||||
if (config_valgrind && chunk_hooks->alloc != chunk_alloc_default)
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, chunksize);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
chunk_record(tsdn_t *tsdn, arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache,
|
||||
void *chunk, size_t size, bool zeroed, bool committed)
|
||||
{
|
||||
@@ -485,8 +514,8 @@ chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
unzeroed = cache || !zeroed;
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(chunk, size);
|
||||
|
||||
malloc_mutex_lock(&arena->chunks_mtx);
|
||||
chunk_hooks_assure_initialized_locked(arena, chunk_hooks);
|
||||
malloc_mutex_lock(tsdn, &arena->chunks_mtx);
|
||||
chunk_hooks_assure_initialized_locked(tsdn, arena, chunk_hooks);
|
||||
extent_node_init(&key, arena, (void *)((uintptr_t)chunk + size), 0,
|
||||
false, false);
|
||||
node = extent_tree_ad_nsearch(chunks_ad, &key);
|
||||
@@ -511,7 +540,7 @@ chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
arena_chunk_cache_maybe_insert(arena, node, cache);
|
||||
} else {
|
||||
/* Coalescing forward failed, so insert a new node. */
|
||||
node = arena_node_alloc(arena);
|
||||
node = arena_node_alloc(tsdn, arena);
|
||||
if (node == NULL) {
|
||||
/*
|
||||
* Node allocation failed, which is an exceedingly
|
||||
@@ -520,8 +549,8 @@ chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
* a virtual memory leak.
|
||||
*/
|
||||
if (cache) {
|
||||
chunk_purge_wrapper(arena, chunk_hooks, chunk,
|
||||
size, 0, size);
|
||||
chunk_purge_wrapper(tsdn, arena, chunk_hooks,
|
||||
chunk, size, 0, size);
|
||||
}
|
||||
goto label_return;
|
||||
}
|
||||
@@ -557,16 +586,16 @@ chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
extent_tree_szad_insert(chunks_szad, node);
|
||||
arena_chunk_cache_maybe_insert(arena, node, cache);
|
||||
|
||||
arena_node_dalloc(arena, prev);
|
||||
arena_node_dalloc(tsdn, arena, prev);
|
||||
}
|
||||
|
||||
label_return:
|
||||
malloc_mutex_unlock(&arena->chunks_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->chunks_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_dalloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
|
||||
size_t size, bool committed)
|
||||
chunk_dalloc_cache(tsdn_t *tsdn, arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *chunk, size_t size, bool committed)
|
||||
{
|
||||
|
||||
assert(chunk != NULL);
|
||||
@@ -574,24 +603,48 @@ chunk_dalloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
|
||||
assert(size != 0);
|
||||
assert((size & chunksize_mask) == 0);
|
||||
|
||||
chunk_record(arena, chunk_hooks, &arena->chunks_szad_cached,
|
||||
chunk_record(tsdn, arena, chunk_hooks, &arena->chunks_szad_cached,
|
||||
&arena->chunks_ad_cached, true, chunk, size, false, committed);
|
||||
arena_maybe_purge(arena);
|
||||
arena_maybe_purge(tsdn, arena);
|
||||
}
|
||||
|
||||
static bool
|
||||
chunk_dalloc_default_impl(void *chunk, size_t size)
|
||||
{
|
||||
|
||||
if (!have_dss || !chunk_in_dss(chunk))
|
||||
return (chunk_dalloc_mmap(chunk, size));
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool
|
||||
chunk_dalloc_default(void *chunk, size_t size, bool committed,
|
||||
unsigned arena_ind)
|
||||
{
|
||||
|
||||
return (chunk_dalloc_default_impl(chunk, size));
|
||||
}
|
||||
|
||||
void
|
||||
chunk_dalloc_arena(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
|
||||
size_t size, bool zeroed, bool committed)
|
||||
chunk_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *chunk, size_t size, bool zeroed, bool committed)
|
||||
{
|
||||
bool err;
|
||||
|
||||
assert(chunk != NULL);
|
||||
assert(CHUNK_ADDR2BASE(chunk) == chunk);
|
||||
assert(size != 0);
|
||||
assert((size & chunksize_mask) == 0);
|
||||
|
||||
chunk_hooks_assure_initialized(arena, chunk_hooks);
|
||||
chunk_hooks_assure_initialized(tsdn, arena, chunk_hooks);
|
||||
/* Try to deallocate. */
|
||||
if (!chunk_hooks->dalloc(chunk, size, committed, arena->ind))
|
||||
if (chunk_hooks->dalloc == chunk_dalloc_default) {
|
||||
/* Call directly to propagate tsdn. */
|
||||
err = chunk_dalloc_default_impl(chunk, size);
|
||||
} else
|
||||
err = chunk_hooks->dalloc(chunk, size, committed, arena->ind);
|
||||
|
||||
if (!err)
|
||||
return;
|
||||
/* Try to decommit; purge if that fails. */
|
||||
if (committed) {
|
||||
@@ -600,29 +653,11 @@ chunk_dalloc_arena(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
|
||||
}
|
||||
zeroed = !committed || !chunk_hooks->purge(chunk, size, 0, size,
|
||||
arena->ind);
|
||||
chunk_record(arena, chunk_hooks, &arena->chunks_szad_retained,
|
||||
chunk_record(tsdn, arena, chunk_hooks, &arena->chunks_szad_retained,
|
||||
&arena->chunks_ad_retained, false, chunk, size, zeroed, committed);
|
||||
}
|
||||
|
||||
static bool
|
||||
chunk_dalloc_default(void *chunk, size_t size, bool committed,
|
||||
unsigned arena_ind)
|
||||
{
|
||||
|
||||
if (!have_dss || !chunk_in_dss(chunk))
|
||||
return (chunk_dalloc_mmap(chunk, size));
|
||||
return (true);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_dalloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
|
||||
size_t size, bool committed)
|
||||
{
|
||||
|
||||
chunk_hooks_assure_initialized(arena, chunk_hooks);
|
||||
chunk_hooks->dalloc(chunk, size, committed, arena->ind);
|
||||
if (config_valgrind && chunk_hooks->dalloc != chunk_dalloc_default)
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(chunk, size);
|
||||
if (config_stats)
|
||||
arena->stats.retained += size;
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -643,8 +678,9 @@ chunk_decommit_default(void *chunk, size_t size, size_t offset, size_t length,
|
||||
length));
|
||||
}
|
||||
|
||||
bool
|
||||
chunk_purge_arena(arena_t *arena, void *chunk, size_t offset, size_t length)
|
||||
static bool
|
||||
chunk_purge_default(void *chunk, size_t size, size_t offset, size_t length,
|
||||
unsigned arena_ind)
|
||||
{
|
||||
|
||||
assert(chunk != NULL);
|
||||
@@ -657,21 +693,12 @@ chunk_purge_arena(arena_t *arena, void *chunk, size_t offset, size_t length)
|
||||
length));
|
||||
}
|
||||
|
||||
static bool
|
||||
chunk_purge_default(void *chunk, size_t size, size_t offset, size_t length,
|
||||
unsigned arena_ind)
|
||||
{
|
||||
|
||||
return (chunk_purge_arena(chunk_arena_get(arena_ind), chunk, offset,
|
||||
length));
|
||||
}
|
||||
|
||||
bool
|
||||
chunk_purge_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
|
||||
size_t size, size_t offset, size_t length)
|
||||
chunk_purge_wrapper(tsdn_t *tsdn, arena_t *arena, chunk_hooks_t *chunk_hooks,
|
||||
void *chunk, size_t size, size_t offset, size_t length)
|
||||
{
|
||||
|
||||
chunk_hooks_assure_initialized(arena, chunk_hooks);
|
||||
chunk_hooks_assure_initialized(tsdn, arena, chunk_hooks);
|
||||
return (chunk_hooks->purge(chunk, size, offset, length, arena->ind));
|
||||
}
|
||||
|
||||
@@ -686,23 +713,30 @@ chunk_split_default(void *chunk, size_t size, size_t size_a, size_t size_b,
|
||||
}
|
||||
|
||||
static bool
|
||||
chunk_merge_default(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b,
|
||||
bool committed, unsigned arena_ind)
|
||||
chunk_merge_default_impl(void *chunk_a, void *chunk_b)
|
||||
{
|
||||
|
||||
if (!maps_coalesce)
|
||||
return (true);
|
||||
if (have_dss && chunk_in_dss(chunk_a) != chunk_in_dss(chunk_b))
|
||||
if (have_dss && !chunk_dss_mergeable(chunk_a, chunk_b))
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
static bool
|
||||
chunk_merge_default(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b,
|
||||
bool committed, unsigned arena_ind)
|
||||
{
|
||||
|
||||
return (chunk_merge_default_impl(chunk_a, chunk_b));
|
||||
}
|
||||
|
||||
static rtree_node_elm_t *
|
||||
chunks_rtree_node_alloc(size_t nelms)
|
||||
{
|
||||
|
||||
return ((rtree_node_elm_t *)base_alloc(nelms *
|
||||
return ((rtree_node_elm_t *)base_alloc(TSDN_NULL, nelms *
|
||||
sizeof(rtree_node_elm_t)));
|
||||
}
|
||||
|
||||
@@ -739,32 +773,11 @@ chunk_boot(void)
|
||||
chunksize_mask = chunksize - 1;
|
||||
chunk_npages = (chunksize >> LG_PAGE);
|
||||
|
||||
if (have_dss && chunk_dss_boot())
|
||||
return (true);
|
||||
if (have_dss)
|
||||
chunk_dss_boot();
|
||||
if (rtree_new(&chunks_rtree, (unsigned)((ZU(1) << (LG_SIZEOF_PTR+3)) -
|
||||
opt_lg_chunk), chunks_rtree_node_alloc, NULL))
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_prefork(void)
|
||||
{
|
||||
|
||||
chunk_dss_prefork();
|
||||
}
|
||||
|
||||
void
|
||||
chunk_postfork_parent(void)
|
||||
{
|
||||
|
||||
chunk_dss_postfork_parent();
|
||||
}
|
||||
|
||||
void
|
||||
chunk_postfork_child(void)
|
||||
{
|
||||
|
||||
chunk_dss_postfork_child();
|
||||
}
|
||||
|
||||
189
src/chunk_dss.c
189
src/chunk_dss.c
@@ -10,20 +10,19 @@ const char *dss_prec_names[] = {
|
||||
"N/A"
|
||||
};
|
||||
|
||||
/* Current dss precedence default, used when creating new arenas. */
|
||||
static dss_prec_t dss_prec_default = DSS_PREC_DEFAULT;
|
||||
|
||||
/*
|
||||
* Protects sbrk() calls. This avoids malloc races among threads, though it
|
||||
* does not protect against races with threads that call sbrk() directly.
|
||||
* Current dss precedence default, used when creating new arenas. NB: This is
|
||||
* stored as unsigned rather than dss_prec_t because in principle there's no
|
||||
* guarantee that sizeof(dss_prec_t) is the same as sizeof(unsigned), and we use
|
||||
* atomic operations to synchronize the setting.
|
||||
*/
|
||||
static malloc_mutex_t dss_mtx;
|
||||
static unsigned dss_prec_default = (unsigned)DSS_PREC_DEFAULT;
|
||||
|
||||
/* Base address of the DSS. */
|
||||
static void *dss_base;
|
||||
/* Current end of the DSS, or ((void *)-1) if the DSS is exhausted. */
|
||||
static void *dss_prev;
|
||||
/* Current upper limit on DSS addresses. */
|
||||
/* Atomic boolean indicating whether the DSS is exhausted. */
|
||||
static unsigned dss_exhausted;
|
||||
/* Atomic current upper limit on DSS addresses. */
|
||||
static void *dss_max;
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -47,9 +46,7 @@ chunk_dss_prec_get(void)
|
||||
|
||||
if (!have_dss)
|
||||
return (dss_prec_disabled);
|
||||
malloc_mutex_lock(&dss_mtx);
|
||||
ret = dss_prec_default;
|
||||
malloc_mutex_unlock(&dss_mtx);
|
||||
ret = (dss_prec_t)atomic_read_u(&dss_prec_default);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -59,15 +56,46 @@ chunk_dss_prec_set(dss_prec_t dss_prec)
|
||||
|
||||
if (!have_dss)
|
||||
return (dss_prec != dss_prec_disabled);
|
||||
malloc_mutex_lock(&dss_mtx);
|
||||
dss_prec_default = dss_prec;
|
||||
malloc_mutex_unlock(&dss_mtx);
|
||||
atomic_write_u(&dss_prec_default, (unsigned)dss_prec);
|
||||
return (false);
|
||||
}
|
||||
|
||||
static void *
|
||||
chunk_dss_max_update(void *new_addr)
|
||||
{
|
||||
void *max_cur;
|
||||
spin_t spinner;
|
||||
|
||||
/*
|
||||
* Get the current end of the DSS as max_cur and assure that dss_max is
|
||||
* up to date.
|
||||
*/
|
||||
spin_init(&spinner);
|
||||
while (true) {
|
||||
void *max_prev = atomic_read_p(&dss_max);
|
||||
|
||||
max_cur = chunk_dss_sbrk(0);
|
||||
if ((uintptr_t)max_prev > (uintptr_t)max_cur) {
|
||||
/*
|
||||
* Another thread optimistically updated dss_max. Wait
|
||||
* for it to finish.
|
||||
*/
|
||||
spin_adaptive(&spinner);
|
||||
continue;
|
||||
}
|
||||
if (!atomic_cas_p(&dss_max, max_prev, max_cur))
|
||||
break;
|
||||
}
|
||||
/* Fixed new_addr can only be supported if it is at the edge of DSS. */
|
||||
if (new_addr != NULL && max_cur != new_addr)
|
||||
return (NULL);
|
||||
|
||||
return (max_cur);
|
||||
}
|
||||
|
||||
void *
|
||||
chunk_alloc_dss(arena_t *arena, void *new_addr, size_t size, size_t alignment,
|
||||
bool *zero, bool *commit)
|
||||
chunk_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
|
||||
size_t alignment, bool *zero, bool *commit)
|
||||
{
|
||||
cassert(have_dss);
|
||||
assert(size > 0 && (size & chunksize_mask) == 0);
|
||||
@@ -80,28 +108,20 @@ chunk_alloc_dss(arena_t *arena, void *new_addr, size_t size, size_t alignment,
|
||||
if ((intptr_t)size < 0)
|
||||
return (NULL);
|
||||
|
||||
malloc_mutex_lock(&dss_mtx);
|
||||
if (dss_prev != (void *)-1) {
|
||||
|
||||
if (!atomic_read_u(&dss_exhausted)) {
|
||||
/*
|
||||
* The loop is necessary to recover from races with other
|
||||
* threads that are using the DSS for something other than
|
||||
* malloc.
|
||||
*/
|
||||
do {
|
||||
void *ret, *cpad, *dss_next;
|
||||
while (true) {
|
||||
void *ret, *cpad, *max_cur, *dss_next, *dss_prev;
|
||||
size_t gap_size, cpad_size;
|
||||
intptr_t incr;
|
||||
/* Avoid an unnecessary system call. */
|
||||
if (new_addr != NULL && dss_max != new_addr)
|
||||
break;
|
||||
|
||||
/* Get the current end of the DSS. */
|
||||
dss_max = chunk_dss_sbrk(0);
|
||||
|
||||
/* Make sure the earlier condition still holds. */
|
||||
if (new_addr != NULL && dss_max != new_addr)
|
||||
break;
|
||||
max_cur = chunk_dss_max_update(new_addr);
|
||||
if (max_cur == NULL)
|
||||
goto label_oom;
|
||||
|
||||
/*
|
||||
* Calculate how much padding is necessary to
|
||||
@@ -120,23 +140,29 @@ chunk_alloc_dss(arena_t *arena, void *new_addr, size_t size, size_t alignment,
|
||||
cpad_size = (uintptr_t)ret - (uintptr_t)cpad;
|
||||
dss_next = (void *)((uintptr_t)ret + size);
|
||||
if ((uintptr_t)ret < (uintptr_t)dss_max ||
|
||||
(uintptr_t)dss_next < (uintptr_t)dss_max) {
|
||||
/* Wrap-around. */
|
||||
malloc_mutex_unlock(&dss_mtx);
|
||||
return (NULL);
|
||||
}
|
||||
(uintptr_t)dss_next < (uintptr_t)dss_max)
|
||||
goto label_oom; /* Wrap-around. */
|
||||
incr = gap_size + cpad_size + size;
|
||||
|
||||
/*
|
||||
* Optimistically update dss_max, and roll back below if
|
||||
* sbrk() fails. No other thread will try to extend the
|
||||
* DSS while dss_max is greater than the current DSS
|
||||
* max reported by sbrk(0).
|
||||
*/
|
||||
if (atomic_cas_p(&dss_max, max_cur, dss_next))
|
||||
continue;
|
||||
|
||||
/* Try to allocate. */
|
||||
dss_prev = chunk_dss_sbrk(incr);
|
||||
if (dss_prev == dss_max) {
|
||||
if (dss_prev == max_cur) {
|
||||
/* Success. */
|
||||
dss_max = dss_next;
|
||||
malloc_mutex_unlock(&dss_mtx);
|
||||
if (cpad_size != 0) {
|
||||
chunk_hooks_t chunk_hooks =
|
||||
CHUNK_HOOKS_INITIALIZER;
|
||||
chunk_dalloc_wrapper(arena,
|
||||
chunk_dalloc_wrapper(tsdn, arena,
|
||||
&chunk_hooks, cpad, cpad_size,
|
||||
true);
|
||||
false, true);
|
||||
}
|
||||
if (*zero) {
|
||||
JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(
|
||||
@@ -147,68 +173,65 @@ chunk_alloc_dss(arena_t *arena, void *new_addr, size_t size, size_t alignment,
|
||||
*commit = pages_decommit(ret, size);
|
||||
return (ret);
|
||||
}
|
||||
} while (dss_prev != (void *)-1);
|
||||
}
|
||||
malloc_mutex_unlock(&dss_mtx);
|
||||
|
||||
/*
|
||||
* Failure, whether due to OOM or a race with a raw
|
||||
* sbrk() call from outside the allocator. Try to roll
|
||||
* back optimistic dss_max update; if rollback fails,
|
||||
* it's due to another caller of this function having
|
||||
* succeeded since this invocation started, in which
|
||||
* case rollback is not necessary.
|
||||
*/
|
||||
atomic_cas_p(&dss_max, dss_next, max_cur);
|
||||
if (dss_prev == (void *)-1) {
|
||||
/* OOM. */
|
||||
atomic_write_u(&dss_exhausted, (unsigned)true);
|
||||
goto label_oom;
|
||||
}
|
||||
}
|
||||
}
|
||||
label_oom:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
chunk_in_dss_helper(void *chunk, void *max)
|
||||
{
|
||||
|
||||
return ((uintptr_t)chunk >= (uintptr_t)dss_base && (uintptr_t)chunk <
|
||||
(uintptr_t)max);
|
||||
}
|
||||
|
||||
bool
|
||||
chunk_in_dss(void *chunk)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
cassert(have_dss);
|
||||
|
||||
malloc_mutex_lock(&dss_mtx);
|
||||
if ((uintptr_t)chunk >= (uintptr_t)dss_base
|
||||
&& (uintptr_t)chunk < (uintptr_t)dss_max)
|
||||
ret = true;
|
||||
else
|
||||
ret = false;
|
||||
malloc_mutex_unlock(&dss_mtx);
|
||||
|
||||
return (ret);
|
||||
return (chunk_in_dss_helper(chunk, atomic_read_p(&dss_max)));
|
||||
}
|
||||
|
||||
bool
|
||||
chunk_dss_mergeable(void *chunk_a, void *chunk_b)
|
||||
{
|
||||
void *max;
|
||||
|
||||
cassert(have_dss);
|
||||
|
||||
max = atomic_read_p(&dss_max);
|
||||
return (chunk_in_dss_helper(chunk_a, max) ==
|
||||
chunk_in_dss_helper(chunk_b, max));
|
||||
}
|
||||
|
||||
void
|
||||
chunk_dss_boot(void)
|
||||
{
|
||||
|
||||
cassert(have_dss);
|
||||
|
||||
if (malloc_mutex_init(&dss_mtx))
|
||||
return (true);
|
||||
dss_base = chunk_dss_sbrk(0);
|
||||
dss_prev = dss_base;
|
||||
dss_exhausted = (unsigned)(dss_base == (void *)-1);
|
||||
dss_max = dss_base;
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_dss_prefork(void)
|
||||
{
|
||||
|
||||
if (have_dss)
|
||||
malloc_mutex_prefork(&dss_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_dss_postfork_parent(void)
|
||||
{
|
||||
|
||||
if (have_dss)
|
||||
malloc_mutex_postfork_parent(&dss_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_dss_postfork_child(void)
|
||||
{
|
||||
|
||||
if (have_dss)
|
||||
malloc_mutex_postfork_child(&dss_mtx);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -16,18 +16,16 @@ chunk_alloc_mmap_slow(size_t size, size_t alignment, bool *zero, bool *commit)
|
||||
do {
|
||||
void *pages;
|
||||
size_t leadsize;
|
||||
pages = pages_map(NULL, alloc_size);
|
||||
pages = pages_map(NULL, alloc_size, commit);
|
||||
if (pages == NULL)
|
||||
return (NULL);
|
||||
leadsize = ALIGNMENT_CEILING((uintptr_t)pages, alignment) -
|
||||
(uintptr_t)pages;
|
||||
ret = pages_trim(pages, alloc_size, leadsize, size);
|
||||
ret = pages_trim(pages, alloc_size, leadsize, size, commit);
|
||||
} while (ret == NULL);
|
||||
|
||||
assert(ret != NULL);
|
||||
*zero = true;
|
||||
if (!*commit)
|
||||
*commit = pages_decommit(ret, size);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -54,7 +52,7 @@ chunk_alloc_mmap(void *new_addr, size_t size, size_t alignment, bool *zero,
|
||||
assert(alignment != 0);
|
||||
assert((alignment & chunksize_mask) == 0);
|
||||
|
||||
ret = pages_map(new_addr, size);
|
||||
ret = pages_map(new_addr, size, commit);
|
||||
if (ret == NULL || ret == new_addr)
|
||||
return (ret);
|
||||
assert(new_addr == NULL);
|
||||
@@ -66,8 +64,6 @@ chunk_alloc_mmap(void *new_addr, size_t size, size_t alignment, bool *zero,
|
||||
|
||||
assert(ret != NULL);
|
||||
*zero = true;
|
||||
if (!*commit)
|
||||
*commit = pages_decommit(ret, size);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
29
src/ckh.c
29
src/ckh.c
@@ -99,7 +99,8 @@ ckh_try_bucket_insert(ckh_t *ckh, size_t bucket, const void *key,
|
||||
* Cycle through the cells in the bucket, starting at a random position.
|
||||
* The randomness avoids worst-case search overhead as buckets fill up.
|
||||
*/
|
||||
offset = (unsigned)prng_lg_range(&ckh->prng_state, LG_CKH_BUCKET_CELLS);
|
||||
offset = (unsigned)prng_lg_range_u64(&ckh->prng_state,
|
||||
LG_CKH_BUCKET_CELLS);
|
||||
for (i = 0; i < (ZU(1) << LG_CKH_BUCKET_CELLS); i++) {
|
||||
cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) +
|
||||
((i + offset) & ((ZU(1) << LG_CKH_BUCKET_CELLS) - 1))];
|
||||
@@ -141,7 +142,7 @@ ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey,
|
||||
* were an item for which both hashes indicated the same
|
||||
* bucket.
|
||||
*/
|
||||
i = (unsigned)prng_lg_range(&ckh->prng_state,
|
||||
i = (unsigned)prng_lg_range_u64(&ckh->prng_state,
|
||||
LG_CKH_BUCKET_CELLS);
|
||||
cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + i];
|
||||
assert(cell->key != NULL);
|
||||
@@ -270,8 +271,8 @@ ckh_grow(tsd_t *tsd, ckh_t *ckh)
|
||||
ret = true;
|
||||
goto label_return;
|
||||
}
|
||||
tab = (ckhc_t *)ipallocztm(tsd, usize, CACHELINE, true, NULL,
|
||||
true, NULL);
|
||||
tab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE,
|
||||
true, NULL, true, arena_ichoose(tsd, NULL));
|
||||
if (tab == NULL) {
|
||||
ret = true;
|
||||
goto label_return;
|
||||
@@ -283,12 +284,12 @@ ckh_grow(tsd_t *tsd, ckh_t *ckh)
|
||||
ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS;
|
||||
|
||||
if (!ckh_rebuild(ckh, tab)) {
|
||||
idalloctm(tsd, tab, tcache_get(tsd, false), true, true);
|
||||
idalloctm(tsd_tsdn(tsd), tab, NULL, true, true);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Rebuilding failed, so back out partially rebuilt table. */
|
||||
idalloctm(tsd, ckh->tab, tcache_get(tsd, false), true, true);
|
||||
idalloctm(tsd_tsdn(tsd), ckh->tab, NULL, true, true);
|
||||
ckh->tab = tab;
|
||||
ckh->lg_curbuckets = lg_prevbuckets;
|
||||
}
|
||||
@@ -314,8 +315,8 @@ ckh_shrink(tsd_t *tsd, ckh_t *ckh)
|
||||
usize = sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);
|
||||
if (unlikely(usize == 0 || usize > HUGE_MAXCLASS))
|
||||
return;
|
||||
tab = (ckhc_t *)ipallocztm(tsd, usize, CACHELINE, true, NULL, true,
|
||||
NULL);
|
||||
tab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, true, NULL,
|
||||
true, arena_ichoose(tsd, NULL));
|
||||
if (tab == NULL) {
|
||||
/*
|
||||
* An OOM error isn't worth propagating, since it doesn't
|
||||
@@ -330,7 +331,7 @@ ckh_shrink(tsd_t *tsd, ckh_t *ckh)
|
||||
ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS;
|
||||
|
||||
if (!ckh_rebuild(ckh, tab)) {
|
||||
idalloctm(tsd, tab, tcache_get(tsd, false), true, true);
|
||||
idalloctm(tsd_tsdn(tsd), tab, NULL, true, true);
|
||||
#ifdef CKH_COUNT
|
||||
ckh->nshrinks++;
|
||||
#endif
|
||||
@@ -338,7 +339,7 @@ ckh_shrink(tsd_t *tsd, ckh_t *ckh)
|
||||
}
|
||||
|
||||
/* Rebuilding failed, so back out partially rebuilt table. */
|
||||
idalloctm(tsd, ckh->tab, tcache_get(tsd, false), true, true);
|
||||
idalloctm(tsd_tsdn(tsd), ckh->tab, NULL, true, true);
|
||||
ckh->tab = tab;
|
||||
ckh->lg_curbuckets = lg_prevbuckets;
|
||||
#ifdef CKH_COUNT
|
||||
@@ -391,8 +392,8 @@ ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,
|
||||
ret = true;
|
||||
goto label_return;
|
||||
}
|
||||
ckh->tab = (ckhc_t *)ipallocztm(tsd, usize, CACHELINE, true, NULL, true,
|
||||
NULL);
|
||||
ckh->tab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, true,
|
||||
NULL, true, arena_ichoose(tsd, NULL));
|
||||
if (ckh->tab == NULL) {
|
||||
ret = true;
|
||||
goto label_return;
|
||||
@@ -421,9 +422,9 @@ ckh_delete(tsd_t *tsd, ckh_t *ckh)
|
||||
(unsigned long long)ckh->nrelocs);
|
||||
#endif
|
||||
|
||||
idalloctm(tsd, ckh->tab, tcache_get(tsd, false), true, true);
|
||||
idalloctm(tsd_tsdn(tsd), ckh->tab, NULL, true, true);
|
||||
if (config_debug)
|
||||
memset(ckh, 0x5a, sizeof(ckh_t));
|
||||
memset(ckh, JEMALLOC_FREE_JUNK, sizeof(ckh_t));
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
205
src/huge.c
205
src/huge.c
@@ -15,12 +15,21 @@ huge_node_get(const void *ptr)
|
||||
}
|
||||
|
||||
static bool
|
||||
huge_node_set(const void *ptr, extent_node_t *node)
|
||||
huge_node_set(tsdn_t *tsdn, const void *ptr, extent_node_t *node)
|
||||
{
|
||||
|
||||
assert(extent_node_addr_get(node) == ptr);
|
||||
assert(!extent_node_achunk_get(node));
|
||||
return (chunk_register(ptr, node));
|
||||
return (chunk_register(tsdn, ptr, node));
|
||||
}
|
||||
|
||||
static void
|
||||
huge_node_reset(tsdn_t *tsdn, const void *ptr, extent_node_t *node)
|
||||
{
|
||||
bool err;
|
||||
|
||||
err = huge_node_set(tsdn, ptr, node);
|
||||
assert(!err);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -31,34 +40,37 @@ huge_node_unset(const void *ptr, const extent_node_t *node)
|
||||
}
|
||||
|
||||
void *
|
||||
huge_malloc(tsd_t *tsd, arena_t *arena, size_t usize, bool zero,
|
||||
tcache_t *tcache)
|
||||
huge_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero)
|
||||
{
|
||||
|
||||
assert(usize == s2u(usize));
|
||||
|
||||
return (huge_palloc(tsd, arena, usize, chunksize, zero, tcache));
|
||||
return (huge_palloc(tsdn, arena, usize, chunksize, zero));
|
||||
}
|
||||
|
||||
void *
|
||||
huge_palloc(tsd_t *tsd, arena_t *arena, size_t usize, size_t alignment,
|
||||
bool zero, tcache_t *tcache)
|
||||
huge_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
|
||||
bool zero)
|
||||
{
|
||||
void *ret;
|
||||
size_t ausize;
|
||||
arena_t *iarena;
|
||||
extent_node_t *node;
|
||||
bool is_zeroed;
|
||||
|
||||
/* Allocate one or more contiguous chunks for this request. */
|
||||
|
||||
assert(!tsdn_null(tsdn) || arena != NULL);
|
||||
|
||||
ausize = sa2u(usize, alignment);
|
||||
if (unlikely(ausize == 0 || ausize > HUGE_MAXCLASS))
|
||||
return (NULL);
|
||||
assert(ausize >= chunksize);
|
||||
|
||||
/* Allocate an extent node with which to track the chunk. */
|
||||
node = ipallocztm(tsd, CACHELINE_CEILING(sizeof(extent_node_t)),
|
||||
CACHELINE, false, tcache, true, arena);
|
||||
iarena = (!tsdn_null(tsdn)) ? arena_ichoose(tsdn_tsd(tsdn), NULL) : a0get();
|
||||
node = ipallocztm(tsdn, CACHELINE_CEILING(sizeof(extent_node_t)),
|
||||
CACHELINE, false, NULL, true, iarena);
|
||||
if (node == NULL)
|
||||
return (NULL);
|
||||
|
||||
@@ -67,34 +79,35 @@ huge_palloc(tsd_t *tsd, arena_t *arena, size_t usize, size_t alignment,
|
||||
* it is possible to make correct junk/zero fill decisions below.
|
||||
*/
|
||||
is_zeroed = zero;
|
||||
arena = arena_choose(tsd, arena);
|
||||
if (unlikely(arena == NULL) || (ret = arena_chunk_alloc_huge(arena,
|
||||
usize, alignment, &is_zeroed)) == NULL) {
|
||||
idalloctm(tsd, node, tcache, true, true);
|
||||
if (likely(!tsdn_null(tsdn)))
|
||||
arena = arena_choose(tsdn_tsd(tsdn), arena);
|
||||
if (unlikely(arena == NULL) || (ret = arena_chunk_alloc_huge(tsdn,
|
||||
arena, usize, alignment, &is_zeroed)) == NULL) {
|
||||
idalloctm(tsdn, node, NULL, true, true);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
extent_node_init(node, arena, ret, usize, is_zeroed, true);
|
||||
|
||||
if (huge_node_set(ret, node)) {
|
||||
arena_chunk_dalloc_huge(arena, ret, usize);
|
||||
idalloctm(tsd, node, tcache, true, true);
|
||||
if (huge_node_set(tsdn, ret, node)) {
|
||||
arena_chunk_dalloc_huge(tsdn, arena, ret, usize);
|
||||
idalloctm(tsdn, node, NULL, true, true);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Insert node into huge. */
|
||||
malloc_mutex_lock(&arena->huge_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->huge_mtx);
|
||||
ql_elm_new(node, ql_link);
|
||||
ql_tail_insert(&arena->huge, node, ql_link);
|
||||
malloc_mutex_unlock(&arena->huge_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->huge_mtx);
|
||||
|
||||
if (zero || (config_fill && unlikely(opt_zero))) {
|
||||
if (!is_zeroed)
|
||||
memset(ret, 0, usize);
|
||||
} else if (config_fill && unlikely(opt_junk_alloc))
|
||||
memset(ret, 0xa5, usize);
|
||||
memset(ret, JEMALLOC_ALLOC_JUNK, usize);
|
||||
|
||||
arena_decay_tick(tsd, arena);
|
||||
arena_decay_tick(tsdn, arena);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -112,7 +125,7 @@ huge_dalloc_junk(void *ptr, size_t usize)
|
||||
* unmapped.
|
||||
*/
|
||||
if (!config_munmap || (have_dss && chunk_in_dss(ptr)))
|
||||
memset(ptr, 0x5a, usize);
|
||||
memset(ptr, JEMALLOC_FREE_JUNK, usize);
|
||||
}
|
||||
}
|
||||
#ifdef JEMALLOC_JET
|
||||
@@ -122,8 +135,8 @@ huge_dalloc_junk_t *huge_dalloc_junk = JEMALLOC_N(huge_dalloc_junk_impl);
|
||||
#endif
|
||||
|
||||
static void
|
||||
huge_ralloc_no_move_similar(void *ptr, size_t oldsize, size_t usize_min,
|
||||
size_t usize_max, bool zero)
|
||||
huge_ralloc_no_move_similar(tsdn_t *tsdn, void *ptr, size_t oldsize,
|
||||
size_t usize_min, size_t usize_max, bool zero)
|
||||
{
|
||||
size_t usize, usize_next;
|
||||
extent_node_t *node;
|
||||
@@ -147,24 +160,28 @@ huge_ralloc_no_move_similar(void *ptr, size_t oldsize, size_t usize_min,
|
||||
if (oldsize > usize) {
|
||||
size_t sdiff = oldsize - usize;
|
||||
if (config_fill && unlikely(opt_junk_free)) {
|
||||
memset((void *)((uintptr_t)ptr + usize), 0x5a, sdiff);
|
||||
memset((void *)((uintptr_t)ptr + usize),
|
||||
JEMALLOC_FREE_JUNK, sdiff);
|
||||
post_zeroed = false;
|
||||
} else {
|
||||
post_zeroed = !chunk_purge_wrapper(arena, &chunk_hooks,
|
||||
ptr, CHUNK_CEILING(oldsize), usize, sdiff);
|
||||
post_zeroed = !chunk_purge_wrapper(tsdn, arena,
|
||||
&chunk_hooks, ptr, CHUNK_CEILING(oldsize), usize,
|
||||
sdiff);
|
||||
}
|
||||
} else
|
||||
post_zeroed = pre_zeroed;
|
||||
|
||||
malloc_mutex_lock(&arena->huge_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->huge_mtx);
|
||||
/* Update the size of the huge allocation. */
|
||||
huge_node_unset(ptr, node);
|
||||
assert(extent_node_size_get(node) != usize);
|
||||
extent_node_size_set(node, usize);
|
||||
huge_node_reset(tsdn, ptr, node);
|
||||
/* Update zeroed. */
|
||||
extent_node_zeroed_set(node, post_zeroed);
|
||||
malloc_mutex_unlock(&arena->huge_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->huge_mtx);
|
||||
|
||||
arena_chunk_ralloc_huge_similar(arena, ptr, oldsize, usize);
|
||||
arena_chunk_ralloc_huge_similar(tsdn, arena, ptr, oldsize, usize);
|
||||
|
||||
/* Fill if necessary (growing). */
|
||||
if (oldsize < usize) {
|
||||
@@ -174,14 +191,15 @@ huge_ralloc_no_move_similar(void *ptr, size_t oldsize, size_t usize_min,
|
||||
usize - oldsize);
|
||||
}
|
||||
} else if (config_fill && unlikely(opt_junk_alloc)) {
|
||||
memset((void *)((uintptr_t)ptr + oldsize), 0xa5, usize -
|
||||
oldsize);
|
||||
memset((void *)((uintptr_t)ptr + oldsize),
|
||||
JEMALLOC_ALLOC_JUNK, usize - oldsize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
huge_ralloc_no_move_shrink(void *ptr, size_t oldsize, size_t usize)
|
||||
huge_ralloc_no_move_shrink(tsdn_t *tsdn, void *ptr, size_t oldsize,
|
||||
size_t usize)
|
||||
{
|
||||
extent_node_t *node;
|
||||
arena_t *arena;
|
||||
@@ -192,7 +210,7 @@ huge_ralloc_no_move_shrink(void *ptr, size_t oldsize, size_t usize)
|
||||
node = huge_node_get(ptr);
|
||||
arena = extent_node_arena_get(node);
|
||||
pre_zeroed = extent_node_zeroed_get(node);
|
||||
chunk_hooks = chunk_hooks_get(arena);
|
||||
chunk_hooks = chunk_hooks_get(tsdn, arena);
|
||||
|
||||
assert(oldsize > usize);
|
||||
|
||||
@@ -209,53 +227,58 @@ huge_ralloc_no_move_shrink(void *ptr, size_t oldsize, size_t usize)
|
||||
sdiff);
|
||||
post_zeroed = false;
|
||||
} else {
|
||||
post_zeroed = !chunk_purge_wrapper(arena, &chunk_hooks,
|
||||
CHUNK_ADDR2BASE((uintptr_t)ptr + usize),
|
||||
CHUNK_CEILING(oldsize),
|
||||
post_zeroed = !chunk_purge_wrapper(tsdn, arena,
|
||||
&chunk_hooks, CHUNK_ADDR2BASE((uintptr_t)ptr +
|
||||
usize), CHUNK_CEILING(oldsize),
|
||||
CHUNK_ADDR2OFFSET((uintptr_t)ptr + usize), sdiff);
|
||||
}
|
||||
} else
|
||||
post_zeroed = pre_zeroed;
|
||||
|
||||
malloc_mutex_lock(&arena->huge_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->huge_mtx);
|
||||
/* Update the size of the huge allocation. */
|
||||
huge_node_unset(ptr, node);
|
||||
extent_node_size_set(node, usize);
|
||||
huge_node_reset(tsdn, ptr, node);
|
||||
/* Update zeroed. */
|
||||
extent_node_zeroed_set(node, post_zeroed);
|
||||
malloc_mutex_unlock(&arena->huge_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->huge_mtx);
|
||||
|
||||
/* Zap the excess chunks. */
|
||||
arena_chunk_ralloc_huge_shrink(arena, ptr, oldsize, usize);
|
||||
arena_chunk_ralloc_huge_shrink(tsdn, arena, ptr, oldsize, usize);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
static bool
|
||||
huge_ralloc_no_move_expand(void *ptr, size_t oldsize, size_t usize, bool zero) {
|
||||
huge_ralloc_no_move_expand(tsdn_t *tsdn, void *ptr, size_t oldsize,
|
||||
size_t usize, bool zero) {
|
||||
extent_node_t *node;
|
||||
arena_t *arena;
|
||||
bool is_zeroed_subchunk, is_zeroed_chunk;
|
||||
|
||||
node = huge_node_get(ptr);
|
||||
arena = extent_node_arena_get(node);
|
||||
malloc_mutex_lock(&arena->huge_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->huge_mtx);
|
||||
is_zeroed_subchunk = extent_node_zeroed_get(node);
|
||||
malloc_mutex_unlock(&arena->huge_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->huge_mtx);
|
||||
|
||||
/*
|
||||
* Copy zero into is_zeroed_chunk and pass the copy to chunk_alloc(), so
|
||||
* that it is possible to make correct junk/zero fill decisions below.
|
||||
* Use is_zeroed_chunk to detect whether the trailing memory is zeroed,
|
||||
* update extent's zeroed field, and zero as necessary.
|
||||
*/
|
||||
is_zeroed_chunk = zero;
|
||||
|
||||
if (arena_chunk_ralloc_huge_expand(arena, ptr, oldsize, usize,
|
||||
is_zeroed_chunk = false;
|
||||
if (arena_chunk_ralloc_huge_expand(tsdn, arena, ptr, oldsize, usize,
|
||||
&is_zeroed_chunk))
|
||||
return (true);
|
||||
|
||||
malloc_mutex_lock(&arena->huge_mtx);
|
||||
/* Update the size of the huge allocation. */
|
||||
malloc_mutex_lock(tsdn, &arena->huge_mtx);
|
||||
huge_node_unset(ptr, node);
|
||||
extent_node_size_set(node, usize);
|
||||
malloc_mutex_unlock(&arena->huge_mtx);
|
||||
extent_node_zeroed_set(node, extent_node_zeroed_get(node) &&
|
||||
is_zeroed_chunk);
|
||||
huge_node_reset(tsdn, ptr, node);
|
||||
malloc_mutex_unlock(tsdn, &arena->huge_mtx);
|
||||
|
||||
if (zero || (config_fill && unlikely(opt_zero))) {
|
||||
if (!is_zeroed_subchunk) {
|
||||
@@ -268,15 +291,15 @@ huge_ralloc_no_move_expand(void *ptr, size_t oldsize, size_t usize, bool zero) {
|
||||
CHUNK_CEILING(oldsize));
|
||||
}
|
||||
} else if (config_fill && unlikely(opt_junk_alloc)) {
|
||||
memset((void *)((uintptr_t)ptr + oldsize), 0xa5, usize -
|
||||
oldsize);
|
||||
memset((void *)((uintptr_t)ptr + oldsize), JEMALLOC_ALLOC_JUNK,
|
||||
usize - oldsize);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
huge_ralloc_no_move(tsd_t *tsd, void *ptr, size_t oldsize, size_t usize_min,
|
||||
huge_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t usize_min,
|
||||
size_t usize_max, bool zero)
|
||||
{
|
||||
|
||||
@@ -290,16 +313,16 @@ huge_ralloc_no_move(tsd_t *tsd, void *ptr, size_t oldsize, size_t usize_min,
|
||||
|
||||
if (CHUNK_CEILING(usize_max) > CHUNK_CEILING(oldsize)) {
|
||||
/* Attempt to expand the allocation in-place. */
|
||||
if (!huge_ralloc_no_move_expand(ptr, oldsize, usize_max,
|
||||
if (!huge_ralloc_no_move_expand(tsdn, ptr, oldsize, usize_max,
|
||||
zero)) {
|
||||
arena_decay_tick(tsd, huge_aalloc(ptr));
|
||||
arena_decay_tick(tsdn, huge_aalloc(ptr));
|
||||
return (false);
|
||||
}
|
||||
/* Try again, this time with usize_min. */
|
||||
if (usize_min < usize_max && CHUNK_CEILING(usize_min) >
|
||||
CHUNK_CEILING(oldsize) && huge_ralloc_no_move_expand(ptr,
|
||||
oldsize, usize_min, zero)) {
|
||||
arena_decay_tick(tsd, huge_aalloc(ptr));
|
||||
CHUNK_CEILING(oldsize) && huge_ralloc_no_move_expand(tsdn,
|
||||
ptr, oldsize, usize_min, zero)) {
|
||||
arena_decay_tick(tsdn, huge_aalloc(ptr));
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
@@ -310,16 +333,17 @@ huge_ralloc_no_move(tsd_t *tsd, void *ptr, size_t oldsize, size_t usize_min,
|
||||
*/
|
||||
if (CHUNK_CEILING(oldsize) >= CHUNK_CEILING(usize_min)
|
||||
&& CHUNK_CEILING(oldsize) <= CHUNK_CEILING(usize_max)) {
|
||||
huge_ralloc_no_move_similar(ptr, oldsize, usize_min, usize_max,
|
||||
zero);
|
||||
arena_decay_tick(tsd, huge_aalloc(ptr));
|
||||
huge_ralloc_no_move_similar(tsdn, ptr, oldsize, usize_min,
|
||||
usize_max, zero);
|
||||
arena_decay_tick(tsdn, huge_aalloc(ptr));
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* Attempt to shrink the allocation in-place. */
|
||||
if (CHUNK_CEILING(oldsize) > CHUNK_CEILING(usize_max)) {
|
||||
if (!huge_ralloc_no_move_shrink(ptr, oldsize, usize_max)) {
|
||||
arena_decay_tick(tsd, huge_aalloc(ptr));
|
||||
if (!huge_ralloc_no_move_shrink(tsdn, ptr, oldsize,
|
||||
usize_max)) {
|
||||
arena_decay_tick(tsdn, huge_aalloc(ptr));
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
@@ -327,18 +351,18 @@ huge_ralloc_no_move(tsd_t *tsd, void *ptr, size_t oldsize, size_t usize_min,
|
||||
}
|
||||
|
||||
static void *
|
||||
huge_ralloc_move_helper(tsd_t *tsd, arena_t *arena, size_t usize,
|
||||
size_t alignment, bool zero, tcache_t *tcache)
|
||||
huge_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
size_t alignment, bool zero)
|
||||
{
|
||||
|
||||
if (alignment <= chunksize)
|
||||
return (huge_malloc(tsd, arena, usize, zero, tcache));
|
||||
return (huge_palloc(tsd, arena, usize, alignment, zero, tcache));
|
||||
return (huge_malloc(tsdn, arena, usize, zero));
|
||||
return (huge_palloc(tsdn, arena, usize, alignment, zero));
|
||||
}
|
||||
|
||||
void *
|
||||
huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t usize,
|
||||
size_t alignment, bool zero, tcache_t *tcache)
|
||||
huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize,
|
||||
size_t usize, size_t alignment, bool zero, tcache_t *tcache)
|
||||
{
|
||||
void *ret;
|
||||
size_t copysize;
|
||||
@@ -347,7 +371,8 @@ huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t usize,
|
||||
assert(usize > 0 && usize <= HUGE_MAXCLASS);
|
||||
|
||||
/* Try to avoid moving the allocation. */
|
||||
if (!huge_ralloc_no_move(tsd, ptr, oldsize, usize, usize, zero))
|
||||
if (!huge_ralloc_no_move(tsd_tsdn(tsd), ptr, oldsize, usize, usize,
|
||||
zero))
|
||||
return (ptr);
|
||||
|
||||
/*
|
||||
@@ -355,19 +380,19 @@ huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t usize,
|
||||
* different size class. In that case, fall back to allocating new
|
||||
* space and copying.
|
||||
*/
|
||||
ret = huge_ralloc_move_helper(tsd, arena, usize, alignment, zero,
|
||||
tcache);
|
||||
ret = huge_ralloc_move_helper(tsd_tsdn(tsd), arena, usize, alignment,
|
||||
zero);
|
||||
if (ret == NULL)
|
||||
return (NULL);
|
||||
|
||||
copysize = (usize < oldsize) ? usize : oldsize;
|
||||
memcpy(ret, ptr, copysize);
|
||||
isqalloc(tsd, ptr, oldsize, tcache);
|
||||
isqalloc(tsd, ptr, oldsize, tcache, true);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
huge_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache)
|
||||
huge_dalloc(tsdn_t *tsdn, void *ptr)
|
||||
{
|
||||
extent_node_t *node;
|
||||
arena_t *arena;
|
||||
@@ -375,17 +400,17 @@ huge_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache)
|
||||
node = huge_node_get(ptr);
|
||||
arena = extent_node_arena_get(node);
|
||||
huge_node_unset(ptr, node);
|
||||
malloc_mutex_lock(&arena->huge_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->huge_mtx);
|
||||
ql_remove(&arena->huge, node, ql_link);
|
||||
malloc_mutex_unlock(&arena->huge_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->huge_mtx);
|
||||
|
||||
huge_dalloc_junk(extent_node_addr_get(node),
|
||||
extent_node_size_get(node));
|
||||
arena_chunk_dalloc_huge(extent_node_arena_get(node),
|
||||
arena_chunk_dalloc_huge(tsdn, extent_node_arena_get(node),
|
||||
extent_node_addr_get(node), extent_node_size_get(node));
|
||||
idalloctm(tsd, node, tcache, true, true);
|
||||
idalloctm(tsdn, node, NULL, true, true);
|
||||
|
||||
arena_decay_tick(tsd, arena);
|
||||
arena_decay_tick(tsdn, arena);
|
||||
}
|
||||
|
||||
arena_t *
|
||||
@@ -396,7 +421,7 @@ huge_aalloc(const void *ptr)
|
||||
}
|
||||
|
||||
size_t
|
||||
huge_salloc(const void *ptr)
|
||||
huge_salloc(tsdn_t *tsdn, const void *ptr)
|
||||
{
|
||||
size_t size;
|
||||
extent_node_t *node;
|
||||
@@ -404,15 +429,15 @@ huge_salloc(const void *ptr)
|
||||
|
||||
node = huge_node_get(ptr);
|
||||
arena = extent_node_arena_get(node);
|
||||
malloc_mutex_lock(&arena->huge_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->huge_mtx);
|
||||
size = extent_node_size_get(node);
|
||||
malloc_mutex_unlock(&arena->huge_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->huge_mtx);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
prof_tctx_t *
|
||||
huge_prof_tctx_get(const void *ptr)
|
||||
huge_prof_tctx_get(tsdn_t *tsdn, const void *ptr)
|
||||
{
|
||||
prof_tctx_t *tctx;
|
||||
extent_node_t *node;
|
||||
@@ -420,29 +445,29 @@ huge_prof_tctx_get(const void *ptr)
|
||||
|
||||
node = huge_node_get(ptr);
|
||||
arena = extent_node_arena_get(node);
|
||||
malloc_mutex_lock(&arena->huge_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->huge_mtx);
|
||||
tctx = extent_node_prof_tctx_get(node);
|
||||
malloc_mutex_unlock(&arena->huge_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->huge_mtx);
|
||||
|
||||
return (tctx);
|
||||
}
|
||||
|
||||
void
|
||||
huge_prof_tctx_set(const void *ptr, prof_tctx_t *tctx)
|
||||
huge_prof_tctx_set(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx)
|
||||
{
|
||||
extent_node_t *node;
|
||||
arena_t *arena;
|
||||
|
||||
node = huge_node_get(ptr);
|
||||
arena = extent_node_arena_get(node);
|
||||
malloc_mutex_lock(&arena->huge_mtx);
|
||||
malloc_mutex_lock(tsdn, &arena->huge_mtx);
|
||||
extent_node_prof_tctx_set(node, tctx);
|
||||
malloc_mutex_unlock(&arena->huge_mtx);
|
||||
malloc_mutex_unlock(tsdn, &arena->huge_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
huge_prof_tctx_reset(const void *ptr)
|
||||
huge_prof_tctx_reset(tsdn_t *tsdn, const void *ptr)
|
||||
{
|
||||
|
||||
huge_prof_tctx_set(ptr, (prof_tctx_t *)(uintptr_t)1U);
|
||||
huge_prof_tctx_set(tsdn, ptr, (prof_tctx_t *)(uintptr_t)1U);
|
||||
}
|
||||
|
||||
983
src/jemalloc.c
983
src/jemalloc.c
File diff suppressed because it is too large
Load Diff
23
src/mutex.c
23
src/mutex.c
@@ -69,7 +69,7 @@ JEMALLOC_EXPORT int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
|
||||
#endif
|
||||
|
||||
bool
|
||||
malloc_mutex_init(malloc_mutex_t *mutex)
|
||||
malloc_mutex_init(malloc_mutex_t *mutex, const char *name, witness_rank_t rank)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -80,6 +80,8 @@ malloc_mutex_init(malloc_mutex_t *mutex)
|
||||
_CRT_SPINCOUNT))
|
||||
return (true);
|
||||
# endif
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
mutex->lock = OS_UNFAIR_LOCK_INIT;
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
mutex->lock = 0;
|
||||
#elif (defined(JEMALLOC_MUTEX_INIT_CB))
|
||||
@@ -103,31 +105,34 @@ malloc_mutex_init(malloc_mutex_t *mutex)
|
||||
}
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
#endif
|
||||
if (config_debug)
|
||||
witness_init(&mutex->witness, name, rank, NULL);
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
malloc_mutex_prefork(malloc_mutex_t *mutex)
|
||||
malloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex)
|
||||
{
|
||||
|
||||
malloc_mutex_lock(mutex);
|
||||
malloc_mutex_lock(tsdn, mutex);
|
||||
}
|
||||
|
||||
void
|
||||
malloc_mutex_postfork_parent(malloc_mutex_t *mutex)
|
||||
malloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex)
|
||||
{
|
||||
|
||||
malloc_mutex_unlock(mutex);
|
||||
malloc_mutex_unlock(tsdn, mutex);
|
||||
}
|
||||
|
||||
void
|
||||
malloc_mutex_postfork_child(malloc_mutex_t *mutex)
|
||||
malloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex)
|
||||
{
|
||||
|
||||
#ifdef JEMALLOC_MUTEX_INIT_CB
|
||||
malloc_mutex_unlock(mutex);
|
||||
malloc_mutex_unlock(tsdn, mutex);
|
||||
#else
|
||||
if (malloc_mutex_init(mutex)) {
|
||||
if (malloc_mutex_init(mutex, mutex->witness.name,
|
||||
mutex->witness.rank)) {
|
||||
malloc_printf("<jemalloc>: Error re-initializing mutex in "
|
||||
"child\n");
|
||||
if (opt_abort)
|
||||
@@ -137,7 +142,7 @@ malloc_mutex_postfork_child(malloc_mutex_t *mutex)
|
||||
}
|
||||
|
||||
bool
|
||||
mutex_boot(void)
|
||||
malloc_mutex_boot(void)
|
||||
{
|
||||
|
||||
#ifdef JEMALLOC_MUTEX_INIT_CB
|
||||
|
||||
100
src/nstime.c
100
src/nstime.c
@@ -97,9 +97,79 @@ nstime_divide(const nstime_t *time, const nstime_t *divisor)
|
||||
return (time->ns / divisor->ns);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
# define NSTIME_MONOTONIC true
|
||||
static void
|
||||
nstime_get(nstime_t *time)
|
||||
{
|
||||
FILETIME ft;
|
||||
uint64_t ticks_100ns;
|
||||
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
ticks_100ns = (((uint64_t)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||
|
||||
nstime_init(time, ticks_100ns * 100);
|
||||
}
|
||||
#elif JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE
|
||||
# define NSTIME_MONOTONIC true
|
||||
static void
|
||||
nstime_get(nstime_t *time)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
|
||||
nstime_init2(time, ts.tv_sec, ts.tv_nsec);
|
||||
}
|
||||
#elif JEMALLOC_HAVE_CLOCK_MONOTONIC
|
||||
# define NSTIME_MONOTONIC true
|
||||
static void
|
||||
nstime_get(nstime_t *time)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
nstime_init2(time, ts.tv_sec, ts.tv_nsec);
|
||||
}
|
||||
#elif JEMALLOC_HAVE_MACH_ABSOLUTE_TIME
|
||||
# define NSTIME_MONOTONIC true
|
||||
static void
|
||||
nstime_get(nstime_t *time)
|
||||
{
|
||||
|
||||
nstime_init(time, mach_absolute_time());
|
||||
}
|
||||
#else
|
||||
# define NSTIME_MONOTONIC false
|
||||
static void
|
||||
nstime_get(nstime_t *time)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
nstime_init2(time, tv.tv_sec, tv.tv_usec * 1000);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef nstime_monotonic
|
||||
#define nstime_monotonic JEMALLOC_N(n_nstime_monotonic)
|
||||
#endif
|
||||
bool
|
||||
nstime_monotonic(void)
|
||||
{
|
||||
|
||||
return (NSTIME_MONOTONIC);
|
||||
#undef NSTIME_MONOTONIC
|
||||
}
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef nstime_monotonic
|
||||
#define nstime_monotonic JEMALLOC_N(nstime_monotonic)
|
||||
nstime_monotonic_t *nstime_monotonic = JEMALLOC_N(n_nstime_monotonic);
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef nstime_update
|
||||
#define nstime_update JEMALLOC_N(nstime_update_impl)
|
||||
#define nstime_update JEMALLOC_N(n_nstime_update)
|
||||
#endif
|
||||
bool
|
||||
nstime_update(nstime_t *time)
|
||||
@@ -107,31 +177,7 @@ nstime_update(nstime_t *time)
|
||||
nstime_t old_time;
|
||||
|
||||
nstime_copy(&old_time, time);
|
||||
|
||||
#ifdef _WIN32
|
||||
{
|
||||
FILETIME ft;
|
||||
uint64_t ticks;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
ticks = (((uint64_t)ft.dwHighDateTime) << 32) |
|
||||
ft.dwLowDateTime;
|
||||
time->ns = ticks * 100;
|
||||
}
|
||||
#elif JEMALLOC_CLOCK_GETTIME
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
if (sysconf(_SC_MONOTONIC_CLOCK) > 0)
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
else
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
time->ns = ts.tv_sec * BILLION + ts.tv_nsec;
|
||||
}
|
||||
#else
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
time->ns = tv.tv_sec * BILLION + tv.tv_usec * 1000;
|
||||
#endif
|
||||
nstime_get(time);
|
||||
|
||||
/* Handle non-monotonic clocks. */
|
||||
if (unlikely(nstime_compare(&old_time, time) > 0)) {
|
||||
@@ -144,5 +190,5 @@ nstime_update(nstime_t *time)
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef nstime_update
|
||||
#define nstime_update JEMALLOC_N(nstime_update)
|
||||
nstime_update_t *nstime_update = JEMALLOC_N(nstime_update_impl);
|
||||
nstime_update_t *nstime_update = JEMALLOC_N(n_nstime_update);
|
||||
#endif
|
||||
|
||||
136
src/pages.c
136
src/pages.c
@@ -1,29 +1,49 @@
|
||||
#define JEMALLOC_PAGES_C_
|
||||
#include "jemalloc/internal/jemalloc_internal.h"
|
||||
|
||||
#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
/* Data. */
|
||||
|
||||
#ifndef _WIN32
|
||||
# define PAGES_PROT_COMMIT (PROT_READ | PROT_WRITE)
|
||||
# define PAGES_PROT_DECOMMIT (PROT_NONE)
|
||||
static int mmap_flags;
|
||||
#endif
|
||||
static bool os_overcommits;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void *
|
||||
pages_map(void *addr, size_t size)
|
||||
pages_map(void *addr, size_t size, bool *commit)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
assert(size != 0);
|
||||
|
||||
if (os_overcommits)
|
||||
*commit = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
* If VirtualAlloc can't allocate at the given address when one is
|
||||
* given, it fails and returns NULL.
|
||||
*/
|
||||
ret = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE,
|
||||
ret = VirtualAlloc(addr, size, MEM_RESERVE | (*commit ? MEM_COMMIT : 0),
|
||||
PAGE_READWRITE);
|
||||
#else
|
||||
/*
|
||||
* We don't use MAP_FIXED here, because it can cause the *replacement*
|
||||
* of existing mappings, and we only want to create new mappings.
|
||||
*/
|
||||
ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
|
||||
-1, 0);
|
||||
{
|
||||
int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;
|
||||
|
||||
ret = mmap(addr, size, prot, mmap_flags, -1, 0);
|
||||
}
|
||||
assert(ret != NULL);
|
||||
|
||||
if (ret == MAP_FAILED)
|
||||
@@ -67,7 +87,8 @@ pages_unmap(void *addr, size_t size)
|
||||
}
|
||||
|
||||
void *
|
||||
pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size)
|
||||
pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size,
|
||||
bool *commit)
|
||||
{
|
||||
void *ret = (void *)((uintptr_t)addr + leadsize);
|
||||
|
||||
@@ -77,7 +98,7 @@ pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size)
|
||||
void *new_addr;
|
||||
|
||||
pages_unmap(addr, alloc_size);
|
||||
new_addr = pages_map(ret, size);
|
||||
new_addr = pages_map(ret, size, commit);
|
||||
if (new_addr == ret)
|
||||
return (ret);
|
||||
if (new_addr)
|
||||
@@ -101,17 +122,17 @@ static bool
|
||||
pages_commit_impl(void *addr, size_t size, bool commit)
|
||||
{
|
||||
|
||||
#ifndef _WIN32
|
||||
/*
|
||||
* The following decommit/commit implementation is functional, but
|
||||
* always disabled because it doesn't add value beyong improved
|
||||
* debugging (at the cost of extra system calls) on systems that
|
||||
* overcommit.
|
||||
*/
|
||||
if (false) {
|
||||
int prot = commit ? (PROT_READ | PROT_WRITE) : PROT_NONE;
|
||||
void *result = mmap(addr, size, prot, MAP_PRIVATE | MAP_ANON |
|
||||
MAP_FIXED, -1, 0);
|
||||
if (os_overcommits)
|
||||
return (true);
|
||||
|
||||
#ifdef _WIN32
|
||||
return (commit ? (addr != VirtualAlloc(addr, size, MEM_COMMIT,
|
||||
PAGE_READWRITE)) : (!VirtualFree(addr, size, MEM_DECOMMIT)));
|
||||
#else
|
||||
{
|
||||
int prot = commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;
|
||||
void *result = mmap(addr, size, prot, mmap_flags | MAP_FIXED,
|
||||
-1, 0);
|
||||
if (result == MAP_FAILED)
|
||||
return (true);
|
||||
if (result != addr) {
|
||||
@@ -125,7 +146,6 @@ pages_commit_impl(void *addr, size_t size, bool commit)
|
||||
return (false);
|
||||
}
|
||||
#endif
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -171,3 +191,83 @@ pages_purge(void *addr, size_t size)
|
||||
return (unzeroed);
|
||||
}
|
||||
|
||||
#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT
|
||||
static bool
|
||||
os_overcommits_sysctl(void)
|
||||
{
|
||||
int vm_overcommit;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(vm_overcommit);
|
||||
if (sysctlbyname("vm.overcommit", &vm_overcommit, &sz, NULL, 0) != 0)
|
||||
return (false); /* Error. */
|
||||
|
||||
return ((vm_overcommit & 0x3) == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY
|
||||
/*
|
||||
* Use syscall(2) rather than {open,read,close}(2) when possible to avoid
|
||||
* reentry during bootstrapping if another library has interposed system call
|
||||
* wrappers.
|
||||
*/
|
||||
static bool
|
||||
os_overcommits_proc(void)
|
||||
{
|
||||
int fd;
|
||||
char buf[1];
|
||||
ssize_t nread;
|
||||
|
||||
#if defined(JEMALLOC_HAVE_SYSCALL) && defined(SYS_open)
|
||||
fd = (int)syscall(SYS_open, "/proc/sys/vm/overcommit_memory", O_RDONLY);
|
||||
#else
|
||||
fd = open("/proc/sys/vm/overcommit_memory", O_RDONLY);
|
||||
#endif
|
||||
if (fd == -1)
|
||||
return (false); /* Error. */
|
||||
|
||||
#if defined(JEMALLOC_HAVE_SYSCALL) && defined(SYS_read)
|
||||
nread = (ssize_t)syscall(SYS_read, fd, &buf, sizeof(buf));
|
||||
#else
|
||||
nread = read(fd, &buf, sizeof(buf));
|
||||
#endif
|
||||
|
||||
#if defined(JEMALLOC_HAVE_SYSCALL) && defined(SYS_close)
|
||||
syscall(SYS_close, fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
|
||||
if (nread < 1)
|
||||
return (false); /* Error. */
|
||||
/*
|
||||
* /proc/sys/vm/overcommit_memory meanings:
|
||||
* 0: Heuristic overcommit.
|
||||
* 1: Always overcommit.
|
||||
* 2: Never overcommit.
|
||||
*/
|
||||
return (buf[0] == '0' || buf[0] == '1');
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
pages_boot(void)
|
||||
{
|
||||
|
||||
#ifndef _WIN32
|
||||
mmap_flags = MAP_PRIVATE | MAP_ANON;
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT
|
||||
os_overcommits = os_overcommits_sysctl();
|
||||
#elif defined(JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY)
|
||||
os_overcommits = os_overcommits_proc();
|
||||
# ifdef MAP_NORESERVE
|
||||
if (os_overcommits)
|
||||
mmap_flags |= MAP_NORESERVE;
|
||||
# endif
|
||||
#else
|
||||
os_overcommits = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
653
src/prof.c
653
src/prof.c
File diff suppressed because it is too large
Load Diff
@@ -13,24 +13,22 @@
|
||||
/* Function prototypes for non-inline static functions. */
|
||||
|
||||
static quarantine_t *quarantine_grow(tsd_t *tsd, quarantine_t *quarantine);
|
||||
static void quarantine_drain_one(tsd_t *tsd, quarantine_t *quarantine);
|
||||
static void quarantine_drain(tsd_t *tsd, quarantine_t *quarantine,
|
||||
static void quarantine_drain_one(tsdn_t *tsdn, quarantine_t *quarantine);
|
||||
static void quarantine_drain(tsdn_t *tsdn, quarantine_t *quarantine,
|
||||
size_t upper_bound);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static quarantine_t *
|
||||
quarantine_init(tsd_t *tsd, size_t lg_maxobjs)
|
||||
quarantine_init(tsdn_t *tsdn, size_t lg_maxobjs)
|
||||
{
|
||||
quarantine_t *quarantine;
|
||||
size_t size;
|
||||
|
||||
assert(tsd_nominal(tsd));
|
||||
|
||||
size = offsetof(quarantine_t, objs) + ((ZU(1) << lg_maxobjs) *
|
||||
sizeof(quarantine_obj_t));
|
||||
quarantine = (quarantine_t *)iallocztm(tsd, size, size2index(size),
|
||||
false, tcache_get(tsd, true), true, NULL, true);
|
||||
quarantine = (quarantine_t *)iallocztm(tsdn, size, size2index(size),
|
||||
false, NULL, true, arena_get(TSDN_NULL, 0, true), true);
|
||||
if (quarantine == NULL)
|
||||
return (NULL);
|
||||
quarantine->curbytes = 0;
|
||||
@@ -49,7 +47,7 @@ quarantine_alloc_hook_work(tsd_t *tsd)
|
||||
if (!tsd_nominal(tsd))
|
||||
return;
|
||||
|
||||
quarantine = quarantine_init(tsd, LG_MAXOBJS_INIT);
|
||||
quarantine = quarantine_init(tsd_tsdn(tsd), LG_MAXOBJS_INIT);
|
||||
/*
|
||||
* Check again whether quarantine has been initialized, because
|
||||
* quarantine_init() may have triggered recursive initialization.
|
||||
@@ -57,7 +55,7 @@ quarantine_alloc_hook_work(tsd_t *tsd)
|
||||
if (tsd_quarantine_get(tsd) == NULL)
|
||||
tsd_quarantine_set(tsd, quarantine);
|
||||
else
|
||||
idalloctm(tsd, quarantine, tcache_get(tsd, false), true, true);
|
||||
idalloctm(tsd_tsdn(tsd), quarantine, NULL, true, true);
|
||||
}
|
||||
|
||||
static quarantine_t *
|
||||
@@ -65,9 +63,9 @@ quarantine_grow(tsd_t *tsd, quarantine_t *quarantine)
|
||||
{
|
||||
quarantine_t *ret;
|
||||
|
||||
ret = quarantine_init(tsd, quarantine->lg_maxobjs + 1);
|
||||
ret = quarantine_init(tsd_tsdn(tsd), quarantine->lg_maxobjs + 1);
|
||||
if (ret == NULL) {
|
||||
quarantine_drain_one(tsd, quarantine);
|
||||
quarantine_drain_one(tsd_tsdn(tsd), quarantine);
|
||||
return (quarantine);
|
||||
}
|
||||
|
||||
@@ -89,18 +87,18 @@ quarantine_grow(tsd_t *tsd, quarantine_t *quarantine)
|
||||
memcpy(&ret->objs[ncopy_a], quarantine->objs, ncopy_b *
|
||||
sizeof(quarantine_obj_t));
|
||||
}
|
||||
idalloctm(tsd, quarantine, tcache_get(tsd, false), true, true);
|
||||
idalloctm(tsd_tsdn(tsd), quarantine, NULL, true, true);
|
||||
|
||||
tsd_quarantine_set(tsd, ret);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
quarantine_drain_one(tsd_t *tsd, quarantine_t *quarantine)
|
||||
quarantine_drain_one(tsdn_t *tsdn, quarantine_t *quarantine)
|
||||
{
|
||||
quarantine_obj_t *obj = &quarantine->objs[quarantine->first];
|
||||
assert(obj->usize == isalloc(obj->ptr, config_prof));
|
||||
idalloctm(tsd, obj->ptr, NULL, false, true);
|
||||
assert(obj->usize == isalloc(tsdn, obj->ptr, config_prof));
|
||||
idalloctm(tsdn, obj->ptr, NULL, false, true);
|
||||
quarantine->curbytes -= obj->usize;
|
||||
quarantine->curobjs--;
|
||||
quarantine->first = (quarantine->first + 1) & ((ZU(1) <<
|
||||
@@ -108,24 +106,24 @@ quarantine_drain_one(tsd_t *tsd, quarantine_t *quarantine)
|
||||
}
|
||||
|
||||
static void
|
||||
quarantine_drain(tsd_t *tsd, quarantine_t *quarantine, size_t upper_bound)
|
||||
quarantine_drain(tsdn_t *tsdn, quarantine_t *quarantine, size_t upper_bound)
|
||||
{
|
||||
|
||||
while (quarantine->curbytes > upper_bound && quarantine->curobjs > 0)
|
||||
quarantine_drain_one(tsd, quarantine);
|
||||
quarantine_drain_one(tsdn, quarantine);
|
||||
}
|
||||
|
||||
void
|
||||
quarantine(tsd_t *tsd, void *ptr)
|
||||
{
|
||||
quarantine_t *quarantine;
|
||||
size_t usize = isalloc(ptr, config_prof);
|
||||
size_t usize = isalloc(tsd_tsdn(tsd), ptr, config_prof);
|
||||
|
||||
cassert(config_fill);
|
||||
assert(opt_quarantine);
|
||||
|
||||
if ((quarantine = tsd_quarantine_get(tsd)) == NULL) {
|
||||
idalloctm(tsd, ptr, NULL, false, true);
|
||||
idalloctm(tsd_tsdn(tsd), ptr, NULL, false, true);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
@@ -135,7 +133,7 @@ quarantine(tsd_t *tsd, void *ptr)
|
||||
if (quarantine->curbytes + usize > opt_quarantine) {
|
||||
size_t upper_bound = (opt_quarantine >= usize) ? opt_quarantine
|
||||
- usize : 0;
|
||||
quarantine_drain(tsd, quarantine, upper_bound);
|
||||
quarantine_drain(tsd_tsdn(tsd), quarantine, upper_bound);
|
||||
}
|
||||
/* Grow the quarantine ring buffer if it's full. */
|
||||
if (quarantine->curobjs == (ZU(1) << quarantine->lg_maxobjs))
|
||||
@@ -160,11 +158,11 @@ quarantine(tsd_t *tsd, void *ptr)
|
||||
&& usize <= SMALL_MAXCLASS)
|
||||
arena_quarantine_junk_small(ptr, usize);
|
||||
else
|
||||
memset(ptr, 0x5a, usize);
|
||||
memset(ptr, JEMALLOC_FREE_JUNK, usize);
|
||||
}
|
||||
} else {
|
||||
assert(quarantine->curbytes == 0);
|
||||
idalloctm(tsd, ptr, NULL, false, true);
|
||||
idalloctm(tsd_tsdn(tsd), ptr, NULL, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,8 +176,8 @@ quarantine_cleanup(tsd_t *tsd)
|
||||
|
||||
quarantine = tsd_quarantine_get(tsd);
|
||||
if (quarantine != NULL) {
|
||||
quarantine_drain(tsd, quarantine, 0);
|
||||
idalloctm(tsd, quarantine, tcache_get(tsd, false), true, true);
|
||||
quarantine_drain(tsd_tsdn(tsd), quarantine, 0);
|
||||
idalloctm(tsd_tsdn(tsd), quarantine, NULL, true, true);
|
||||
tsd_quarantine_set(tsd, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ rtree_new(rtree_t *rtree, unsigned bits, rtree_node_alloc_t *alloc,
|
||||
{
|
||||
unsigned bits_in_leaf, height, i;
|
||||
|
||||
assert(RTREE_HEIGHT_MAX == ((ZU(1) << (LG_SIZEOF_PTR+3)) /
|
||||
RTREE_BITS_PER_LEVEL));
|
||||
assert(bits > 0 && bits <= (sizeof(uintptr_t) << 3));
|
||||
|
||||
bits_in_leaf = (bits % RTREE_BITS_PER_LEVEL) == 0 ? RTREE_BITS_PER_LEVEL
|
||||
@@ -94,12 +96,15 @@ rtree_node_init(rtree_t *rtree, unsigned level, rtree_node_elm_t **elmp)
|
||||
rtree_node_elm_t *node;
|
||||
|
||||
if (atomic_cas_p((void **)elmp, NULL, RTREE_NODE_INITIALIZING)) {
|
||||
spin_t spinner;
|
||||
|
||||
/*
|
||||
* Another thread is already in the process of initializing.
|
||||
* Spin-wait until initialization is complete.
|
||||
*/
|
||||
spin_init(&spinner);
|
||||
do {
|
||||
CPU_SPINWAIT;
|
||||
spin_adaptive(&spinner);
|
||||
node = atomic_read_p((void **)elmp);
|
||||
} while (node == RTREE_NODE_INITIALIZING);
|
||||
} else {
|
||||
@@ -123,5 +128,5 @@ rtree_node_elm_t *
|
||||
rtree_child_read_hard(rtree_t *rtree, rtree_node_elm_t *elm, unsigned level)
|
||||
{
|
||||
|
||||
return (rtree_node_init(rtree, level, &elm->child));
|
||||
return (rtree_node_init(rtree, level+1, &elm->child));
|
||||
}
|
||||
|
||||
2
src/spin.c
Normal file
2
src/spin.c
Normal file
@@ -0,0 +1,2 @@
|
||||
#define JEMALLOC_SPIN_C_
|
||||
#include "jemalloc/internal/jemalloc_internal.h"
|
||||
1267
src/stats.c
1267
src/stats.c
File diff suppressed because it is too large
Load Diff
129
src/tcache.c
129
src/tcache.c
@@ -23,10 +23,11 @@ static tcaches_t *tcaches_avail;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
size_t tcache_salloc(const void *ptr)
|
||||
size_t
|
||||
tcache_salloc(tsdn_t *tsdn, const void *ptr)
|
||||
{
|
||||
|
||||
return (arena_salloc(ptr, false));
|
||||
return (arena_salloc(tsdn, ptr, false));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -70,12 +71,12 @@ tcache_event_hard(tsd_t *tsd, tcache_t *tcache)
|
||||
}
|
||||
|
||||
void *
|
||||
tcache_alloc_small_hard(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
|
||||
tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
|
||||
tcache_bin_t *tbin, szind_t binind, bool *tcache_success)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
arena_tcache_fill_small(tsd, arena, tbin, binind, config_prof ?
|
||||
arena_tcache_fill_small(tsdn, arena, tbin, binind, config_prof ?
|
||||
tcache->prof_accumbytes : 0);
|
||||
if (config_prof)
|
||||
tcache->prof_accumbytes = 0;
|
||||
@@ -106,12 +107,13 @@ tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin,
|
||||
arena_bin_t *bin = &bin_arena->bins[binind];
|
||||
|
||||
if (config_prof && bin_arena == arena) {
|
||||
if (arena_prof_accum(arena, tcache->prof_accumbytes))
|
||||
prof_idump();
|
||||
if (arena_prof_accum(tsd_tsdn(tsd), arena,
|
||||
tcache->prof_accumbytes))
|
||||
prof_idump(tsd_tsdn(tsd));
|
||||
tcache->prof_accumbytes = 0;
|
||||
}
|
||||
|
||||
malloc_mutex_lock(&bin->lock);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
if (config_stats && bin_arena == arena) {
|
||||
assert(!merged_stats);
|
||||
merged_stats = true;
|
||||
@@ -128,9 +130,9 @@ tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin,
|
||||
size_t pageind = ((uintptr_t)ptr -
|
||||
(uintptr_t)chunk) >> LG_PAGE;
|
||||
arena_chunk_map_bits_t *bitselm =
|
||||
arena_bitselm_get(chunk, pageind);
|
||||
arena_dalloc_bin_junked_locked(bin_arena, chunk,
|
||||
ptr, bitselm);
|
||||
arena_bitselm_get_mutable(chunk, pageind);
|
||||
arena_dalloc_bin_junked_locked(tsd_tsdn(tsd),
|
||||
bin_arena, chunk, ptr, bitselm);
|
||||
} else {
|
||||
/*
|
||||
* This object was allocated via a different
|
||||
@@ -142,8 +144,8 @@ tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin,
|
||||
ndeferred++;
|
||||
}
|
||||
}
|
||||
malloc_mutex_unlock(&bin->lock);
|
||||
arena_decay_ticks(tsd, bin_arena, nflush - ndeferred);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
arena_decay_ticks(tsd_tsdn(tsd), bin_arena, nflush - ndeferred);
|
||||
}
|
||||
if (config_stats && !merged_stats) {
|
||||
/*
|
||||
@@ -151,11 +153,11 @@ tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin,
|
||||
* arena, so the stats didn't get merged. Manually do so now.
|
||||
*/
|
||||
arena_bin_t *bin = &arena->bins[binind];
|
||||
malloc_mutex_lock(&bin->lock);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
bin->stats.nflushes++;
|
||||
bin->stats.nrequests += tbin->tstats.nrequests;
|
||||
tbin->tstats.nrequests = 0;
|
||||
malloc_mutex_unlock(&bin->lock);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
}
|
||||
|
||||
memmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem *
|
||||
@@ -188,7 +190,7 @@ tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, szind_t binind,
|
||||
|
||||
if (config_prof)
|
||||
idump = false;
|
||||
malloc_mutex_lock(&locked_arena->lock);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &locked_arena->lock);
|
||||
if ((config_prof || config_stats) && locked_arena == arena) {
|
||||
if (config_prof) {
|
||||
idump = arena_prof_accum_locked(arena,
|
||||
@@ -211,8 +213,8 @@ tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, szind_t binind,
|
||||
chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
|
||||
if (extent_node_arena_get(&chunk->node) ==
|
||||
locked_arena) {
|
||||
arena_dalloc_large_junked_locked(locked_arena,
|
||||
chunk, ptr);
|
||||
arena_dalloc_large_junked_locked(tsd_tsdn(tsd),
|
||||
locked_arena, chunk, ptr);
|
||||
} else {
|
||||
/*
|
||||
* This object was allocated via a different
|
||||
@@ -224,22 +226,23 @@ tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, szind_t binind,
|
||||
ndeferred++;
|
||||
}
|
||||
}
|
||||
malloc_mutex_unlock(&locked_arena->lock);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &locked_arena->lock);
|
||||
if (config_prof && idump)
|
||||
prof_idump();
|
||||
arena_decay_ticks(tsd, locked_arena, nflush - ndeferred);
|
||||
prof_idump(tsd_tsdn(tsd));
|
||||
arena_decay_ticks(tsd_tsdn(tsd), locked_arena, nflush -
|
||||
ndeferred);
|
||||
}
|
||||
if (config_stats && !merged_stats) {
|
||||
/*
|
||||
* The flush loop didn't happen to flush to this thread's
|
||||
* arena, so the stats didn't get merged. Manually do so now.
|
||||
*/
|
||||
malloc_mutex_lock(&arena->lock);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &arena->lock);
|
||||
arena->stats.nrequests_large += tbin->tstats.nrequests;
|
||||
arena->stats.lstats[binind - NBINS].nrequests +=
|
||||
tbin->tstats.nrequests;
|
||||
tbin->tstats.nrequests = 0;
|
||||
malloc_mutex_unlock(&arena->lock);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &arena->lock);
|
||||
}
|
||||
|
||||
memmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem *
|
||||
@@ -249,34 +252,26 @@ tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, szind_t binind,
|
||||
tbin->low_water = tbin->ncached;
|
||||
}
|
||||
|
||||
void
|
||||
tcache_arena_associate(tcache_t *tcache, arena_t *arena)
|
||||
static void
|
||||
tcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena)
|
||||
{
|
||||
|
||||
if (config_stats) {
|
||||
/* Link into list of extant tcaches. */
|
||||
malloc_mutex_lock(&arena->lock);
|
||||
malloc_mutex_lock(tsdn, &arena->lock);
|
||||
ql_elm_new(tcache, link);
|
||||
ql_tail_insert(&arena->tcache_ql, tcache, link);
|
||||
malloc_mutex_unlock(&arena->lock);
|
||||
malloc_mutex_unlock(tsdn, &arena->lock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tcache_arena_reassociate(tcache_t *tcache, arena_t *oldarena, arena_t *newarena)
|
||||
{
|
||||
|
||||
tcache_arena_dissociate(tcache, oldarena);
|
||||
tcache_arena_associate(tcache, newarena);
|
||||
}
|
||||
|
||||
void
|
||||
tcache_arena_dissociate(tcache_t *tcache, arena_t *arena)
|
||||
static void
|
||||
tcache_arena_dissociate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena)
|
||||
{
|
||||
|
||||
if (config_stats) {
|
||||
/* Unlink from list of extant tcaches. */
|
||||
malloc_mutex_lock(&arena->lock);
|
||||
malloc_mutex_lock(tsdn, &arena->lock);
|
||||
if (config_debug) {
|
||||
bool in_ql = false;
|
||||
tcache_t *iter;
|
||||
@@ -289,11 +284,20 @@ tcache_arena_dissociate(tcache_t *tcache, arena_t *arena)
|
||||
assert(in_ql);
|
||||
}
|
||||
ql_remove(&arena->tcache_ql, tcache, link);
|
||||
tcache_stats_merge(tcache, arena);
|
||||
malloc_mutex_unlock(&arena->lock);
|
||||
tcache_stats_merge(tsdn, tcache, arena);
|
||||
malloc_mutex_unlock(tsdn, &arena->lock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache, arena_t *oldarena,
|
||||
arena_t *newarena)
|
||||
{
|
||||
|
||||
tcache_arena_dissociate(tsdn, tcache, oldarena);
|
||||
tcache_arena_associate(tsdn, tcache, newarena);
|
||||
}
|
||||
|
||||
tcache_t *
|
||||
tcache_get_hard(tsd_t *tsd)
|
||||
{
|
||||
@@ -307,11 +311,11 @@ tcache_get_hard(tsd_t *tsd)
|
||||
arena = arena_choose(tsd, NULL);
|
||||
if (unlikely(arena == NULL))
|
||||
return (NULL);
|
||||
return (tcache_create(tsd, arena));
|
||||
return (tcache_create(tsd_tsdn(tsd), arena));
|
||||
}
|
||||
|
||||
tcache_t *
|
||||
tcache_create(tsd_t *tsd, arena_t *arena)
|
||||
tcache_create(tsdn_t *tsdn, arena_t *arena)
|
||||
{
|
||||
tcache_t *tcache;
|
||||
size_t size, stack_offset;
|
||||
@@ -325,12 +329,12 @@ tcache_create(tsd_t *tsd, arena_t *arena)
|
||||
/* Avoid false cacheline sharing. */
|
||||
size = sa2u(size, CACHELINE);
|
||||
|
||||
tcache = ipallocztm(tsd, size, CACHELINE, true, false, true,
|
||||
arena_get(0, false));
|
||||
tcache = ipallocztm(tsdn, size, CACHELINE, true, NULL, true,
|
||||
arena_get(TSDN_NULL, 0, true));
|
||||
if (tcache == NULL)
|
||||
return (NULL);
|
||||
|
||||
tcache_arena_associate(tcache, arena);
|
||||
tcache_arena_associate(tsdn, tcache, arena);
|
||||
|
||||
ticker_init(&tcache->gc_ticker, TCACHE_GC_INCR);
|
||||
|
||||
@@ -357,7 +361,7 @@ tcache_destroy(tsd_t *tsd, tcache_t *tcache)
|
||||
unsigned i;
|
||||
|
||||
arena = arena_choose(tsd, NULL);
|
||||
tcache_arena_dissociate(tcache, arena);
|
||||
tcache_arena_dissociate(tsd_tsdn(tsd), tcache, arena);
|
||||
|
||||
for (i = 0; i < NBINS; i++) {
|
||||
tcache_bin_t *tbin = &tcache->tbins[i];
|
||||
@@ -365,9 +369,9 @@ tcache_destroy(tsd_t *tsd, tcache_t *tcache)
|
||||
|
||||
if (config_stats && tbin->tstats.nrequests != 0) {
|
||||
arena_bin_t *bin = &arena->bins[i];
|
||||
malloc_mutex_lock(&bin->lock);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
bin->stats.nrequests += tbin->tstats.nrequests;
|
||||
malloc_mutex_unlock(&bin->lock);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,19 +380,19 @@ tcache_destroy(tsd_t *tsd, tcache_t *tcache)
|
||||
tcache_bin_flush_large(tsd, tbin, i, 0, tcache);
|
||||
|
||||
if (config_stats && tbin->tstats.nrequests != 0) {
|
||||
malloc_mutex_lock(&arena->lock);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &arena->lock);
|
||||
arena->stats.nrequests_large += tbin->tstats.nrequests;
|
||||
arena->stats.lstats[i - NBINS].nrequests +=
|
||||
tbin->tstats.nrequests;
|
||||
malloc_mutex_unlock(&arena->lock);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &arena->lock);
|
||||
}
|
||||
}
|
||||
|
||||
if (config_prof && tcache->prof_accumbytes > 0 &&
|
||||
arena_prof_accum(arena, tcache->prof_accumbytes))
|
||||
prof_idump();
|
||||
arena_prof_accum(tsd_tsdn(tsd), arena, tcache->prof_accumbytes))
|
||||
prof_idump(tsd_tsdn(tsd));
|
||||
|
||||
idalloctm(tsd, tcache, false, true, true);
|
||||
idalloctm(tsd_tsdn(tsd), tcache, NULL, true, true);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -412,21 +416,22 @@ tcache_enabled_cleanup(tsd_t *tsd)
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
/* Caller must own arena->lock. */
|
||||
void
|
||||
tcache_stats_merge(tcache_t *tcache, arena_t *arena)
|
||||
tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
cassert(config_stats);
|
||||
|
||||
malloc_mutex_assert_owner(tsdn, &arena->lock);
|
||||
|
||||
/* Merge and reset tcache stats. */
|
||||
for (i = 0; i < NBINS; i++) {
|
||||
arena_bin_t *bin = &arena->bins[i];
|
||||
tcache_bin_t *tbin = &tcache->tbins[i];
|
||||
malloc_mutex_lock(&bin->lock);
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
bin->stats.nrequests += tbin->tstats.nrequests;
|
||||
malloc_mutex_unlock(&bin->lock);
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
tbin->tstats.nrequests = 0;
|
||||
}
|
||||
|
||||
@@ -442,11 +447,12 @@ tcache_stats_merge(tcache_t *tcache, arena_t *arena)
|
||||
bool
|
||||
tcaches_create(tsd_t *tsd, unsigned *r_ind)
|
||||
{
|
||||
arena_t *arena;
|
||||
tcache_t *tcache;
|
||||
tcaches_t *elm;
|
||||
|
||||
if (tcaches == NULL) {
|
||||
tcaches = base_alloc(sizeof(tcache_t *) *
|
||||
tcaches = base_alloc(tsd_tsdn(tsd), sizeof(tcache_t *) *
|
||||
(MALLOCX_TCACHE_MAX+1));
|
||||
if (tcaches == NULL)
|
||||
return (true);
|
||||
@@ -454,7 +460,10 @@ tcaches_create(tsd_t *tsd, unsigned *r_ind)
|
||||
|
||||
if (tcaches_avail == NULL && tcaches_past > MALLOCX_TCACHE_MAX)
|
||||
return (true);
|
||||
tcache = tcache_create(tsd, arena_get(0, false));
|
||||
arena = arena_ichoose(tsd, NULL);
|
||||
if (unlikely(arena == NULL))
|
||||
return (true);
|
||||
tcache = tcache_create(tsd_tsdn(tsd), arena);
|
||||
if (tcache == NULL)
|
||||
return (true);
|
||||
|
||||
@@ -500,7 +509,7 @@ tcaches_destroy(tsd_t *tsd, unsigned ind)
|
||||
}
|
||||
|
||||
bool
|
||||
tcache_boot(void)
|
||||
tcache_boot(tsdn_t *tsdn)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
@@ -518,7 +527,7 @@ tcache_boot(void)
|
||||
nhbins = size2index(tcache_maxclass) + 1;
|
||||
|
||||
/* Initialize tcache_bin_info. */
|
||||
tcache_bin_info = (tcache_bin_info_t *)base_alloc(nhbins *
|
||||
tcache_bin_info = (tcache_bin_info_t *)base_alloc(tsdn, nhbins *
|
||||
sizeof(tcache_bin_info_t));
|
||||
if (tcache_bin_info == NULL)
|
||||
return (true);
|
||||
|
||||
22
src/tsd.c
22
src/tsd.c
@@ -77,7 +77,7 @@ tsd_cleanup(void *arg)
|
||||
/* Do nothing. */
|
||||
break;
|
||||
case tsd_state_nominal:
|
||||
#define O(n, t) \
|
||||
#define O(n, t) \
|
||||
n##_cleanup(tsd);
|
||||
MALLOC_TSD
|
||||
#undef O
|
||||
@@ -106,15 +106,17 @@ MALLOC_TSD
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
tsd_t *
|
||||
malloc_tsd_boot0(void)
|
||||
{
|
||||
tsd_t *tsd;
|
||||
|
||||
ncleanups = 0;
|
||||
if (tsd_boot0())
|
||||
return (true);
|
||||
*tsd_arenas_tdata_bypassp_get(tsd_fetch()) = true;
|
||||
return (false);
|
||||
return (NULL);
|
||||
tsd = tsd_fetch();
|
||||
*tsd_arenas_tdata_bypassp_get(tsd) = true;
|
||||
return (tsd);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -169,10 +171,10 @@ tsd_init_check_recursion(tsd_init_head_t *head, tsd_init_block_t *block)
|
||||
tsd_init_block_t *iter;
|
||||
|
||||
/* Check whether this thread has already inserted into the list. */
|
||||
malloc_mutex_lock(&head->lock);
|
||||
malloc_mutex_lock(TSDN_NULL, &head->lock);
|
||||
ql_foreach(iter, &head->blocks, link) {
|
||||
if (iter->thread == self) {
|
||||
malloc_mutex_unlock(&head->lock);
|
||||
malloc_mutex_unlock(TSDN_NULL, &head->lock);
|
||||
return (iter->data);
|
||||
}
|
||||
}
|
||||
@@ -180,7 +182,7 @@ tsd_init_check_recursion(tsd_init_head_t *head, tsd_init_block_t *block)
|
||||
ql_elm_new(block, link);
|
||||
block->thread = self;
|
||||
ql_tail_insert(&head->blocks, block, link);
|
||||
malloc_mutex_unlock(&head->lock);
|
||||
malloc_mutex_unlock(TSDN_NULL, &head->lock);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
@@ -188,8 +190,8 @@ void
|
||||
tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block)
|
||||
{
|
||||
|
||||
malloc_mutex_lock(&head->lock);
|
||||
malloc_mutex_lock(TSDN_NULL, &head->lock);
|
||||
ql_remove(&head->blocks, block, link);
|
||||
malloc_mutex_unlock(&head->lock);
|
||||
malloc_mutex_unlock(TSDN_NULL, &head->lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
18
src/util.c
18
src/util.c
@@ -14,6 +14,7 @@
|
||||
malloc_write("<jemalloc>: Unreachable code reached\n"); \
|
||||
abort(); \
|
||||
} \
|
||||
unreachable(); \
|
||||
} while (0)
|
||||
|
||||
#define not_implemented() do { \
|
||||
@@ -48,7 +49,7 @@ static void
|
||||
wrtmessage(void *cbopaque, const char *s)
|
||||
{
|
||||
|
||||
#ifdef SYS_write
|
||||
#if defined(JEMALLOC_HAVE_SYSCALL) && defined(SYS_write)
|
||||
/*
|
||||
* Use syscall(2) rather than write(2) when possible in order to avoid
|
||||
* the possibility of memory allocation within libc. This is necessary
|
||||
@@ -314,10 +315,9 @@ x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p)
|
||||
return (s);
|
||||
}
|
||||
|
||||
int
|
||||
size_t
|
||||
malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
|
||||
{
|
||||
int ret;
|
||||
size_t i;
|
||||
const char *f;
|
||||
|
||||
@@ -408,6 +408,8 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
|
||||
int prec = -1;
|
||||
int width = -1;
|
||||
unsigned char len = '?';
|
||||
char *s;
|
||||
size_t slen;
|
||||
|
||||
f++;
|
||||
/* Flags. */
|
||||
@@ -498,8 +500,6 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
|
||||
}
|
||||
/* Conversion specifier. */
|
||||
switch (*f) {
|
||||
char *s;
|
||||
size_t slen;
|
||||
case '%':
|
||||
/* %% */
|
||||
APPEND_C(*f);
|
||||
@@ -585,21 +585,19 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
|
||||
str[i] = '\0';
|
||||
else
|
||||
str[size - 1] = '\0';
|
||||
assert(i < INT_MAX);
|
||||
ret = (int)i;
|
||||
|
||||
#undef APPEND_C
|
||||
#undef APPEND_S
|
||||
#undef APPEND_PADDED_S
|
||||
#undef GET_ARG_NUMERIC
|
||||
return (ret);
|
||||
return (i);
|
||||
}
|
||||
|
||||
JEMALLOC_FORMAT_PRINTF(3, 4)
|
||||
int
|
||||
size_t
|
||||
malloc_snprintf(char *str, size_t size, const char *format, ...)
|
||||
{
|
||||
int ret;
|
||||
size_t ret;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
|
||||
136
src/witness.c
Normal file
136
src/witness.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#define JEMALLOC_WITNESS_C_
|
||||
#include "jemalloc/internal/jemalloc_internal.h"
|
||||
|
||||
void
|
||||
witness_init(witness_t *witness, const char *name, witness_rank_t rank,
|
||||
witness_comp_t *comp)
|
||||
{
|
||||
|
||||
witness->name = name;
|
||||
witness->rank = rank;
|
||||
witness->comp = comp;
|
||||
}
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef witness_lock_error
|
||||
#define witness_lock_error JEMALLOC_N(n_witness_lock_error)
|
||||
#endif
|
||||
void
|
||||
witness_lock_error(const witness_list_t *witnesses, const witness_t *witness)
|
||||
{
|
||||
witness_t *w;
|
||||
|
||||
malloc_printf("<jemalloc>: Lock rank order reversal:");
|
||||
ql_foreach(w, witnesses, link) {
|
||||
malloc_printf(" %s(%u)", w->name, w->rank);
|
||||
}
|
||||
malloc_printf(" %s(%u)\n", witness->name, witness->rank);
|
||||
abort();
|
||||
}
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef witness_lock_error
|
||||
#define witness_lock_error JEMALLOC_N(witness_lock_error)
|
||||
witness_lock_error_t *witness_lock_error = JEMALLOC_N(n_witness_lock_error);
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef witness_owner_error
|
||||
#define witness_owner_error JEMALLOC_N(n_witness_owner_error)
|
||||
#endif
|
||||
void
|
||||
witness_owner_error(const witness_t *witness)
|
||||
{
|
||||
|
||||
malloc_printf("<jemalloc>: Should own %s(%u)\n", witness->name,
|
||||
witness->rank);
|
||||
abort();
|
||||
}
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef witness_owner_error
|
||||
#define witness_owner_error JEMALLOC_N(witness_owner_error)
|
||||
witness_owner_error_t *witness_owner_error = JEMALLOC_N(n_witness_owner_error);
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef witness_not_owner_error
|
||||
#define witness_not_owner_error JEMALLOC_N(n_witness_not_owner_error)
|
||||
#endif
|
||||
void
|
||||
witness_not_owner_error(const witness_t *witness)
|
||||
{
|
||||
|
||||
malloc_printf("<jemalloc>: Should not own %s(%u)\n", witness->name,
|
||||
witness->rank);
|
||||
abort();
|
||||
}
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef witness_not_owner_error
|
||||
#define witness_not_owner_error JEMALLOC_N(witness_not_owner_error)
|
||||
witness_not_owner_error_t *witness_not_owner_error =
|
||||
JEMALLOC_N(n_witness_not_owner_error);
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef witness_lockless_error
|
||||
#define witness_lockless_error JEMALLOC_N(n_witness_lockless_error)
|
||||
#endif
|
||||
void
|
||||
witness_lockless_error(const witness_list_t *witnesses)
|
||||
{
|
||||
witness_t *w;
|
||||
|
||||
malloc_printf("<jemalloc>: Should not own any locks:");
|
||||
ql_foreach(w, witnesses, link) {
|
||||
malloc_printf(" %s(%u)", w->name, w->rank);
|
||||
}
|
||||
malloc_printf("\n");
|
||||
abort();
|
||||
}
|
||||
#ifdef JEMALLOC_JET
|
||||
#undef witness_lockless_error
|
||||
#define witness_lockless_error JEMALLOC_N(witness_lockless_error)
|
||||
witness_lockless_error_t *witness_lockless_error =
|
||||
JEMALLOC_N(n_witness_lockless_error);
|
||||
#endif
|
||||
|
||||
void
|
||||
witnesses_cleanup(tsd_t *tsd)
|
||||
{
|
||||
|
||||
witness_assert_lockless(tsd_tsdn(tsd));
|
||||
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
void
|
||||
witness_fork_cleanup(tsd_t *tsd)
|
||||
{
|
||||
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
void
|
||||
witness_prefork(tsd_t *tsd)
|
||||
{
|
||||
|
||||
tsd_witness_fork_set(tsd, true);
|
||||
}
|
||||
|
||||
void
|
||||
witness_postfork_parent(tsd_t *tsd)
|
||||
{
|
||||
|
||||
tsd_witness_fork_set(tsd, false);
|
||||
}
|
||||
|
||||
void
|
||||
witness_postfork_child(tsd_t *tsd)
|
||||
{
|
||||
#ifndef JEMALLOC_MUTEX_INIT_CB
|
||||
witness_list_t *witnesses;
|
||||
|
||||
witnesses = tsd_witnessesp_get(tsd);
|
||||
ql_new(witnesses);
|
||||
#endif
|
||||
tsd_witness_fork_set(tsd, false);
|
||||
}
|
||||
194
src/zone.c
194
src/zone.c
@@ -4,7 +4,7 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The malloc_default_purgeable_zone function is only available on >= 10.6.
|
||||
* The malloc_default_purgeable_zone() function is only available on >= 10.6.
|
||||
* We need to check whether it is present at runtime, thus the weak_import.
|
||||
*/
|
||||
extern malloc_zone_t *malloc_default_purgeable_zone(void)
|
||||
@@ -13,8 +13,9 @@ JEMALLOC_ATTR(weak_import);
|
||||
/******************************************************************************/
|
||||
/* Data. */
|
||||
|
||||
static malloc_zone_t zone;
|
||||
static struct malloc_introspection_t zone_introspect;
|
||||
static malloc_zone_t *default_zone, *purgeable_zone;
|
||||
static malloc_zone_t jemalloc_zone;
|
||||
static struct malloc_introspection_t jemalloc_zone_introspect;
|
||||
|
||||
/******************************************************************************/
|
||||
/* Function prototypes for non-inline static functions. */
|
||||
@@ -56,7 +57,7 @@ zone_size(malloc_zone_t *zone, void *ptr)
|
||||
* not work in practice, we must check all pointers to assure that they
|
||||
* reside within a mapped chunk before determining size.
|
||||
*/
|
||||
return (ivsalloc(ptr, config_prof));
|
||||
return (ivsalloc(tsdn_fetch(), ptr, config_prof));
|
||||
}
|
||||
|
||||
static void *
|
||||
@@ -87,7 +88,7 @@ static void
|
||||
zone_free(malloc_zone_t *zone, void *ptr)
|
||||
{
|
||||
|
||||
if (ivsalloc(ptr, config_prof) != 0) {
|
||||
if (ivsalloc(tsdn_fetch(), ptr, config_prof) != 0) {
|
||||
je_free(ptr);
|
||||
return;
|
||||
}
|
||||
@@ -99,7 +100,7 @@ static void *
|
||||
zone_realloc(malloc_zone_t *zone, void *ptr, size_t size)
|
||||
{
|
||||
|
||||
if (ivsalloc(ptr, config_prof) != 0)
|
||||
if (ivsalloc(tsdn_fetch(), ptr, config_prof) != 0)
|
||||
return (je_realloc(ptr, size));
|
||||
|
||||
return (realloc(ptr, size));
|
||||
@@ -123,7 +124,7 @@ zone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size)
|
||||
{
|
||||
size_t alloc_size;
|
||||
|
||||
alloc_size = ivsalloc(ptr, config_prof);
|
||||
alloc_size = ivsalloc(tsdn_fetch(), ptr, config_prof);
|
||||
if (alloc_size != 0) {
|
||||
assert(alloc_size == size);
|
||||
je_free(ptr);
|
||||
@@ -164,89 +165,103 @@ static void
|
||||
zone_force_unlock(malloc_zone_t *zone)
|
||||
{
|
||||
|
||||
/*
|
||||
* Call jemalloc_postfork_child() rather than
|
||||
* jemalloc_postfork_parent(), because this function is executed by both
|
||||
* parent and child. The parent can tolerate having state
|
||||
* reinitialized, but the child cannot unlock mutexes that were locked
|
||||
* by the parent.
|
||||
*/
|
||||
if (isthreaded)
|
||||
jemalloc_postfork_parent();
|
||||
jemalloc_postfork_child();
|
||||
}
|
||||
|
||||
JEMALLOC_ATTR(constructor)
|
||||
void
|
||||
register_zone(void)
|
||||
static void
|
||||
zone_init(void)
|
||||
{
|
||||
|
||||
/*
|
||||
* If something else replaced the system default zone allocator, don't
|
||||
* register jemalloc's.
|
||||
*/
|
||||
malloc_zone_t *default_zone = malloc_default_zone();
|
||||
malloc_zone_t *purgeable_zone = NULL;
|
||||
if (!default_zone->zone_name ||
|
||||
strcmp(default_zone->zone_name, "DefaultMallocZone") != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
zone.size = (void *)zone_size;
|
||||
zone.malloc = (void *)zone_malloc;
|
||||
zone.calloc = (void *)zone_calloc;
|
||||
zone.valloc = (void *)zone_valloc;
|
||||
zone.free = (void *)zone_free;
|
||||
zone.realloc = (void *)zone_realloc;
|
||||
zone.destroy = (void *)zone_destroy;
|
||||
zone.zone_name = "jemalloc_zone";
|
||||
zone.batch_malloc = NULL;
|
||||
zone.batch_free = NULL;
|
||||
zone.introspect = &zone_introspect;
|
||||
zone.version = JEMALLOC_ZONE_VERSION;
|
||||
jemalloc_zone.size = (void *)zone_size;
|
||||
jemalloc_zone.malloc = (void *)zone_malloc;
|
||||
jemalloc_zone.calloc = (void *)zone_calloc;
|
||||
jemalloc_zone.valloc = (void *)zone_valloc;
|
||||
jemalloc_zone.free = (void *)zone_free;
|
||||
jemalloc_zone.realloc = (void *)zone_realloc;
|
||||
jemalloc_zone.destroy = (void *)zone_destroy;
|
||||
jemalloc_zone.zone_name = "jemalloc_zone";
|
||||
jemalloc_zone.batch_malloc = NULL;
|
||||
jemalloc_zone.batch_free = NULL;
|
||||
jemalloc_zone.introspect = &jemalloc_zone_introspect;
|
||||
jemalloc_zone.version = JEMALLOC_ZONE_VERSION;
|
||||
#if (JEMALLOC_ZONE_VERSION >= 5)
|
||||
zone.memalign = zone_memalign;
|
||||
jemalloc_zone.memalign = zone_memalign;
|
||||
#endif
|
||||
#if (JEMALLOC_ZONE_VERSION >= 6)
|
||||
zone.free_definite_size = zone_free_definite_size;
|
||||
jemalloc_zone.free_definite_size = zone_free_definite_size;
|
||||
#endif
|
||||
#if (JEMALLOC_ZONE_VERSION >= 8)
|
||||
zone.pressure_relief = NULL;
|
||||
jemalloc_zone.pressure_relief = NULL;
|
||||
#endif
|
||||
|
||||
zone_introspect.enumerator = NULL;
|
||||
zone_introspect.good_size = (void *)zone_good_size;
|
||||
zone_introspect.check = NULL;
|
||||
zone_introspect.print = NULL;
|
||||
zone_introspect.log = NULL;
|
||||
zone_introspect.force_lock = (void *)zone_force_lock;
|
||||
zone_introspect.force_unlock = (void *)zone_force_unlock;
|
||||
zone_introspect.statistics = NULL;
|
||||
jemalloc_zone_introspect.enumerator = NULL;
|
||||
jemalloc_zone_introspect.good_size = (void *)zone_good_size;
|
||||
jemalloc_zone_introspect.check = NULL;
|
||||
jemalloc_zone_introspect.print = NULL;
|
||||
jemalloc_zone_introspect.log = NULL;
|
||||
jemalloc_zone_introspect.force_lock = (void *)zone_force_lock;
|
||||
jemalloc_zone_introspect.force_unlock = (void *)zone_force_unlock;
|
||||
jemalloc_zone_introspect.statistics = NULL;
|
||||
#if (JEMALLOC_ZONE_VERSION >= 6)
|
||||
zone_introspect.zone_locked = NULL;
|
||||
jemalloc_zone_introspect.zone_locked = NULL;
|
||||
#endif
|
||||
#if (JEMALLOC_ZONE_VERSION >= 7)
|
||||
zone_introspect.enable_discharge_checking = NULL;
|
||||
zone_introspect.disable_discharge_checking = NULL;
|
||||
zone_introspect.discharge = NULL;
|
||||
#ifdef __BLOCKS__
|
||||
zone_introspect.enumerate_discharged_pointers = NULL;
|
||||
#else
|
||||
zone_introspect.enumerate_unavailable_without_blocks = NULL;
|
||||
#endif
|
||||
jemalloc_zone_introspect.enable_discharge_checking = NULL;
|
||||
jemalloc_zone_introspect.disable_discharge_checking = NULL;
|
||||
jemalloc_zone_introspect.discharge = NULL;
|
||||
# ifdef __BLOCKS__
|
||||
jemalloc_zone_introspect.enumerate_discharged_pointers = NULL;
|
||||
# else
|
||||
jemalloc_zone_introspect.enumerate_unavailable_without_blocks = NULL;
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static malloc_zone_t *
|
||||
zone_default_get(void)
|
||||
{
|
||||
malloc_zone_t **zones = NULL;
|
||||
unsigned int num_zones = 0;
|
||||
|
||||
/*
|
||||
* The default purgeable zone is created lazily by OSX's libc. It uses
|
||||
* the default zone when it is created for "small" allocations
|
||||
* (< 15 KiB), but assumes the default zone is a scalable_zone. This
|
||||
* obviously fails when the default zone is the jemalloc zone, so
|
||||
* malloc_default_purgeable_zone is called beforehand so that the
|
||||
* default purgeable zone is created when the default zone is still
|
||||
* a scalable_zone. As purgeable zones only exist on >= 10.6, we need
|
||||
* to check for the existence of malloc_default_purgeable_zone() at
|
||||
* run time.
|
||||
* On OSX 10.12, malloc_default_zone returns a special zone that is not
|
||||
* present in the list of registered zones. That zone uses a "lite zone"
|
||||
* if one is present (apparently enabled when malloc stack logging is
|
||||
* enabled), or the first registered zone otherwise. In practice this
|
||||
* means unless malloc stack logging is enabled, the first registered
|
||||
* zone is the default. So get the list of zones to get the first one,
|
||||
* instead of relying on malloc_default_zone.
|
||||
*/
|
||||
if (malloc_default_purgeable_zone != NULL)
|
||||
purgeable_zone = malloc_default_purgeable_zone();
|
||||
if (KERN_SUCCESS != malloc_get_all_zones(0, NULL,
|
||||
(vm_address_t**)&zones, &num_zones)) {
|
||||
/*
|
||||
* Reset the value in case the failure happened after it was
|
||||
* set.
|
||||
*/
|
||||
num_zones = 0;
|
||||
}
|
||||
|
||||
/* Register the custom zone. At this point it won't be the default. */
|
||||
malloc_zone_register(&zone);
|
||||
if (num_zones)
|
||||
return (zones[0]);
|
||||
|
||||
return (malloc_default_zone());
|
||||
}
|
||||
|
||||
/* As written, this function can only promote jemalloc_zone. */
|
||||
static void
|
||||
zone_promote(void)
|
||||
{
|
||||
malloc_zone_t *zone;
|
||||
|
||||
do {
|
||||
default_zone = malloc_default_zone();
|
||||
/*
|
||||
* Unregister and reregister the default zone. On OSX >= 10.6,
|
||||
* unregistering takes the last registered zone and places it
|
||||
@@ -257,6 +272,7 @@ register_zone(void)
|
||||
*/
|
||||
malloc_zone_unregister(default_zone);
|
||||
malloc_zone_register(default_zone);
|
||||
|
||||
/*
|
||||
* On OSX 10.6, having the default purgeable zone appear before
|
||||
* the default zone makes some things crash because it thinks it
|
||||
@@ -268,9 +284,47 @@ register_zone(void)
|
||||
* above, i.e. the default zone. Registering it again then puts
|
||||
* it at the end, obviously after the default zone.
|
||||
*/
|
||||
if (purgeable_zone) {
|
||||
if (purgeable_zone != NULL) {
|
||||
malloc_zone_unregister(purgeable_zone);
|
||||
malloc_zone_register(purgeable_zone);
|
||||
}
|
||||
} while (malloc_default_zone() != &zone);
|
||||
|
||||
zone = zone_default_get();
|
||||
} while (zone != &jemalloc_zone);
|
||||
}
|
||||
|
||||
JEMALLOC_ATTR(constructor)
|
||||
void
|
||||
zone_register(void)
|
||||
{
|
||||
|
||||
/*
|
||||
* If something else replaced the system default zone allocator, don't
|
||||
* register jemalloc's.
|
||||
*/
|
||||
default_zone = zone_default_get();
|
||||
if (!default_zone->zone_name || strcmp(default_zone->zone_name,
|
||||
"DefaultMallocZone") != 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The default purgeable zone is created lazily by OSX's libc. It uses
|
||||
* the default zone when it is created for "small" allocations
|
||||
* (< 15 KiB), but assumes the default zone is a scalable_zone. This
|
||||
* obviously fails when the default zone is the jemalloc zone, so
|
||||
* malloc_default_purgeable_zone() is called beforehand so that the
|
||||
* default purgeable zone is created when the default zone is still
|
||||
* a scalable_zone. As purgeable zones only exist on >= 10.6, we need
|
||||
* to check for the existence of malloc_default_purgeable_zone() at
|
||||
* run time.
|
||||
*/
|
||||
purgeable_zone = (malloc_default_purgeable_zone == NULL) ? NULL :
|
||||
malloc_default_purgeable_zone();
|
||||
|
||||
/* Register the custom zone. At this point it won't be the default. */
|
||||
zone_init();
|
||||
malloc_zone_register(&jemalloc_zone);
|
||||
|
||||
/* Promote the custom zone to be default. */
|
||||
zone_promote();
|
||||
}
|
||||
|
||||
@@ -19,39 +19,6 @@
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Define always-enabled assertion macros, so that test assertions execute even
|
||||
* if assertions are disabled in the library code. These definitions must
|
||||
* exist prior to including "jemalloc/internal/util.h".
|
||||
*/
|
||||
#define assert(e) do { \
|
||||
if (!(e)) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: %s:%d: Failed assertion: \"%s\"\n", \
|
||||
__FILE__, __LINE__, #e); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define not_reached() do { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: %s:%d: Unreachable code reached\n", \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
|
||||
#define not_implemented() do { \
|
||||
malloc_printf("<jemalloc>: %s:%d: Not implemented\n", \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
|
||||
#define assert_not_implemented(e) do { \
|
||||
if (!(e)) \
|
||||
not_implemented(); \
|
||||
} while (0)
|
||||
|
||||
#include "test/jemalloc_test_defs.h"
|
||||
|
||||
#ifdef JEMALLOC_OSSPIN
|
||||
@@ -86,6 +53,14 @@
|
||||
# include "jemalloc/internal/jemalloc_internal_defs.h"
|
||||
# include "jemalloc/internal/jemalloc_internal_macros.h"
|
||||
|
||||
static const bool config_debug =
|
||||
#ifdef JEMALLOC_DEBUG
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
;
|
||||
|
||||
# define JEMALLOC_N(n) @private_namespace@##n
|
||||
# include "jemalloc/internal/private_namespace.h"
|
||||
|
||||
@@ -149,3 +124,40 @@
|
||||
#include "test/thd.h"
|
||||
#define MEXP 19937
|
||||
#include "test/SFMT.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Define always-enabled assertion macros, so that test assertions execute even
|
||||
* if assertions are disabled in the library code.
|
||||
*/
|
||||
#undef assert
|
||||
#undef not_reached
|
||||
#undef not_implemented
|
||||
#undef assert_not_implemented
|
||||
|
||||
#define assert(e) do { \
|
||||
if (!(e)) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: %s:%d: Failed assertion: \"%s\"\n", \
|
||||
__FILE__, __LINE__, #e); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define not_reached() do { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: %s:%d: Unreachable code reached\n", \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
|
||||
#define not_implemented() do { \
|
||||
malloc_printf("<jemalloc>: %s:%d: Not implemented\n", \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
|
||||
#define assert_not_implemented(e) do { \
|
||||
if (!(e)) \
|
||||
not_implemented(); \
|
||||
} while (0)
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
typedef struct {
|
||||
#ifdef _WIN32
|
||||
CRITICAL_SECTION lock;
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
os_unfair_lock lock;
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
OSSpinLock lock;
|
||||
#else
|
||||
|
||||
@@ -311,6 +311,9 @@ label_test_end: \
|
||||
#define test(...) \
|
||||
p_test(__VA_ARGS__, NULL)
|
||||
|
||||
#define test_no_malloc_init(...) \
|
||||
p_test_no_malloc_init(__VA_ARGS__, NULL)
|
||||
|
||||
#define test_skip_if(e) do { \
|
||||
if (e) { \
|
||||
test_skip("%s:%s:%d: Test skipped: (%s)", \
|
||||
@@ -324,6 +327,7 @@ void test_fail(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);
|
||||
|
||||
/* For private use by macros. */
|
||||
test_status_t p_test(test_t *t, ...);
|
||||
test_status_t p_test_no_malloc_init(test_t *t, ...);
|
||||
void p_test_init(const char *name);
|
||||
void p_test_fini(void);
|
||||
void p_test_fail(const char *prefix, const char *message);
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
#define CHUNK 0x400000
|
||||
/* #define MAXALIGN ((size_t)UINT64_C(0x80000000000)) */
|
||||
#define MAXALIGN ((size_t)0x2000000LU)
|
||||
#define NITER 4
|
||||
#define MAXALIGN (((size_t)1) << 23)
|
||||
|
||||
/*
|
||||
* On systems which can't merge extents, tests that call this function generate
|
||||
* a lot of dirty memory very quickly. Purging between cycles mitigates
|
||||
* potential OOM on e.g. 32-bit Windows.
|
||||
*/
|
||||
static void
|
||||
purge(void)
|
||||
{
|
||||
|
||||
assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected mallctl error");
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_alignment_errors)
|
||||
{
|
||||
@@ -74,6 +85,7 @@ TEST_END
|
||||
|
||||
TEST_BEGIN(test_alignment_and_size)
|
||||
{
|
||||
#define NITER 4
|
||||
size_t alignment, size, total;
|
||||
unsigned i;
|
||||
void *ps[NITER];
|
||||
@@ -110,7 +122,9 @@ TEST_BEGIN(test_alignment_and_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
purge();
|
||||
}
|
||||
#undef NITER
|
||||
}
|
||||
TEST_END
|
||||
|
||||
|
||||
@@ -121,6 +121,10 @@ TEST_BEGIN(test_chunk)
|
||||
{
|
||||
void *p;
|
||||
size_t old_size, new_size, large0, large1, huge0, huge1, huge2, sz;
|
||||
unsigned arena_ind;
|
||||
int flags;
|
||||
size_t hooks_mib[3], purge_mib[3];
|
||||
size_t hooks_miblen, purge_miblen;
|
||||
chunk_hooks_t new_hooks = {
|
||||
chunk_alloc,
|
||||
chunk_dalloc,
|
||||
@@ -132,10 +136,19 @@ TEST_BEGIN(test_chunk)
|
||||
};
|
||||
bool xallocx_success_a, xallocx_success_b, xallocx_success_c;
|
||||
|
||||
sz = sizeof(unsigned);
|
||||
assert_d_eq(mallctl("arenas.extend", &arena_ind, &sz, NULL, 0), 0,
|
||||
"Unexpected mallctl() failure");
|
||||
flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
|
||||
|
||||
/* Install custom chunk hooks. */
|
||||
hooks_miblen = sizeof(hooks_mib)/sizeof(size_t);
|
||||
assert_d_eq(mallctlnametomib("arena.0.chunk_hooks", hooks_mib,
|
||||
&hooks_miblen), 0, "Unexpected mallctlnametomib() failure");
|
||||
hooks_mib[1] = (size_t)arena_ind;
|
||||
old_size = sizeof(chunk_hooks_t);
|
||||
new_size = sizeof(chunk_hooks_t);
|
||||
assert_d_eq(mallctl("arena.0.chunk_hooks", &old_hooks, &old_size,
|
||||
assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, &old_hooks, &old_size,
|
||||
&new_hooks, new_size), 0, "Unexpected chunk_hooks error");
|
||||
orig_hooks = old_hooks;
|
||||
assert_ptr_ne(old_hooks.alloc, chunk_alloc, "Unexpected alloc error");
|
||||
@@ -165,45 +178,49 @@ TEST_BEGIN(test_chunk)
|
||||
"Unexpected arenas.hchunk.2.size failure");
|
||||
|
||||
/* Test dalloc/decommit/purge cascade. */
|
||||
purge_miblen = sizeof(purge_mib)/sizeof(size_t);
|
||||
assert_d_eq(mallctlnametomib("arena.0.purge", purge_mib, &purge_miblen),
|
||||
0, "Unexpected mallctlnametomib() failure");
|
||||
purge_mib[1] = (size_t)arena_ind;
|
||||
do_dalloc = false;
|
||||
do_decommit = false;
|
||||
p = mallocx(huge0 * 2, 0);
|
||||
p = mallocx(huge0 * 2, flags);
|
||||
assert_ptr_not_null(p, "Unexpected mallocx() error");
|
||||
did_dalloc = false;
|
||||
did_decommit = false;
|
||||
did_purge = false;
|
||||
did_split = false;
|
||||
xallocx_success_a = (xallocx(p, huge0, 0, 0) == huge0);
|
||||
assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected arena.0.purge error");
|
||||
xallocx_success_a = (xallocx(p, huge0, 0, flags) == huge0);
|
||||
assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),
|
||||
0, "Unexpected arena.%u.purge error", arena_ind);
|
||||
if (xallocx_success_a) {
|
||||
assert_true(did_dalloc, "Expected dalloc");
|
||||
assert_false(did_decommit, "Unexpected decommit");
|
||||
assert_true(did_purge, "Expected purge");
|
||||
}
|
||||
assert_true(did_split, "Expected split");
|
||||
dallocx(p, 0);
|
||||
dallocx(p, flags);
|
||||
do_dalloc = true;
|
||||
|
||||
/* Test decommit/commit and observe split/merge. */
|
||||
do_dalloc = false;
|
||||
do_decommit = true;
|
||||
p = mallocx(huge0 * 2, 0);
|
||||
p = mallocx(huge0 * 2, flags);
|
||||
assert_ptr_not_null(p, "Unexpected mallocx() error");
|
||||
did_decommit = false;
|
||||
did_commit = false;
|
||||
did_split = false;
|
||||
did_merge = false;
|
||||
xallocx_success_b = (xallocx(p, huge0, 0, 0) == huge0);
|
||||
assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected arena.0.purge error");
|
||||
xallocx_success_b = (xallocx(p, huge0, 0, flags) == huge0);
|
||||
assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),
|
||||
0, "Unexpected arena.%u.purge error", arena_ind);
|
||||
if (xallocx_success_b)
|
||||
assert_true(did_split, "Expected split");
|
||||
xallocx_success_c = (xallocx(p, huge0 * 2, 0, 0) == huge0 * 2);
|
||||
xallocx_success_c = (xallocx(p, huge0 * 2, 0, flags) == huge0 * 2);
|
||||
assert_b_eq(did_decommit, did_commit, "Expected decommit/commit match");
|
||||
if (xallocx_success_b && xallocx_success_c)
|
||||
assert_true(did_merge, "Expected merge");
|
||||
dallocx(p, 0);
|
||||
dallocx(p, flags);
|
||||
do_dalloc = true;
|
||||
do_decommit = false;
|
||||
|
||||
@@ -214,42 +231,42 @@ TEST_BEGIN(test_chunk)
|
||||
* successful xallocx() from size=huge2 to size=huge1 is
|
||||
* guaranteed to leave trailing purgeable memory.
|
||||
*/
|
||||
p = mallocx(huge2, 0);
|
||||
p = mallocx(huge2, flags);
|
||||
assert_ptr_not_null(p, "Unexpected mallocx() error");
|
||||
did_purge = false;
|
||||
assert_zu_eq(xallocx(p, huge1, 0, 0), huge1,
|
||||
assert_zu_eq(xallocx(p, huge1, 0, flags), huge1,
|
||||
"Unexpected xallocx() failure");
|
||||
assert_true(did_purge, "Expected purge");
|
||||
dallocx(p, 0);
|
||||
dallocx(p, flags);
|
||||
}
|
||||
|
||||
/* Test decommit for large allocations. */
|
||||
do_decommit = true;
|
||||
p = mallocx(large1, 0);
|
||||
p = mallocx(large1, flags);
|
||||
assert_ptr_not_null(p, "Unexpected mallocx() error");
|
||||
assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected arena.0.purge error");
|
||||
assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),
|
||||
0, "Unexpected arena.%u.purge error", arena_ind);
|
||||
did_decommit = false;
|
||||
assert_zu_eq(xallocx(p, large0, 0, 0), large0,
|
||||
assert_zu_eq(xallocx(p, large0, 0, flags), large0,
|
||||
"Unexpected xallocx() failure");
|
||||
assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected arena.0.purge error");
|
||||
assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),
|
||||
0, "Unexpected arena.%u.purge error", arena_ind);
|
||||
did_commit = false;
|
||||
assert_zu_eq(xallocx(p, large1, 0, 0), large1,
|
||||
assert_zu_eq(xallocx(p, large1, 0, flags), large1,
|
||||
"Unexpected xallocx() failure");
|
||||
assert_b_eq(did_decommit, did_commit, "Expected decommit/commit match");
|
||||
dallocx(p, 0);
|
||||
dallocx(p, flags);
|
||||
do_decommit = false;
|
||||
|
||||
/* Make sure non-huge allocation succeeds. */
|
||||
p = mallocx(42, 0);
|
||||
p = mallocx(42, flags);
|
||||
assert_ptr_not_null(p, "Unexpected mallocx() error");
|
||||
dallocx(p, 0);
|
||||
dallocx(p, flags);
|
||||
|
||||
/* Restore chunk hooks. */
|
||||
assert_d_eq(mallctl("arena.0.chunk_hooks", NULL, NULL, &old_hooks,
|
||||
new_size), 0, "Unexpected chunk_hooks error");
|
||||
assert_d_eq(mallctl("arena.0.chunk_hooks", &old_hooks, &old_size,
|
||||
assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, NULL, NULL,
|
||||
&old_hooks, new_size), 0, "Unexpected chunk_hooks error");
|
||||
assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, &old_hooks, &old_size,
|
||||
NULL, 0), 0, "Unexpected chunk_hooks error");
|
||||
assert_ptr_eq(old_hooks.alloc, orig_hooks.alloc,
|
||||
"Unexpected alloc error");
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
#ifdef JEMALLOC_FILL
|
||||
const char *malloc_conf = "junk:false";
|
||||
#endif
|
||||
|
||||
static unsigned
|
||||
get_nsizes_impl(const char *cmd)
|
||||
{
|
||||
@@ -46,6 +50,19 @@ get_huge_size(size_t ind)
|
||||
return (get_size_impl("arenas.hchunk.0.size", ind));
|
||||
}
|
||||
|
||||
/*
|
||||
* On systems which can't merge extents, tests that call this function generate
|
||||
* a lot of dirty memory very quickly. Purging between cycles mitigates
|
||||
* potential OOM on e.g. 32-bit Windows.
|
||||
*/
|
||||
static void
|
||||
purge(void)
|
||||
{
|
||||
|
||||
assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected mallctl error");
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_overflow)
|
||||
{
|
||||
size_t hugemax;
|
||||
@@ -69,41 +86,48 @@ TEST_END
|
||||
|
||||
TEST_BEGIN(test_oom)
|
||||
{
|
||||
size_t hugemax, size, alignment;
|
||||
|
||||
hugemax = get_huge_size(get_nhuge()-1);
|
||||
size_t hugemax;
|
||||
bool oom;
|
||||
void *ptrs[3];
|
||||
unsigned i;
|
||||
|
||||
/*
|
||||
* It should be impossible to allocate two objects that each consume
|
||||
* more than half the virtual address space.
|
||||
* It should be impossible to allocate three objects that each consume
|
||||
* nearly half the virtual address space.
|
||||
*/
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = mallocx(hugemax, 0);
|
||||
if (p != NULL) {
|
||||
assert_ptr_null(mallocx(hugemax, 0),
|
||||
"Expected OOM for mallocx(size=%#zx, 0)", hugemax);
|
||||
dallocx(p, 0);
|
||||
}
|
||||
hugemax = get_huge_size(get_nhuge()-1);
|
||||
oom = false;
|
||||
for (i = 0; i < sizeof(ptrs) / sizeof(void *); i++) {
|
||||
ptrs[i] = mallocx(hugemax, 0);
|
||||
if (ptrs[i] == NULL)
|
||||
oom = true;
|
||||
}
|
||||
assert_true(oom,
|
||||
"Expected OOM during series of calls to mallocx(size=%zu, 0)",
|
||||
hugemax);
|
||||
for (i = 0; i < sizeof(ptrs) / sizeof(void *); i++) {
|
||||
if (ptrs[i] != NULL)
|
||||
dallocx(ptrs[i], 0);
|
||||
}
|
||||
purge();
|
||||
|
||||
#if LG_SIZEOF_PTR == 3
|
||||
size = ZU(0x8000000000000000);
|
||||
alignment = ZU(0x8000000000000000);
|
||||
assert_ptr_null(mallocx(0x8000000000000000ULL,
|
||||
MALLOCX_ALIGN(0x8000000000000000ULL)),
|
||||
"Expected OOM for mallocx()");
|
||||
assert_ptr_null(mallocx(0x8000000000000000ULL,
|
||||
MALLOCX_ALIGN(0x80000000)),
|
||||
"Expected OOM for mallocx()");
|
||||
#else
|
||||
size = ZU(0x80000000);
|
||||
alignment = ZU(0x80000000);
|
||||
assert_ptr_null(mallocx(0x80000000UL, MALLOCX_ALIGN(0x80000000UL)),
|
||||
"Expected OOM for mallocx()");
|
||||
#endif
|
||||
assert_ptr_null(mallocx(size, MALLOCX_ALIGN(alignment)),
|
||||
"Expected OOM for mallocx(size=%#zx, MALLOCX_ALIGN(%#zx)", size,
|
||||
alignment);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_basic)
|
||||
{
|
||||
#define MAXSZ (((size_t)1) << 26)
|
||||
#define MAXSZ (((size_t)1) << 23)
|
||||
size_t sz;
|
||||
|
||||
for (sz = 1; sz < MAXSZ; sz = nallocx(sz, 0) + 1) {
|
||||
@@ -112,23 +136,28 @@ TEST_BEGIN(test_basic)
|
||||
nsz = nallocx(sz, 0);
|
||||
assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
|
||||
p = mallocx(sz, 0);
|
||||
assert_ptr_not_null(p, "Unexpected mallocx() error");
|
||||
assert_ptr_not_null(p,
|
||||
"Unexpected mallocx(size=%zx, flags=0) error", sz);
|
||||
rsz = sallocx(p, 0);
|
||||
assert_zu_ge(rsz, sz, "Real size smaller than expected");
|
||||
assert_zu_eq(nsz, rsz, "nallocx()/sallocx() size mismatch");
|
||||
dallocx(p, 0);
|
||||
|
||||
p = mallocx(sz, 0);
|
||||
assert_ptr_not_null(p, "Unexpected mallocx() error");
|
||||
assert_ptr_not_null(p,
|
||||
"Unexpected mallocx(size=%zx, flags=0) error", sz);
|
||||
dallocx(p, 0);
|
||||
|
||||
nsz = nallocx(sz, MALLOCX_ZERO);
|
||||
assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
|
||||
p = mallocx(sz, MALLOCX_ZERO);
|
||||
assert_ptr_not_null(p, "Unexpected mallocx() error");
|
||||
assert_ptr_not_null(p,
|
||||
"Unexpected mallocx(size=%zx, flags=MALLOCX_ZERO) error",
|
||||
nsz);
|
||||
rsz = sallocx(p, 0);
|
||||
assert_zu_eq(nsz, rsz, "nallocx()/sallocx() rsize mismatch");
|
||||
dallocx(p, 0);
|
||||
purge();
|
||||
}
|
||||
#undef MAXSZ
|
||||
}
|
||||
@@ -136,7 +165,7 @@ TEST_END
|
||||
|
||||
TEST_BEGIN(test_alignment_and_size)
|
||||
{
|
||||
#define MAXALIGN (((size_t)1) << 25)
|
||||
#define MAXALIGN (((size_t)1) << 23)
|
||||
#define NITER 4
|
||||
size_t nsz, rsz, sz, alignment, total;
|
||||
unsigned i;
|
||||
@@ -186,6 +215,7 @@ TEST_BEGIN(test_alignment_and_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
purge();
|
||||
}
|
||||
#undef MAXALIGN
|
||||
#undef NITER
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
#define CHUNK 0x400000
|
||||
/* #define MAXALIGN ((size_t)UINT64_C(0x80000000000)) */
|
||||
#define MAXALIGN ((size_t)0x2000000LU)
|
||||
#define NITER 4
|
||||
#define MAXALIGN (((size_t)1) << 23)
|
||||
|
||||
/*
|
||||
* On systems which can't merge extents, tests that call this function generate
|
||||
* a lot of dirty memory very quickly. Purging between cycles mitigates
|
||||
* potential OOM on e.g. 32-bit Windows.
|
||||
*/
|
||||
static void
|
||||
purge(void)
|
||||
{
|
||||
|
||||
assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected mallctl error");
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_alignment_errors)
|
||||
{
|
||||
@@ -66,6 +77,7 @@ TEST_END
|
||||
|
||||
TEST_BEGIN(test_alignment_and_size)
|
||||
{
|
||||
#define NITER 4
|
||||
size_t alignment, size, total;
|
||||
unsigned i;
|
||||
int err;
|
||||
@@ -104,7 +116,9 @@ TEST_BEGIN(test_alignment_and_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
purge();
|
||||
}
|
||||
#undef NITER
|
||||
}
|
||||
TEST_END
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
#ifdef JEMALLOC_FILL
|
||||
const char *malloc_conf = "junk:false";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use a separate arena for xallocx() extension/contraction tests so that
|
||||
* internal allocation e.g. by heap profiling can't interpose allocations where
|
||||
|
||||
@@ -11,6 +11,8 @@ mtx_init(mtx_t *mtx)
|
||||
#ifdef _WIN32
|
||||
if (!InitializeCriticalSectionAndSpinCount(&mtx->lock, _CRT_SPINCOUNT))
|
||||
return (true);
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
mtx->lock = OS_UNFAIR_LOCK_INIT;
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
mtx->lock = 0;
|
||||
#else
|
||||
@@ -33,6 +35,7 @@ mtx_fini(mtx_t *mtx)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
#else
|
||||
pthread_mutex_destroy(&mtx->lock);
|
||||
@@ -45,6 +48,8 @@ mtx_lock(mtx_t *mtx)
|
||||
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection(&mtx->lock);
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
os_unfair_lock_lock(&mtx->lock);
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
OSSpinLockLock(&mtx->lock);
|
||||
#else
|
||||
@@ -58,6 +63,8 @@ mtx_unlock(mtx_t *mtx)
|
||||
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection(&mtx->lock);
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
os_unfair_lock_unlock(&mtx->lock);
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
OSSpinLockUnlock(&mtx->lock);
|
||||
#else
|
||||
|
||||
@@ -60,32 +60,30 @@ p_test_fini(void)
|
||||
malloc_printf("%s: %s\n", test_name, test_status_string(test_status));
|
||||
}
|
||||
|
||||
test_status_t
|
||||
p_test(test_t *t, ...)
|
||||
static test_status_t
|
||||
p_test_impl(bool do_malloc_init, test_t *t, va_list ap)
|
||||
{
|
||||
test_status_t ret;
|
||||
va_list ap;
|
||||
|
||||
/*
|
||||
* Make sure initialization occurs prior to running tests. Tests are
|
||||
* special because they may use internal facilities prior to triggering
|
||||
* initialization as a side effect of calling into the public API. This
|
||||
* is a final safety that works even if jemalloc_constructor() doesn't
|
||||
* run, as for MSVC builds.
|
||||
*/
|
||||
if (nallocx(1, 0) == 0) {
|
||||
malloc_printf("Initialization error");
|
||||
return (test_status_fail);
|
||||
if (do_malloc_init) {
|
||||
/*
|
||||
* Make sure initialization occurs prior to running tests.
|
||||
* Tests are special because they may use internal facilities
|
||||
* prior to triggering initialization as a side effect of
|
||||
* calling into the public API.
|
||||
*/
|
||||
if (nallocx(1, 0) == 0) {
|
||||
malloc_printf("Initialization error");
|
||||
return (test_status_fail);
|
||||
}
|
||||
}
|
||||
|
||||
ret = test_status_pass;
|
||||
va_start(ap, t);
|
||||
for (; t != NULL; t = va_arg(ap, test_t *)) {
|
||||
t();
|
||||
if (test_status > ret)
|
||||
ret = test_status;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
malloc_printf("--- %s: %u/%u, %s: %u/%u, %s: %u/%u ---\n",
|
||||
test_status_string(test_status_pass),
|
||||
@@ -98,6 +96,34 @@ p_test(test_t *t, ...)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
test_status_t
|
||||
p_test(test_t *t, ...)
|
||||
{
|
||||
test_status_t ret;
|
||||
va_list ap;
|
||||
|
||||
ret = test_status_pass;
|
||||
va_start(ap, t);
|
||||
ret = p_test_impl(true, t, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
test_status_t
|
||||
p_test_no_malloc_init(test_t *t, ...)
|
||||
{
|
||||
test_status_t ret;
|
||||
va_list ap;
|
||||
|
||||
ret = test_status_pass;
|
||||
va_start(ap, t);
|
||||
ret = p_test_impl(false, t, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
p_test_fail(const char *prefix, const char *message)
|
||||
{
|
||||
|
||||
@@ -32,9 +32,8 @@ timer_ratio(timedelta_t *a, timedelta_t *b, char *buf, size_t buflen)
|
||||
uint64_t t0 = timer_usec(a);
|
||||
uint64_t t1 = timer_usec(b);
|
||||
uint64_t mult;
|
||||
unsigned i = 0;
|
||||
unsigned j;
|
||||
int n;
|
||||
size_t i = 0;
|
||||
size_t j, n;
|
||||
|
||||
/* Whole. */
|
||||
n = malloc_snprintf(&buf[i], buflen-i, "%"FMTu64, t0 / t1);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
JEMALLOC_INLINE_C void
|
||||
time_func(timedelta_t *timer, uint64_t nwarmup, uint64_t niter, void (*func)(void))
|
||||
time_func(timedelta_t *timer, uint64_t nwarmup, uint64_t niter,
|
||||
void (*func)(void))
|
||||
{
|
||||
uint64_t i;
|
||||
|
||||
|
||||
19
test/unit/a0.c
Normal file
19
test/unit/a0.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
TEST_BEGIN(test_a0)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = a0malloc(1);
|
||||
assert_ptr_not_null(p, "Unexpected a0malloc() error");
|
||||
a0dalloc(p);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
|
||||
return (test_no_malloc_init(
|
||||
test_a0));
|
||||
}
|
||||
159
test/unit/arena_reset.c
Normal file
159
test/unit/arena_reset.c
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
#ifdef JEMALLOC_PROF
|
||||
const char *malloc_conf = "prof:true,lg_prof_sample:0";
|
||||
#endif
|
||||
|
||||
static unsigned
|
||||
get_nsizes_impl(const char *cmd)
|
||||
{
|
||||
unsigned ret;
|
||||
size_t z;
|
||||
|
||||
z = sizeof(unsigned);
|
||||
assert_d_eq(mallctl(cmd, &ret, &z, NULL, 0), 0,
|
||||
"Unexpected mallctl(\"%s\", ...) failure", cmd);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static unsigned
|
||||
get_nsmall(void)
|
||||
{
|
||||
|
||||
return (get_nsizes_impl("arenas.nbins"));
|
||||
}
|
||||
|
||||
static unsigned
|
||||
get_nlarge(void)
|
||||
{
|
||||
|
||||
return (get_nsizes_impl("arenas.nlruns"));
|
||||
}
|
||||
|
||||
static unsigned
|
||||
get_nhuge(void)
|
||||
{
|
||||
|
||||
return (get_nsizes_impl("arenas.nhchunks"));
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_size_impl(const char *cmd, size_t ind)
|
||||
{
|
||||
size_t ret;
|
||||
size_t z;
|
||||
size_t mib[4];
|
||||
size_t miblen = 4;
|
||||
|
||||
z = sizeof(size_t);
|
||||
assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
|
||||
0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
|
||||
mib[2] = ind;
|
||||
z = sizeof(size_t);
|
||||
assert_d_eq(mallctlbymib(mib, miblen, &ret, &z, NULL, 0),
|
||||
0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_small_size(size_t ind)
|
||||
{
|
||||
|
||||
return (get_size_impl("arenas.bin.0.size", ind));
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_large_size(size_t ind)
|
||||
{
|
||||
|
||||
return (get_size_impl("arenas.lrun.0.size", ind));
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_huge_size(size_t ind)
|
||||
{
|
||||
|
||||
return (get_size_impl("arenas.hchunk.0.size", ind));
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_arena_reset)
|
||||
{
|
||||
#define NHUGE 4
|
||||
unsigned arena_ind, nsmall, nlarge, nhuge, nptrs, i;
|
||||
size_t sz, miblen;
|
||||
void **ptrs;
|
||||
int flags;
|
||||
size_t mib[3];
|
||||
tsdn_t *tsdn;
|
||||
|
||||
test_skip_if((config_valgrind && unlikely(in_valgrind)) || (config_fill
|
||||
&& unlikely(opt_quarantine)));
|
||||
|
||||
sz = sizeof(unsigned);
|
||||
assert_d_eq(mallctl("arenas.extend", &arena_ind, &sz, NULL, 0), 0,
|
||||
"Unexpected mallctl() failure");
|
||||
|
||||
flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
|
||||
|
||||
nsmall = get_nsmall();
|
||||
nlarge = get_nlarge();
|
||||
nhuge = get_nhuge() > NHUGE ? NHUGE : get_nhuge();
|
||||
nptrs = nsmall + nlarge + nhuge;
|
||||
ptrs = (void **)malloc(nptrs * sizeof(void *));
|
||||
assert_ptr_not_null(ptrs, "Unexpected malloc() failure");
|
||||
|
||||
/* Allocate objects with a wide range of sizes. */
|
||||
for (i = 0; i < nsmall; i++) {
|
||||
sz = get_small_size(i);
|
||||
ptrs[i] = mallocx(sz, flags);
|
||||
assert_ptr_not_null(ptrs[i],
|
||||
"Unexpected mallocx(%zu, %#x) failure", sz, flags);
|
||||
}
|
||||
for (i = 0; i < nlarge; i++) {
|
||||
sz = get_large_size(i);
|
||||
ptrs[nsmall + i] = mallocx(sz, flags);
|
||||
assert_ptr_not_null(ptrs[i],
|
||||
"Unexpected mallocx(%zu, %#x) failure", sz, flags);
|
||||
}
|
||||
for (i = 0; i < nhuge; i++) {
|
||||
sz = get_huge_size(i);
|
||||
ptrs[nsmall + nlarge + i] = mallocx(sz, flags);
|
||||
assert_ptr_not_null(ptrs[i],
|
||||
"Unexpected mallocx(%zu, %#x) failure", sz, flags);
|
||||
}
|
||||
|
||||
tsdn = tsdn_fetch();
|
||||
|
||||
/* Verify allocations. */
|
||||
for (i = 0; i < nptrs; i++) {
|
||||
assert_zu_gt(ivsalloc(tsdn, ptrs[i], false), 0,
|
||||
"Allocation should have queryable size");
|
||||
}
|
||||
|
||||
/* Reset. */
|
||||
miblen = sizeof(mib)/sizeof(size_t);
|
||||
assert_d_eq(mallctlnametomib("arena.0.reset", mib, &miblen), 0,
|
||||
"Unexpected mallctlnametomib() failure");
|
||||
mib[1] = (size_t)arena_ind;
|
||||
assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
|
||||
"Unexpected mallctlbymib() failure");
|
||||
|
||||
/* Verify allocations no longer exist. */
|
||||
for (i = 0; i < nptrs; i++) {
|
||||
assert_zu_eq(ivsalloc(tsdn, ptrs[i], false), 0,
|
||||
"Allocation should no longer exist");
|
||||
}
|
||||
|
||||
free(ptrs);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
|
||||
return (test(
|
||||
test_arena_reset));
|
||||
}
|
||||
@@ -101,7 +101,7 @@ TEST_BEGIN(test_bitmap_sfu)
|
||||
bitmap_info_t binfo;
|
||||
bitmap_info_init(&binfo, i);
|
||||
{
|
||||
ssize_t j;
|
||||
size_t j;
|
||||
bitmap_t *bitmap = (bitmap_t *)malloc(
|
||||
bitmap_size(&binfo));
|
||||
bitmap_init(bitmap, &binfo);
|
||||
@@ -119,7 +119,7 @@ TEST_BEGIN(test_bitmap_sfu)
|
||||
* Iteratively unset bits starting at the end, and
|
||||
* verify that bitmap_sfu() reaches the unset bits.
|
||||
*/
|
||||
for (j = i - 1; j >= 0; j--) {
|
||||
for (j = i - 1; j < i; j--) { /* (i..0] */
|
||||
bitmap_unset(bitmap, &binfo, j);
|
||||
assert_zd_eq(bitmap_sfu(bitmap, &binfo), j,
|
||||
"First unset bit should the bit previously "
|
||||
|
||||
@@ -7,8 +7,8 @@ TEST_BEGIN(test_new_delete)
|
||||
|
||||
tsd = tsd_fetch();
|
||||
|
||||
assert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash, ckh_string_keycomp),
|
||||
"Unexpected ckh_new() error");
|
||||
assert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,
|
||||
ckh_string_keycomp), "Unexpected ckh_new() error");
|
||||
ckh_delete(tsd, &ckh);
|
||||
|
||||
assert_false(ckh_new(tsd, &ckh, 3, ckh_pointer_hash,
|
||||
@@ -32,8 +32,8 @@ TEST_BEGIN(test_count_insert_search_remove)
|
||||
|
||||
tsd = tsd_fetch();
|
||||
|
||||
assert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash, ckh_string_keycomp),
|
||||
"Unexpected ckh_new() error");
|
||||
assert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,
|
||||
ckh_string_keycomp), "Unexpected ckh_new() error");
|
||||
assert_zu_eq(ckh_count(&ckh), 0,
|
||||
"ckh_count() should return %zu, but it returned %zu", ZU(0),
|
||||
ckh_count(&ckh));
|
||||
|
||||
@@ -2,20 +2,28 @@
|
||||
|
||||
const char *malloc_conf = "purge:decay,decay_time:1";
|
||||
|
||||
static nstime_monotonic_t *nstime_monotonic_orig;
|
||||
static nstime_update_t *nstime_update_orig;
|
||||
|
||||
static unsigned nupdates_mock;
|
||||
static nstime_t time_mock;
|
||||
static bool nonmonotonic_mock;
|
||||
static bool monotonic_mock;
|
||||
|
||||
static bool
|
||||
nstime_monotonic_mock(void)
|
||||
{
|
||||
|
||||
return (monotonic_mock);
|
||||
}
|
||||
|
||||
static bool
|
||||
nstime_update_mock(nstime_t *time)
|
||||
{
|
||||
|
||||
nupdates_mock++;
|
||||
if (!nonmonotonic_mock)
|
||||
if (monotonic_mock)
|
||||
nstime_copy(time, &time_mock);
|
||||
return (nonmonotonic_mock);
|
||||
return (!monotonic_mock);
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_decay_ticks)
|
||||
@@ -245,9 +253,11 @@ TEST_BEGIN(test_decay_ticker)
|
||||
nupdates_mock = 0;
|
||||
nstime_init(&time_mock, 0);
|
||||
nstime_update(&time_mock);
|
||||
nonmonotonic_mock = false;
|
||||
monotonic_mock = true;
|
||||
|
||||
nstime_monotonic_orig = nstime_monotonic;
|
||||
nstime_update_orig = nstime_update;
|
||||
nstime_monotonic = nstime_monotonic_mock;
|
||||
nstime_update = nstime_update_mock;
|
||||
|
||||
for (i = 0; i < NPS; i++) {
|
||||
@@ -259,6 +269,7 @@ TEST_BEGIN(test_decay_ticker)
|
||||
"Expected nstime_update() to be called");
|
||||
}
|
||||
|
||||
nstime_monotonic = nstime_monotonic_orig;
|
||||
nstime_update = nstime_update_orig;
|
||||
|
||||
nstime_init(&time, 0);
|
||||
@@ -316,9 +327,11 @@ TEST_BEGIN(test_decay_nonmonotonic)
|
||||
nupdates_mock = 0;
|
||||
nstime_init(&time_mock, 0);
|
||||
nstime_update(&time_mock);
|
||||
nonmonotonic_mock = true;
|
||||
monotonic_mock = false;
|
||||
|
||||
nstime_monotonic_orig = nstime_monotonic;
|
||||
nstime_update_orig = nstime_update;
|
||||
nstime_monotonic = nstime_monotonic_mock;
|
||||
nstime_update = nstime_update_mock;
|
||||
|
||||
for (i = 0; i < NPS; i++) {
|
||||
@@ -342,8 +355,9 @@ TEST_BEGIN(test_decay_nonmonotonic)
|
||||
config_stats ? 0 : ENOENT, "Unexpected mallctl result");
|
||||
|
||||
if (config_stats)
|
||||
assert_u64_gt(npurge1, npurge0, "Expected purging to occur");
|
||||
assert_u64_eq(npurge0, npurge1, "Unexpected purging occurred");
|
||||
|
||||
nstime_monotonic = nstime_monotonic_orig;
|
||||
nstime_update = nstime_update_orig;
|
||||
#undef NPS
|
||||
}
|
||||
|
||||
64
test/unit/fork.c
Normal file
64
test/unit/fork.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
TEST_BEGIN(test_fork)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
void *p;
|
||||
pid_t pid;
|
||||
|
||||
p = malloc(1);
|
||||
assert_ptr_not_null(p, "Unexpected malloc() failure");
|
||||
|
||||
pid = fork();
|
||||
|
||||
free(p);
|
||||
|
||||
p = malloc(64);
|
||||
assert_ptr_not_null(p, "Unexpected malloc() failure");
|
||||
free(p);
|
||||
|
||||
if (pid == -1) {
|
||||
/* Error. */
|
||||
test_fail("Unexpected fork() failure");
|
||||
} else if (pid == 0) {
|
||||
/* Child. */
|
||||
_exit(0);
|
||||
} else {
|
||||
int status;
|
||||
|
||||
/* Parent. */
|
||||
while (true) {
|
||||
if (waitpid(pid, &status, 0) == -1)
|
||||
test_fail("Unexpected waitpid() failure");
|
||||
if (WIFSIGNALED(status)) {
|
||||
test_fail("Unexpected child termination due to "
|
||||
"signal %d", WTERMSIG(status));
|
||||
break;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
test_fail(
|
||||
"Unexpected child exit value %d",
|
||||
WEXITSTATUS(status));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
test_skip("fork(2) is irrelevant to Windows");
|
||||
#endif
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
|
||||
return (test(
|
||||
test_fork));
|
||||
}
|
||||
@@ -64,14 +64,15 @@ static void
|
||||
hash_variant_verify_key(hash_variant_t variant, uint8_t *key)
|
||||
{
|
||||
const int hashbytes = hash_variant_bits(variant) / 8;
|
||||
VARIABLE_ARRAY(uint8_t, hashes, hashbytes * 256);
|
||||
const int hashes_size = hashbytes * 256;
|
||||
VARIABLE_ARRAY(uint8_t, hashes, hashes_size);
|
||||
VARIABLE_ARRAY(uint8_t, final, hashbytes);
|
||||
unsigned i;
|
||||
uint32_t computed, expected;
|
||||
|
||||
memset(key, 0, KEY_SIZE);
|
||||
memset(hashes, 0, sizeof(hashes));
|
||||
memset(final, 0, sizeof(final));
|
||||
memset(hashes, 0, hashes_size);
|
||||
memset(final, 0, hashbytes);
|
||||
|
||||
/*
|
||||
* Hash keys of the form {0}, {0,1}, {0,1,2}, ..., {0,1,...,255} as the
|
||||
@@ -102,17 +103,17 @@ hash_variant_verify_key(hash_variant_t variant, uint8_t *key)
|
||||
/* Hash the result array. */
|
||||
switch (variant) {
|
||||
case hash_variant_x86_32: {
|
||||
uint32_t out = hash_x86_32(hashes, hashbytes*256, 0);
|
||||
uint32_t out = hash_x86_32(hashes, hashes_size, 0);
|
||||
memcpy(final, &out, sizeof(out));
|
||||
break;
|
||||
} case hash_variant_x86_128: {
|
||||
uint64_t out[2];
|
||||
hash_x86_128(hashes, hashbytes*256, 0, out);
|
||||
hash_x86_128(hashes, hashes_size, 0, out);
|
||||
memcpy(final, out, sizeof(out));
|
||||
break;
|
||||
} case hash_variant_x64_128: {
|
||||
uint64_t out[2];
|
||||
hash_x64_128(hashes, hashbytes*256, 0, out);
|
||||
hash_x64_128(hashes, hashes_size, 0, out);
|
||||
memcpy(final, out, sizeof(out));
|
||||
break;
|
||||
} default: not_reached();
|
||||
|
||||
@@ -29,7 +29,7 @@ arena_dalloc_junk_small_intercept(void *ptr, arena_bin_info_t *bin_info)
|
||||
|
||||
arena_dalloc_junk_small_orig(ptr, bin_info);
|
||||
for (i = 0; i < bin_info->reg_size; i++) {
|
||||
assert_c_eq(((char *)ptr)[i], 0x5a,
|
||||
assert_u_eq(((uint8_t *)ptr)[i], JEMALLOC_FREE_JUNK,
|
||||
"Missing junk fill for byte %zu/%zu of deallocated region",
|
||||
i, bin_info->reg_size);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ arena_dalloc_junk_large_intercept(void *ptr, size_t usize)
|
||||
|
||||
arena_dalloc_junk_large_orig(ptr, usize);
|
||||
for (i = 0; i < usize; i++) {
|
||||
assert_c_eq(((char *)ptr)[i], 0x5a,
|
||||
assert_u_eq(((uint8_t *)ptr)[i], JEMALLOC_FREE_JUNK,
|
||||
"Missing junk fill for byte %zu/%zu of deallocated region",
|
||||
i, usize);
|
||||
}
|
||||
@@ -69,7 +69,7 @@ huge_dalloc_junk_intercept(void *ptr, size_t usize)
|
||||
static void
|
||||
test_junk(size_t sz_min, size_t sz_max)
|
||||
{
|
||||
char *s;
|
||||
uint8_t *s;
|
||||
size_t sz_prev, sz, i;
|
||||
|
||||
if (opt_junk_free) {
|
||||
@@ -82,23 +82,23 @@ test_junk(size_t sz_min, size_t sz_max)
|
||||
}
|
||||
|
||||
sz_prev = 0;
|
||||
s = (char *)mallocx(sz_min, 0);
|
||||
s = (uint8_t *)mallocx(sz_min, 0);
|
||||
assert_ptr_not_null((void *)s, "Unexpected mallocx() failure");
|
||||
|
||||
for (sz = sallocx(s, 0); sz <= sz_max;
|
||||
sz_prev = sz, sz = sallocx(s, 0)) {
|
||||
if (sz_prev > 0) {
|
||||
assert_c_eq(s[0], 'a',
|
||||
assert_u_eq(s[0], 'a',
|
||||
"Previously allocated byte %zu/%zu is corrupted",
|
||||
ZU(0), sz_prev);
|
||||
assert_c_eq(s[sz_prev-1], 'a',
|
||||
assert_u_eq(s[sz_prev-1], 'a',
|
||||
"Previously allocated byte %zu/%zu is corrupted",
|
||||
sz_prev-1, sz_prev);
|
||||
}
|
||||
|
||||
for (i = sz_prev; i < sz; i++) {
|
||||
if (opt_junk_alloc) {
|
||||
assert_c_eq(s[i], 0xa5,
|
||||
assert_u_eq(s[i], JEMALLOC_ALLOC_JUNK,
|
||||
"Newly allocated byte %zu/%zu isn't "
|
||||
"junk-filled", i, sz);
|
||||
}
|
||||
@@ -107,7 +107,7 @@ test_junk(size_t sz_min, size_t sz_max)
|
||||
|
||||
if (xallocx(s, sz+1, 0, 0) == sz) {
|
||||
watch_junking(s);
|
||||
s = (char *)rallocx(s, sz+1, 0);
|
||||
s = (uint8_t *)rallocx(s, sz+1, 0);
|
||||
assert_ptr_not_null((void *)s,
|
||||
"Unexpected rallocx() failure");
|
||||
assert_true(!opt_junk_free || saw_junking,
|
||||
@@ -244,7 +244,6 @@ int
|
||||
main(void)
|
||||
{
|
||||
|
||||
assert(!config_fill || opt_junk_alloc || opt_junk_free);
|
||||
return (test(
|
||||
test_junk_small,
|
||||
test_junk_large,
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#define JEMALLOC_TEST_JUNK_OPT "junk:alloc"
|
||||
#define JEMALLOC_TEST_JUNK_OPT "junk:alloc"
|
||||
#include "junk.c"
|
||||
#undef JEMALLOC_TEST_JUNK_OPT
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#define JEMALLOC_TEST_JUNK_OPT "junk:free"
|
||||
#define JEMALLOC_TEST_JUNK_OPT "junk:free"
|
||||
#include "junk.c"
|
||||
#undef JEMALLOC_TEST_JUNK_OPT
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#ifdef __PGI
|
||||
#undef INFINITY
|
||||
#endif
|
||||
|
||||
#ifndef INFINITY
|
||||
#define INFINITY (DBL_MAX + DBL_MAX)
|
||||
#endif
|
||||
|
||||
@@ -176,6 +176,13 @@ TEST_BEGIN(test_nstime_divide)
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_nstime_monotonic)
|
||||
{
|
||||
|
||||
nstime_monotonic();
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_nstime_update)
|
||||
{
|
||||
nstime_t nst;
|
||||
@@ -198,7 +205,6 @@ TEST_BEGIN(test_nstime_update)
|
||||
assert_d_eq(nstime_compare(&nst, &nst0), 0,
|
||||
"Time should not have been modified");
|
||||
}
|
||||
|
||||
}
|
||||
TEST_END
|
||||
|
||||
@@ -216,5 +222,6 @@ main(void)
|
||||
test_nstime_imultiply,
|
||||
test_nstime_idivide,
|
||||
test_nstime_divide,
|
||||
test_nstime_monotonic,
|
||||
test_nstime_update));
|
||||
}
|
||||
|
||||
290
test/unit/ph.c
Normal file
290
test/unit/ph.c
Normal file
@@ -0,0 +1,290 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
typedef struct node_s node_t;
|
||||
|
||||
struct node_s {
|
||||
#define NODE_MAGIC 0x9823af7e
|
||||
uint32_t magic;
|
||||
phn(node_t) link;
|
||||
uint64_t key;
|
||||
};
|
||||
|
||||
static int
|
||||
node_cmp(const node_t *a, const node_t *b)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = (a->key > b->key) - (a->key < b->key);
|
||||
if (ret == 0) {
|
||||
/*
|
||||
* Duplicates are not allowed in the heap, so force an
|
||||
* arbitrary ordering for non-identical items with equal keys.
|
||||
*/
|
||||
ret = (((uintptr_t)a) > ((uintptr_t)b))
|
||||
- (((uintptr_t)a) < ((uintptr_t)b));
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
node_cmp_magic(const node_t *a, const node_t *b) {
|
||||
|
||||
assert_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
|
||||
assert_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
|
||||
|
||||
return (node_cmp(a, b));
|
||||
}
|
||||
|
||||
typedef ph(node_t) heap_t;
|
||||
ph_gen(static, heap_, heap_t, node_t, link, node_cmp_magic);
|
||||
|
||||
static void
|
||||
node_print(const node_t *node, unsigned depth)
|
||||
{
|
||||
unsigned i;
|
||||
node_t *leftmost_child, *sibling;
|
||||
|
||||
for (i = 0; i < depth; i++)
|
||||
malloc_printf("\t");
|
||||
malloc_printf("%2"FMTu64"\n", node->key);
|
||||
|
||||
leftmost_child = phn_lchild_get(node_t, link, node);
|
||||
if (leftmost_child == NULL)
|
||||
return;
|
||||
node_print(leftmost_child, depth + 1);
|
||||
|
||||
for (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=
|
||||
NULL; sibling = phn_next_get(node_t, link, sibling)) {
|
||||
node_print(sibling, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
heap_print(const heap_t *heap)
|
||||
{
|
||||
node_t *auxelm;
|
||||
|
||||
malloc_printf("vvv heap %p vvv\n", heap);
|
||||
if (heap->ph_root == NULL)
|
||||
goto label_return;
|
||||
|
||||
node_print(heap->ph_root, 0);
|
||||
|
||||
for (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;
|
||||
auxelm = phn_next_get(node_t, link, auxelm)) {
|
||||
assert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,
|
||||
link, auxelm)), auxelm,
|
||||
"auxelm's prev doesn't link to auxelm");
|
||||
node_print(auxelm, 0);
|
||||
}
|
||||
|
||||
label_return:
|
||||
malloc_printf("^^^ heap %p ^^^\n", heap);
|
||||
}
|
||||
|
||||
static unsigned
|
||||
node_validate(const node_t *node, const node_t *parent)
|
||||
{
|
||||
unsigned nnodes = 1;
|
||||
node_t *leftmost_child, *sibling;
|
||||
|
||||
if (parent != NULL) {
|
||||
assert_d_ge(node_cmp_magic(node, parent), 0,
|
||||
"Child is less than parent");
|
||||
}
|
||||
|
||||
leftmost_child = phn_lchild_get(node_t, link, node);
|
||||
if (leftmost_child == NULL)
|
||||
return (nnodes);
|
||||
assert_ptr_eq((void *)phn_prev_get(node_t, link, leftmost_child),
|
||||
(void *)node, "Leftmost child does not link to node");
|
||||
nnodes += node_validate(leftmost_child, node);
|
||||
|
||||
for (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=
|
||||
NULL; sibling = phn_next_get(node_t, link, sibling)) {
|
||||
assert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,
|
||||
link, sibling)), sibling,
|
||||
"sibling's prev doesn't link to sibling");
|
||||
nnodes += node_validate(sibling, node);
|
||||
}
|
||||
return (nnodes);
|
||||
}
|
||||
|
||||
static unsigned
|
||||
heap_validate(const heap_t *heap)
|
||||
{
|
||||
unsigned nnodes = 0;
|
||||
node_t *auxelm;
|
||||
|
||||
if (heap->ph_root == NULL)
|
||||
goto label_return;
|
||||
|
||||
nnodes += node_validate(heap->ph_root, NULL);
|
||||
|
||||
for (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;
|
||||
auxelm = phn_next_get(node_t, link, auxelm)) {
|
||||
assert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,
|
||||
link, auxelm)), auxelm,
|
||||
"auxelm's prev doesn't link to auxelm");
|
||||
nnodes += node_validate(auxelm, NULL);
|
||||
}
|
||||
|
||||
label_return:
|
||||
if (false)
|
||||
heap_print(heap);
|
||||
return (nnodes);
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_ph_empty)
|
||||
{
|
||||
heap_t heap;
|
||||
|
||||
heap_new(&heap);
|
||||
assert_true(heap_empty(&heap), "Heap should be empty");
|
||||
assert_ptr_null(heap_first(&heap), "Unexpected node");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
static void
|
||||
node_remove(heap_t *heap, node_t *node)
|
||||
{
|
||||
|
||||
heap_remove(heap, node);
|
||||
|
||||
node->magic = 0;
|
||||
}
|
||||
|
||||
static node_t *
|
||||
node_remove_first(heap_t *heap)
|
||||
{
|
||||
node_t *node = heap_remove_first(heap);
|
||||
node->magic = 0;
|
||||
return (node);
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_ph_random)
|
||||
{
|
||||
#define NNODES 25
|
||||
#define NBAGS 250
|
||||
#define SEED 42
|
||||
sfmt_t *sfmt;
|
||||
uint64_t bag[NNODES];
|
||||
heap_t heap;
|
||||
node_t nodes[NNODES];
|
||||
unsigned i, j, k;
|
||||
|
||||
sfmt = init_gen_rand(SEED);
|
||||
for (i = 0; i < NBAGS; i++) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* Insert in order. */
|
||||
for (j = 0; j < NNODES; j++)
|
||||
bag[j] = j;
|
||||
break;
|
||||
case 1:
|
||||
/* Insert in reverse order. */
|
||||
for (j = 0; j < NNODES; j++)
|
||||
bag[j] = NNODES - j - 1;
|
||||
break;
|
||||
default:
|
||||
for (j = 0; j < NNODES; j++)
|
||||
bag[j] = gen_rand64_range(sfmt, NNODES);
|
||||
}
|
||||
|
||||
for (j = 1; j <= NNODES; j++) {
|
||||
/* Initialize heap and nodes. */
|
||||
heap_new(&heap);
|
||||
assert_u_eq(heap_validate(&heap), 0,
|
||||
"Incorrect node count");
|
||||
for (k = 0; k < j; k++) {
|
||||
nodes[k].magic = NODE_MAGIC;
|
||||
nodes[k].key = bag[k];
|
||||
}
|
||||
|
||||
/* Insert nodes. */
|
||||
for (k = 0; k < j; k++) {
|
||||
heap_insert(&heap, &nodes[k]);
|
||||
if (i % 13 == 12) {
|
||||
/* Trigger merging. */
|
||||
assert_ptr_not_null(heap_first(&heap),
|
||||
"Heap should not be empty");
|
||||
}
|
||||
assert_u_eq(heap_validate(&heap), k + 1,
|
||||
"Incorrect node count");
|
||||
}
|
||||
|
||||
assert_false(heap_empty(&heap),
|
||||
"Heap should not be empty");
|
||||
|
||||
/* Remove nodes. */
|
||||
switch (i % 4) {
|
||||
case 0:
|
||||
for (k = 0; k < j; k++) {
|
||||
assert_u_eq(heap_validate(&heap), j - k,
|
||||
"Incorrect node count");
|
||||
node_remove(&heap, &nodes[k]);
|
||||
assert_u_eq(heap_validate(&heap), j - k
|
||||
- 1, "Incorrect node count");
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
for (k = j; k > 0; k--) {
|
||||
node_remove(&heap, &nodes[k-1]);
|
||||
assert_u_eq(heap_validate(&heap), k - 1,
|
||||
"Incorrect node count");
|
||||
}
|
||||
break;
|
||||
case 2: {
|
||||
node_t *prev = NULL;
|
||||
for (k = 0; k < j; k++) {
|
||||
node_t *node = node_remove_first(&heap);
|
||||
assert_u_eq(heap_validate(&heap), j - k
|
||||
- 1, "Incorrect node count");
|
||||
if (prev != NULL) {
|
||||
assert_d_ge(node_cmp(node,
|
||||
prev), 0,
|
||||
"Bad removal order");
|
||||
}
|
||||
prev = node;
|
||||
}
|
||||
break;
|
||||
} case 3: {
|
||||
node_t *prev = NULL;
|
||||
for (k = 0; k < j; k++) {
|
||||
node_t *node = heap_first(&heap);
|
||||
assert_u_eq(heap_validate(&heap), j - k,
|
||||
"Incorrect node count");
|
||||
if (prev != NULL) {
|
||||
assert_d_ge(node_cmp(node,
|
||||
prev), 0,
|
||||
"Bad removal order");
|
||||
}
|
||||
node_remove(&heap, node);
|
||||
assert_u_eq(heap_validate(&heap), j - k
|
||||
- 1, "Incorrect node count");
|
||||
prev = node;
|
||||
}
|
||||
break;
|
||||
} default:
|
||||
not_reached();
|
||||
}
|
||||
|
||||
assert_ptr_null(heap_first(&heap),
|
||||
"Heap should be empty");
|
||||
assert_true(heap_empty(&heap), "Heap should be empty");
|
||||
}
|
||||
}
|
||||
fini_gen_rand(sfmt);
|
||||
#undef NNODES
|
||||
#undef SEED
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
|
||||
return (test(
|
||||
test_ph_empty,
|
||||
test_ph_random));
|
||||
}
|
||||
219
test/unit/prng.c
219
test/unit/prng.c
@@ -1,33 +1,71 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
TEST_BEGIN(test_prng_lg_range)
|
||||
static void
|
||||
test_prng_lg_range_u32(bool atomic)
|
||||
{
|
||||
uint32_t sa, sb, ra, rb;
|
||||
unsigned lg_range;
|
||||
|
||||
sa = 42;
|
||||
ra = prng_lg_range_u32(&sa, 32, atomic);
|
||||
sa = 42;
|
||||
rb = prng_lg_range_u32(&sa, 32, atomic);
|
||||
assert_u32_eq(ra, rb,
|
||||
"Repeated generation should produce repeated results");
|
||||
|
||||
sb = 42;
|
||||
rb = prng_lg_range_u32(&sb, 32, atomic);
|
||||
assert_u32_eq(ra, rb,
|
||||
"Equivalent generation should produce equivalent results");
|
||||
|
||||
sa = 42;
|
||||
ra = prng_lg_range_u32(&sa, 32, atomic);
|
||||
rb = prng_lg_range_u32(&sa, 32, atomic);
|
||||
assert_u32_ne(ra, rb,
|
||||
"Full-width results must not immediately repeat");
|
||||
|
||||
sa = 42;
|
||||
ra = prng_lg_range_u32(&sa, 32, atomic);
|
||||
for (lg_range = 31; lg_range > 0; lg_range--) {
|
||||
sb = 42;
|
||||
rb = prng_lg_range_u32(&sb, lg_range, atomic);
|
||||
assert_u32_eq((rb & (UINT32_C(0xffffffff) << lg_range)),
|
||||
0, "High order bits should be 0, lg_range=%u", lg_range);
|
||||
assert_u32_eq(rb, (ra >> (32 - lg_range)),
|
||||
"Expected high order bits of full-width result, "
|
||||
"lg_range=%u", lg_range);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_prng_lg_range_u64(void)
|
||||
{
|
||||
uint64_t sa, sb, ra, rb;
|
||||
unsigned lg_range;
|
||||
|
||||
sa = 42;
|
||||
ra = prng_lg_range(&sa, 64);
|
||||
ra = prng_lg_range_u64(&sa, 64);
|
||||
sa = 42;
|
||||
rb = prng_lg_range(&sa, 64);
|
||||
rb = prng_lg_range_u64(&sa, 64);
|
||||
assert_u64_eq(ra, rb,
|
||||
"Repeated generation should produce repeated results");
|
||||
|
||||
sb = 42;
|
||||
rb = prng_lg_range(&sb, 64);
|
||||
rb = prng_lg_range_u64(&sb, 64);
|
||||
assert_u64_eq(ra, rb,
|
||||
"Equivalent generation should produce equivalent results");
|
||||
|
||||
sa = 42;
|
||||
ra = prng_lg_range(&sa, 64);
|
||||
rb = prng_lg_range(&sa, 64);
|
||||
ra = prng_lg_range_u64(&sa, 64);
|
||||
rb = prng_lg_range_u64(&sa, 64);
|
||||
assert_u64_ne(ra, rb,
|
||||
"Full-width results must not immediately repeat");
|
||||
|
||||
sa = 42;
|
||||
ra = prng_lg_range(&sa, 64);
|
||||
ra = prng_lg_range_u64(&sa, 64);
|
||||
for (lg_range = 63; lg_range > 0; lg_range--) {
|
||||
sb = 42;
|
||||
rb = prng_lg_range(&sb, lg_range);
|
||||
rb = prng_lg_range_u64(&sb, lg_range);
|
||||
assert_u64_eq((rb & (UINT64_C(0xffffffffffffffff) << lg_range)),
|
||||
0, "High order bits should be 0, lg_range=%u", lg_range);
|
||||
assert_u64_eq(rb, (ra >> (64 - lg_range)),
|
||||
@@ -35,9 +73,103 @@ TEST_BEGIN(test_prng_lg_range)
|
||||
"lg_range=%u", lg_range);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_prng_lg_range_zu(bool atomic)
|
||||
{
|
||||
size_t sa, sb, ra, rb;
|
||||
unsigned lg_range;
|
||||
|
||||
sa = 42;
|
||||
ra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
|
||||
sa = 42;
|
||||
rb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
|
||||
assert_zu_eq(ra, rb,
|
||||
"Repeated generation should produce repeated results");
|
||||
|
||||
sb = 42;
|
||||
rb = prng_lg_range_zu(&sb, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
|
||||
assert_zu_eq(ra, rb,
|
||||
"Equivalent generation should produce equivalent results");
|
||||
|
||||
sa = 42;
|
||||
ra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
|
||||
rb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
|
||||
assert_zu_ne(ra, rb,
|
||||
"Full-width results must not immediately repeat");
|
||||
|
||||
sa = 42;
|
||||
ra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
|
||||
for (lg_range = (ZU(1) << (3 + LG_SIZEOF_PTR)) - 1; lg_range > 0;
|
||||
lg_range--) {
|
||||
sb = 42;
|
||||
rb = prng_lg_range_zu(&sb, lg_range, atomic);
|
||||
assert_zu_eq((rb & (SIZE_T_MAX << lg_range)),
|
||||
0, "High order bits should be 0, lg_range=%u", lg_range);
|
||||
assert_zu_eq(rb, (ra >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) -
|
||||
lg_range)), "Expected high order bits of full-width "
|
||||
"result, lg_range=%u", lg_range);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_prng_lg_range_u32_nonatomic)
|
||||
{
|
||||
|
||||
test_prng_lg_range_u32(false);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_prng_range)
|
||||
TEST_BEGIN(test_prng_lg_range_u32_atomic)
|
||||
{
|
||||
|
||||
test_prng_lg_range_u32(true);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_prng_lg_range_u64_nonatomic)
|
||||
{
|
||||
|
||||
test_prng_lg_range_u64();
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_prng_lg_range_zu_nonatomic)
|
||||
{
|
||||
|
||||
test_prng_lg_range_zu(false);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_prng_lg_range_zu_atomic)
|
||||
{
|
||||
|
||||
test_prng_lg_range_zu(true);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
static void
|
||||
test_prng_range_u32(bool atomic)
|
||||
{
|
||||
uint32_t range;
|
||||
#define MAX_RANGE 10000000
|
||||
#define RANGE_STEP 97
|
||||
#define NREPS 10
|
||||
|
||||
for (range = 2; range < MAX_RANGE; range += RANGE_STEP) {
|
||||
uint32_t s;
|
||||
unsigned rep;
|
||||
|
||||
s = range;
|
||||
for (rep = 0; rep < NREPS; rep++) {
|
||||
uint32_t r = prng_range_u32(&s, range, atomic);
|
||||
|
||||
assert_u32_lt(r, range, "Out of range");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_prng_range_u64(void)
|
||||
{
|
||||
uint64_t range;
|
||||
#define MAX_RANGE 10000000
|
||||
@@ -50,12 +182,67 @@ TEST_BEGIN(test_prng_range)
|
||||
|
||||
s = range;
|
||||
for (rep = 0; rep < NREPS; rep++) {
|
||||
uint64_t r = prng_range(&s, range);
|
||||
uint64_t r = prng_range_u64(&s, range);
|
||||
|
||||
assert_u64_lt(r, range, "Out of range");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_prng_range_zu(bool atomic)
|
||||
{
|
||||
size_t range;
|
||||
#define MAX_RANGE 10000000
|
||||
#define RANGE_STEP 97
|
||||
#define NREPS 10
|
||||
|
||||
for (range = 2; range < MAX_RANGE; range += RANGE_STEP) {
|
||||
size_t s;
|
||||
unsigned rep;
|
||||
|
||||
s = range;
|
||||
for (rep = 0; rep < NREPS; rep++) {
|
||||
size_t r = prng_range_zu(&s, range, atomic);
|
||||
|
||||
assert_zu_lt(r, range, "Out of range");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_prng_range_u32_nonatomic)
|
||||
{
|
||||
|
||||
test_prng_range_u32(false);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_prng_range_u32_atomic)
|
||||
{
|
||||
|
||||
test_prng_range_u32(true);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_prng_range_u64_nonatomic)
|
||||
{
|
||||
|
||||
test_prng_range_u64();
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_prng_range_zu_nonatomic)
|
||||
{
|
||||
|
||||
test_prng_range_zu(false);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_prng_range_zu_atomic)
|
||||
{
|
||||
|
||||
test_prng_range_zu(true);
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
@@ -63,6 +250,14 @@ main(void)
|
||||
{
|
||||
|
||||
return (test(
|
||||
test_prng_lg_range,
|
||||
test_prng_range));
|
||||
test_prng_lg_range_u32_nonatomic,
|
||||
test_prng_lg_range_u32_atomic,
|
||||
test_prng_lg_range_u64_nonatomic,
|
||||
test_prng_lg_range_zu_nonatomic,
|
||||
test_prng_lg_range_zu_atomic,
|
||||
test_prng_range_u32_nonatomic,
|
||||
test_prng_range_u32_atomic,
|
||||
test_prng_range_u64_nonatomic,
|
||||
test_prng_range_zu_nonatomic,
|
||||
test_prng_range_zu_atomic));
|
||||
}
|
||||
|
||||
@@ -94,7 +94,8 @@ TEST_END
|
||||
bool prof_dump_header_intercepted = false;
|
||||
prof_cnt_t cnt_all_copy = {0, 0, 0, 0};
|
||||
static bool
|
||||
prof_dump_header_intercept(bool propagate_err, const prof_cnt_t *cnt_all)
|
||||
prof_dump_header_intercept(tsdn_t *tsdn, bool propagate_err,
|
||||
const prof_cnt_t *cnt_all)
|
||||
{
|
||||
|
||||
prof_dump_header_intercepted = true;
|
||||
|
||||
@@ -111,7 +111,7 @@ TEST_BEGIN(test_monotonic)
|
||||
|
||||
floor_prev = 0;
|
||||
ceil_prev = 0;
|
||||
for (i = 1; i < run_quantize_max >> LG_PAGE; i++) {
|
||||
for (i = 1; i <= chunksize >> LG_PAGE; i++) {
|
||||
size_t run_size, floor, ceil;
|
||||
|
||||
run_size = i << LG_PAGE;
|
||||
|
||||
@@ -80,25 +80,96 @@ TEST_BEGIN(test_size_classes)
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_psize_classes)
|
||||
{
|
||||
size_t size_class, max_size_class;
|
||||
pszind_t pind, max_pind;
|
||||
|
||||
max_size_class = get_max_size_class();
|
||||
max_pind = psz2ind(max_size_class);
|
||||
|
||||
for (pind = 0, size_class = pind2sz(pind); pind < max_pind ||
|
||||
size_class < max_size_class; pind++, size_class =
|
||||
pind2sz(pind)) {
|
||||
assert_true(pind < max_pind,
|
||||
"Loop conditionals should be equivalent; pind=%u, "
|
||||
"size_class=%zu (%#zx)", pind, size_class, size_class);
|
||||
assert_true(size_class < max_size_class,
|
||||
"Loop conditionals should be equivalent; pind=%u, "
|
||||
"size_class=%zu (%#zx)", pind, size_class, size_class);
|
||||
|
||||
assert_u_eq(pind, psz2ind(size_class),
|
||||
"psz2ind() does not reverse pind2sz(): pind=%u -->"
|
||||
" size_class=%zu --> pind=%u --> size_class=%zu", pind,
|
||||
size_class, psz2ind(size_class),
|
||||
pind2sz(psz2ind(size_class)));
|
||||
assert_zu_eq(size_class, pind2sz(psz2ind(size_class)),
|
||||
"pind2sz() does not reverse psz2ind(): pind=%u -->"
|
||||
" size_class=%zu --> pind=%u --> size_class=%zu", pind,
|
||||
size_class, psz2ind(size_class),
|
||||
pind2sz(psz2ind(size_class)));
|
||||
|
||||
assert_u_eq(pind+1, psz2ind(size_class+1),
|
||||
"Next size_class does not round up properly");
|
||||
|
||||
assert_zu_eq(size_class, (pind > 0) ?
|
||||
psz2u(pind2sz(pind-1)+1) : psz2u(1),
|
||||
"psz2u() does not round up to size class");
|
||||
assert_zu_eq(size_class, psz2u(size_class-1),
|
||||
"psz2u() does not round up to size class");
|
||||
assert_zu_eq(size_class, psz2u(size_class),
|
||||
"psz2u() does not compute same size class");
|
||||
assert_zu_eq(psz2u(size_class+1), pind2sz(pind+1),
|
||||
"psz2u() does not round up to next size class");
|
||||
}
|
||||
|
||||
assert_u_eq(pind, psz2ind(pind2sz(pind)),
|
||||
"psz2ind() does not reverse pind2sz()");
|
||||
assert_zu_eq(max_size_class, pind2sz(psz2ind(max_size_class)),
|
||||
"pind2sz() does not reverse psz2ind()");
|
||||
|
||||
assert_zu_eq(size_class, psz2u(pind2sz(pind-1)+1),
|
||||
"psz2u() does not round up to size class");
|
||||
assert_zu_eq(size_class, psz2u(size_class-1),
|
||||
"psz2u() does not round up to size class");
|
||||
assert_zu_eq(size_class, psz2u(size_class),
|
||||
"psz2u() does not compute same size class");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_overflow)
|
||||
{
|
||||
size_t max_size_class;
|
||||
|
||||
max_size_class = get_max_size_class();
|
||||
|
||||
assert_u_ge(size2index(max_size_class+1), NSIZES,
|
||||
"size2index() should return >= NSIZES on overflow");
|
||||
assert_u_ge(size2index(ZU(PTRDIFF_MAX)+1), NSIZES,
|
||||
"size2index() should return >= NSIZES on overflow");
|
||||
assert_u_ge(size2index(SIZE_T_MAX), NSIZES,
|
||||
"size2index() should return >= NSIZES on overflow");
|
||||
assert_u_eq(size2index(max_size_class+1), NSIZES,
|
||||
"size2index() should return NSIZES on overflow");
|
||||
assert_u_eq(size2index(ZU(PTRDIFF_MAX)+1), NSIZES,
|
||||
"size2index() should return NSIZES on overflow");
|
||||
assert_u_eq(size2index(SIZE_T_MAX), NSIZES,
|
||||
"size2index() should return NSIZES on overflow");
|
||||
|
||||
assert_zu_gt(s2u(max_size_class+1), HUGE_MAXCLASS,
|
||||
"s2u() should return > HUGE_MAXCLASS for unsupported size");
|
||||
assert_zu_gt(s2u(ZU(PTRDIFF_MAX)+1), HUGE_MAXCLASS,
|
||||
"s2u() should return > HUGE_MAXCLASS for unsupported size");
|
||||
assert_zu_eq(s2u(max_size_class+1), 0,
|
||||
"s2u() should return 0 for unsupported size");
|
||||
assert_zu_eq(s2u(ZU(PTRDIFF_MAX)+1), 0,
|
||||
"s2u() should return 0 for unsupported size");
|
||||
assert_zu_eq(s2u(SIZE_T_MAX), 0,
|
||||
"s2u() should return 0 on overflow");
|
||||
|
||||
assert_u_eq(psz2ind(max_size_class+1), NPSIZES,
|
||||
"psz2ind() should return NPSIZES on overflow");
|
||||
assert_u_eq(psz2ind(ZU(PTRDIFF_MAX)+1), NPSIZES,
|
||||
"psz2ind() should return NPSIZES on overflow");
|
||||
assert_u_eq(psz2ind(SIZE_T_MAX), NPSIZES,
|
||||
"psz2ind() should return NPSIZES on overflow");
|
||||
|
||||
assert_zu_eq(psz2u(max_size_class+1), 0,
|
||||
"psz2u() should return 0 for unsupported size");
|
||||
assert_zu_eq(psz2u(ZU(PTRDIFF_MAX)+1), 0,
|
||||
"psz2u() should return 0 for unsupported size");
|
||||
assert_zu_eq(psz2u(SIZE_T_MAX), 0,
|
||||
"psz2u() should return 0 on overflow");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
@@ -108,5 +179,6 @@ main(void)
|
||||
|
||||
return (test(
|
||||
test_size_classes,
|
||||
test_psize_classes,
|
||||
test_overflow));
|
||||
}
|
||||
|
||||
@@ -220,11 +220,11 @@ TEST_BEGIN(test_stats_arenas_large)
|
||||
if (config_stats) {
|
||||
assert_zu_gt(allocated, 0,
|
||||
"allocated should be greater than zero");
|
||||
assert_zu_gt(nmalloc, 0,
|
||||
assert_u64_gt(nmalloc, 0,
|
||||
"nmalloc should be greater than zero");
|
||||
assert_zu_ge(nmalloc, ndalloc,
|
||||
assert_u64_ge(nmalloc, ndalloc,
|
||||
"nmalloc should be at least as large as ndalloc");
|
||||
assert_zu_gt(nrequests, 0,
|
||||
assert_u64_gt(nrequests, 0,
|
||||
"nrequests should be greater than zero");
|
||||
}
|
||||
|
||||
@@ -262,9 +262,9 @@ TEST_BEGIN(test_stats_arenas_huge)
|
||||
if (config_stats) {
|
||||
assert_zu_gt(allocated, 0,
|
||||
"allocated should be greater than zero");
|
||||
assert_zu_gt(nmalloc, 0,
|
||||
assert_u64_gt(nmalloc, 0,
|
||||
"nmalloc should be greater than zero");
|
||||
assert_zu_ge(nmalloc, ndalloc,
|
||||
assert_u64_ge(nmalloc, ndalloc,
|
||||
"nmalloc should be at least as large as ndalloc");
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user